@instruments/materialgraph 0.33.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 +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 +57 -100
  20. package/client.js +63 -101
  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 +4 -13
  33. package/index.js +1 -3
  34. package/list-variant-facets-types.d.ts +4 -4
  35. package/list-variants-types.d.ts +71 -21
  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 +13 -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 +39 -70
  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 +20 -2
  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/material-query-plan-types.js +0 -1
  60. package/palette-search-types.d.ts +0 -171
  61. package/palette-search-types.js +0 -1
  62. package/query-understanding-types.d.ts +0 -254
  63. package/query-understanding-types.js +0 -1
  64. package/resolve-colour-types.js +0 -1
  65. package/search-card-types.d.ts +0 -86
  66. package/search-card-types.js +0 -1
  67. package/search-index-plan-types.d.ts +0 -74
  68. package/search-index-plan-types.js +0 -1
  69. package/search-refinement-types.d.ts +0 -103
  70. package/search-refinement-types.js +0 -2
  71. package/search-request-types.d.ts +0 -170
  72. package/search-request-types.js +0 -1
  73. package/search-response-receipt-types.d.ts +0 -24
  74. package/search-response-receipt-types.js +0 -1
  75. package/search-response-types.d.ts +0 -11
  76. package/search-response-types.js +0 -1
  77. package/search-result-validation.d.ts +0 -9
  78. package/search-result-validation.js +0 -83
  79. package/spec-types.d.ts +0 -109
  80. package/spec-types.js +0 -54
  81. package/specifications.d.ts +0 -34
  82. package/specifications.js +0 -56
  83. package/validation.d.ts +0 -43
  84. package/validation.js +0 -117
  85. /package/{by-colour-types.js → color-profiles-types.js} +0 -0
  86. /package/{colour-region-types.js → find-variants-by-color-types.js} +0 -0
  87. /package/{corpus-scope-types.js → resolve-color-types.js} +0 -0
  88. /package/{find-variants-by-colour-types.js → search-catalogue-subset-types.js} +0 -0
@@ -1,86 +0,0 @@
1
- /**
2
- * @module sdk/search-card-types
3
- * @description The `view:"card"` search projection — the lean grid item and the
4
- * envelope that carries it.
5
- *
6
- * Split out of `types.ts` — which is over the repository's source-size limit —
7
- * along the same seam `match-types.ts` took: one view of one surface, and
8
- * nothing else. The full-view item and the shared envelope stay in `types.ts`,
9
- * which this module reads `SearchResponse` from.
10
- */
11
- import type { ProductId, VariantId } from "./identifiers.js";
12
- import type { OpenEnum, SearchData, SearchResponse } from "./types.js";
13
- /**
14
- * The lean `view:"card"` result item — identity + name + code + thumbnail +
15
- * score.
16
- *
17
- * A PROJECTION, not a smaller answer: a field the card omits (palette, verdict,
18
- * signals, availability) is unread here, never known-absent. Re-read the same
19
- * `variantId` at `view: "full"` or via `getVariant()` before claiming anything the
20
- * card does not carry.
21
- */
22
- export type SearchCardItem = {
23
- /** Accepted sampleability fact. Absent means unknown, never false. */
24
- sampleable?: boolean;
25
- /** Opaque — round-trip verbatim. */
26
- variantId: VariantId;
27
- productId: ProductId | null;
28
- brandSlug: string | null;
29
- brand: string;
30
- /** Product name (the full item's `product`). */
31
- name: string;
32
- variantName: string | null;
33
- imageUrl: string | null;
34
- /**
35
- * SHA-256 of the displayed image `imageUrl` points at, as a hex digest. Key a
36
- * cache or dedupe on it rather than the URL. ABSENT means no hash is on
37
- * record, never that there is no image.
38
- */
39
- imageSha256?: string;
40
- /**
41
- * How many eligible variants the row's product has, the same set
42
- * `listVariants` pages, so a follow-up list returns this many: the card's
43
- * "12 colourways". ABSENT means the product is unknown or has no eligible
44
- * variant.
45
- */
46
- variantCount?: number;
47
- /**
48
- * The manufacturer's own item code, the same key the full item carries.
49
- * Reach for it when two cards read alike: `name` is the PRODUCT name, and a
50
- * paint product is a whole collection, so brand, product name and variant
51
- * name together are shared by most of the catalogue. ABSENT means no code is
52
- * recorded, never that the variant cannot be ordered.
53
- */
54
- manufacturerCode?: string;
55
- projectSaveCapability?: "allowed";
56
- sourceStatus?: "provisional";
57
- score: number;
58
- /**
59
- * Resolved values for the attribute ids sent in `returnAttributes`, keyed by
60
- * attribute id. ABSENT entirely when `returnAttributes` was not sent.
61
- *
62
- * Three states, all different, and reading them as two is the mistake this
63
- * shape exists to prevent: no `attributes` key means you did not ask; a key
64
- * whose value is `null` means this material carries no accepted value for that
65
- * attribute; anything else is what the graph holds. Values resolve with the
66
- * normal variant-over-product precedence, and an array is a multi-valued
67
- * attribute rather than a list of candidates.
68
- */
69
- attributes?: Record<string, string | string[] | null>;
70
- };
71
- /** The `view:"card"` response form — the lean grid projection of {@link SearchResponse}. */
72
- export type SearchCardResponse = {
73
- data: Omit<SearchData, "view" | "results"> & {
74
- view: OpenEnum<"card">;
75
- results: SearchCardItem[];
76
- };
77
- } & Omit<SearchResponse, "data">;
78
- /**
79
- * The request forms the server answers with {@link SearchCardResponse}:
80
- * `view: "card"`, or `intent: "typeahead"`, which forces the card view.
81
- */
82
- export type CardProjectionRequest = {
83
- view: "card";
84
- } | {
85
- intent: "typeahead";
86
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,74 +0,0 @@
1
- /**
2
- * @module search-index-plan-types
3
- * @description The plan the one search ran on, as `/search` reports it under
4
- * `explain.plan` (ADR 0031). Mirrors `searchIndexPlanReceiptSchema` in
5
- * `@materialgraph/products/search-index/plan-schema`; `key-parity.test.ts`
6
- * holds the two to the same fields.
7
- */
8
- import type { ColourRegionRange } from "./colour-region-types.js";
9
- import type { CorpusScope } from "./corpus-scope-types.js";
10
- /** One attribute the plan wants: its terms, or any value when `presence`. */
11
- type SearchIndexPlanWant = {
12
- attributeId: string;
13
- /** Share of the category's materials stating the attribute, 0 to 1. */
14
- fill: number;
15
- /** The words it was read from, or `"(filter)"` for a caller's filter. */
16
- phrase: string;
17
- /** Any stated value satisfies it; `terms` is then empty. */
18
- presence?: true;
19
- /** A hard want removes rows; a soft one only counts toward the order. */
20
- strength: "hard" | "soft";
21
- terms: string[];
22
- };
23
- /**
24
- * The query's plan: the category, brands, wanted and excluded attribute terms,
25
- * the colour, the words left for text, and the caller's corpus scope.
26
- */
27
- export type SearchIndexPlan = {
28
- brands: {
29
- ids: string[];
30
- names: string[];
31
- phrase: string;
32
- } | null;
33
- category: {
34
- label: string;
35
- phrase: string;
36
- productTypeIds: string[];
37
- } | null;
38
- colour: {
39
- family12: string[];
40
- familyId: string;
41
- hex: string;
42
- oklab: [number, number, number];
43
- phrase: string;
44
- /** The colour's region, when the plan holds one rather than a point. */
45
- range?: ColourRegionRange;
46
- } | null;
47
- /** Present only when the caller sent a scope narrower than the whole graph. */
48
- corpusScope?: Exclude<CorpusScope, "all">;
49
- excludeBrands: string[];
50
- excludes: {
51
- attributeId: string;
52
- phrase: string;
53
- term: string;
54
- }[];
55
- /** Words the reading did not place, left for the text match. */
56
- leftover: string[];
57
- normalised: string;
58
- softAlone: boolean;
59
- vendorColourNames: string[];
60
- wants: SearchIndexPlanWant[];
61
- };
62
- /**
63
- * Which reading a search ran on (`version`, `source`), whether it came from
64
- * the cache, the plan itself, and its signature, so two runs of one request
65
- * can be compared plan for plan.
66
- */
67
- export type SearchPlanReceipt = {
68
- cached: boolean;
69
- plan: SearchIndexPlan;
70
- signature: string;
71
- source: "dictionary" | "jev";
72
- version: string;
73
- };
74
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,103 +0,0 @@
1
- /** The spec-filter vocabulary MaterialGraph's filter and preference inputs share. */
2
- /**
3
- * A hard canonical-attribute filter accepted by the search engine — a term match
4
- * (`{ attributeId, term }`), a presence check (`{ attributeId, hasValue: true }`)
5
- * or a NUMERIC RANGE (`{ attributeId, min, max }`, bounds inclusive, at least one
6
- * stated). Applied as a restriction BEFORE ranking.
7
- *
8
- * The `attributeId` may be a real registry id (`"fire_classifications"`,
9
- * `"environmental_product_declaration"`) OR the virtual
10
- * `"certification_programme"` (see `CERTIFICATION_PROGRAMME_ATTRIBUTE_ID`),
11
- * which matches a programme — GREENGUARD, FloorScore, LEED, an EPD — wherever it
12
- * landed across the four certification buckets. Presence (`hasValue`) is NOT
13
- * supported for the virtual programme attribute. The typed `spec-types` mirror
14
- * and the `specifications` builders produce values assignable to this type.
15
- */
16
- /**
17
- * Optional per-filter TRUST bar the matching value must clear.
18
- *
19
- * Raising the bar shrinks the result set for a reason worth stating: the
20
- * materials it removes are not materials that fail the spec, they are materials
21
- * whose claim is weaker than you demanded. That distinction is the whole point
22
- * of this object — without it, "no GREENGUARD results" reads as "nobody is
23
- * certified" when it means "nobody is certified TO THIS EVIDENTIAL STANDARD".
24
- */
25
- export type SearchFilterTrust = {
26
- /** Confidence floor, 0–1. A value below it is excluded, not marked failed. */
27
- minConfidence?: number;
28
- /**
29
- * Demand an EXACT source basis (closed enum — an unknown basis is rejected).
30
- * Strongest to weakest as evidence:
31
- * - `declared` — the manufacturer stated it. Attributable, not independent.
32
- * - `catalog_feed` — it arrived in a catalog feed. Trusted at ingest, unreviewed.
33
- * - `derived` — MaterialGraph inferred it. Ours, not the manufacturer's — never
34
- * present a `derived` value as something the brand claims.
35
- *
36
- * There is NO `tested` basis. This docblock advertised one as "the strongest
37
- * claim available" through 0.12.0, and the union listed `declared` twice in
38
- * its place — so a caller reading the documentation would write a value the
39
- * server rejects with a 400, and the type would not stop them because the
40
- * fourth arm was a duplicate rather than the value described.
41
- * `packages/schema/src/source-basis.test.ts` asserts `tested` parses false;
42
- * its absence is deliberate, not an omission.
43
- */
44
- sourceBasis?: "catalog_feed" | "declared" | "derived";
45
- /** Fold the attribute's own registry confidence requirement in, server-side. */
46
- meetsRequirement?: true;
47
- };
48
- /**
49
- * One HARD criterion. Applied as a restriction BEFORE ranking, so a filtered
50
- * search's empty result means "nothing on record satisfies this", never "nothing
51
- * exists" — the corpus's silence and the world's are different things. Use
52
- * `preferences` instead when a miss should demote rather than remove.
53
- */
54
- export type SearchFilter = {
55
- attributeId: string;
56
- term: string;
57
- trust?: SearchFilterTrust;
58
- } | {
59
- attributeId: string;
60
- excludesTerm: string;
61
- trust?: SearchFilterTrust;
62
- /**
63
- * What an UNKNOWN effective value does. Absent or `"drop"` (default)
64
- * requires a known effective value — a variant that never states the
65
- * attribute does not pass. `"pass"` admits it — a variant only fails by
66
- * STATING the excluded term.
67
- */
68
- whenUnstated?: "drop" | "pass";
69
- }
70
- /**
71
- * TERM SET — the attribute must equal any ONE of these. OR within the entry;
72
- * AND still holds between entries, so a filter rail composes the same way
73
- * whatever it is filtering on.
74
- *
75
- * The server has accepted this since the term-set arm shipped, and a consumer
76
- * has been sending it successfully on the wire — but it was missing from this
77
- * union through 0.12.0, so multi-value filtering type-checked nowhere while
78
- * working everywhere. Brand multi-select is the common case:
79
- * `{attributeId: "brand", terms: ["kvadrat", "interface"]}`.
80
- */
81
- | {
82
- attributeId: string;
83
- terms: string[];
84
- trust?: SearchFilterTrust;
85
- } | {
86
- attributeId: string;
87
- hasValue: true;
88
- trust?: SearchFilterTrust;
89
- }
90
- /** Date-sensitive presence is currently executable only for EPD expiration. */
91
- | {
92
- attributeId: "epd_expiration_date";
93
- hasValue: true;
94
- asOf: string;
95
- trust?: SearchFilterTrust;
96
- } | {
97
- attributeId: string;
98
- /** Inclusive lower bound. Omit for a ceiling-only range. */
99
- min?: number;
100
- /** Inclusive upper bound. Omit for a floor-only range. */
101
- max?: number;
102
- trust?: SearchFilterTrust;
103
- };
@@ -1,2 +0,0 @@
1
- /** The spec-filter vocabulary MaterialGraph's filter and preference inputs share. */
2
- export {};
@@ -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
- };