@ultimat3/schema
Advanced tools
+8
-0
@@ -121,2 +121,10 @@ # @ultimat3/schema — agent notes | ||
| reintroduce either half, and never assume a parsed object has `Object.prototype` on it. | ||
| - **Every map keyed by a DECLARED FIELD NAME is built on `Object.create(null)`** — `objectSchema`'s | ||
| `properties`, `json-schema.ts`'s converted `properties`, and `pick`/`omit`'s rebuilt shape. That | ||
| last pair is the one with teeth: `next['__proto__'] = member` on a `{}` literal hits the setter, | ||
| so `pick('__proto__')` answered a schema with **zero** properties and dropped the one field the | ||
| caller asked to keep from validation, from the IR and from `parse()`'s output. `extend` needs | ||
| nothing: object spread defines own properties and never invokes a setter. A field named | ||
| `__proto__` is declarable — `t.object({ ['__proto__']: t.string })`, a COMPUTED key, which is an | ||
| ordinary own property where the literal `{ __proto__: … }` form is the setter. | ||
| - `t.date` refuses a clock time with no offset and no `Z` (`iso-date.ts`): a zone-less string is a | ||
@@ -123,0 +131,0 @@ different instant per host `TZ`, and `coerceQuery` puts it one query parameter from the wire. |
+1
-1
| { | ||
| "name": "@ultimat3/schema", | ||
| "version": "16.0.0", | ||
| "version": "17.0.0", | ||
| "description": "Ultimate's validation seam: Standard Schema interface, the t namespace, JSON Schema output", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+6
-6
@@ -19,3 +19,7 @@ // Single responsibility: this package's error codes. `@ultimat3/schema` is tier 0 and may not | ||
| const CONTROL = /[\u0000-\u001f\u007f\u2028\u2029]/g; | ||
| const ESCAPES: Readonly<Record<string, string>> = { | ||
| // Null-prototype, so `ESCAPES[char]` cannot answer an `Object.prototype` member no matter what | ||
| // `CONTROL` matched. The domain argument — a key is exactly one control character, and no | ||
| // prototype member has a single-character name — was true and is no longer load-bearing; a table | ||
| // that cannot reach the prototype needs no argument, and `bun run proto-index` stops reporting it. | ||
| const ESCAPES: Readonly<Record<string, string>> = Object.assign(Object.create(null), { | ||
| '\n': String.raw`\n`, | ||
@@ -26,7 +30,3 @@ '\r': String.raw`\r`, | ||
| '\f': String.raw`\f`, | ||
| }; | ||
| // `ESCAPES[char]` is a computed read on a plain object and is safe by DOMAIN, not by luck: the | ||
| // key is whatever `CONTROL` matched, so it is exactly one control character — and no | ||
| // `Object.prototype` member has a single-character name. Every other computed read in this file | ||
| // goes through `Object.hasOwn`. | ||
| }); | ||
| const singleLine = (text: string): string => | ||
@@ -33,0 +33,0 @@ text.replace( |
@@ -234,3 +234,9 @@ // Single responsibility: SchemaNode -> JSON Schema. Load-bearing: OpenAPI request/response | ||
| case 'object': { | ||
| const properties: Record<string, JsonSchema> = {}; | ||
| // `Object.create(null)`, the same rule `validators.ts` applies to the IR this reads: a | ||
| // `__proto__` property assigned into a `{}` literal reaches the setter and is published | ||
| // nowhere, so the document would omit a field the server enforces. | ||
| const properties: Record<string, JsonSchema> = Object.create(null) as Record< | ||
| string, | ||
| JsonSchema | ||
| >; | ||
| for (const [key, child] of Object.entries(node.properties ?? {})) { | ||
@@ -237,0 +243,0 @@ properties[key] = convert(child); |
+13
-3
@@ -158,3 +158,7 @@ // Single responsibility: the builtin, dependency-free validators behind `t`. Small on purpose — | ||
| export function objectSchema<S extends Shape>(shape: S): ObjectSchema<S> { | ||
| const properties: Record<string, SchemaNode> = {}; | ||
| // `Object.create(null)` for the same reason the parse output below uses one, on the other side | ||
| // of the same rule: a DECLARED `__proto__` field assigned into a `{}` literal hits the prototype | ||
| // setter and never becomes a key, so the field vanished from the IR `json-schema.ts` publishes | ||
| // and `coerce.ts` walks while the `checks` list beside it went on validating it. | ||
| const properties: Record<string, SchemaNode> = Object.create(null) as Record<string, SchemaNode>; | ||
| const checks: [string, Check<unknown>][] = []; | ||
@@ -199,4 +203,10 @@ for (const [key, member] of Object.entries(shape)) { | ||
| extend: (extra) => objectSchema({ ...shape, ...extra } as Simplified<S & typeof extra>), | ||
| // `Object.create(null)` in both, for the reason the IR above states and with a worse outcome | ||
| // here: `next['__proto__'] = member` on a `{}` literal hits the prototype SETTER, so the field | ||
| // is not mis-published, it is GONE — `pick('__proto__')` answered a schema with no properties | ||
| // at all, dropping the one field the caller asked to keep from validation, from the IR and | ||
| // from `parse()`'s output. `extend` needs none of this: object spread defines own properties | ||
| // and never invokes a setter. | ||
| pick: (...keys) => { | ||
| const next: Record<string, AnySchema> = {}; | ||
| const next: Record<string, AnySchema> = Object.create(null) as Record<string, AnySchema>; | ||
| for (const key of keys) next[key] = shape[key] as AnySchema; | ||
@@ -207,3 +217,3 @@ return objectSchema(next) as ObjectSchema<Pick<S, (typeof keys)[number]>>; | ||
| const drop = new Set<string>(keys); | ||
| const next: Record<string, AnySchema> = {}; | ||
| const next: Record<string, AnySchema> = Object.create(null) as Record<string, AnySchema>; | ||
| for (const [key, member] of Object.entries(shape)) { | ||
@@ -210,0 +220,0 @@ if (!drop.has(key)) next[key] = member; |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
123106
1.87%2206
0.73%