@instruments/materialgraph 0.25.0 → 0.27.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 (158) hide show
  1. package/association-types.d.ts +38 -27
  2. package/associations-client.d.ts +4 -4
  3. package/associations-client.js +3 -10
  4. package/attribution-types.d.ts +4 -4
  5. package/brand-types.d.ts +14 -6
  6. package/brands-client.d.ts +7 -38
  7. package/brands-client.js +10 -40
  8. package/by-colour-types.d.ts +58 -31
  9. package/by-spec-types.d.ts +6 -73
  10. package/by-spec-types.js +7 -5
  11. package/catalog-discovery-types.d.ts +11 -26
  12. package/catalogue-client.d.ts +16 -35
  13. package/catalogue-client.js +32 -67
  14. package/catalogue-types.d.ts +44 -4
  15. package/categories-client.d.ts +63 -0
  16. package/categories-client.js +48 -0
  17. package/category-types.d.ts +114 -0
  18. package/citation-types.d.ts +4 -4
  19. package/client-config.d.ts +4 -6
  20. package/client-config.js +2 -4
  21. package/client-source-material-comments.d.ts +4 -4
  22. package/client-source-material-comments.js +4 -38
  23. package/client-vocabulary.d.ts +7 -9
  24. package/client-vocabulary.js +22 -30
  25. package/client.d.ts +110 -244
  26. package/client.js +217 -387
  27. package/codes-client.d.ts +2 -2
  28. package/codes-client.js +8 -8
  29. package/codes-types.d.ts +55 -34
  30. package/colour-aggregate-types.d.ts +24 -24
  31. package/colours-client.d.ts +6 -11
  32. package/colours-client.js +11 -19
  33. package/coordinate-client.d.ts +2 -2
  34. package/coordinate-client.js +1 -13
  35. package/corpus-scope-types.d.ts +3 -3
  36. package/corpus-scope-types.js +1 -25
  37. package/demand-batch-types.d.ts +18 -18
  38. package/demand-types.d.ts +24 -24
  39. package/errors.d.ts +13 -13
  40. package/errors.js +54 -52
  41. package/http-transport.d.ts +13 -21
  42. package/http-transport.js +31 -28
  43. package/identify-types.d.ts +54 -44
  44. package/identify-types.js +5 -3
  45. package/index.d.ts +11 -27
  46. package/index.js +4 -6
  47. package/list-variant-facets-types.d.ts +60 -0
  48. package/list-variants-types.d.ts +142 -0
  49. package/list-variants-types.js +12 -0
  50. package/match-stream-types.d.ts +45 -32
  51. package/match-stream-types.js +2 -0
  52. package/match-stream.d.ts +2 -2
  53. package/match-stream.js +67 -49
  54. package/match-types.d.ts +32 -8
  55. package/material-asset-types.d.ts +89 -69
  56. package/material-block-types.d.ts +8 -6
  57. package/material-composition-client.d.ts +21 -0
  58. package/material-composition-client.js +13 -0
  59. package/material-composition-types.d.ts +403 -0
  60. package/material-query-plan-types.d.ts +61 -145
  61. package/material-resources-client.d.ts +12 -10
  62. package/material-resources-client.js +27 -22
  63. package/material-types.d.ts +16 -24
  64. package/ndjson.js +16 -8
  65. package/operations.d.ts +6 -6
  66. package/operations.js +29 -156
  67. package/package.json +5 -1
  68. package/palette-search-types.d.ts +87 -52
  69. package/query-understanding-types.d.ts +65 -40
  70. package/resolve-client.d.ts +8 -12
  71. package/resolve-client.js +9 -13
  72. package/resolve-colour-types.d.ts +8 -8
  73. package/resolve-types.d.ts +10 -10
  74. package/response-schemas.d.ts +20 -0
  75. package/response-schemas.generated.d.ts +5 -0
  76. package/response-schemas.generated.js +160 -0
  77. package/response-schemas.js +113 -0
  78. package/run-types.d.ts +64 -145
  79. package/runs.d.ts +9 -58
  80. package/runs.js +60 -149
  81. package/samplize-catalogue-types.d.ts +18 -18
  82. package/samplize-colorvec.d.ts +20 -20
  83. package/samplize-colorvec.js +66 -42
  84. package/schema-resource-types.d.ts +52 -39
  85. package/schema-resource-types.js +5 -5
  86. package/search-card-types.d.ts +26 -4
  87. package/search-index-plan-types.d.ts +70 -0
  88. package/search-refinement-types.d.ts +10 -40
  89. package/search-refinement-types.js +1 -0
  90. package/search-request-types.d.ts +121 -204
  91. package/search-response-receipt-types.d.ts +13 -253
  92. package/search-response-types.d.ts +2 -2
  93. package/search-result-validation.d.ts +3 -19
  94. package/search-result-validation.js +42 -183
  95. package/source-material-validation.d.ts +3 -3
  96. package/source-material-validation.js +35 -39
  97. package/spec-facet-types.d.ts +11 -16
  98. package/spec-types.d.ts +23 -16
  99. package/spec-types.js +8 -7
  100. package/specifications.d.ts +14 -72
  101. package/specifications.js +39 -89
  102. package/sse.d.ts +2 -2
  103. package/sse.js +32 -16
  104. package/substitute-types.d.ts +22 -22
  105. package/suggest-types.d.ts +11 -17
  106. package/suggest-types.js +3 -3
  107. package/taxonomy-coverage-types.d.ts +8 -8
  108. package/types.d.ts +202 -405
  109. package/validation.d.ts +26 -26
  110. package/validation.js +97 -92
  111. package/webmcp.d.ts +3 -3
  112. package/webmcp.generated.js +10 -93
  113. package/brand-most-sampled-types.d.ts +0 -33
  114. package/browse-types.d.ts +0 -236
  115. package/client-browse.d.ts +0 -78
  116. package/client-browse.js +0 -72
  117. package/client-exploration.d.ts +0 -9
  118. package/client-exploration.js +0 -7
  119. package/compare-types.d.ts +0 -89
  120. package/composition-client.d.ts +0 -33
  121. package/composition-client.js +0 -27
  122. package/composition-types.d.ts +0 -455
  123. package/composition-types.js +0 -10
  124. package/correction-types.d.ts +0 -88
  125. package/exploration-types.d.ts +0 -118
  126. package/exploration-types.js +0 -1
  127. package/fan-deck-types.d.ts +0 -42
  128. package/fan-deck-types.js +0 -9
  129. package/match-rejection-types.d.ts +0 -43
  130. package/match-rejection-types.js +0 -7
  131. package/palette-completion-types.d.ts +0 -115
  132. package/palette-completion-types.js +0 -1
  133. package/project-types.d.ts +0 -79
  134. package/project-types.js +0 -9
  135. package/projects.d.ts +0 -13
  136. package/projects.js +0 -46
  137. package/render-assets-types.d.ts +0 -65
  138. package/render-assets-types.js +0 -1
  139. package/run-events.d.ts +0 -106
  140. package/run-events.js +0 -32
  141. package/schedule-reference-types.d.ts +0 -46
  142. package/schedule-reference-types.js +0 -1
  143. package/schedule-references-client.d.ts +0 -14
  144. package/schedule-references-client.js +0 -26
  145. package/schedule-types.d.ts +0 -261
  146. package/schedule-types.js +0 -1
  147. package/search-collapse-types.d.ts +0 -69
  148. package/search-collapse-types.js +0 -1
  149. package/search-mood-types.d.ts +0 -6
  150. package/search-mood-types.js +0 -1
  151. package/search-window-types.d.ts +0 -25
  152. package/search-window-types.js +0 -1
  153. package/slot-pool-types.d.ts +0 -372
  154. package/slot-pool-types.js +0 -1
  155. /package/{brand-most-sampled-types.js → category-types.js} +0 -0
  156. /package/{browse-types.js → list-variant-facets-types.js} +0 -0
  157. /package/{compare-types.js → material-composition-types.js} +0 -0
  158. /package/{correction-types.js → search-index-plan-types.js} +0 -0
package/types.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- export type { SearchResponse } from "./search-response-types.js";
2
1
  import type { ResponseAttribution } from "./attribution-types.js";
3
- import type { BySpecVerdict } from "./by-spec-types.js";
4
2
  /**
5
3
  * Hand-mirrored wire types for the MaterialGraph public API (spec 0.5.0).
6
4
  * These interfaces intentionally have NO runtime dependency on any
@@ -27,24 +25,20 @@ import type { BySpecVerdict } from "./by-spec-types.js";
27
25
  * match set. Follow the cursor for the rest, and never report a page size as
28
26
  * a total.
29
27
  */
30
- import type { CorpusScopeReceipt } from "./corpus-scope-types.js";
28
+ import type { CorpusScope } from "./corpus-scope-types.js";
31
29
  import type { Cursor, ProductId, VariantId } from "./identifiers.js";
32
- import type { MaterialDetail, MaterialTile, SiblingTile } from "./material-types.js";
33
- import type { QueryUnderstanding } from "./query-understanding-types.js";
34
- import type { CollapsedVariant, SearchAnatomyMember } from "./search-collapse-types.js";
35
- import type { MoodInterpretation } from "./search-mood-types.js";
36
- import type { SearchDisambiguation, SearchSuggestion } from "./search-refinement-types.js";
37
- import type { CompoundColourConstraintReceipt, CompoundFormConstraintReceipt, SearchExecutionPlan, SearchMode, SearchRefinementReceipt, SearchRerankReport, SearchResponseExplainReceipts } from "./search-response-receipt-types.js";
38
- import type { SearchWindowReceipt } from "./search-window-types.js";
30
+ import type { FilterOp } from "./list-variants-types.js";
31
+ import type { MaterialDetail, MaterialTile } from "./material-types.js";
32
+ import type { SearchMode, SearchResponseExplainReceipts } from "./search-response-receipt-types.js";
33
+ export type { SearchResponse } from "./search-response-types.js";
39
34
  export type * from "./search-response-receipt-types.js";
40
- export type { MoodInterpretation } from "./search-mood-types.js";
41
35
  export type OpenEnum<T extends string> = T | (string & {});
42
36
  export type { JsonValue } from "./json-value-types.js";
43
37
  /**
44
38
  * One palette swatch of a material's main image — colour DERIVED FROM PRODUCT
45
39
  * IMAGERY, so read it as visual likeness, never as a declared colour spec.
46
40
  */
47
- export interface PaletteSwatch {
41
+ export type PaletteSwatch = {
48
42
  hex: string;
49
43
  proportion: number;
50
44
  /** Stored human colour name for the swatch, when one was extracted. */
@@ -56,21 +50,21 @@ export interface PaletteSwatch {
56
50
  * derived here" — never "achromatic".
57
51
  */
58
52
  oklab?: OklabTriple;
59
- }
53
+ };
60
54
  /** Loose colour metadata for a search result's main image; every field is independently optional. */
61
- export interface ColourMeta {
55
+ export type ColourMeta = {
62
56
  mood?: string;
63
57
  dominantFamily?: string;
64
58
  dominantHex?: string;
65
- }
59
+ };
66
60
  /** OKLab triple. */
67
- export interface OklabTriple {
61
+ export type OklabTriple = {
68
62
  l: number;
69
63
  a: number;
70
64
  b: number;
71
- }
65
+ };
72
66
  /** The material's dominant-colour block (tile/detail projections). */
73
- export interface MaterialColour {
67
+ export type MaterialColour = {
74
68
  dominantHex: string | null;
75
69
  hsl: {
76
70
  h: number;
@@ -80,42 +74,26 @@ export interface MaterialColour {
80
74
  oklab: OklabTriple | null;
81
75
  chroma: number | null;
82
76
  vibrancy: number | null;
83
- }
77
+ };
84
78
  /** Where the served image came from. */
85
79
  export type ImageSource = OpenEnum<"r2" | "origin">;
86
- export type { DimensionEntry, MaterialAvailability, MaterialDetail, MaterialFacet, MaterialSuccessor, MaterialTile, SiblingTile, } from "./material-types.js";
87
- /** Per-signal scores, present on a result when `explain` was requested. */
88
- export interface SearchResultSignals {
89
- /** Voyage similarity in the dedicated variant text-portrait space. */
90
- portrait?: number | null;
80
+ export type { DimensionEntry, MaterialAvailability, MaterialDetail, MaterialFacet, MaterialSuccessor, MaterialTile, } from "./material-types.js";
81
+ /**
82
+ * Match only: the image-space cosine between the query image and this row's
83
+ * image, in [0,1]. Null when it was not measured.
84
+ */
85
+ export type SearchResultSignals = {
91
86
  image: number | null;
92
- category: number | null;
93
- colour: number | null;
94
- lexical: number | null;
95
- /** Compatibility slot for retired seeded secondary scoring; currently null. */
96
- text: number | null;
97
- }
98
- /** An accepted/measured visual meaning — descriptive evidence, never a ranking cause. */
99
- export interface VisualMeaning {
100
- id: string;
101
- label: string;
102
- values: string[];
103
- basis: OpenEnum<"canonical_attribute" | "image_colour_analysis">;
104
- confidence?: number;
105
- sourceBasis?: string;
106
- version?: string;
107
- appliedAt?: string;
108
- }
87
+ };
109
88
  /**
110
89
  * One material in a search / match result list.
111
90
  *
112
91
  * ABSENT IS NOT FALSE. The optional fields here (`colourMeta`, `signals`,
113
- * `retired`, `siblingCount`, `visualMeanings`, `verdict`) are absent when the
114
- * request did not ask for them or the graph holds no answer — never because the
115
- * answer is "no". A missing `retired` is not "still current"; a missing
116
- * `visualMeanings` is not "this material has no visual meaning".
92
+ * `retired`, `productType`) are absent when the request did not ask for them or
93
+ * the graph holds no answer — never because the answer is "no". A missing
94
+ * `retired` is not "still current".
117
95
  */
118
- export interface SearchResultItem {
96
+ export type SearchResultItem = {
119
97
  /** This hit's attribution key — see {@link ResponseAttribution}. Opaque. */
120
98
  resultId?: string;
121
99
  /** Accepted sampleability fact. Absent means unknown, never false. */
@@ -157,7 +135,20 @@ export interface SearchResultItem {
157
135
  color: string | null;
158
136
  variantName: string | null;
159
137
  imageUrl: string | null;
138
+ /**
139
+ * SHA-256 of the displayed image `imageUrl` points at, as a hex digest. Key a
140
+ * cache or dedupe on it rather than the URL. ABSENT means no hash is on
141
+ * record, never that there is no image.
142
+ */
143
+ imageSha256?: string;
160
144
  hasImage: boolean;
145
+ /**
146
+ * How many eligible variants the row's product has, the same set
147
+ * `listVariants` pages, so a follow-up list returns this many: the card's
148
+ * "12 colourways". ABSENT means the product is unknown or has no eligible
149
+ * variant.
150
+ */
151
+ variantCount?: number;
161
152
  palette: PaletteSwatch[];
162
153
  colourMeta?: ColourMeta;
163
154
  /**
@@ -175,28 +166,14 @@ export interface SearchResultItem {
175
166
  hex: string;
176
167
  };
177
168
  /**
178
- * EXPLAIN-only: the two identities a COLLAPSED result holds, which are not the
179
- * same thing and are routinely confused.
180
- *
181
- * - `representative` — the RANKING identity. Its score is the group's cursor
182
- * key and the item's `signals` describe IT, not the photograph on the card.
183
- * Omitted when the face IS the representative.
184
- * - the item's own `variantId` — the DISPLAY identity (the face).
185
- *
186
- * `siblingsTruncated` says the sibling list was capped: a product can hold
187
- * hundreds of colourways, so absence of a sibling is never evidence it does
188
- * not exist.
169
+ * Selected mode's weighted primary score; see `maxScore`. On a Match page
170
+ * this is the retrieval score and does NOT order the page: the order is
171
+ * colour agreement, then `confidence` (MG-1900).
189
172
  */
190
- anatomy?: {
191
- representative?: SearchAnatomyMember;
192
- siblings?: SearchAnatomyMember[];
193
- siblingsTruncated?: boolean;
194
- };
195
- /** Selected mode's weighted primary score; see `maxScore`. */
196
173
  score: number;
197
174
  /**
198
175
  * Match only. How close this material is to the query, as an ABSOLUTE
199
- * image-space similarity in [0,1] adjusted for colour — the same unit as
176
+ * image-space similarity in [0,1], not adjusted for colour — the same unit as
200
177
  * `qualityFloor` and `comparison.bestRejected`, so all three sit on one axis.
201
178
  *
202
179
  * Never normalised against the page, so the same material reads the same in
@@ -205,7 +182,9 @@ export interface SearchResultItem {
205
182
  * "99%+"); `Math.round` prints 100% from 0.995 up. Capping it server-side
206
183
  * would put it on a different ceiling from `comparison.bestRejected`, which
207
184
  * publishes the same unit uncapped, and the two are meant to be comparable.
208
- * `score` is the ranking value and lives on a different scale.
185
+ * A Match page lists rows whose colour agrees (`colourDistance` under 0.1)
186
+ * first, then unmeasured ones, then the rest, and orders each set by this
187
+ * number. `score` is the retrieval score and lives on a different scale.
209
188
  */
210
189
  confidence?: number;
211
190
  /**
@@ -215,8 +194,9 @@ export interface SearchResultItem {
215
194
  */
216
195
  confidenceBand?: "strong" | "good" | "fair" | "weak";
217
196
  /**
218
- * Match only. Proportion-weighted OKLab distance from the query crop's palette
219
- * to this material's. `null` means the material carries no stored palette —
197
+ * Match only. OKLab earth mover's distance between the query crop's palette
198
+ * and this material's, reading both palettes' proportions. Under 0.1 the
199
+ * colour agrees. `null` means the material carries no stored palette —
220
200
  * UNKNOWN colour, never "no colour" and never "distant". Absent when the
221
201
  * colour stage did not run.
222
202
  */
@@ -242,18 +222,6 @@ export interface SearchResultItem {
242
222
  * `includeBelowFloor`.
243
223
  */
244
224
  admittedOnColour?: boolean;
245
- /** Variants HIDDEN behind this card by `collapse: "product"` — the COMPLETE tally, even when {@link variants} is capped. */
246
- siblingCount?: number;
247
- /** The product's matching variants, MOST RELEVANT FIRST — present exactly under `collapse: "product"`, capped, and led by the group's RANKING representative, which is NOT always this item. {@link CollapsedVariant} states the full contract. */
248
- variants?: CollapsedVariant[];
249
- visualMeanings?: VisualMeaning[];
250
- /**
251
- * The shared report card for this hit against the request's `filters` (required
252
- * cells) and `preferences` (preferred cells). Present (full view only) whenever a
253
- * spec filter or preference was given. Reuses the by-spec verdict shape — the
254
- * shared CandidateVerdict mirror; see {@link MaterialVerdict}.
255
- */
256
- verdict?: BySpecVerdict;
257
225
  /**
258
226
  * Resolved values for the attribute ids sent in `returnAttributes`, keyed by
259
227
  * attribute id. ABSENT entirely when `returnAttributes` was not sent.
@@ -266,48 +234,40 @@ export interface SearchResultItem {
266
234
  * attribute rather than a list of candidates.
267
235
  */
268
236
  attributes?: Record<string, string | string[] | null>;
269
- }
237
+ };
270
238
  /**
271
239
  * A feed total with its honesty basis — THE CLAIM ABOUT THE MATCH SET, which is
272
240
  * a different number from `results.length` (this page).
273
241
  *
274
242
  * `exact` — the true count. Say "473 materials match".
275
- * `lowerBound` — a floor (cap hit, ANN-pool bound, or a degraded count). Say
276
- * "at least 1,000", never "1,000". Never a silent approximation.
243
+ * `lowerBound` — a floor (cap hit or an ANN-pool bound). Say "at least
244
+ * 1,000", never "1,000". Never a silent approximation.
245
+ * `unknown` — the count query itself faulted (MG-2104): `value` is the pool
246
+ * this page was drawn from, NOT a claim about the corpus — do not print it as
247
+ * a bound. Check the response's `degraded.parts` for `"variant-count"`.
277
248
  *
278
- * The CAP differs by lane, and only `lowerBound` ever tells you one was hit. The
279
- * SEARCH feed counts exactly to 1,000 and reports a floor beyond it, because it
280
- * counts over attribute and tsquery universes where an unbounded count is a
281
- * timeout. The BROWSE feed ({@link BrowseCounts}) is uncapped — a plain count
282
- * over the same predicate its page scan ran, so a category of 66,000 reports
283
- * 66,000 exactly. Read `basis`; never assume a ceiling.
249
+ * Only `lowerBound` ever tells you a cap was hit. The SEARCH feed counts
250
+ * exactly to 1,000 and reports a floor beyond it, because it counts over
251
+ * attribute and tsquery universes where an unbounded count is a timeout. Read
252
+ * `basis`; never assume a ceiling.
284
253
  *
285
254
  * A response with no `counts` at all states no total: `count: true` is opt-in,
286
255
  * and the absence of a total is not a total of zero.
287
256
  */
288
- export interface CountedTotal {
257
+ export type CountedTotal = {
289
258
  value: number;
290
- basis: OpenEnum<"exact" | "lowerBound">;
291
- }
292
- /**
293
- * Feed counts — present ONLY under `count:true` or `collapse:"product"`. This is
294
- * the corpus-level claim; `results.length` is the page. Never report one as the
295
- * other.
296
- */
297
- export interface SearchCounts {
298
- variants: CountedTotal;
299
- products?: CountedTotal;
300
- }
259
+ basis: OpenEnum<"exact" | "lowerBound" | "unknown">;
260
+ };
301
261
  /**
302
262
  * One value within a facet's counts: the CANONICAL token (send it back verbatim
303
263
  * as a filter term), its display label, and how many variants in the counted
304
264
  * set carry it.
305
265
  */
306
- export interface SearchFacetValue {
266
+ export type SearchFacetValue = {
307
267
  value: string;
308
268
  label: string;
309
269
  variants: number;
310
- }
270
+ };
311
271
  /**
312
272
  * One facet's counts. `basis` is the honesty label, and — per the negotiated
313
273
  * MG-1010 contract — its union is CLOSED: exactly `"exact"` or `"lowerBound"`,
@@ -318,11 +278,11 @@ export interface SearchFacetValue {
318
278
  * pool-counted value is NEVER `exact`. `truncated` is ALWAYS present: true
319
279
  * when more distinct values existed beyond the reported list.
320
280
  */
321
- export interface SearchFacetCount {
281
+ export type SearchFacetCount = {
322
282
  values: SearchFacetValue[];
323
283
  truncated: boolean;
324
284
  basis: "exact" | "lowerBound";
325
- }
285
+ };
326
286
  /**
327
287
  * A requested facet that was not counted, with its reason: `"unsupported"` —
328
288
  * the id is known but facet counting does not cover it; `"timeout"` — the
@@ -330,159 +290,74 @@ export interface SearchFacetCount {
330
290
  * rail could not count; the page is intact). Render it as "couldn't count",
331
291
  * never as zero.
332
292
  */
333
- export interface SearchAbsentFacet {
293
+ export type SearchAbsentFacet = {
334
294
  facetId: string;
335
295
  reason: OpenEnum<"unsupported" | "timeout">;
336
- }
296
+ };
337
297
  /**
338
298
  * Per-query facet counts (MG-1010) — present ONLY when the request sent
339
299
  * `facetCounts`. `counts` is keyed by requested facet id; every requested
340
300
  * facet not in `absent` has an entry, including empty ones. `poolSize` is
341
301
  * present exactly when the counts are pool-derived.
342
302
  */
343
- export interface SearchFacets {
303
+ export type SearchFacets = {
344
304
  poolSize?: number;
345
305
  requested: string[];
346
306
  absent: SearchAbsentFacet[];
347
307
  counts: Record<string, SearchFacetCount>;
348
- }
308
+ };
349
309
  /**
350
310
  * The search response envelope. `view` discriminates the `results` union:
351
311
  * `"full"` (default) carries {@link SearchResultItem}s, `"card"` the lean
352
- * {@link SearchCardItem}s. Every other field is shared. `counts` is present
353
- * under `count:true` or `collapse:"product"` (replacing the former
354
- * `variantCount`/`productCount`).
312
+ * {@link SearchCardItem}s. Every other field is shared.
355
313
  *
356
- * A PAGE, NOT A CORPUS. `results.length` is what you were handed;
357
- * {@link SearchCounts} is the only statement about the match set, and only when
358
- * you asked for it. Follow `cursor` for the rest. "I found 24 materials" is
359
- * wrong whenever `limit` was 24.
314
+ * A PAGE, NOT A CORPUS. `results.length` is what you were handed; follow
315
+ * `cursor` for the rest. "I found 24 materials" is wrong whenever `limit` was
316
+ * 24.
360
317
  */
361
- export interface SearchData extends SearchResponseExplainReceipts {
318
+ export type SearchData = {
362
319
  /** Result attribution — see {@link ResponseAttribution}. `basis` is `mode` verbatim here. */
363
320
  attribution?: ResponseAttribution;
321
+ /**
322
+ * Present when a word no material carries was corrected before the search
323
+ * ran: the page shows results for `to` (ADR 0031).
324
+ */
325
+ correctedQuery?: {
326
+ from: string;
327
+ to: string;
328
+ };
364
329
  view: OpenEnum<"full" | "card">;
365
330
  /** THIS PAGE. Its length is a page size, never a total. */
366
331
  results: SearchResultItem[];
367
- suggestions: SearchSuggestion[];
332
+ /** Always empty: the one search offers no refinements. */
333
+ suggestions: never[];
368
334
  mode: SearchMode;
369
- /**
370
- * Which dense text lane answered, when one did (MG-1796). `image-text` — the
371
- * query carried evidence it describes appearance, so it was ranked against
372
- * image vectors; `variant-text` — ranked against each variant's written
373
- * portrait.
374
- *
375
- * ABSENT means the answer did not come from the dense text arm at all: `mode`
376
- * was never `text`, or the text arm resolved without a lane. Absence is
377
- * UNKNOWN and never a default of `image-text` — treating it as one asserts a
378
- * dense reading of a lexical, colour or browse answer. Absent too from any
379
- * response produced by a server predating MG-1793.
380
- *
381
- * An {@link OpenEnum} because the lane vocabulary can grow: a value you do
382
- * not recognise is a lane this client predates, not a malformed response.
383
- */
384
- textLane?: OpenEnum<"image-text" | "variant-text">;
335
+ /** The first result's rank score, 1. */
385
336
  maxScore: number;
386
337
  cursor?: Cursor;
387
- window?: SearchWindowReceipt;
388
- rerank?: SearchRerankReport;
389
- /** Hard compound eligibility and abundance receipts, when applied. */
390
- colourConstraint?: CompoundColourConstraintReceipt;
391
- formConstraint?: CompoundFormConstraintReceipt;
392
- /** Explain-only receipt of the query shape and transform ledger that ran. */
393
- plan?: SearchExecutionPlan;
394
- counts?: SearchCounts;
395
- /**
396
- * The `#rrggbb` colours the colour signal ACTUALLY ran on, after
397
- * minor-proportion accents were dropped. Present whenever the request carried
398
- * explicit `colours`. Compare against your request: a difference means the
399
- * search that ran is not the search that was asked for.
400
- */
401
- effectiveColours?: string[];
402
- /**
403
- * Colours removed from the query for carrying a `weight` below the 5% accent
404
- * threshold. EMPTY is the normal case. Non-empty and the dropped colours are
405
- * absent from colour retrieval and ranking entirely — they did not merely
406
- * rank low. To keep a small accent in the query, give it `role: "accent"`.
407
- */
408
- droppedColours?: string[];
409
- /** Per-query facet counts — present ONLY when the request sent `facetCounts`. */
410
- facets?: SearchFacets;
411
- /** Receipt that narrows the exact finalized pool behind lower-bound facets. */
412
- refinement?: SearchRefinementReceipt;
338
+ /** One or more sentences written for a person; absent when there is nothing to say. */
413
339
  note?: string;
414
340
  timings?: Record<string, number>;
415
- /** A detected mood term overridden by a stronger colour reading. */
416
- supersededMoodId?: string;
417
- /**
418
- * Explicit standard-search mood reading; null ids mean no selected mood.
419
- * Absent when mood was not evaluated or an older server produced the result.
420
- */
421
- moodInterpretation?: MoodInterpretation;
422
- /**
423
- * Present when the query had multiple defensible readings. Says which one
424
- * ran, or leaves every reading non-default when the engine chose none. Every
425
- * reading carries a complete executable replacement request.
426
- */
427
- disambiguation?: SearchDisambiguation;
428
- /** Structured receipt of the standard query-understanding decisions. */
429
- understanding?: QueryUnderstanding;
430
- /**
431
- * The corpus this page AND its `counts` were drawn from — present ONLY when
432
- * the request narrowed it. ABSENT means the whole graph: every material
433
- * MaterialGraph knows, orderable or not. Read it alongside `counts`: a short
434
- * page under a scope is a small SCOPED corpus, not a truncated one.
435
- */
436
- corpusScope?: CorpusScopeReceipt;
437
341
  /** Echo of the resolved query input (raw image bytes/vector are never echoed). */
438
342
  query?: Record<string, unknown>;
439
343
  /** A `getVariantBrief` echo of the seed variant, present only in `variantId` seed mode. */
440
344
  seed?: MaterialTile | Record<string, unknown>;
441
- }
442
- export type ColourDictionaryId = OpenEnum<"colour_family" | "colour_mood" | "colour_undertone">;
443
- export interface ColourFacetTerm {
444
- id: string;
445
- label: string;
446
- aliases: string[];
447
- variants: number;
448
- brands: number;
449
- }
450
- export interface ColourFacetDictionary {
451
- attributeId: ColourDictionaryId;
452
- name: string;
453
- description: string;
454
- terms: ColourFacetTerm[];
455
- }
456
- export interface ColourFacetsResponse {
457
- count: number;
458
- dictionaries: ColourFacetDictionary[];
459
- }
460
- export interface CategoryNode {
461
- key: string;
462
- name: string;
463
- parentKey: string | null;
464
- path: string;
465
- level: OpenEnum<"group" | "type">;
466
- }
467
- export interface TaxonomyResponse {
468
- count: number;
469
- categories: CategoryNode[];
470
- }
471
- export interface DemandMomentum {
345
+ } & SearchResponseExplainReceipts;
346
+ export type DemandMomentum = {
472
347
  state: OpenEnum<"rising" | "fading" | "stable" | "emerging" | "insufficient_volume">;
473
348
  percentChange: number | null;
474
349
  trailingSpecCount: number;
475
350
  priorSpecCount: number;
476
- }
477
- export interface CompanionDemand {
351
+ };
352
+ export type CompanionDemand = {
478
353
  specCount: number;
479
354
  projectCount: number;
480
355
  momentum: DemandMomentum | null;
481
- }
482
- export interface ProjectTypeShare {
356
+ };
357
+ export type ProjectTypeShare = {
483
358
  projectType: string;
484
359
  projectCount: number;
485
- }
360
+ };
486
361
  /**
487
362
  * A pairing item from the behavioural arm — real order evidence, or board
488
363
  * curation when this seed has no order edges.
@@ -499,9 +374,15 @@ export interface ProjectTypeShare {
499
374
  *
500
375
  * See {@link ColourPairing} for the third, non-evidential arm.
501
376
  */
502
- export interface CospecPairing {
377
+ export type CospecPairing = {
503
378
  /** The evidence tier. `cospec` = orders; `considered` = boards. Read it before phrasing. */
504
379
  source: "cospec" | "considered";
380
+ /**
381
+ * Membership of the requested `corpusScope`: true in, false out, null when
382
+ * the graph cannot say. Absent when no narrowing scope was asked for. It
383
+ * flags, it never removes (MG-2292).
384
+ */
385
+ inScope?: boolean | null;
505
386
  /** Opaque — round-trip verbatim. */
506
387
  variantId: VariantId;
507
388
  brand: string;
@@ -536,29 +417,34 @@ export interface CospecPairing {
536
417
  topProjectTypes: ProjectTypeShare[];
537
418
  /** Graph-owned colour ranking facts, present only for an explicitly ordered rail. */
538
419
  ranking?: PairingRanking;
539
- }
420
+ };
540
421
  /**
541
422
  * A pairing item topped up from colour palette-similarity — a LOOK-ALIKE, not
542
423
  * evidence. Nobody ordered these together and nobody boarded them together; the
543
424
  * palettes sit near each other in OKLab. Say "shares a palette with" and never
544
425
  * "specified together" or "often paired with".
545
426
  */
546
- export interface ColourPairing extends MaterialTile {
427
+ export type ColourPairing = {
547
428
  source: "colour";
548
429
  score: number;
430
+ /**
431
+ * Membership of the requested `corpusScope`: true in, false out, null when
432
+ * the graph cannot say. Absent when no narrowing scope was asked for. It
433
+ * flags, it never removes (MG-2292).
434
+ */
435
+ inScope?: boolean | null;
549
436
  /** Graph-owned colour ranking facts, present only for an explicitly ordered rail. */
550
437
  ranking?: PairingRanking;
551
- }
438
+ } & MaterialTile;
552
439
  export type PairingOrderMode = "relevance" | "lightness_asc" | "lightness_desc";
553
- export type PairingCorpus = "all" | "material_bank";
554
- export interface PairingRanking {
440
+ export type PairingRanking = {
555
441
  anchorColourSimilarity: number | null;
556
442
  lightness: number | null;
557
443
  anchorColourSimilarityMeasurable: boolean;
558
444
  lightnessMeasurable: boolean;
559
445
  basis: OpenEnum<"dominant_colour_oklab">;
560
- }
561
- export interface PairingOrdering {
446
+ };
447
+ export type PairingOrdering = {
562
448
  requested: OpenEnum<PairingOrderMode>;
563
449
  applied: OpenEnum<PairingOrderMode>;
564
450
  direction: OpenEnum<"ascending" | "descending">;
@@ -570,14 +456,14 @@ export interface PairingOrdering {
570
456
  };
571
457
  missingValues: OpenEnum<"last">;
572
458
  tieBreak: OpenEnum<"variantId_asc">;
573
- }
459
+ };
574
460
  /**
575
461
  * One pairing, discriminated by `source` into three tiers of very different
576
462
  * strength: `cospec` (real orders) > `considered` (board activity) > `colour`
577
463
  * (palette look-alike). Branch on `source` before you write a sentence about it.
578
464
  */
579
465
  export type PairingItem = CospecPairing | ColourPairing;
580
- export interface PairingsResponse {
466
+ export type PairingsResponse = {
581
467
  seed: MaterialTile;
582
468
  /** A rail, not a ranked corpus — mixed evidence tiers, capped by `limit`. */
583
469
  items: PairingItem[];
@@ -598,70 +484,86 @@ export interface PairingsResponse {
598
484
  ordering?: PairingOrdering;
599
485
  /** Opaque continuation bound to the seed, filters, corpus, and order. */
600
486
  nextCursor?: Cursor;
601
- }
602
- export interface MaterialResponse {
487
+ };
488
+ export type MaterialResponse = {
603
489
  material: MaterialDetail;
604
- /** Present only when `include: "siblings"` was requested. */
605
- siblings?: SiblingTile[];
606
- }
607
- export interface SiblingsResponse {
608
- siblings: SiblingTile[];
609
- }
610
- /**
611
- * Batch read of tile projections. `materials` is NOT positional — resolve by
612
- * `variantId`, and read `missingIds` rather than inferring absence from length.
613
- */
614
- export interface MaterialsBatchTileResponse {
615
- materials: MaterialTile[];
616
- /** Requested ids that resolved to nothing. Echoed verbatim, opaque. */
617
- missingIds: VariantId[];
618
- }
619
- /**
620
- * Batch read of detail projections. `materials` is NOT positional — resolve by
621
- * `variantId`, and read `missingIds` rather than inferring absence from length.
622
- */
623
- export interface MaterialsBatchDetailResponse {
624
- materials: MaterialDetail[];
625
- /** Requested ids that resolved to nothing. Echoed verbatim, opaque. */
626
- missingIds: VariantId[];
627
- }
628
- export interface SuggestColourResponse {
629
- suggestions: Array<{
490
+ };
491
+ export type SuggestColourResponse = {
492
+ suggestions: {
630
493
  term: string;
631
494
  hex: string;
632
495
  libraryId?: string;
633
496
  kind: string;
634
- }>;
635
- }
636
- export interface AttributeCandidate {
637
- attributeId: string;
497
+ }[];
498
+ };
499
+ /**
500
+ * How a {@link AttributeListItem} matched the query. Declared here rather than
501
+ * imported: this package is published, so it may not reach across into
502
+ * `@materialgraph/products`, whose `listAttributesResultSchema` is the source
503
+ * of truth this wire type tracks. `method` is deliberately open - a server
504
+ * that grows a method must not break an older client.
505
+ */
506
+ export type AttributeMatch = {
507
+ method: OpenEnum<"exact" | "alias" | "fuzzy" | "vector">;
508
+ /** The id, name or alias that matched, or null for a match by meaning. */
509
+ matchedText: string | null;
510
+ /** Fuse distance, 0 best. Compares only within one method. */
511
+ distance?: number;
512
+ /** Cosine similarity, 1 best. Compares only within one method. */
513
+ similarity?: number;
514
+ };
515
+ /** One attribute definition on a `listAttributes()` page. */
516
+ export type AttributeListItem = {
517
+ id: string;
638
518
  name: string;
639
519
  description: string;
640
- score: number;
641
- facetable: boolean;
520
+ valueKind: string;
521
+ unitId: string | null;
522
+ /** The value dictionary its values come from, or null. */
523
+ dictionaryId: string | null;
524
+ blockIds: string[];
525
+ /** The `listVariants()` filter ops it accepts; empty when it cannot filter on it. */
526
+ filterOps: FilterOp[];
527
+ filterable: boolean;
528
+ /** Present only when the page answers a query. */
529
+ match?: AttributeMatch;
530
+ /** Live variants in the requested categories stating it; absent without `categoryIds`. */
531
+ statedBy?: number;
532
+ };
533
+ /** An attribute a block-scoped `listAttributes()` query missed, named with its real blocks. */
534
+ export type OutsideScopeAttribute = {
535
+ id: string;
536
+ name: string;
642
537
  blockIds: string[];
643
- }
644
- export interface ResolveAttributeResponse {
645
- query: string;
646
- candidates: AttributeCandidate[];
647
- }
538
+ };
539
+ export type ListAttributesResult = {
540
+ items: AttributeListItem[];
541
+ /** Page one only: matches outside `blockIds` ranking above the best item. Absent when the check did not apply. */
542
+ outsideScope?: OutsideScopeAttribute[];
543
+ /** Live variants across the requested categories; absent without `categoryIds`. */
544
+ liveVariants?: number;
545
+ pageInfo: {
546
+ endCursor: string | null;
547
+ hasNextPage: boolean;
548
+ };
549
+ };
648
550
  export type * from "./catalog-discovery-types.js";
649
551
  export type * from "./resolve-types.js";
650
552
  export type * from "./resolve-colour-types.js";
651
553
  export type * from "./suggest-types.js";
652
554
  export type * from "./by-spec-types.js";
653
- export interface WhoamiResponse {
555
+ export type WhoamiResponse = {
654
556
  principal: string;
655
557
  scopes: string[];
656
- }
558
+ };
657
559
  /** A single colour entry in a search palette — exactly one of `hex` or `name`. */
658
- export interface ColourInput {
560
+ export type ColourInput = {
659
561
  hex?: string;
660
562
  name?: string;
661
563
  weight?: number;
662
564
  role?: OpenEnum<"any" | "is" | "accent">;
663
565
  sourceName?: string;
664
- }
566
+ };
665
567
  /** A raw incoming image to search by. */
666
568
  export type SearchImageInput = {
667
569
  type: "url";
@@ -670,21 +572,10 @@ export type SearchImageInput = {
670
572
  type: "base64";
671
573
  data: string;
672
574
  };
673
- export type { SearchGetParams, SearchInput } from "./search-request-types.js";
674
- /** Options for a batch material read. */
675
- export interface MaterialsBatchOptions {
676
- /** Projection depth. `tile` omits fields rather than reporting them absent. */
677
- view?: "tile" | "detail";
678
- /**
679
- * Which id space `ids` are in — MaterialGraph's own opaque `variantId`
680
- * (default) or Material Bank's `materialBankId`. Both are opaque; they are NOT
681
- * interchangeable, and passing one under the other's `idKind` returns
682
- * `missingIds`, not an error.
683
- */
684
- idKind?: OpenEnum<"variantId" | "materialBankId">;
685
- }
575
+ export type { SearchGetParams, SearchIndexFilter, SearchInput, } from "./search-request-types.js";
576
+ export type { SearchIndexPlan, SearchPlanReceipt, } from "./search-index-plan-types.js";
686
577
  /** Options for a pairings read. */
687
- export interface PairingsOptions {
578
+ export type PairingsOptions = {
688
579
  /** Comma-separated coarse category display names; filters both arms. */
689
580
  categories?: string;
690
581
  /** Page size, 1–24 inclusive (default 12). The server rejects (400), not clamps, values outside this range. */
@@ -693,34 +584,33 @@ export interface PairingsOptions {
693
584
  sampledOnly?: boolean;
694
585
  /** Graph-owned order applied within fixed cospec → considered → colour tiers. */
695
586
  order?: PairingOrderMode;
696
- /** Optional Material Bank/orderable gate; requires an explicit `order`. */
697
- corpus?: PairingCorpus;
587
+ /** Flags each item's `inScope` against this scope; never narrows the rail. */
588
+ corpusScope?: CorpusScope;
698
589
  /** The previous response's `nextCursor`, passed back verbatim. */
699
590
  cursor?: Cursor;
700
- }
701
- /** Options for `catalog/attributes/resolve`. */
702
- export interface ResolveAttributeOptions {
703
- limit?: number;
704
- facetableOnly?: boolean;
705
- }
706
- /** Options for `search/specifications`. */
707
- export interface BySpecOptions {
591
+ };
592
+ /** Options for `listAttributes()` (GET `/api/v1/catalog/attributes`). */
593
+ export type ListAttributesOptions = {
594
+ /** An attribute id, name, misspelling or meaning, e.g. "reduces echo". */
595
+ query?: string;
596
+ /** Keep attributes in any of these blocks. */
597
+ blockIds?: string[];
598
+ /** Category ids from `categories.list()`; keeps attributes their templates carry. */
599
+ categoryIds?: string[];
600
+ /** Keep only attributes `listVariants()` can filter on. */
601
+ filterableOnly?: boolean;
602
+ /** Items per page, 1 to 50. Default 20. */
708
603
  limit?: number;
709
- /**
710
- * Numeric range criteria as `attribute:min..max` strings, ANDed with the
711
- * `spec` criteria. Build them with `specRangeCriterion` rather than by hand —
712
- * both bounds are inclusive and at least one is required, and a hand-built
713
- * token with neither is refused by the server rather than matching everything.
714
- */
715
- ranges?: string[];
716
- }
604
+ /** `pageInfo.endCursor` from the previous page, with the same query and scope. */
605
+ cursor?: string;
606
+ };
717
607
  export type * from "./demand-types.js";
718
608
  /** One quarter's tally. Sparse across a series — an absent quarter is not a zero. */
719
- export interface DemandQuarter {
609
+ export type DemandQuarter = {
720
610
  quarter: string;
721
611
  specCount: number;
722
612
  projectCount: number;
723
- }
613
+ };
724
614
  /**
725
615
  * Which behavioural signal a demand read is scoped to — and therefore what a
726
616
  * number drawn from it may honestly be called.
@@ -736,12 +626,12 @@ export interface DemandQuarter {
736
626
  */
737
627
  export type DemandSignal = OpenEnum<"sampled" | "considered" | "specified">;
738
628
  export type * from "./demand-batch-types.js";
739
- export interface ExtractedPalette {
740
- colors: Array<{
629
+ export type ExtractedPalette = {
630
+ colors: {
741
631
  hex: string;
742
632
  name: string;
743
633
  proportion: number;
744
- }>;
634
+ }[];
745
635
  dominant: {
746
636
  family: string;
747
637
  hex: string;
@@ -749,108 +639,15 @@ export interface ExtractedPalette {
749
639
  oklch: string;
750
640
  } | null;
751
641
  mood: string;
752
- }
753
- export interface ExtractPaletteResponse {
642
+ };
643
+ export type ExtractPaletteResponse = {
754
644
  palette: ExtractedPalette | null;
755
- }
645
+ };
756
646
  export type * from "./spec-facet-types.js";
757
- export interface ApplicableSpecAttribute {
758
- id: string;
759
- name: string;
760
- valueKind: string;
761
- required: boolean;
762
- }
763
- export interface ApplicableSpecBlock {
764
- blockId: string;
765
- name: string;
766
- applicability: OpenEnum<"required" | "recommended" | "optional" | "unusual">;
767
- attributes: ApplicableSpecAttribute[];
768
- }
769
- export interface ApplicableSpecsResponse {
770
- found: boolean;
771
- productType: {
772
- id: string;
773
- name: string;
774
- } | null;
775
- blocks: ApplicableSpecBlock[];
776
- hint?: string;
777
- }
778
- export interface GlossaryAttributeMatch {
779
- kind: "attribute";
780
- id: string;
781
- name: string;
782
- description: string;
783
- valueKind: string;
784
- unit?: string;
785
- facetable: boolean;
786
- comparable: boolean;
787
- dictionaryId?: string;
788
- usedInBlocks: string[];
789
- }
790
- export interface GlossaryValueMatch {
791
- kind: "value";
792
- id: string;
793
- label: string;
794
- description?: string;
795
- aliases: string[];
796
- dictionaryId: string;
797
- dictionaryName: string;
798
- attributeIds: string[];
799
- }
800
- export interface GlossaryTermResponse {
801
- found: boolean;
802
- term: string;
803
- match: GlossaryAttributeMatch | GlossaryValueMatch | null;
804
- alternates?: Array<{
805
- kind: OpenEnum<"attribute" | "value">;
806
- id: string;
807
- label: string;
808
- }>;
809
- }
810
- export interface BrandProfile {
811
- id: string;
812
- slug: string;
813
- name: string;
814
- marketSegment: string;
815
- headquartersCity: string | null;
816
- headquartersCountry: string | null;
817
- parentBrand: {
818
- id: string;
819
- name: string;
820
- } | null;
821
- positioning: string | null;
822
- ownershipType: string | null;
823
- foundedYear: number | null;
824
- description: string | null;
825
- website: string | null;
826
- }
827
- export interface BrandProfileResponse {
828
- found: boolean;
829
- query: string;
830
- brand: BrandProfile | null;
831
- /** Trade / spec signals ON RECORD, keyed by attribute id. Absent = no value held (never infer false). */
832
- trade: Record<string, boolean | string>;
833
- links: Array<{
834
- label: string;
835
- url: string;
836
- }>;
837
- alternates: Array<{
838
- id: string;
839
- name: string;
840
- }>;
841
- }
842
647
  /** Body for `POST /api/v1/search/palettes/extract`. */
843
- export interface ExtractPaletteInput {
648
+ export type ExtractPaletteInput = {
844
649
  /** The query image as a data URI or bare base64 string. */
845
650
  image: string;
846
651
  /** Max palette swatches to return (default 6). */
847
652
  max?: number;
848
- }
849
- /** Options for a cacheable GET batch read (`materialsCached`). */
850
- export interface MaterialsBatchGetOptions extends MaterialsBatchOptions {
851
- }
852
- /** Options for `brands/{brandSlug}`. */
853
- export interface BrandOptions {
854
- /** Alternate fuzzy query; takes precedence over the path slug when present. */
855
- q?: string;
856
- }
653
+ };