@instruments/materialgraph 0.32.0 → 0.35.0

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.
Files changed (88) hide show
  1. package/association-types.d.ts +11 -11
  2. package/associations-client.d.ts +7 -7
  3. package/associations-client.js +5 -5
  4. package/brand-types.d.ts +37 -41
  5. package/brand-types.js +3 -5
  6. package/brands-client.d.ts +22 -9
  7. package/brands-client.js +20 -6
  8. package/by-spec-types.d.ts +2 -3
  9. package/catalog-discovery-types.d.ts +3 -3
  10. package/catalogue-client.d.ts +2 -50
  11. package/catalogue-client.js +2 -10
  12. package/catalogue-types.d.ts +1 -1
  13. package/categories-client.d.ts +3 -5
  14. package/categories-client.js +1 -1
  15. package/category-types.d.ts +17 -30
  16. package/citation-types.d.ts +2 -2
  17. package/client-vocabulary.d.ts +2 -2
  18. package/client-vocabulary.js +4 -1
  19. package/client.d.ts +75 -102
  20. package/client.js +83 -100
  21. package/color-profiles-types.d.ts +70 -0
  22. package/colors-client.d.ts +76 -0
  23. package/colors-client.js +52 -0
  24. package/find-companions-types.d.ts +8 -8
  25. package/{find-variants-by-colour-types.d.ts → find-variants-by-color-types.d.ts} +70 -64
  26. package/find-variants-by-vector-types.d.ts +4 -1
  27. package/get-variant-types.d.ts +14 -2
  28. package/http-transport.d.ts +1 -1
  29. package/http-transport.js +1 -2
  30. package/identifiers.d.ts +2 -2
  31. package/identifiers.js +1 -1
  32. package/index.d.ts +5 -13
  33. package/index.js +1 -3
  34. package/list-variant-facets-types.d.ts +4 -4
  35. package/list-variants-types.d.ts +108 -19
  36. package/material-asset-types.d.ts +3 -3
  37. package/material-block-types.d.ts +9 -9
  38. package/material-types.d.ts +10 -8
  39. package/operations.d.ts +2 -2
  40. package/operations.js +14 -24
  41. package/package.json +2 -2
  42. package/{resolve-colour-types.d.ts → resolve-color-types.d.ts} +18 -18
  43. package/response-schemas.generated.d.ts +2 -2
  44. package/response-schemas.generated.js +40 -68
  45. package/response-schemas.js +1 -1
  46. package/samplize-catalogue-types.d.ts +4 -4
  47. package/samplize-colorvec.js +6 -6
  48. package/{corpus-scope-types.d.ts → search-catalogue-subset-types.d.ts} +3 -3
  49. package/search-variants-types.d.ts +91 -0
  50. package/types.d.ts +31 -283
  51. package/webmcp.generated.js +4 -9
  52. package/attribution-types.d.ts +0 -51
  53. package/attribution-types.js +0 -13
  54. package/by-colour-types.d.ts +0 -320
  55. package/colour-region-types.d.ts +0 -18
  56. package/colours-client.d.ts +0 -70
  57. package/colours-client.js +0 -38
  58. package/material-query-plan-types.d.ts +0 -70
  59. package/palette-search-types.d.ts +0 -171
  60. package/palette-search-types.js +0 -1
  61. package/query-understanding-types.d.ts +0 -254
  62. package/query-understanding-types.js +0 -1
  63. package/resolve-colour-types.js +0 -1
  64. package/search-card-types.d.ts +0 -86
  65. package/search-card-types.js +0 -1
  66. package/search-index-plan-types.d.ts +0 -74
  67. package/search-index-plan-types.js +0 -1
  68. package/search-refinement-types.d.ts +0 -103
  69. package/search-refinement-types.js +0 -2
  70. package/search-request-types.d.ts +0 -170
  71. package/search-request-types.js +0 -1
  72. package/search-response-receipt-types.d.ts +0 -24
  73. package/search-response-receipt-types.js +0 -1
  74. package/search-response-types.d.ts +0 -11
  75. package/search-response-types.js +0 -1
  76. package/search-result-validation.d.ts +0 -9
  77. package/search-result-validation.js +0 -83
  78. package/spec-types.d.ts +0 -109
  79. package/spec-types.js +0 -54
  80. package/specifications.d.ts +0 -34
  81. package/specifications.js +0 -56
  82. package/validation.d.ts +0 -43
  83. package/validation.js +0 -117
  84. /package/{by-colour-types.js → color-profiles-types.js} +0 -0
  85. /package/{colour-region-types.js → find-variants-by-color-types.js} +0 -0
  86. /package/{corpus-scope-types.js → resolve-color-types.js} +0 -0
  87. /package/{find-variants-by-colour-types.js → search-catalogue-subset-types.js} +0 -0
  88. /package/{material-query-plan-types.js → search-variants-types.js} +0 -0
package/spec-types.d.ts DELETED
@@ -1,109 +0,0 @@
1
- /**
2
- * The specification surface — the typed vocabulary for asking a material
3
- * "does it meet this spec?": certifications (GREENGUARD, FloorScore, Declare),
4
- * VOC / emissions testing (CDPH v1.2), sustainability programmes (LEED, EPDs),
5
- * and fire / abrasion / slip classifications.
6
- *
7
- * Every shape here is a hand-authored, zero-dependency mirror of the server's
8
- * spec filter contract (`specFilterSchema` in
9
- * `@materialgraph/products/search/schema`). It exists so a caller answering a
10
- * question like "find me a GREENGUARD-certified alternative" or "the same fire
11
- * class, but with an EPD on record" reaches for a NAMED, typed criterion instead
12
- * of hand-assembling a magic `attribute:value` string.
13
- *
14
- * These criteria drive two operations equally, and choosing between them IS the
15
- * routing decision:
16
- * - `client.search({ filters: [...] })` — spec gates layered onto a RANKED
17
- * (colour / visual / lexical) search. Reach for it when relevance still
18
- * matters: "something like this seed, but Class A".
19
- * - `client.listVariants({ filters })` — the value-anchored, SPEC-ONLY
20
- * primitive, which takes a `{field: attributeId, op, ...}` filter leaf
21
- * (build one term with the helpers in `./specifications`, or a plain
22
- * object literal for anything else). Reach for it when the spec is the
23
- * whole question and ranking would be noise.
24
- *
25
- * Discover the tokens before you spend them: `client.listVariantFacets()` for
26
- * the live values of the attributes you name, `client.attributeValues()` for one attribute's
27
- * values, `client.listAttributes({ query })` to turn a phrase into a canonical
28
- * attribute id. A hand-guessed token returns an honest empty answer, which is
29
- * the hardest kind of bug to see.
30
- */
31
- /**
32
- * The virtual attribute id that matches a certification PROGRAMME wherever it
33
- * landed at ingest.
34
- *
35
- * Certifications are not stored under one attribute: each record is an object
36
- * (`{ programme, name, status, … }`) split across FOUR real object-array
37
- * attributes by intent — general `certifications`,
38
- * `sustainability_certifications`, `material_health_certifications`, and
39
- * `voc_emissions_testing_certificate` (GREENGUARD Gold / FloorScore / CDPH land
40
- * in the last). A caller shouldn't have to know which bucket a programme fell
41
- * into, so this single virtual id matches the `programme` term across all four.
42
- *
43
- * The `term` may be a display name or alias ("GREENGUARD Gold", "LEED"), resolved
44
- * server-side to its canonical programme token, or a canonical token directly
45
- * (e.g. `greenguard_certificate`, `has_greenguard_gold_certification`).
46
- *
47
- * @example
48
- * // "find a GREENGUARD-certified alternative"
49
- * client.search({
50
- * similarTo: { variantId: seed },
51
- * filters: [{ attributeId: CERTIFICATION_PROGRAMME_ATTRIBUTE_ID, term: "GREENGUARD Gold" }],
52
- * });
53
- */
54
- export declare const CERTIFICATION_PROGRAMME_ATTRIBUTE_ID = "certification_programme";
55
- /** The literal type of {@link CERTIFICATION_PROGRAMME_ATTRIBUTE_ID}. */
56
- export type CertificationProgrammeAttributeId = typeof CERTIFICATION_PROGRAMME_ATTRIBUTE_ID;
57
- /**
58
- * A term-equality spec criterion: the attribute must carry this canonical value,
59
- * e.g. `{ attributeId: "fire_classifications", term: "astm_e84_class_a" }`.
60
- * Discover valid `attributeId`/`term` pairs via `client.listVariantFacets()`
61
- * and `client.listEnumValues()`, or find an attribute from free text with
62
- * `client.listAttributes({ query })`.
63
- */
64
- export type SpecTermFilter = {
65
- attributeId: string;
66
- term: string;
67
- };
68
- /**
69
- * A negative spec criterion. `whenUnstated` (absent = `"drop"`) controls what an
70
- * UNKNOWN effective value does: `"drop"` requires a known value, so a variant
71
- * that never states the attribute does not pass; `"pass"` admits it — a variant
72
- * only fails by STATING the excluded term.
73
- */
74
- export type SpecExclusionFilter = {
75
- attributeId: string;
76
- excludesTerm: string;
77
- whenUnstated?: "drop" | "pass";
78
- };
79
- /**
80
- * A presence spec criterion: the attribute must have ANY accepted value —
81
- * "has an EPD on record", "has a measured slip figure at all". Not supported for
82
- * {@link CERTIFICATION_PROGRAMME_ATTRIBUTE_ID} (a programme is matched by name,
83
- * never by bare presence).
84
- */
85
- export type SpecPresenceFilter = {
86
- attributeId: string;
87
- hasValue: true;
88
- }
89
- /** Date-sensitive presence is currently executable only for EPD expiration. */
90
- | {
91
- attributeId: "epd_expiration_date";
92
- hasValue: true;
93
- asOf: string;
94
- };
95
- /**
96
- * A certification-programme criterion — a {@link SpecTermFilter} pinned to the
97
- * virtual {@link CERTIFICATION_PROGRAMME_ATTRIBUTE_ID}. Structurally assignable
98
- * to {@link SpecFilter}, so it drops straight into `search({ filters })`.
99
- */
100
- export type CertificationProgrammeFilter = {
101
- attributeId: CertificationProgrammeAttributeId;
102
- term: string;
103
- };
104
- /**
105
- * A hard, canonical-attribute spec criterion — either a term match or a
106
- * presence check. The union the search engine applies as a restriction BEFORE
107
- * ranking. Identical in shape to the SDK's `SearchFilter`.
108
- */
109
- export type SpecFilter = SpecTermFilter | SpecExclusionFilter | SpecPresenceFilter;
package/spec-types.js DELETED
@@ -1,54 +0,0 @@
1
- /**
2
- * The specification surface — the typed vocabulary for asking a material
3
- * "does it meet this spec?": certifications (GREENGUARD, FloorScore, Declare),
4
- * VOC / emissions testing (CDPH v1.2), sustainability programmes (LEED, EPDs),
5
- * and fire / abrasion / slip classifications.
6
- *
7
- * Every shape here is a hand-authored, zero-dependency mirror of the server's
8
- * spec filter contract (`specFilterSchema` in
9
- * `@materialgraph/products/search/schema`). It exists so a caller answering a
10
- * question like "find me a GREENGUARD-certified alternative" or "the same fire
11
- * class, but with an EPD on record" reaches for a NAMED, typed criterion instead
12
- * of hand-assembling a magic `attribute:value` string.
13
- *
14
- * These criteria drive two operations equally, and choosing between them IS the
15
- * routing decision:
16
- * - `client.search({ filters: [...] })` — spec gates layered onto a RANKED
17
- * (colour / visual / lexical) search. Reach for it when relevance still
18
- * matters: "something like this seed, but Class A".
19
- * - `client.listVariants({ filters })` — the value-anchored, SPEC-ONLY
20
- * primitive, which takes a `{field: attributeId, op, ...}` filter leaf
21
- * (build one term with the helpers in `./specifications`, or a plain
22
- * object literal for anything else). Reach for it when the spec is the
23
- * whole question and ranking would be noise.
24
- *
25
- * Discover the tokens before you spend them: `client.listVariantFacets()` for
26
- * the live values of the attributes you name, `client.attributeValues()` for one attribute's
27
- * values, `client.listAttributes({ query })` to turn a phrase into a canonical
28
- * attribute id. A hand-guessed token returns an honest empty answer, which is
29
- * the hardest kind of bug to see.
30
- */
31
- /**
32
- * The virtual attribute id that matches a certification PROGRAMME wherever it
33
- * landed at ingest.
34
- *
35
- * Certifications are not stored under one attribute: each record is an object
36
- * (`{ programme, name, status, … }`) split across FOUR real object-array
37
- * attributes by intent — general `certifications`,
38
- * `sustainability_certifications`, `material_health_certifications`, and
39
- * `voc_emissions_testing_certificate` (GREENGUARD Gold / FloorScore / CDPH land
40
- * in the last). A caller shouldn't have to know which bucket a programme fell
41
- * into, so this single virtual id matches the `programme` term across all four.
42
- *
43
- * The `term` may be a display name or alias ("GREENGUARD Gold", "LEED"), resolved
44
- * server-side to its canonical programme token, or a canonical token directly
45
- * (e.g. `greenguard_certificate`, `has_greenguard_gold_certification`).
46
- *
47
- * @example
48
- * // "find a GREENGUARD-certified alternative"
49
- * client.search({
50
- * similarTo: { variantId: seed },
51
- * filters: [{ attributeId: CERTIFICATION_PROGRAMME_ATTRIBUTE_ID, term: "GREENGUARD Gold" }],
52
- * });
53
- */
54
- export const CERTIFICATION_PROGRAMME_ATTRIBUTE_ID = "certification_programme";
@@ -1,34 +0,0 @@
1
- import type { CertificationProgrammeFilter, SpecPresenceFilter, SpecTermFilter } from "./spec-types.js";
2
- /**
3
- * A term-equality criterion for `search({ filters })` — the attribute must
4
- * carry this canonical value.
5
- *
6
- * @example
7
- * // "same fire class as the seed"
8
- * client.search({ similarTo: { variantId }, filters: [specTerm("fire_classifications", "astm_e84_class_a")] });
9
- */
10
- export declare const specTerm: (attributeId: string, term: string) => SpecTermFilter;
11
- /**
12
- * A presence criterion for `search({ filters })` — the attribute must have ANY
13
- * accepted value.
14
- *
15
- * @example
16
- * // "…and it must have an EPD on record"
17
- * client.search({ similarTo: { variantId }, filters: [hasSpec("environmental_product_declaration")] });
18
- */
19
- export declare const hasSpec: (attributeId: string) => SpecPresenceFilter;
20
- /**
21
- * A certification-programme criterion for `search({ filters })`, pinned to the
22
- * virtual attribute so a programme matches wherever it landed at ingest
23
- * (compliance / sustainability / material-health / VOC buckets).
24
- *
25
- * The value is a canonical programme token (e.g. `greenguard_certificate`,
26
- * `has_greenguard_gold_certification`). The one search matches tokens on its
27
- * index and does not resolve display names, so a name such as
28
- * "GREENGUARD Gold" is a 400 naming the term.
29
- *
30
- * @example
31
- * // "find a GREENGUARD-certified alternative"
32
- * client.search({ similarTo: { variantId: seed }, filters: [certificationProgramme("has_greenguard_gold_certification")] });
33
- */
34
- export declare const certificationProgramme: (programme: string) => CertificationProgrammeFilter;
package/specifications.js DELETED
@@ -1,56 +0,0 @@
1
- /**
2
- * Zero-dependency builders for the specification surface. Turn a
3
- * plain-language spec intent into a typed {@link SpecFilter} `search({
4
- * filters })` accepts: hard token sets on the one search's index, run in the
5
- * same query. Build them with {@link specTerm}, {@link hasSpec}, and
6
- * {@link certificationProgramme}.
7
- *
8
- * The point of these helpers: Desk (and any spec-driven caller) never has to
9
- * remember that a certification programme lives under a VIRTUAL attribute.
10
- *
11
- * A hard specification across every brand (formerly `bySpec`, built with
12
- * `specCriterion`/`specRangeCriterion`) is now `list_variants`'s `filters`:
13
- * `{field: attributeId, op: "in", values: [...]}` for a term,
14
- * `{field: attributeId, op: "gte"/"lte"/etc, value}` for a number — a plain
15
- * object literal needs no encoder (MG-2222).
16
- */
17
- import { CERTIFICATION_PROGRAMME_ATTRIBUTE_ID } from "./spec-types.js";
18
- /**
19
- * A term-equality criterion for `search({ filters })` — the attribute must
20
- * carry this canonical value.
21
- *
22
- * @example
23
- * // "same fire class as the seed"
24
- * client.search({ similarTo: { variantId }, filters: [specTerm("fire_classifications", "astm_e84_class_a")] });
25
- */
26
- export const specTerm = (attributeId, term) => ({ attributeId, term });
27
- /**
28
- * A presence criterion for `search({ filters })` — the attribute must have ANY
29
- * accepted value.
30
- *
31
- * @example
32
- * // "…and it must have an EPD on record"
33
- * client.search({ similarTo: { variantId }, filters: [hasSpec("environmental_product_declaration")] });
34
- */
35
- export const hasSpec = (attributeId) => ({
36
- attributeId,
37
- hasValue: true,
38
- });
39
- /**
40
- * A certification-programme criterion for `search({ filters })`, pinned to the
41
- * virtual attribute so a programme matches wherever it landed at ingest
42
- * (compliance / sustainability / material-health / VOC buckets).
43
- *
44
- * The value is a canonical programme token (e.g. `greenguard_certificate`,
45
- * `has_greenguard_gold_certification`). The one search matches tokens on its
46
- * index and does not resolve display names, so a name such as
47
- * "GREENGUARD Gold" is a 400 naming the term.
48
- *
49
- * @example
50
- * // "find a GREENGUARD-certified alternative"
51
- * client.search({ similarTo: { variantId: seed }, filters: [certificationProgramme("has_greenguard_gold_certification")] });
52
- */
53
- export const certificationProgramme = (programme) => ({
54
- attributeId: CERTIFICATION_PROGRAMME_ATTRIBUTE_ID,
55
- term: programme,
56
- });
package/validation.d.ts DELETED
@@ -1,43 +0,0 @@
1
- /**
2
- * The SDK's shared wire-validation primitives — the vocabulary every hand-rolled
3
- * mirror of a canonical Zod schema is built from.
4
- *
5
- * The SDK publishes with ZERO runtime dependencies, so it cannot call
6
- * `schema.parse()` on anything. Where a wire payload must be proven rather than
7
- * trusted, it is checked with these helpers and the result is pinned against the
8
- * real schema in `contract-parity.test.ts`. Each helper mirrors one Zod
9
- * construct, named for it, so a reviewer can read the mirror against the schema
10
- * line by line.
11
- *
12
- * Every helper takes the same three shapes: the value, a `what` path used in the
13
- * diagnostic (`results[3].verdict.cells[0].state`), and a {@link ValidationFailure}
14
- * owned by the calling module — so one payload's failures all read alike without
15
- * this file knowing what a Match frame or a Run event is.
16
- */
17
- import type { MaterialGraphError } from "./errors.js";
18
- /**
19
- * Builds the one error a validation run raises. The calling module supplies it,
20
- * so the message names that payload and quotes its source.
21
- */
22
- export type ValidationFailure = (reason: string) => MaterialGraphError;
23
- /** A `z.object({…})`. */
24
- export declare const requireRecord: (value: unknown, what: string, fail: ValidationFailure) => Record<string, unknown>;
25
- /** A `z.string()`. */
26
- export declare const requireString: (value: unknown, what: string, fail: ValidationFailure) => string;
27
- /** A `z.string().optional()`: absent is fine, present-but-wrong is not. */
28
- export declare const requireOptionalString: (value: unknown, what: string, fail: ValidationFailure) => void;
29
- /** A `z.string().nullable()`. */
30
- export declare const requireNullableString: (value: unknown, what: string, fail: ValidationFailure) => string | null;
31
- /** A `z.boolean()`. */
32
- export declare const requireBoolean: (value: unknown, what: string, fail: ValidationFailure) => boolean;
33
- /** A `z.boolean().optional()`. */
34
- export declare const requireOptionalBoolean: (value: unknown, what: string, fail: ValidationFailure) => void;
35
- /**
36
- * A `z.number()`. Zod 4 rejects `NaN` AND `±Infinity` for it, and `1e999` parses
37
- * out of JSON as `Infinity`, so finiteness is a reachable check.
38
- */
39
- export declare const requireNumber: (value: unknown, what: string, fail: ValidationFailure) => number;
40
- /** A `z.enum([…])` — CLOSED, exactly like the canonical member list. */
41
- export declare const requireEnum: (value: unknown, allowed: readonly string[], what: string, fail: ValidationFailure) => string;
42
- /** A `z.array(…)`. */
43
- export declare const requireArray: (value: unknown, what: string, fail: ValidationFailure) => unknown[];
package/validation.js DELETED
@@ -1,117 +0,0 @@
1
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2
- /** A `z.object({…})`. */
3
- export const requireRecord = (value, what, fail) => {
4
- if (!isRecord(value)) {
5
- throw fail(`${what} is not a JSON object`);
6
- }
7
- return value;
8
- };
9
- /** A `z.string()`. */
10
- export const requireString = (value, what, fail) => {
11
- if (typeof value !== "string") {
12
- throw fail(`${what} is not a string`);
13
- }
14
- return value;
15
- };
16
- /** A `z.string().optional()`: absent is fine, present-but-wrong is not. */
17
- export const requireOptionalString = (value, what, fail) => {
18
- if (value === undefined) {
19
- return;
20
- }
21
- requireString(value, what, fail);
22
- };
23
- /** A `z.string().nullable()`. */
24
- export const requireNullableString = (value, what, fail) => {
25
- if (value === null) {
26
- return null;
27
- }
28
- return requireString(value, what, fail);
29
- };
30
- /** A `z.boolean()`. */
31
- export const requireBoolean = (value, what, fail) => {
32
- if (typeof value !== "boolean") {
33
- throw fail(`${what} is not a boolean`);
34
- }
35
- return value;
36
- };
37
- /** A `z.boolean().optional()`. */
38
- export const requireOptionalBoolean = (value, what, fail) => {
39
- if (value === undefined) {
40
- return;
41
- }
42
- requireBoolean(value, what, fail);
43
- };
44
- /**
45
- * A `z.number()`. Zod 4 rejects `NaN` AND `±Infinity` for it, and `1e999` parses
46
- * out of JSON as `Infinity`, so finiteness is a reachable check.
47
- */
48
- export const requireNumber = (value, what, fail) => {
49
- if (typeof value !== "number" || !Number.isFinite(value)) {
50
- throw fail(`${what} is not a finite number`);
51
- }
52
- return value;
53
- };
54
- /** A `z.enum([…])` — CLOSED, exactly like the canonical member list. */
55
- export const requireEnum = (value, allowed, what, fail) => {
56
- const text = requireString(value, what, fail);
57
- if (!allowed.includes(text)) {
58
- throw fail(`${what} is not one of ${allowed.join(" | ")} (${text})`);
59
- }
60
- return text;
61
- };
62
- /** A `z.array(…)`. */
63
- export const requireArray = (value, what, fail) => {
64
- if (!Array.isArray(value)) {
65
- throw fail(`${what} is not an array`);
66
- }
67
- return value;
68
- };
69
- /**
70
- * A `z.json()` — a JSON value, recursively: `string | number | boolean | null`,
71
- * an array of them, or an object of them, at ANY depth.
72
- *
73
- * WHY THIS EXISTS, given it can never fire over the wire. Every SDK response
74
- * has already been through `JSON.parse`, so a `Date`, `bigint`, `undefined`, or
75
- * function CANNOT be present by construction — this check is unreachable in
76
- * practice. It is here for PARITY: the canonical `sourceImageAnalysisResultSchema`
77
- * uses `z.json()` precisely because those four break differently on the way to
78
- * `jsonb` (a `bigint` throws in `JSON.stringify`, an `undefined` is silently
79
- * dropped, and a `Date` round-trips as an ISO *string* — a different type read
80
- * back than written). The mirror has to reject what the schema rejects or the
81
- * parity test is lying. Do not delete this as dead code.
82
- */
83
- const requireJsonValue = (value, what, fail) => {
84
- if (value === null) {
85
- return null;
86
- }
87
- if (typeof value === "string" || typeof value === "boolean") {
88
- return value;
89
- }
90
- if (typeof value === "number") {
91
- // `z.json()`'s number leaf rejects NaN and ±Infinity, and `1e999` parses out
92
- // of JSON as `Infinity` — so finiteness IS reachable, unlike the rest.
93
- if (!Number.isFinite(value)) {
94
- throw fail(`${what} is not a finite number`);
95
- }
96
- return value;
97
- }
98
- if (Array.isArray(value)) {
99
- return value.map((item, index) => requireJsonValue(item, `${what}[${index}]`, fail));
100
- }
101
- if (isRecord(value)) {
102
- // A PLAIN object only. `isRecord` alone is not enough: it answers true for a
103
- // `Date`, a `Map`, and any class instance, none of which have own enumerable
104
- // entries — so recursing over them would silently accept them as `{}` while
105
- // `z.json()` rejects them outright. A `Date` reaching `jsonb` is exactly the
106
- // failure this check exists to stop.
107
- const prototype = Object.getPrototypeOf(value);
108
- if (prototype !== Object.prototype && prototype !== null) {
109
- throw fail(`${what} is not a JSON value`);
110
- }
111
- return Object.fromEntries(Object.entries(value).map(([key, item]) => [
112
- key,
113
- requireJsonValue(item, `${what}.${key}`, fail),
114
- ]));
115
- }
116
- throw fail(`${what} is not a JSON value`);
117
- };
File without changes