@ultimat3/schema
Advanced tools
+1
-1
| { | ||
| "name": "@ultimat3/schema", | ||
| "version": "2.0.0", | ||
| "version": "3.0.0", | ||
| "description": "Ultimate's validation seam: Standard Schema interface, the t namespace, JSON Schema output", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+14
-4
@@ -84,3 +84,6 @@ // Single responsibility: HTTP-boundary coercion. Kept out of validation on purpose — only the | ||
| for (const [key, child] of Object.entries(node.properties)) { | ||
| if (key in source) out[key] = coerceNode(child, source[key]); | ||
| // `Object.hasOwn`, never `key in source`: `{ ...source }` above already dropped what a | ||
| // prototype carries, so an `in` check put it back — a schema field named `toString` or | ||
| // `valueOf` was coerced from the INHERITED member and handed validation a function. | ||
| if (Object.hasOwn(source, key)) out[key] = coerceNode(child, source[key]); | ||
| } | ||
@@ -115,7 +118,13 @@ return out; | ||
| /** | ||
| * `Object.create(null)`, for the reason `@ultimat3/http`'s `parseQuery` already uses one: this | ||
| * record is built from caller-controlled keys. On a `{}` literal `out['__proto__'] = …` hits the | ||
| * prototype accessor instead of declaring a key, and every member of `Object.prototype` reads | ||
| * back as present when the client sent nothing. | ||
| */ | ||
| function toRecord(source: QuerySource): Record<string, string | readonly string[] | undefined> { | ||
| const out = Object.create(null) as Record<string, string | readonly string[] | undefined>; | ||
| if (!(source instanceof URLSearchParams)) { | ||
| return { ...source }; | ||
| return Object.assign(out, source); | ||
| } | ||
| const out: Record<string, string | readonly string[]> = {}; | ||
| for (const key of new Set(source.keys())) { | ||
@@ -139,3 +148,4 @@ const all = source.getAll(key); | ||
| for (const [key, child] of Object.entries(node.properties)) { | ||
| if (!(key in record)) continue; | ||
| // See `toRecord`: a declared property is coerced only when the caller actually sent it. | ||
| if (!Object.hasOwn(record, key)) continue; | ||
| const raw = record[key]; | ||
@@ -142,0 +152,0 @@ out[key] = coerceNode(child, child.kind === 'array' ? (raw ?? []) : normaliseSingle(raw)); |
+4
-2
@@ -14,4 +14,6 @@ // Single responsibility: the swap point. One provider is active per process; everything that | ||
| /** | ||
| * Return the IR for one of this provider's schemas. Required for OpenAPI and MCP tool | ||
| * schemas; omit it only if the provider also supplies `toJsonSchema`. | ||
| * Return the IR for one of this provider's schemas. Required for OpenAPI, MCP tool schemas | ||
| * and the admin form generator: `toJsonSchema()` calls it unconditionally and throws | ||
| * `X_SCHEMA_UNSUPPORTED` without it. There is no second way to describe a schema — the IR is | ||
| * the one projection surface, and every generator reads it. | ||
| */ | ||
@@ -18,0 +20,0 @@ introspect?(schema: unknown): SchemaNode | undefined; |
100544
0.98%1968
0.61%