@instruments/materialgraph 0.33.0 → 0.36.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 (92) 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 +1 -1
  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 +62 -105
  20. package/client.js +68 -106
  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-sampled-together-types.d.ts +70 -0
  25. package/{find-companions-types.js → find-sampled-together-types.js} +4 -4
  26. package/{find-variants-by-colour-types.d.ts → find-variants-by-color-types.d.ts} +70 -64
  27. package/find-variants-by-vector-types.d.ts +4 -1
  28. package/get-variant-types.d.ts +14 -2
  29. package/http-transport.d.ts +1 -1
  30. package/http-transport.js +1 -2
  31. package/identifiers.d.ts +2 -2
  32. package/identifiers.js +1 -1
  33. package/index.d.ts +5 -14
  34. package/index.js +1 -3
  35. package/list-variant-facets-types.d.ts +4 -4
  36. package/list-variants-types.d.ts +71 -21
  37. package/material-asset-types.d.ts +3 -3
  38. package/material-block-types.d.ts +9 -9
  39. package/material-types.d.ts +10 -8
  40. package/operations.d.ts +2 -2
  41. package/operations.js +19 -25
  42. package/package.json +2 -2
  43. package/{resolve-colour-types.d.ts → resolve-color-types.d.ts} +18 -18
  44. package/response-schemas.generated.d.ts +2 -2
  45. package/response-schemas.generated.js +41 -71
  46. package/response-schemas.js +1 -1
  47. package/samplize-catalogue-types.d.ts +4 -4
  48. package/samplize-colorvec.js +6 -6
  49. package/{corpus-scope-types.d.ts → search-catalogue-subset-types.d.ts} +3 -3
  50. package/search-variants-types.d.ts +33 -2
  51. package/types.d.ts +32 -284
  52. package/webmcp.generated.js +4 -9
  53. package/attribution-types.d.ts +0 -51
  54. package/attribution-types.js +0 -13
  55. package/by-colour-types.d.ts +0 -320
  56. package/colour-region-types.d.ts +0 -18
  57. package/colours-client.d.ts +0 -70
  58. package/colours-client.js +0 -38
  59. package/find-companions-types.d.ts +0 -70
  60. package/material-query-plan-types.d.ts +0 -70
  61. package/material-query-plan-types.js +0 -1
  62. package/ndjson.d.ts +0 -46
  63. package/ndjson.js +0 -111
  64. package/palette-search-types.d.ts +0 -171
  65. package/palette-search-types.js +0 -1
  66. package/query-understanding-types.d.ts +0 -254
  67. package/query-understanding-types.js +0 -1
  68. package/resolve-colour-types.js +0 -1
  69. package/search-card-types.d.ts +0 -86
  70. package/search-card-types.js +0 -1
  71. package/search-index-plan-types.d.ts +0 -74
  72. package/search-index-plan-types.js +0 -1
  73. package/search-refinement-types.d.ts +0 -103
  74. package/search-refinement-types.js +0 -2
  75. package/search-request-types.d.ts +0 -170
  76. package/search-request-types.js +0 -1
  77. package/search-response-receipt-types.d.ts +0 -24
  78. package/search-response-receipt-types.js +0 -1
  79. package/search-response-types.d.ts +0 -11
  80. package/search-response-types.js +0 -1
  81. package/search-result-validation.d.ts +0 -9
  82. package/search-result-validation.js +0 -83
  83. package/spec-types.d.ts +0 -109
  84. package/spec-types.js +0 -54
  85. package/specifications.d.ts +0 -34
  86. package/specifications.js +0 -56
  87. package/validation.d.ts +0 -43
  88. package/validation.js +0 -117
  89. /package/{by-colour-types.js → color-profiles-types.js} +0 -0
  90. /package/{colour-region-types.js → find-variants-by-color-types.js} +0 -0
  91. /package/{corpus-scope-types.js → resolve-color-types.js} +0 -0
  92. /package/{find-variants-by-colour-types.js → search-catalogue-subset-types.js} +0 -0
@@ -1,170 +0,0 @@
1
- /**
2
- * @module search-request-types
3
- * The two request shapes for the `/search` surface: the POST JSON body and the
4
- * GET query-string form. Split out of `types.ts` so neither file outgrows the
5
- * repo's source-size gate; every name here is re-exported from `types.ts`, so
6
- * no import path changes.
7
- */
8
- import type { ColourRegionRange } from "./colour-region-types.js";
9
- import type { CorpusScope } from "./corpus-scope-types.js";
10
- import type { Cursor, VariantId } from "./identifiers.js";
11
- import type { OpenEnum, SearchImageInput } from "./types.js";
12
- /**
13
- * One attribute filter the one search runs as a hard token set on its index
14
- * (ADR 0031, MG-2158). Entries on one attribute are OR'd; entries on different
15
- * attributes AND together.
16
- *
17
- * - `term`: the value must carry this canonical term.
18
- * - `terms`: any one of these terms satisfies it.
19
- * - `excludesTerm`: the value must not carry this term. A material that states
20
- * nothing for the attribute passes.
21
- * - `hasValue: true`: any stated value.
22
- * - `min` and/or `max`: an inclusive numeric range, on a numeric attribute
23
- * (`"nrc_rating"`), a dimensions lens in millimetres
24
- * (`"dimensions.thickness_mm"`, `"dimensions.short_side_mm"`) or a
25
- * composition share on 0 to 1 (`"composition_weighted.wool"`). It is read as
26
- * the number bands wholly inside it, so a bound between two band edges is
27
- * read inward; a range no band fits inside is a 400.
28
- *
29
- * `attributeId` is an attribute the index carries tokens for, or
30
- * `"certification_programme"` for any certification programme. Terms are
31
- * canonical dictionary terms, not display names. Numeric attributes take
32
- * `hasValue` or a range. An attribute or term the index does not carry is a
33
- * 400 naming it.
34
- */
35
- export type SearchIndexFilter = {
36
- attributeId: string;
37
- term: string;
38
- } | {
39
- attributeId: string;
40
- terms: string[];
41
- } | {
42
- attributeId: string;
43
- excludesTerm: string;
44
- } | {
45
- attributeId: string;
46
- hasValue: true;
47
- } | {
48
- attributeId: string;
49
- min?: number;
50
- max?: number;
51
- };
52
- /**
53
- * The POST `/search` JSON body (ADR 0031): the words, or a look-alike seed, and
54
- * the page wanted. `category`, `filters`, `corpusScope`, `colour` and `brands`
55
- * become fields of the query's plan and run in the same index query. The order
56
- * is the index's own, so nothing here sets weights, a blend or a sort. Send
57
- * `text`, `similarTo`, `image`, `colour`, `category`, `filters` or `brands`; a
58
- * corpus scope alone does not say what to find. Any other field is a 400
59
- * naming it.
60
- */
61
- export type SearchInput = {
62
- /**
63
- * Free text ("oak flooring", "sage green velvet", "Kvadrat wool"). Brands,
64
- * categories, colours and attributes are read from the words unless
65
- * `brands`, `category` or `colour` says so instead; a corrected word is
66
- * reported under `correctedQuery`.
67
- */
68
- text?: string;
69
- /** A query image (URL or base64); `text`, when also given, narrows it. */
70
- image?: SearchImageInput;
71
- /**
72
- * Materials that LOOK like a seed: an existing variant, a Material Bank SKU
73
- * (an unknown SKU is a 400 naming it), or a pre-computed image vector.
74
- */
75
- similarTo?: {
76
- variantId: VariantId;
77
- } | {
78
- materialBankId: string;
79
- } | {
80
- vector: string;
81
- };
82
- /**
83
- * A hard category gate: public category-group or product-type names, or
84
- * taxonomy keys. It replaces the category the words were read as. An
85
- * unknown value is a 400 listing every valid name.
86
- */
87
- category?: {
88
- filter: string[];
89
- };
90
- /** Hard attribute filters, 1 to 24. See {@link SearchIndexFilter}. */
91
- filters?: SearchIndexFilter[];
92
- /**
93
- * Order the page by a colour: a `hex`, or `variantId` for the colour of an
94
- * existing Variant. The page is the colour's family, nearest first by OKLab
95
- * distance; it replaces any colour read from `text`. A Variant with no colour
96
- * on the index is a 400.
97
- *
98
- * `oklab` and `range` are additive: send them when you already resolved the
99
- * exact OKLab point or the named colour's region (see `resolveColour()`), and
100
- * the plan carries that colour without re-deriving it from `hex`. `hex` stays
101
- * required.
102
- */
103
- colour?: {
104
- hex: string;
105
- oklab?: [number, number, number];
106
- range?: ColourRegionRange;
107
- } | {
108
- variantId: VariantId;
109
- };
110
- /**
111
- * Brands the page must come from: canonical brand ids, exact slugs
112
- * ("sherwin-williams") or exact display names ("Sherwin-Williams"), up to
113
- * 50. They replace the brands `text` was read as. An empty list is the
114
- * same request as omitting it — every brand. An unrecognised value is a
115
- * 400 naming it; a display name shared by more than one brand is a 400
116
- * naming the exact slugs to choose between; a brand MaterialGraph holds no
117
- * materials under is a 400 naming a populated sibling when one exists.
118
- */
119
- brands?: string[];
120
- /**
121
- * Attribute ids (at most 8) whose resolved value should ride each result
122
- * under `attributes`, keyed by attribute id. Not a plan field: it changes
123
- * no ordering and plays no part in the cursor. An attribute you did not
124
- * ask for is not a key on `attributes` at all; one you asked for that a
125
- * result carries no accepted value for is `null`; anything else is the
126
- * value. An unknown attribute id is a 400 naming it.
127
- */
128
- returnAttributes?: string[];
129
- /**
130
- * Which corpus to answer from, applied inside the index query. ABSENT (or
131
- * `"all"`) means the whole graph. `"materialBankOrderable"` is a provenance
132
- * signal (a Material Bank identity), not a storefront; the channel-backed
133
- * scopes (`"materialBankNaOrderable"`, `"materialBankEuOrderable"`,
134
- * `"designShopOrderable"`) answer where a material can be ordered,
135
- * `"materialBankNaAndCatalogue"` is a governed discovery corpus (the union
136
- * of Material Bank North America and Catalogue editorial membership, not a
137
- * buyability claim), and `"samplizeCatalogue"` admits published US Samplize
138
- * paint colours, regardless of stock.
139
- */
140
- corpusScope?: CorpusScope;
141
- /** Products per page (max 60). Absent, the search's own page size. */
142
- limit?: number;
143
- /**
144
- * The previous response's `cursor`, passed back verbatim. A cursor from
145
- * another query (other words, filters or scope) is a 409.
146
- */
147
- cursor?: Cursor;
148
- /** Add `timings` and `explain.plan`, the plan the search ran on. */
149
- explain?: boolean;
150
- /** Result projection: `"full"` (default) or the lean `"card"` grid form. */
151
- view?: OpenEnum<"full" | "card">;
152
- };
153
- /**
154
- * Query-string params for the GET `/search` form. `category` and `filters`
155
- * have no query-string form; send them through `search()`.
156
- */
157
- export type SearchGetParams = {
158
- q?: string;
159
- /** Seed the search from this variant's image. Opaque; pass it through verbatim. */
160
- variantId?: VariantId;
161
- /** Corpus applied inside the index query; absent means the whole graph. */
162
- corpusScope?: CorpusScope;
163
- limit?: number;
164
- /** The previous response's `cursor`, passed back verbatim. */
165
- cursor?: Cursor;
166
- /** Result projection: `"full"` (default) or the lean `"card"` grid form. */
167
- view?: OpenEnum<"full" | "card">;
168
- /** `"1"` adds `timings` and `explain.plan`. */
169
- explain?: OpenEnum<"0" | "1">;
170
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,24 +0,0 @@
1
- /**
2
- * @module sdk/search-response-receipt-types
3
- * @description The ranking source and the explain-only plan receipt a Search
4
- * response carries.
5
- */
6
- import type { SearchPlanReceipt } from "./search-index-plan-types.js";
7
- import type { OpenEnum } from "./types.js";
8
- /**
9
- * What ordered the page: `image` (a look-alike), `colour` (colour distance
10
- * inside the named colour's family), `lexical` (the query's words, by text
11
- * rank on the index), `attribute` (the category and filters alone define the
12
- * page) or `agent` (the search agent chose the variants and their order).
13
- */
14
- export type SearchMode = OpenEnum<"image" | "colour" | "lexical" | "agent" | "attribute">;
15
- /** The explain-only receipt shared by full and card search responses. */
16
- export type SearchResponseExplainReceipts = {
17
- /**
18
- * Present under `explain` only: the receipt of the plan the one search ran
19
- * on, with the plan itself (ADR 0031). Diagnostic, like `timings`.
20
- */
21
- explain?: {
22
- plan: SearchPlanReceipt;
23
- };
24
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,11 +0,0 @@
1
- import type { SearchData } from "./types.js";
2
- /** Canonical Search wire response. Raw SDK methods preserve this envelope. */
3
- export type SearchResponse = {
4
- data: SearchData;
5
- meta: {
6
- operationId: "search_materials_post" | "search_materials_get";
7
- requestId: string;
8
- };
9
- links: Record<string, never>;
10
- warnings: never[];
11
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,9 +0,0 @@
1
- import type { ValidationFailure } from "./validation.js";
2
- /**
3
- * Prove one wire value is a {@link SearchResultItem}, raising through `fail` at
4
- * the first field that does not match `searchResultItemSchema`. Checks the shape
5
- * only — unknown keys ride through untouched, per the SDK's pass-through
6
- * contract (nothing on the emit path parses, so stripping would invent a
7
- * normalisation the wire never had).
8
- */
9
- export declare const checkSearchResultItem: (value: unknown, what: string, fail: ValidationFailure) => void;
@@ -1,83 +0,0 @@
1
- /**
2
- * The one runtime validator for a {@link SearchResultItem} on the wire — the
3
- * hand-rolled, zero-runtime-dependency mirror of `searchResultItemSchema`
4
- * (`packages/products/src/search/search-result-schema.ts`) and its full
5
- * transitive closure, including `searchResultSignalsSchema`.
6
- *
7
- * WHY THIS EXISTS SEPARATELY FROM THE TYPE MIRROR. `contract-parity.test.ts`
8
- * proves the SDK's STATIC types match the canonical schemas. That is a
9
- * compile-time proof about shapes; it validates nothing at run time. A malformed
10
- * result item arriving over a stream was caught by neither, so the Match reader
11
- * now checks every item through here.
12
- *
13
- * ONE HOME. This module is the single implementation — no copy lives inside
14
- * `match-stream.ts`. It is package-internal (not re-exported from `index.ts`)
15
- * because a published symbol is a permanent contract, and nothing outside the
16
- * SDK needs to call it yet; promoting it later is additive.
17
- *
18
- * ENUMS ARE CLOSED, matching the canonical `z.enum(…)` members.
19
- */
20
- import { requireArray, requireBoolean, requireEnum, requireNullableString, requireNumber, requireOptionalBoolean, requireOptionalString, requireRecord, requireString, } from "./validation.js";
21
- // ── Leaf checks ──────────────────────────────────────────────────────────────
22
- /** `palette[]` — `{ hex, proportion, name: string.nullable().optional() }`. */
23
- const checkPaletteSwatch = (value, what, fail) => {
24
- const record = requireRecord(value, what, fail);
25
- requireString(record.hex, `${what}.hex`, fail);
26
- requireNumber(record.proportion, `${what}.proportion`, fail);
27
- // `.nullable().optional()` — absent OR null OR a string.
28
- if (record.name !== undefined) {
29
- requireNullableString(record.name, `${what}.name`, fail);
30
- }
31
- };
32
- /** `colourMeta` — three independently optional strings. */
33
- const checkColourMeta = (value, what, fail) => {
34
- const record = requireRecord(value, what, fail);
35
- requireOptionalString(record.mood, `${what}.mood`, fail);
36
- requireOptionalString(record.dominantFamily, `${what}.dominantFamily`, fail);
37
- requireOptionalString(record.dominantHex, `${what}.dominantHex`, fail);
38
- };
39
- // ── The item ─────────────────────────────────────────────────────────────────
40
- /**
41
- * Prove one wire value is a {@link SearchResultItem}, raising through `fail` at
42
- * the first field that does not match `searchResultItemSchema`. Checks the shape
43
- * only — unknown keys ride through untouched, per the SDK's pass-through
44
- * contract (nothing on the emit path parses, so stripping would invent a
45
- * normalisation the wire never had).
46
- */
47
- export const checkSearchResultItem = (value, what, fail) => {
48
- const record = requireRecord(value, what, fail);
49
- requireString(record.variantId, `${what}.variantId`, fail);
50
- requireString(record.brand, `${what}.brand`, fail);
51
- requireString(record.product, `${what}.product`, fail);
52
- requireNullableString(record.brandSlug, `${what}.brandSlug`, fail);
53
- requireNullableString(record.productId, `${what}.productId`, fail);
54
- // `z.string().optional()` — absent, or a string. NOT nullable: the emit path
55
- // omits the key rather than sending null when no MB identity is known.
56
- requireOptionalString(record.materialBankId, `${what}.materialBankId`, fail);
57
- requireOptionalString(record.manufacturerCode, `${what}.manufacturerCode`, fail);
58
- if (record.projectSaveCapability !== undefined) {
59
- requireEnum(record.projectSaveCapability, ["allowed"], `${what}.projectSaveCapability`, fail);
60
- }
61
- if (record.sourceStatus !== undefined) {
62
- requireEnum(record.sourceStatus, ["provisional"], `${what}.sourceStatus`, fail);
63
- }
64
- // Same shape as `materialBankId`: absent, or a string. The emit path omits
65
- // the key when no primary product type is on record rather than sending null,
66
- // so "we don't know what this is" and "this is a null kind of thing" cannot
67
- // be confused.
68
- requireOptionalString(record.productType, `${what}.productType`, fail);
69
- requireNullableString(record.color, `${what}.color`, fail);
70
- requireNullableString(record.variantName, `${what}.variantName`, fail);
71
- requireNullableString(record.imageUrl, `${what}.imageUrl`, fail);
72
- requireBoolean(record.hasImage, `${what}.hasImage`, fail);
73
- requireNumber(record.score, `${what}.score`, fail);
74
- const palette = requireArray(record.palette, `${what}.palette`, fail);
75
- palette.forEach((swatch, index) => {
76
- checkPaletteSwatch(swatch, `${what}.palette[${index}]`, fail);
77
- });
78
- if (record.colourMeta !== undefined) {
79
- checkColourMeta(record.colourMeta, `${what}.colourMeta`, fail);
80
- }
81
- requireOptionalBoolean(record.retired, `${what}.retired`, fail);
82
- requireOptionalBoolean(record.sampleable, `${what}.sampleable`, fail);
83
- };
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[];