Errors
Structured core errors, their factories, validation result shapes, the notation error thrown by the card and bid converters, and the PBN error classes.
Import from @workspace/bridge/errors. Core errors are plain objects discriminated by type, decoupled from translation keys. InvalidNotationError is the one class that carries such an object. PBN errors are classes extending DisplayableError from @workspace/shared/errors.
Base shape
ErrorSeverity is "error" | "warning" | "info". Every core error extends BaseError<TContext> and adds a literal type.
Prop
Type
Hands errors
Union HandsValidationError.
| Type | type literal | Context |
|---|---|---|
IncompleteHandsError | INCOMPLETE_HANDS | { incompletePlayers: Player[] } |
TooManyCardsError | TOO_MANY_CARDS_HAND | { tooManyPlayers: Player[] } |
DuplicateCardError | DUPLICATE_CARD | { card: string } |
InvalidCardRankError | INVALID_CARD_RANK | { player, suit, card, validRanks, line? } |
Auction errors
Union AuctionValidationError.
| Type | type literal | Context |
|---|---|---|
AuctionNotUsedError | AUCTION_NOT_USED | { mode: string } |
AuctionRequiredError | AUCTION_REQUIRED | { mode: string } |
IncompleteAuctionError | INCOMPLETE_AUCTION | none |
NoContractError | NO_CONTRACT | none |
ContractCalculationError | CONTRACT_CALCULATION_ERROR | { error: string } |
Play errors
Union PlayValidationError. It includes InvalidCardRankError, shared with the hands union.
| Type | type literal | Context |
|---|---|---|
CardNotOwnedError | CARD_NOT_OWNED | { card, player, line } |
RevokeError | REVOKE | { card, player, line } |
TooManyCardsInLineError | TOO_MANY_CARDS_HAND_IN_LINE | { line } |
InvalidPlayFormatError | INVALID_PLAY_FORMAT | { line } |
InvalidCardRankError | INVALID_CARD_RANK | { player, suit, card, validRanks, line? } |
General errors
Union GeneralValidationError.
| Type | type literal | Context |
|---|---|---|
PayloadValidationError | PAYLOAD_VALIDATION_ERROR | { message } |
ZodValidationError | ZOD_VALIDATION_ERROR | { code, message } |
CoreError is the union of all four groups. Narrow on error.type.
Notation errors
Union NotationValidationError, kept out of CoreError: these describe a value that is not a card or a bid at all, not a rule broken by a deal.
| Type | type literal | Context |
|---|---|---|
InvalidCardError | INVALID_CARD | { input: string } |
InvalidBidError | INVALID_BID | { input: string } |
toCard and toBid throw them wrapped in InvalidNotationError, an Error whose detail field holds the plain object. The parse* functions never throw; reach for them when the input is untrusted.
const invalidCard = createNotationError.invalidCard("H1");
// => { type: "INVALID_CARD", severity: "error", context: { input: "H1" } }
function readCard(read: () => Card): Card | null {
try {
return read();
} catch (error) {
if (error instanceof InvalidNotationError) {
error.detail.type; // "INVALID_CARD" | "INVALID_BID"
return null;
}
throw error;
}
}Factories
Five namespaced objects build errors with the right severity, field default and path. All field parameters are optional and default to the group's field ("hands", "auction.moves", "payload").
| Factory | Method | Signature |
|---|---|---|
createHandsError | incompleteHands | (incompletePlayers: Player[], field?) => IncompleteHandsError |
tooManyCards | (tooManyPlayers: Player[], field?) => TooManyCardsError | |
duplicateCard | (card: string, field?) => DuplicateCardError | |
invalidCardRank | (player, suit, card: Rank, validRanks: Rank[], field?) => InvalidCardRankError | |
createAuctionError | auctionNotUsed | (mode: string, field?) => AuctionNotUsedError, severity warning |
auctionRequired | (mode: string, field?) => AuctionRequiredError | |
incompleteAuction | (field?) => IncompleteAuctionError | |
noContract | (field?) => NoContractError | |
contractCalculationError | (error: string, field?) => ContractCalculationError | |
createPlayError | cardNotOwned | (card: string, player, line: number) => CardNotOwnedError |
revoke | (card: string, player, line: number) => RevokeError | |
tooManyCardsInLine | (line: number) => TooManyCardsInLineError | |
invalidPlayFormat | (line: number) => InvalidPlayFormatError | |
invalidCardRank | (player, suit, card: Rank, validRanks: Rank[], line: number) => InvalidCardRankError | |
createGeneralError | payloadValidation | (message: string, field?) => PayloadValidationError |
zodValidation | (code: string, message: string, field?, path?: (string | number)[]) => ZodValidationError | |
createNotationError | invalidCard | (input: string) => InvalidCardError |
invalidBid | (input: string) => InvalidBidError |
const incomplete = createHandsError.incompleteHands(["N", "S"]);
// => { type: "INCOMPLETE_HANDS", severity: "error", field: "hands", path: ["hands"], context: { incompletePlayers: ["N", "S"] } }
const notUsed = createAuctionError.auctionNotUsed("no-auction");
// severity: "warning"
const revoke = createPlayError.revoke("H2", "E", 3);
const zod = createGeneralError.zodValidation("invalid_type", "Expected string", undefined, ["metadata", "board"]);
// field: "metadata.board"Validation results
CoreValidationResult
Prop
Type
CorePlayValidationResult
Prop
Type
const result: CoreValidationResult = {
isValid: false,
errors: [incomplete, revoke],
warnings: [notUsed],
infos: [],
};
function describe(error: CoreError): string {
switch (error.type) {
case "INCOMPLETE_HANDS":
return `Incomplete: ${error.context?.incompletePlayers.join(", ")}`;
case "REVOKE":
return `Revoke by ${error.context?.player} at line ${error.context?.line}`;
default:
return error.type;
}
}PBN errors
Values and types
| Export | Definition |
|---|---|
SUPPORTED_ENCODINGS | ["UTF-8", "ISO-8859-1", "windows-1252", "UTF-16LE", "UTF-16BE"] |
SupportedEncoding | Union of the tuple above |
PBNErrorCode | "MAX_FILE_SIZE_EXCEEDED" | "ENCODING_MISMATCH" | "TOO_MANY_DEALS" | "PARSE_ERROR" |
PBNErrorDetails | { type: "encoding-mismatch"; expected; detected } | { type: "too-many-deals"; max; actual } | { type: "parse-error"; message } |
PBNError
Callback-friendly shape produced by toPBNError.
Prop
Type
Classes
| Class | Extends | Constructor | Fields |
|---|---|---|---|
PBNParseError | DisplayableError | (message, options?: { cause? }) | |
PBNEncodingMismatchError | PBNParseError | (expectedEncoding: SupportedEncoding, detectedEncoding: string) | expectedEncoding, detectedEncoding |
PBNTooManyDealsError | PBNParseError | (maxDeals: number, dealCount: number) | maxDeals, dealCount |
PNBAggregateError | DisplayableError | (message, details: PBNErrorDetails, options?: { cause? }) | details |
MaxFileSizeError | DisplayableError | (maxSize: number, actualSize: number) | type, maxSize, actualSize |
The aggregate class is spelled PNBAggregateError, with N before B. That is the exported name.
toPBNError
toPBNError(error: MaxFileSizeError | PNBAggregateError | PBNTooManyDealsError | PBNParseError | Error): PBNError
| Instance | Code |
|---|---|
MaxFileSizeError | MAX_FILE_SIZE_EXCEEDED |
PNBAggregateError | Mapped from details.type |
PBNTooManyDealsError | TOO_MANY_DEALS |
Anything else, including PBNEncodingMismatchError | PARSE_ERROR |
const tooBig = new MaxFileSizeError(10, 12);
const mismatch = new PBNEncodingMismatchError("UTF-8", "windows-1252");
const aggregate = new PNBAggregateError("Encoding mismatch", {
type: "encoding-mismatch",
expected: "UTF-8",
detected: "windows-1252",
});
const callbackErrors: PBNError[] = [tooBig, mismatch, aggregate].map(toPBNError);
// codes: "MAX_FILE_SIZE_EXCEEDED", "PARSE_ERROR", "ENCODING_MISMATCH"
SUPPORTED_ENCODINGS.includes("UTF-16LE"); // true