Validation
Structural deal validation, soft save warnings and the save decision, with the complete code registry.
Import from @workspace/bridge/validation. Pure TypeScript, synchronous, usable from React hooks and from the server alike. The engine supplies the variant-specific queries.
validateDeal
validateDeal(input: DealValidationInput): DealValidationResult
Runs the structural rule set. Hand checks always apply; the auction rules dispatch on the engine's input axis (engine.ctx.auctionInput), never on the mode name.
DealValidationInput
Prop
Type
| Code | When checked | Condition | params |
|---|---|---|---|
HANDS_INCOMPLETE | Always | A hand does not have the variant's card count | { players } |
DUPLICATE_CARDS | Always | A card appears in more than one hand | { cards } |
CONTRACT_MISSING | sequence or contract axis | No contract can be derived from the auction | |
DECLARER_NOT_SOUTH | sequence or contract axis | The derived declarer is not South | { declarer } |
TRUMP_MISSING | trump axis | No denomination was authored |
const engine = createBridgeEngine(createBridgeContext({ teachingMode: "auction" }));
const structural: DealValidationResult = validateDeal({
hands: createHands({ N: "AKQ2.KQ3.A54.K32", S: "7654.A87.T98.AT8" }),
auction: new AuctionBuilder().fromCompact("S / 1NT pass 3NT AP").build(),
engine,
});
structural.isValid; // true: complete hands, 3NT by SouthdecideSave
decideSave<E, Code>(structural: { isValid; errors: E[] }, warningChecks?: readonly WarningCheck<Code>[]): SaveDecision<E, Code>
Combines a structural result with soft warning checks into one outcome.
| Status | Meaning | Payload |
|---|---|---|
blocked | Structural result invalid, do not save | errors: E[] |
confirm-required | At least one check fired, ask before saving | warnings, a non-empty tuple in registration order |
ready | Save now |
A WarningCheck<Code> is () => SaveWarning<Code> | null. Checks are closures: resolve async data first, then build the check.
createParWarningCheck
createParWarningCheck(input: ParWarningInput): WarningCheck<ParWarningCode>
Fires EXPECTED_OUTSIDE_PAR when the chosen expected result is unreachable for the player's partnership, using the same NS-frame predicate as the result selector. Any null input makes the check a no-op. params carries objective and max, both NS-frame.
ParWarningInput
Prop
Type
const hint: NSParHint = { tricks: 9, partnership: "NS" };
const decision: SaveDecision<ValidationError, typeof EXPECTED_OUTSIDE_PAR_CODE> = decideSave(
structural,
[createParWarningCheck({ expectedTricks: 10, hint })],
);
switch (decision.status) {
case "blocked":
decision.errors; // ValidationError[]
break;
case "confirm-required":
decision.warnings[0].code; // "EXPECTED_OUTSIDE_PAR"
decision.warnings[0].params; // { objective: 10, max: 9 }
break;
case "ready":
break;
}Codes
VALIDATION_ERROR_CODE
Terminal codes, they block the save. ValidationErrorCode is their union.
| Key | Value |
|---|---|
HANDS_INCOMPLETE | "HANDS_INCOMPLETE" |
DUPLICATE_CARDS | "DUPLICATE_CARDS" |
CONTRACT_MISSING | "CONTRACT_MISSING" |
TRUMP_MISSING | "TRUMP_MISSING" |
DECLARER_NOT_SOUTH | "DECLARER_NOT_SOUTH" |
WARNING_CODE
Soft codes, they require confirmation. WarningCode is their union.
| Key | Value |
|---|---|
EXPECTED_OUTSIDE_PAR | "EXPECTED_OUTSIDE_PAR" |
EXPECTED_OUTSIDE_PAR_CODE is an alias for WARNING_CODE.EXPECTED_OUTSIDE_PAR, and ParWarningCode is its type.
Types
ValidationError
Prop
Type
DealValidationResult
Prop
Type
SaveWarning
Generic over Code extends string, defaulting to WarningCode.
Prop
Type
SaveDecision
SaveDecision<E, Code> is the union { status: "blocked"; errors: E[] } | { status: "ready" } | { status: "confirm-required"; warnings: [SaveWarning<Code>, ...SaveWarning<Code>[]] }.