@webpieces/core-util 0.4.827 → 0.4.828

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/core-util",
3
- "version": "0.4.827",
3
+ "version": "0.4.828",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -254,6 +254,13 @@ class ApiJsonSchemaValidator {
254
254
  const extra = schema.additionalProperties;
255
255
  for (const key of Object.keys(value)) {
256
256
  const entry = Object.getOwnPropertyDescriptor(value, key)?.value;
257
+ // A key HOLDING `undefined` is ABSENT: `JSON.stringify` drops it, so the document the
258
+ // caller receives never carries it, and the `required` loop below already reads
259
+ // `=== undefined` as missing. Validating it here made the two halves of this one walk
260
+ // disagree — `{ next: hasMore ? cursor : undefined }` was refused as "must be a string"
261
+ // although its JSON is valid (#1055). `null` is a VALUE and is still checked.
262
+ if (entry === undefined)
263
+ continue;
257
264
  const declared = Object.getOwnPropertyDescriptor(properties, key)?.value;
258
265
  if (declared === undefined) {
259
266
  if (typeof extra !== 'object' || extra === null) {
@@ -1 +1 @@
1
- {"version":3,"file":"DtoSchema.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/DtoSchema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The JSON Schema subset webpieces publishes on its API boundaries, and the ONE runtime validator of\n * it.\n *\n * ## Nothing here reads reflect-metadata any more\n *\n * Until #984 this file also carried `@WpDto`, `@WpDtoField`, `WpDtoFieldOptions`,\n * `WpDtoMapFieldOptions`, `WpMcpHeader`, `@WpResponseDto` and `DtoSchemaBuilder` — a second, runtime\n * spelling of facts the TypeScript compiler already holds. #983 measured the two against each other\n * and found the compiler reproduces every one of those arguments byte for byte, so they are deleted\n * rather than deprecated (`.claude/rules/no-backwards-compat.md`): a schema is now BUILT at build\n * time from the source by `@webpieces/api-doc-model`, and the runtime is HANDED the result.\n *\n * What survives is the schema itself, a builder for the boundaries that construct one by hand, and a\n * validator that reads the schema instead of a decorator.\n */\n\n/** Runtime values accepted at the DTO validation boundary. */\nexport type DtoValue = object | string | number | boolean | null;\n\n/**\n * One JSON Schema `type`. `null` is a member because NULLABLE is expressed as `[\"string\", \"null\"]`,\n * which is how JSON Schema says \"present, and may hold null\" — a different wire document from an\n * ABSENT property, and one the reflect-metadata runtime could neither see nor write down.\n */\nexport type ApiJsonSchemaType =\n | 'object'\n | 'string'\n | 'number'\n | 'integer'\n | 'boolean'\n | 'array'\n | 'null';\n\n/**\n * The DISCRIMINATOR of a {@link ApiJsonSchema.oneOf} — which property narrows the union, and what\n * each of its values names.\n *\n * The shape MIRRORS what `OpenApiGenerator`'s `SchemaRenderer` already emits for the same union\n * (`propertyName` + `mapping`), because a union has exactly ONE published spelling in this repo and\n * the OpenAPI renderer is the reference implementation of it. The one difference is what a mapping\n * VALUE holds: OpenAPI maps a value to `#/components/schemas/ScheduledWindow`, and an MCP tool\n * schema is INLINE and has no `$ref` to point at, so it maps to the branch's type NAME.\n *\n * `mapping` is DOCUMENTATION for the reader. {@link ApiJsonSchemaValidator} never narrows with it:\n * it selects the branch whose own `enum` carries the value, so the accepted request and the\n * published document cannot disagree about which branch a value means.\n */\nexport class ApiJsonSchemaDiscriminator {\n constructor(\n /** The property every branch declares as exactly one string literal, e.g. `kind`. */\n public propertyName: string,\n /** discriminator value -> the branch's TYPE NAME (MCP is inline: there is no `$ref`). */\n public mapping: Record<string, string>,\n ) {}\n}\n\n/** The JSON Schema subset emitted for Webpieces API DTOs. */\nexport class ApiJsonSchema {\n /** One type, or `[T, \"null\"]` for a nullable field. */\n type?: ApiJsonSchemaType | readonly ApiJsonSchemaType[];\n description?: string;\n properties?: Record<string, ApiJsonSchema>;\n required?: string[];\n /** `false` closes an object; a schema makes it a typed map whose values all match that schema. */\n additionalProperties?: boolean | ApiJsonSchema;\n items?: ApiJsonSchema;\n enum?: string[];\n minimum?: number;\n maximum?: number;\n /**\n * The branches of a union, INLINE (MCP tool schemas have no `$ref`), in declaration order.\n *\n * A union is legal NESTED, inside a property. It is NOT legal at the ROOT of a tool's parameter\n * schema — both the OpenAI and the Anthropic function-calling APIs reject a top-level\n * `oneOf`/`anyOf`/`allOf`, and because a server sends its whole tool list on every request, one\n * such tool makes EVERY request 400. That is refused at build time by the `no-root-union-api-type`\n * rule, before a contract can be published at all.\n */\n oneOf?: readonly ApiJsonSchema[];\n /**\n * Set ONLY when every branch carries the same property typed as one string literal — DERIVED,\n * never invented. A union TypeScript itself cannot narrow is published as a bare {@link oneOf},\n * because claiming a narrowing the source does not have is worse than admitting there is none.\n */\n discriminator?: ApiJsonSchemaDiscriminator;\n /** MCP 2026 SEP-2243 schema extension consumed by the official SDK. */\n declare 'x-mcp-header'?: string;\n\n constructor(type?: ApiJsonSchemaType | readonly ApiJsonSchemaType[]) {\n this.type = type;\n }\n\n /**\n * The non-`null` member of the type, which is the one that decides how a value is checked.\n *\n * These three are STATIC and not instance methods on purpose: an instance method would put a\n * function on the structural type, and this class is handed straight to the MCP SDK, whose\n * parameter types are index-signature JSON objects that a class carrying methods no longer\n * satisfies. It stays a data-only structure (`CLAUDE.md` §1) and the helpers live beside it.\n */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static baseTypeOf(schema: ApiJsonSchema): ApiJsonSchemaType | undefined {\n if (schema.type === undefined) return undefined;\n if (typeof schema.type === 'string') return schema.type;\n return schema.type.find((one: ApiJsonSchemaType) => one !== 'null');\n }\n\n /** True when the schema was widened with `\"null\"`, i.e. the property may hold `null`. */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static allowsNull(schema: ApiJsonSchema): boolean {\n return typeof schema.type !== 'string' && (schema.type?.includes('null') ?? false);\n }\n\n /**\n * The one closure gate for an object schema. Closed means no key can carry an unspecified value:\n * either extra keys are forbidden (`additionalProperties: false`) or every extra key's value is\n * typed (`additionalProperties: <schema>`, a typed map). A missing or `true` value is open.\n * Use this instead of `schema.additionalProperties === false`, which rejects typed maps.\n */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static isClosed(schema: ApiJsonSchema): boolean {\n const extra = schema.additionalProperties;\n return extra === false || (typeof extra === 'object' && extra !== null);\n }\n}\n\n/**\n * Builds the CLOSED object schema a hand-written runtime boundary validates against — `@WpStream`'s\n * two event schemas are the whole of its client list today.\n *\n * It exists because the alternative spellings are both worse: a decorator that re-states the field\n * types the compiler already knows is exactly what #984 deleted, and a bare object literal is what\n * `CLAUDE.md` §3 forbids. A generated schema never comes through here — it is read from the build's\n * own artifact.\n */\nexport class ObjectSchemaBuilder {\n private readonly schema = new ApiJsonSchema('object');\n private readonly requiredNames: string[] = [];\n\n constructor() {\n this.schema.properties = {};\n this.schema.additionalProperties = false;\n }\n\n required(name: string, type: ApiJsonSchema): this {\n this.requiredNames.push(name);\n return this.optional(name, type);\n }\n\n optional(name: string, type: ApiJsonSchema): this {\n this.schema.properties![name] = type;\n return this;\n }\n\n build(): ApiJsonSchema {\n if (this.requiredNames.length > 0) this.schema.required = [...this.requiredNames];\n return this.schema;\n }\n}\n\n/** A validation result safe to show to an API caller/model. */\nexport class DtoValidationFailure {\n constructor(\n /** JSON path of the offending value, e.g. `$.address.city`. */\n public readonly field: string,\n public readonly message: string,\n ) {}\n}\n\n/**\n * Validates a value against an {@link ApiJsonSchema} — the ONE runtime narrowing boundary for MCP\n * tool arguments, MCP structured output and typed stream events.\n *\n * It reads the SCHEMA, which is the same object the build published to agents, so a document that\n * says a field is required and a server that accepts it missing cannot disagree. The predecessor\n * walked reflect-metadata instead, which meant the published document and the accepted request were\n * derived from two different declarations.\n */\nexport class ApiJsonSchemaValidator {\n validate(schema: ApiJsonSchema, value: DtoValue): DtoValidationFailure | undefined {\n return this.validateAt(schema, value, '$');\n }\n\n private validateAt(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (value === null) {\n return ApiJsonSchema.allowsNull(schema)\n ? undefined\n : new DtoValidationFailure(path, `${path} must not be null`);\n }\n if (schema.oneOf !== undefined) {\n return this.validateUnion(schema, value, path);\n }\n switch (ApiJsonSchema.baseTypeOf(schema)) {\n case 'object':\n return this.validateObject(schema, value, path);\n case 'array':\n return this.validateArray(schema, value, path);\n case 'string':\n return this.validateString(schema, value, path);\n case 'number':\n case 'integer':\n return this.validateNumber(schema, value, path);\n case 'boolean':\n return typeof value === 'boolean'\n ? undefined\n : new DtoValidationFailure(path, `${path} must be a boolean`);\n default:\n return undefined;\n }\n }\n\n /**\n * A union, validated BY DISCRIMINATOR.\n *\n * The discriminator property is read FIRST and the branch chosen from it, so a value naming no\n * branch is reported as what it is — `$.window.kind must be one of: scheduled | asap`, naming the\n * field that is wrong and the values it may hold. Trying every branch and reporting \"no branch\n * matched\" tells a caller nothing they can act on: it names neither the offending property nor\n * the legal values, and it hides a branch that matched on `kind` and failed three levels down.\n *\n * The branch is selected by the branches' own `enum`s, never by\n * {@link ApiJsonSchemaDiscriminator.mapping} — the branches ARE the schema, so selection and\n * validation read one source and cannot disagree.\n */\n private validateUnion(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n const branches = schema.oneOf ?? [];\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an object`);\n }\n const discriminator = schema.discriminator;\n if (discriminator === undefined) {\n return this.validateAnyBranch(branches, value, path);\n }\n const property = discriminator.propertyName;\n const held: DtoValue = Object.getOwnPropertyDescriptor(value, property)?.value;\n const branch = branches.find((one: ApiJsonSchema) =>\n ApiJsonSchemaValidator.branchValuesOf(one, property).includes(held as string),\n );\n if (branch === undefined) {\n const legal = branches\n .flatMap((one: ApiJsonSchema) =>\n ApiJsonSchemaValidator.branchValuesOf(one, property),\n )\n .join(' | ');\n return new DtoValidationFailure(\n `${path}.${property}`,\n `${path}.${property} must be one of: ${legal}`,\n );\n }\n return this.validateAt(branch, value, path);\n }\n\n /**\n * A union with NO discriminator — one TypeScript itself cannot narrow. Every branch is tried and\n * the value accepted when one matches. There is no better report when none does: the source does\n * not narrow, so no property can honestly be named as the wrong one.\n */\n private validateAnyBranch(\n branches: readonly ApiJsonSchema[],\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n for (const branch of branches) {\n if (this.validateAt(branch, value, path) === undefined) {\n return undefined;\n }\n }\n return new DtoValidationFailure(\n path,\n `${path} matches none of the ${branches.length} accepted shapes`,\n );\n }\n\n /** The string literals ONE branch declares for the discriminator property. */\n // webpieces-disable no-function-outside-class -- private static reader of a branch schema\n private static branchValuesOf(branch: ApiJsonSchema, property: string): readonly string[] {\n const declared = Object.getOwnPropertyDescriptor(branch.properties ?? {}, property)\n ?.value as ApiJsonSchema | undefined;\n return declared?.enum ?? [];\n }\n\n private validateObject(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an object`);\n }\n const properties = schema.properties ?? {};\n const extra = schema.additionalProperties;\n for (const key of Object.keys(value)) {\n const entry: DtoValue = Object.getOwnPropertyDescriptor(value, key)?.value;\n const declared = Object.getOwnPropertyDescriptor(properties, key)?.value as\n | ApiJsonSchema\n | undefined;\n if (declared === undefined) {\n if (typeof extra !== 'object' || extra === null) {\n return new DtoValidationFailure(\n `${path}.${key}`,\n `${path}.${key} is not allowed`,\n );\n }\n const failure = this.validateAt(extra, entry, `${path}.${key}`);\n if (failure) return failure;\n continue;\n }\n const failure = this.validateAt(declared, entry, `${path}.${key}`);\n if (failure) return failure;\n }\n for (const name of schema.required ?? []) {\n if (Object.getOwnPropertyDescriptor(value, name)?.value === undefined) {\n return new DtoValidationFailure(`${path}.${name}`, `${path}.${name} is required`);\n }\n }\n return undefined;\n }\n\n private validateArray(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (!Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an array`);\n }\n const items = schema.items;\n if (items === undefined) return undefined;\n for (let index = 0; index < value.length; index += 1) {\n const failure = this.validateAt(items, value[index], `${path}[${index}]`);\n if (failure) return failure;\n }\n return undefined;\n }\n\n private validateString(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'string') {\n return new DtoValidationFailure(path, `${path} must be a string`);\n }\n if (schema.enum && !schema.enum.includes(value)) {\n return new DtoValidationFailure(\n path,\n `${path} must be one of: ${schema.enum.join(', ')}`,\n );\n }\n return undefined;\n }\n\n private validateNumber(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return new DtoValidationFailure(path, `${path} must be a number`);\n }\n if (ApiJsonSchema.baseTypeOf(schema) === 'integer' && !Number.isInteger(value)) {\n return new DtoValidationFailure(path, `${path} must be an integer`);\n }\n if (schema.minimum !== undefined && value < schema.minimum) {\n return new DtoValidationFailure(path, `${path} must be >= ${schema.minimum}`);\n }\n if (schema.maximum !== undefined && value > schema.maximum) {\n return new DtoValidationFailure(path, `${path} must be <= ${schema.maximum}`);\n }\n return undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"DtoSchema.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/DtoSchema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The JSON Schema subset webpieces publishes on its API boundaries, and the ONE runtime validator of\n * it.\n *\n * ## Nothing here reads reflect-metadata any more\n *\n * Until #984 this file also carried `@WpDto`, `@WpDtoField`, `WpDtoFieldOptions`,\n * `WpDtoMapFieldOptions`, `WpMcpHeader`, `@WpResponseDto` and `DtoSchemaBuilder` — a second, runtime\n * spelling of facts the TypeScript compiler already holds. #983 measured the two against each other\n * and found the compiler reproduces every one of those arguments byte for byte, so they are deleted\n * rather than deprecated (`.claude/rules/no-backwards-compat.md`): a schema is now BUILT at build\n * time from the source by `@webpieces/api-doc-model`, and the runtime is HANDED the result.\n *\n * What survives is the schema itself, a builder for the boundaries that construct one by hand, and a\n * validator that reads the schema instead of a decorator.\n */\n\n/** Runtime values accepted at the DTO validation boundary. */\nexport type DtoValue = object | string | number | boolean | null;\n\n/**\n * One JSON Schema `type`. `null` is a member because NULLABLE is expressed as `[\"string\", \"null\"]`,\n * which is how JSON Schema says \"present, and may hold null\" — a different wire document from an\n * ABSENT property, and one the reflect-metadata runtime could neither see nor write down.\n */\nexport type ApiJsonSchemaType =\n | 'object'\n | 'string'\n | 'number'\n | 'integer'\n | 'boolean'\n | 'array'\n | 'null';\n\n/**\n * The DISCRIMINATOR of a {@link ApiJsonSchema.oneOf} — which property narrows the union, and what\n * each of its values names.\n *\n * The shape MIRRORS what `OpenApiGenerator`'s `SchemaRenderer` already emits for the same union\n * (`propertyName` + `mapping`), because a union has exactly ONE published spelling in this repo and\n * the OpenAPI renderer is the reference implementation of it. The one difference is what a mapping\n * VALUE holds: OpenAPI maps a value to `#/components/schemas/ScheduledWindow`, and an MCP tool\n * schema is INLINE and has no `$ref` to point at, so it maps to the branch's type NAME.\n *\n * `mapping` is DOCUMENTATION for the reader. {@link ApiJsonSchemaValidator} never narrows with it:\n * it selects the branch whose own `enum` carries the value, so the accepted request and the\n * published document cannot disagree about which branch a value means.\n */\nexport class ApiJsonSchemaDiscriminator {\n constructor(\n /** The property every branch declares as exactly one string literal, e.g. `kind`. */\n public propertyName: string,\n /** discriminator value -> the branch's TYPE NAME (MCP is inline: there is no `$ref`). */\n public mapping: Record<string, string>,\n ) {}\n}\n\n/** The JSON Schema subset emitted for Webpieces API DTOs. */\nexport class ApiJsonSchema {\n /** One type, or `[T, \"null\"]` for a nullable field. */\n type?: ApiJsonSchemaType | readonly ApiJsonSchemaType[];\n description?: string;\n properties?: Record<string, ApiJsonSchema>;\n required?: string[];\n /** `false` closes an object; a schema makes it a typed map whose values all match that schema. */\n additionalProperties?: boolean | ApiJsonSchema;\n items?: ApiJsonSchema;\n enum?: string[];\n minimum?: number;\n maximum?: number;\n /**\n * The branches of a union, INLINE (MCP tool schemas have no `$ref`), in declaration order.\n *\n * A union is legal NESTED, inside a property. It is NOT legal at the ROOT of a tool's parameter\n * schema — both the OpenAI and the Anthropic function-calling APIs reject a top-level\n * `oneOf`/`anyOf`/`allOf`, and because a server sends its whole tool list on every request, one\n * such tool makes EVERY request 400. That is refused at build time by the `no-root-union-api-type`\n * rule, before a contract can be published at all.\n */\n oneOf?: readonly ApiJsonSchema[];\n /**\n * Set ONLY when every branch carries the same property typed as one string literal — DERIVED,\n * never invented. A union TypeScript itself cannot narrow is published as a bare {@link oneOf},\n * because claiming a narrowing the source does not have is worse than admitting there is none.\n */\n discriminator?: ApiJsonSchemaDiscriminator;\n /** MCP 2026 SEP-2243 schema extension consumed by the official SDK. */\n declare 'x-mcp-header'?: string;\n\n constructor(type?: ApiJsonSchemaType | readonly ApiJsonSchemaType[]) {\n this.type = type;\n }\n\n /**\n * The non-`null` member of the type, which is the one that decides how a value is checked.\n *\n * These three are STATIC and not instance methods on purpose: an instance method would put a\n * function on the structural type, and this class is handed straight to the MCP SDK, whose\n * parameter types are index-signature JSON objects that a class carrying methods no longer\n * satisfies. It stays a data-only structure (`CLAUDE.md` §1) and the helpers live beside it.\n */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static baseTypeOf(schema: ApiJsonSchema): ApiJsonSchemaType | undefined {\n if (schema.type === undefined) return undefined;\n if (typeof schema.type === 'string') return schema.type;\n return schema.type.find((one: ApiJsonSchemaType) => one !== 'null');\n }\n\n /** True when the schema was widened with `\"null\"`, i.e. the property may hold `null`. */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static allowsNull(schema: ApiJsonSchema): boolean {\n return typeof schema.type !== 'string' && (schema.type?.includes('null') ?? false);\n }\n\n /**\n * The one closure gate for an object schema. Closed means no key can carry an unspecified value:\n * either extra keys are forbidden (`additionalProperties: false`) or every extra key's value is\n * typed (`additionalProperties: <schema>`, a typed map). A missing or `true` value is open.\n * Use this instead of `schema.additionalProperties === false`, which rejects typed maps.\n */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static isClosed(schema: ApiJsonSchema): boolean {\n const extra = schema.additionalProperties;\n return extra === false || (typeof extra === 'object' && extra !== null);\n }\n}\n\n/**\n * Builds the CLOSED object schema a hand-written runtime boundary validates against — `@WpStream`'s\n * two event schemas are the whole of its client list today.\n *\n * It exists because the alternative spellings are both worse: a decorator that re-states the field\n * types the compiler already knows is exactly what #984 deleted, and a bare object literal is what\n * `CLAUDE.md` §3 forbids. A generated schema never comes through here — it is read from the build's\n * own artifact.\n */\nexport class ObjectSchemaBuilder {\n private readonly schema = new ApiJsonSchema('object');\n private readonly requiredNames: string[] = [];\n\n constructor() {\n this.schema.properties = {};\n this.schema.additionalProperties = false;\n }\n\n required(name: string, type: ApiJsonSchema): this {\n this.requiredNames.push(name);\n return this.optional(name, type);\n }\n\n optional(name: string, type: ApiJsonSchema): this {\n this.schema.properties![name] = type;\n return this;\n }\n\n build(): ApiJsonSchema {\n if (this.requiredNames.length > 0) this.schema.required = [...this.requiredNames];\n return this.schema;\n }\n}\n\n/** A validation result safe to show to an API caller/model. */\nexport class DtoValidationFailure {\n constructor(\n /** JSON path of the offending value, e.g. `$.address.city`. */\n public readonly field: string,\n public readonly message: string,\n ) {}\n}\n\n/**\n * Validates a value against an {@link ApiJsonSchema} — the ONE runtime narrowing boundary for MCP\n * tool arguments, MCP structured output and typed stream events.\n *\n * It reads the SCHEMA, which is the same object the build published to agents, so a document that\n * says a field is required and a server that accepts it missing cannot disagree. The predecessor\n * walked reflect-metadata instead, which meant the published document and the accepted request were\n * derived from two different declarations.\n */\nexport class ApiJsonSchemaValidator {\n validate(schema: ApiJsonSchema, value: DtoValue): DtoValidationFailure | undefined {\n return this.validateAt(schema, value, '$');\n }\n\n private validateAt(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (value === null) {\n return ApiJsonSchema.allowsNull(schema)\n ? undefined\n : new DtoValidationFailure(path, `${path} must not be null`);\n }\n if (schema.oneOf !== undefined) {\n return this.validateUnion(schema, value, path);\n }\n switch (ApiJsonSchema.baseTypeOf(schema)) {\n case 'object':\n return this.validateObject(schema, value, path);\n case 'array':\n return this.validateArray(schema, value, path);\n case 'string':\n return this.validateString(schema, value, path);\n case 'number':\n case 'integer':\n return this.validateNumber(schema, value, path);\n case 'boolean':\n return typeof value === 'boolean'\n ? undefined\n : new DtoValidationFailure(path, `${path} must be a boolean`);\n default:\n return undefined;\n }\n }\n\n /**\n * A union, validated BY DISCRIMINATOR.\n *\n * The discriminator property is read FIRST and the branch chosen from it, so a value naming no\n * branch is reported as what it is — `$.window.kind must be one of: scheduled | asap`, naming the\n * field that is wrong and the values it may hold. Trying every branch and reporting \"no branch\n * matched\" tells a caller nothing they can act on: it names neither the offending property nor\n * the legal values, and it hides a branch that matched on `kind` and failed three levels down.\n *\n * The branch is selected by the branches' own `enum`s, never by\n * {@link ApiJsonSchemaDiscriminator.mapping} — the branches ARE the schema, so selection and\n * validation read one source and cannot disagree.\n */\n private validateUnion(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n const branches = schema.oneOf ?? [];\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an object`);\n }\n const discriminator = schema.discriminator;\n if (discriminator === undefined) {\n return this.validateAnyBranch(branches, value, path);\n }\n const property = discriminator.propertyName;\n const held: DtoValue = Object.getOwnPropertyDescriptor(value, property)?.value;\n const branch = branches.find((one: ApiJsonSchema) =>\n ApiJsonSchemaValidator.branchValuesOf(one, property).includes(held as string),\n );\n if (branch === undefined) {\n const legal = branches\n .flatMap((one: ApiJsonSchema) =>\n ApiJsonSchemaValidator.branchValuesOf(one, property),\n )\n .join(' | ');\n return new DtoValidationFailure(\n `${path}.${property}`,\n `${path}.${property} must be one of: ${legal}`,\n );\n }\n return this.validateAt(branch, value, path);\n }\n\n /**\n * A union with NO discriminator — one TypeScript itself cannot narrow. Every branch is tried and\n * the value accepted when one matches. There is no better report when none does: the source does\n * not narrow, so no property can honestly be named as the wrong one.\n */\n private validateAnyBranch(\n branches: readonly ApiJsonSchema[],\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n for (const branch of branches) {\n if (this.validateAt(branch, value, path) === undefined) {\n return undefined;\n }\n }\n return new DtoValidationFailure(\n path,\n `${path} matches none of the ${branches.length} accepted shapes`,\n );\n }\n\n /** The string literals ONE branch declares for the discriminator property. */\n // webpieces-disable no-function-outside-class -- private static reader of a branch schema\n private static branchValuesOf(branch: ApiJsonSchema, property: string): readonly string[] {\n const declared = Object.getOwnPropertyDescriptor(branch.properties ?? {}, property)\n ?.value as ApiJsonSchema | undefined;\n return declared?.enum ?? [];\n }\n\n private validateObject(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an object`);\n }\n const properties = schema.properties ?? {};\n const extra = schema.additionalProperties;\n for (const key of Object.keys(value)) {\n const entry: DtoValue = Object.getOwnPropertyDescriptor(value, key)?.value;\n // A key HOLDING `undefined` is ABSENT: `JSON.stringify` drops it, so the document the\n // caller receives never carries it, and the `required` loop below already reads\n // `=== undefined` as missing. Validating it here made the two halves of this one walk\n // disagree — `{ next: hasMore ? cursor : undefined }` was refused as \"must be a string\"\n // although its JSON is valid (#1055). `null` is a VALUE and is still checked.\n if ((entry as DtoValue | undefined) === undefined) continue;\n const declared = Object.getOwnPropertyDescriptor(properties, key)?.value as\n | ApiJsonSchema\n | undefined;\n if (declared === undefined) {\n if (typeof extra !== 'object' || extra === null) {\n return new DtoValidationFailure(\n `${path}.${key}`,\n `${path}.${key} is not allowed`,\n );\n }\n const failure = this.validateAt(extra, entry, `${path}.${key}`);\n if (failure) return failure;\n continue;\n }\n const failure = this.validateAt(declared, entry, `${path}.${key}`);\n if (failure) return failure;\n }\n for (const name of schema.required ?? []) {\n if (Object.getOwnPropertyDescriptor(value, name)?.value === undefined) {\n return new DtoValidationFailure(`${path}.${name}`, `${path}.${name} is required`);\n }\n }\n return undefined;\n }\n\n private validateArray(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (!Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an array`);\n }\n const items = schema.items;\n if (items === undefined) return undefined;\n for (let index = 0; index < value.length; index += 1) {\n const failure = this.validateAt(items, value[index], `${path}[${index}]`);\n if (failure) return failure;\n }\n return undefined;\n }\n\n private validateString(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'string') {\n return new DtoValidationFailure(path, `${path} must be a string`);\n }\n if (schema.enum && !schema.enum.includes(value)) {\n return new DtoValidationFailure(\n path,\n `${path} must be one of: ${schema.enum.join(', ')}`,\n );\n }\n return undefined;\n }\n\n private validateNumber(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return new DtoValidationFailure(path, `${path} must be a number`);\n }\n if (ApiJsonSchema.baseTypeOf(schema) === 'integer' && !Number.isInteger(value)) {\n return new DtoValidationFailure(path, `${path} must be an integer`);\n }\n if (schema.minimum !== undefined && value < schema.minimum) {\n return new DtoValidationFailure(path, `${path} must be >= ${schema.minimum}`);\n }\n if (schema.maximum !== undefined && value > schema.maximum) {\n return new DtoValidationFailure(path, `${path} must be <= ${schema.maximum}`);\n }\n return undefined;\n }\n}\n"]}