(name, selector, has)
| 539 | // selector has distinct values and at most one default, a structural selector |
| 540 | // dispatches on something. Keeps a malformed selector from shipping silently. |
| 541 | function checkSelector(name, selector, has) { |
| 542 | const errors = [] |
| 543 | if (!selector) return [`${name}: union has no selector`] |
| 544 | if (selector.correlated) return [] // resolved by request id, not the payload — nothing to dispatch |
| 545 | if (selector.by) { |
| 546 | const values = selector.variants.map((v) => JSON.stringify(v.value)) |
| 547 | if (new Set(values).size !== values.length) errors.push(`${name}: selector has duplicate discriminator values`) |
| 548 | for (const v of selector.variants) |
| 549 | if (!has(v.ref)) errors.push(`${name}: selector variant ${v.ref} does not resolve`) |
| 550 | if (selector.default && !has(selector.default)) |
| 551 | errors.push(`${name}: selector default ${selector.default} does not resolve`) |
| 552 | } else if (selector.ordered) { |
| 553 | // A structural selector must actually dispatch from the payload: every arm needs |
| 554 | // a distinguishing required field, and no arm's `requires` may be a subset of a |
| 555 | // later arm's — that would shadow the later arm under first-match. A union that |
| 556 | // cannot satisfy this is not payload-dispatchable and must be `correlated`. |
| 557 | selector.ordered.forEach((v, i) => { |
| 558 | if (!has(v.ref)) errors.push(`${name}: selector variant ${v.ref} does not resolve`) |
| 559 | if (!v.requires.length) |
| 560 | errors.push(`${name}: structural selector arm ${v.ref} has no required fields to dispatch on`) |
| 561 | for (let j = i + 1; j < selector.ordered.length; j++) { |
| 562 | const w = selector.ordered[j] |
| 563 | if (v.requires.length && w.requires.length && v.requires.every((k) => w.requires.includes(k))) |
| 564 | errors.push(`${name}: structural selector arm ${v.ref} shadows ${w.ref} (requires is a subset)`) |
| 565 | } |
| 566 | }) |
| 567 | } else { |
| 568 | errors.push(`${name}: selector is neither discriminated, structural, nor correlated`) |
| 569 | } |
| 570 | return errors |
| 571 | } |
| 572 | |
| 573 | // ============================================================ |
| 574 | // CLI: raw ast + model → flat schema (validated) |
no test coverage detected