@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
@@ -10,7 +10,7 @@
10
10
  * ```ts
11
11
  * import { responseSchema } from "@instruments/materialgraph/response-schemas";
12
12
  *
13
- * createTool({ outputSchema: responseSchema("search_materials_post"), … });
13
+ * createTool({ outputSchema: responseSchema("search_variants"), … });
14
14
  * ```
15
15
  *
16
16
  * Keyed by OpenAPI `operationId`, the same id `SDK_OPERATIONS` pairs with each
@@ -30,7 +30,7 @@ export type SamplizeCatalogueProduct = {
30
30
  /** The primary sample variant count, never an aggregate of product orders. */
31
31
  demand: SamplizeDemand | null;
32
32
  collectionMemberships: SamplizeCollectionMembership[];
33
- colourAnalysis: Record<string, unknown>;
33
+ colorAnalysis: Record<string, unknown>;
34
34
  oklab: {
35
35
  L: number;
36
36
  a: number;
@@ -51,7 +51,7 @@ export type SamplizeCatalogueProduct = {
51
51
  };
52
52
  export type SamplizeCataloguePage = {
53
53
  schemaVersion: 2;
54
- corpusScope: "samplizeCatalogue";
54
+ searchCatalogueSubset: "samplizeCatalogue";
55
55
  sourceVersion: string;
56
56
  source: Record<string, unknown>;
57
57
  /** Number of eligible records matching the supplied filters. */
@@ -66,7 +66,7 @@ export type SamplizeHexObservation = {
66
66
  publisher: string;
67
67
  source: string;
68
68
  sourceUrl: string | null;
69
- sourceBasis: string;
69
+ valueOrigin: string;
70
70
  observedAt: string | null;
71
71
  differsFromSelected: boolean;
72
72
  };
@@ -83,7 +83,7 @@ export type SamplizeLrvObservation = {
83
83
  export type SamplizeDemand = {
84
84
  field: "recently_ordered_count";
85
85
  sourceVariantId: string;
86
- grain: "shopify_variant";
86
+ defaultAttributeLevel: "shopify_variant";
87
87
  recentlyOrderedCount: number;
88
88
  /** The publisher does not supply the time window. */
89
89
  window: null;
@@ -4,7 +4,7 @@ export const SAMPLIZE_COLORVEC_EMBEDDING_VERSION = EMBEDDING_VERSION;
4
4
  const decodeVector = (encoded) => {
5
5
  const bytes = Uint8Array.from(atob(encoded), (c) => c.charCodeAt(0));
6
6
  if (bytes.length !== EMBEDDING_VECTOR_SIZE * 4) {
7
- throw new Error("Invalid colour vector size.");
7
+ throw new Error("Invalid color vector size.");
8
8
  }
9
9
  const view = new DataView(bytes.buffer);
10
10
  return Array.from({ length: EMBEDDING_VECTOR_SIZE }, (_, i) => view.getFloat32(i * 4, true));
@@ -13,7 +13,7 @@ const facets = ["brand", "family", "lightness", "intensity"];
13
13
  const identityOrder = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
14
14
  export const samplizeSolidColorVector = (hex) => {
15
15
  if (!/^#[0-9a-f]{6}$/i.test(hex)) {
16
- throw new Error("A six-digit published colour is required.");
16
+ throw new Error("A six-digit published color is required.");
17
17
  }
18
18
  return paletteToEmbedding([{ hex, proportion: 1 }], { validateOklab: true });
19
19
  };
@@ -78,12 +78,12 @@ export const selectSamplizeEligiblePaints = (paints, input) => {
78
78
  export const createSamplizeColorVecIndex = (cache) => {
79
79
  if (cache.embeddingVersion !== EMBEDDING_VERSION ||
80
80
  cache.rankingVersion !== SAMPLIZE_COLORVEC_RANKING_VERSION) {
81
- throw new Error("The colour cache uses an incompatible embedding or ranking version. Refresh it before searching.");
81
+ throw new Error("The color cache uses an incompatible embedding or ranking version. Refresh it before searching.");
82
82
  }
83
83
  if (!cache.sourceVersion ||
84
84
  !cache.dictionaryVersion ||
85
85
  !Number.isFinite(Date.parse(cache.generatedAt))) {
86
- throw new Error("The colour cache is missing its provenance.");
86
+ throw new Error("The color cache is missing its provenance.");
87
87
  }
88
88
  const paints = cache.paints.map((p) => ({
89
89
  ...p,
@@ -95,7 +95,7 @@ export const createSamplizeColorVecIndex = (cache) => {
95
95
  paint.vector.length !== EMBEDDING_VECTOR_SIZE ||
96
96
  !paint.vector.every(Number.isFinite) ||
97
97
  ![paint.oklab.L, paint.oklab.a, paint.oklab.b].every(Number.isFinite)) {
98
- throw new Error("The colour cache contains invalid or duplicate paint records.");
98
+ throw new Error("The color cache contains invalid or duplicate paint records.");
99
99
  }
100
100
  identities.add(paint.variantId);
101
101
  }
@@ -128,7 +128,7 @@ export const createSamplizeColorVecIndex = (cache) => {
128
128
  palette.some((p) => !Number.isFinite(p.proportion) ||
129
129
  p.proportion <= 0 ||
130
130
  p.proportion > 1)) {
131
- throw new Error("Supply up to twelve positive image colour shares.");
131
+ throw new Error("Supply up to twelve positive image color shares.");
132
132
  }
133
133
  const total = palette.reduce((sum, p) => sum + p.proportion, 0);
134
134
  const buckets = palette.map((p, position) => ({
@@ -43,15 +43,15 @@ import type { OpenEnum } from "./types.js";
43
43
  * that were never in the Material Bank feed; if you mean orderable, use one of
44
44
  * the channel-backed scopes above.
45
45
  */
46
- export type CorpusScope = OpenEnum<"samplizeCatalogue" | "all" | "designShopOrderable" | "materialBankEuOrderable" | "materialBankNaAndCatalogue" | "materialBankNaOrderable" | "materialBankOrderable">;
46
+ export type SearchCatalogueSubset = OpenEnum<"samplizeCatalogue" | "all" | "designShopOrderable" | "materialBankEuOrderable" | "materialBankNaAndCatalogue" | "materialBankNaOrderable" | "materialBankOrderable">;
47
47
  /**
48
48
  * The corpus a response AND its counts were drawn from. Present only when a
49
49
  * narrowing scope applied — absence means the whole graph. Read it alongside
50
50
  * the counts: a short page under a scope is a small SCOPED corpus, never a
51
51
  * truncated one.
52
52
  */
53
- export type CorpusScopeReceipt = {
54
- scope: CorpusScope;
53
+ export type SearchCatalogueSubsetReceipt = {
54
+ scope: SearchCatalogueSubset;
55
55
  /** What the scope admits, in one sentence, for a human reader. */
56
56
  description: string;
57
57
  };
@@ -1,3 +1,4 @@
1
+ import type { FindVariantsByVectorItem } from "./find-variants-by-vector-types.js";
1
2
  /**
2
3
  * @module search-variants-types
3
4
  * @description Hand-maintained mirror of `search_variants`'s (MG-2223) input
@@ -7,7 +8,18 @@
7
8
  * `@materialgraph/products`, so the shapes are copied here and
8
9
  * `contract-parity.test.ts` keeps them honest.
9
10
  */
10
- import type { FilterGroups, ListVariantsOutput, OptionalFilterGroups } from "./list-variants-types.js";
11
+ import type { ColorRangeCondition, FilterGroups, ListVariantsInclude, ListVariantsOutput, OptionalFilterGroups } from "./list-variants-types.js";
12
+ import type { SearchCatalogueSubset } from "./search-catalogue-subset-types.js";
13
+ /** Found by meaning, because fewer than half the query's words were understood. Follows `items`; not filtered by its words. */
14
+ export type SearchVariantsMeaning = {
15
+ /** Why no meaning results came back. */
16
+ error?: "timed_out" | "unavailable";
17
+ items: FindVariantsByVectorItem[];
18
+ /** How many of the query's content words were understood. */
19
+ understoodWords: number;
20
+ /** How many content words the query has. */
21
+ words: number;
22
+ };
11
23
  /** Switches one of a previous answer's `choices`: its `handle`, and the `group` and `id` of the alternative to read it as. */
12
24
  export type SearchVariantsSelection = {
13
25
  group: string;
@@ -19,10 +31,14 @@ export type SearchVariantsInput = {
19
31
  attributes?: string[];
20
32
  /** The previous page's `pageInfo.endCursor`. Tied to the vocabulary version the query was read against, and not a `listVariants()` cursor. */
21
33
  cursor?: string;
34
+ /** Optional per-item fields, as `listVariants()` takes; `image` adds each row's image. */
35
+ include?: ListVariantsInclude[];
22
36
  /** 1–50; defaults to 10. */
23
37
  limit?: number;
24
38
  /** What to find, in plain words. 1–200 characters. */
25
39
  query: string;
40
+ /** Only this corpus scope. Absent or all: every variant. */
41
+ searchCatalogueSubset?: SearchCatalogueSubset;
26
42
  /** Up to eight choices to switch, from the previous answer's `choices`. */
27
43
  selections?: SearchVariantsSelection[];
28
44
  };
@@ -34,7 +50,7 @@ export type SearchVariantsUnderstood = {
34
50
  applied: "filter" | "preference" | "not_applied";
35
51
  /** The parser's condition id. */
36
52
  id: string;
37
- kind: "category" | "brand" | "term" | "number" | "colour" | "paint";
53
+ kind: "category" | "brand" | "term" | "number" | "color" | "paint";
38
54
  /** What the words were read as. */
39
55
  label: string;
40
56
  note?: string;
@@ -60,12 +76,27 @@ export type SearchVariantsChoice = {
60
76
  /** The `listVariants()` page, with how the words were read beside it. */
61
77
  export type SearchVariantsOutput = ListVariantsOutput & {
62
78
  choices?: SearchVariantsChoice[];
79
+ /** The measured-color regions that ran. */
80
+ colors?: ColorRangeCondition[];
81
+ /** Words corrected to catalogue spellings. */
82
+ corrections?: {
83
+ correction: string;
84
+ word: string;
85
+ }[];
86
+ /** Results found by meaning, present only when most words were not understood. */
87
+ meaning?: SearchVariantsMeaning;
63
88
  /** The `listVariants()` filters that ran: send them, with `optionalFilters` and `name`, to `listVariants()` to narrow or widen this search. */
64
89
  filters?: FilterGroups;
65
90
  /** The paint name looked up, as `listVariants()` takes it. */
66
91
  name?: string;
92
+ /** A row per product, or per variant for a paint or color. */
93
+ oneRowPer: "product" | "variant";
67
94
  /** The `listVariants()` preferences that ran. */
68
95
  optionalFilters?: OptionalFilterGroups;
96
+ /** How the search ended. */
97
+ outcome: "page" | "no_match" | "nothing_understood" | "out_of_scope" | "brand_not_in_catalogue";
98
+ /** The fixed search scope, also present in `filters`. */
99
+ scope?: FilterGroups;
69
100
  /** One entry per phrase the parser read, in the parser's order. */
70
101
  understood: SearchVariantsUnderstood[];
71
102
  /** Words nothing in the vocabulary could read. */
package/types.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import type { ResponseAttribution } from "./attribution-types.js";
1
+ import type { Cursor, ProductId, VariantId } from "./identifiers.js";
2
+ import type { FilterOp } from "./list-variants-types.js";
3
+ import type { MaterialTile } from "./material-types.js";
2
4
  /**
3
5
  * Hand-mirrored wire types for the MaterialGraph public API (spec 0.5.0).
4
6
  * These interfaces intentionally have NO runtime dependency on any
@@ -19,52 +21,21 @@ import type { ResponseAttribution } from "./attribution-types.js";
19
21
  * are canonical vocabulary you discover.
20
22
  * 2. **Absent is not false.** An optional field that is missing means UNKNOWN,
21
23
  * not "known to be absent in reality" — most dangerously for booleans.
22
- * Values carry `confidence` and `sourceBasis`; report them as claims.
23
- * 3. **A page is not a corpus.** `results.length` is this page.
24
- * {@link CountedTotal} — with the `basis` it states — is the claim about the
25
- * match set. Follow the cursor for the rest, and never report a page size as
26
- * a total.
24
+ * Values carry `confidence` and `valueOrigin`; report them as claims.
25
+ * 3. **A page is not a corpus.** A list is this page. Follow the cursor for
26
+ * the rest, and never report a page size as a total.
27
27
  */
28
- import type { CorpusScope } from "./corpus-scope-types.js";
29
- import type { Cursor, ProductId, VariantId } from "./identifiers.js";
30
- import type { FilterOp } from "./list-variants-types.js";
31
- import type { 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";
34
- export type * from "./search-response-receipt-types.js";
28
+ import type { SearchCatalogueSubset } from "./search-catalogue-subset-types.js";
35
29
  export type OpenEnum<T extends string> = T | (string & {});
36
30
  export type { JsonValue } from "./json-value-types.js";
37
- /**
38
- * One palette swatch of a material's main image — colour DERIVED FROM PRODUCT
39
- * IMAGERY, so read it as visual likeness, never as a declared colour spec.
40
- */
41
- export type PaletteSwatch = {
42
- hex: string;
43
- proportion: number;
44
- /** Stored human colour name for the swatch, when one was extracted. */
45
- name?: string | null;
46
- /**
47
- * `hex` in OKLab, derived server-side so you can sort or measure swatches
48
- * without converting them yourself. Additive, so optional: the colour lane and
49
- * the material record carry it, other surfaces may not, and absent means "not
50
- * derived here" — never "achromatic".
51
- */
52
- oklab?: OklabTriple;
53
- };
54
- /** Loose colour metadata for a search result's main image; every field is independently optional. */
55
- export type ColourMeta = {
56
- mood?: string;
57
- dominantFamily?: string;
58
- dominantHex?: string;
59
- };
60
31
  /** OKLab triple. */
61
32
  export type OklabTriple = {
62
33
  l: number;
63
34
  a: number;
64
35
  b: number;
65
36
  };
66
- /** The material's dominant-colour block (tile/detail projections). */
67
- export type MaterialColour = {
37
+ /** The material's dominant-color block (tile/detail projections). */
38
+ export type MaterialColor = {
68
39
  dominantHex: string | null;
69
40
  hsl: {
70
41
  h: number;
@@ -78,210 +49,6 @@ export type MaterialColour = {
78
49
  /** Where the served image came from. */
79
50
  export type ImageSource = OpenEnum<"r2" | "origin">;
80
51
  export type { MaterialAvailability, MaterialTile } from "./material-types.js";
81
- /**
82
- * One material in a search / match result list.
83
- *
84
- * ABSENT IS NOT FALSE. The optional fields here (`colourMeta`, `signals`,
85
- * `retired`, `productType`) are absent when the request did not ask for them or
86
- * the graph holds no answer — never because the answer is "no". A missing
87
- * `retired` is not "still current".
88
- */
89
- export type SearchResultItem = {
90
- /** This hit's attribution key — see {@link ResponseAttribution}. Opaque. */
91
- resultId?: string;
92
- /** Accepted sampleability fact. Absent means unknown, never false. */
93
- sampleable?: boolean;
94
- /** Opaque — round-trip verbatim into `getVariant()`, `pairings()`, `similarTo`. */
95
- variantId: VariantId;
96
- brand: string;
97
- product: string;
98
- brandSlug: string | null;
99
- productId: ProductId | null;
100
- /**
101
- * The variant's Material Bank SKU, unlike our opaque `variantId`; this is the
102
- * id you are expected to match
103
- * against Material Bank's own catalogue, so a result list can be joined to a
104
- * cart, an order or an analytics event without a per-result lookup.
105
- * Populated on EVERY lane: `search()` (all four arms), `match()`, and
106
- * full search responses.
107
- * ABSENT means no MB identity is known for the variant — never that the SKU
108
- * cannot be ordered. Presence identifies the source; it does not establish sampleability.
109
- */
110
- materialBankId?: string;
111
- manufacturerCode?: string;
112
- projectSaveCapability?: "allowed";
113
- sourceStatus?: "provisional";
114
- /**
115
- * What kind of material this is — the variant's primary product type, as a
116
- * display name (`"Wall Tile"`, `"Modular Carpet"`, `"Interior Paint"`).
117
- *
118
- * This is the field that lets you tell a wall tile from a bar stool without
119
- * parsing `product`. Before it existed, callers grading Match results had to
120
- * regex the product's NAME, and one such grader scored pendant lights and
121
- * electrical sockets as correct answers for a wall — because they are neither
122
- * a stool nor a chair.
123
- *
124
- * ABSENT means no primary type is on record for the variant. Unknown, never
125
- * "uncategorised" and never "not a material".
126
- */
127
- productType?: string;
128
- color: string | null;
129
- variantName: string | null;
130
- imageUrl: string | null;
131
- /**
132
- * SHA-256 of the displayed image `imageUrl` points at, as a hex digest. Key a
133
- * cache or dedupe on it rather than the URL. ABSENT means no hash is on
134
- * record, never that there is no image.
135
- */
136
- imageSha256?: string;
137
- hasImage: boolean;
138
- /**
139
- * How many eligible variants the row's product has, the same set
140
- * `listVariants` pages, so a follow-up list returns this many: the card's
141
- * "12 colourways". ABSENT means the product is unknown or has no eligible
142
- * variant.
143
- */
144
- variantCount?: number;
145
- palette: PaletteSwatch[];
146
- colourMeta?: ColourMeta;
147
- /**
148
- * Present when this material's imagery carries nothing beyond ONE colour, so
149
- * a surface should render that colour rather than the photograph — a paint's
150
- * photo is a flat fill, and the colour we hold structured beats a JPEG of it.
151
- *
152
- * The hex is EXTRACTED FROM the product image (a visual likeness), never a
153
- * manufacturer-declared colour specification; do not present it as one.
154
- * ABSENT means the question was not established (unknown product type,
155
- * unextracted or multi-swatch palette) and the image should be rendered — it
156
- * NEVER asserts "this is not a coating".
157
- */
158
- colourChip?: {
159
- hex: string;
160
- };
161
- /** Selected mode's weighted primary score; see maxScore. */
162
- score: number;
163
- retired?: boolean;
164
- /**
165
- * Resolved values for the attribute ids sent in `returnAttributes`, keyed by
166
- * attribute id. ABSENT entirely when `returnAttributes` was not sent.
167
- *
168
- * Three states, all different, and reading them as two is the mistake this
169
- * shape exists to prevent: no `attributes` key means you did not ask; a key
170
- * whose value is `null` means this material carries no accepted value for that
171
- * attribute; anything else is what the graph holds. Values resolve with the
172
- * normal variant-over-product precedence, and an array is a multi-valued
173
- * attribute rather than a list of candidates.
174
- */
175
- attributes?: Record<string, string | string[] | null>;
176
- };
177
- /**
178
- * A feed total with its honesty basis — THE CLAIM ABOUT THE MATCH SET, which is
179
- * a different number from `results.length` (this page).
180
- *
181
- * `exact` — the true count. Say "473 materials match".
182
- * `lowerBound` — a floor (cap hit or an ANN-pool bound). Say "at least
183
- * 1,000", never "1,000". Never a silent approximation.
184
- * `unknown` — the count query itself faulted (MG-2104): `value` is the pool
185
- * this page was drawn from, NOT a claim about the corpus — do not print it as
186
- * a bound. Check the response's `degraded.parts` for `"variant-count"`.
187
- *
188
- * Only `lowerBound` ever tells you a cap was hit. The SEARCH feed counts
189
- * exactly to 1,000 and reports a floor beyond it, because it counts over
190
- * attribute and tsquery universes where an unbounded count is a timeout. Read
191
- * `basis`; never assume a ceiling.
192
- *
193
- * A response with no `counts` at all states no total: `count: true` is opt-in,
194
- * and the absence of a total is not a total of zero.
195
- */
196
- export type CountedTotal = {
197
- value: number;
198
- basis: OpenEnum<"exact" | "lowerBound" | "unknown">;
199
- };
200
- /**
201
- * One value within a facet's counts: the CANONICAL token (send it back verbatim
202
- * as a filter term), its display label, and how many variants in the counted
203
- * set carry it.
204
- */
205
- export type SearchFacetValue = {
206
- value: string;
207
- label: string;
208
- variants: number;
209
- };
210
- /**
211
- * One facet's counts. `basis` is the honesty label, and — per the negotiated
212
- * MG-1010 contract — its union is CLOSED: exactly `"exact"` or `"lowerBound"`,
213
- * never widened, which is why it is not an {@link OpenEnum}. `exact` — each
214
- * `variants` figure is a true count under the query's whole predicate
215
- * (filter-driven queries only). `lowerBound` — counted over the ranked
216
- * candidate pool (`SearchFacets.poolSize`), so each figure is a floor; a
217
- * pool-counted value is NEVER `exact`. `truncated` is ALWAYS present: true
218
- * when more distinct values existed beyond the reported list.
219
- */
220
- export type SearchFacetCount = {
221
- values: SearchFacetValue[];
222
- truncated: boolean;
223
- basis: "exact" | "lowerBound";
224
- };
225
- /**
226
- * A requested facet that was not counted, with its reason: `"unsupported"` —
227
- * the id is known but facet counting does not cover it; `"timeout"` — the
228
- * facet stage's own time budget expired, and the RESULTS are unaffected (the
229
- * rail could not count; the page is intact). Render it as "couldn't count",
230
- * never as zero.
231
- */
232
- export type SearchAbsentFacet = {
233
- facetId: string;
234
- reason: OpenEnum<"unsupported" | "timeout">;
235
- };
236
- /**
237
- * Per-query facet counts (MG-1010) — present ONLY when the request sent
238
- * `facetCounts`. `counts` is keyed by requested facet id; every requested
239
- * facet not in `absent` has an entry, including empty ones. `poolSize` is
240
- * present exactly when the counts are pool-derived.
241
- */
242
- export type SearchFacets = {
243
- poolSize?: number;
244
- requested: string[];
245
- absent: SearchAbsentFacet[];
246
- counts: Record<string, SearchFacetCount>;
247
- };
248
- /**
249
- * The search response envelope. `view` discriminates the `results` union:
250
- * `"full"` (default) carries {@link SearchResultItem}s, `"card"` the lean
251
- * {@link SearchCardItem}s. Every other field is shared.
252
- *
253
- * A PAGE, NOT A CORPUS. `results.length` is what you were handed; follow
254
- * `cursor` for the rest. "I found 24 materials" is wrong whenever `limit` was
255
- * 24.
256
- */
257
- export type SearchData = {
258
- /** Result attribution — see {@link ResponseAttribution}. `basis` is `mode` verbatim here. */
259
- attribution?: ResponseAttribution;
260
- /**
261
- * Present when a word no material carries was corrected before the search
262
- * ran: the page shows results for `to` (ADR 0031).
263
- */
264
- correctedQuery?: {
265
- from: string;
266
- to: string;
267
- };
268
- view: OpenEnum<"full" | "card">;
269
- /** THIS PAGE. Its length is a page size, never a total. */
270
- results: SearchResultItem[];
271
- /** Always empty: the one search offers no refinements. */
272
- suggestions: never[];
273
- mode: SearchMode;
274
- /** The first result's rank score, 1. */
275
- maxScore: number;
276
- cursor?: Cursor;
277
- /** One or more sentences written for a person; absent when there is nothing to say. */
278
- note?: string;
279
- timings?: Record<string, number>;
280
- /** Echo of the resolved query input (raw image bytes/vector are never echoed). */
281
- query?: Record<string, unknown>;
282
- /** A `getVariantBrief` echo of the seed variant, present only in `variantId` seed mode. */
283
- seed?: MaterialTile | Record<string, unknown>;
284
- } & SearchResponseExplainReceipts;
285
52
  export type DemandMomentum = {
286
53
  state: OpenEnum<"rising" | "fading" | "stable" | "emerging" | "insufficient_volume">;
287
54
  percentChange: number | null;
@@ -311,13 +78,13 @@ export type ProjectTypeShare = {
311
78
  * carries no `sampled` edges, so a single response can be entirely
312
79
  * `considered`.
313
80
  *
314
- * See {@link ColourPairing} for the third, non-evidential arm.
81
+ * See {@link ColorPairing} for the third, non-evidential arm.
315
82
  */
316
83
  export type CospecPairing = {
317
84
  /** The evidence tier. `cospec` = orders; `considered` = boards. Read it before phrasing. */
318
85
  source: "cospec" | "considered";
319
86
  /**
320
- * Membership of the requested `corpusScope`: true in, false out, null when
87
+ * Membership of the requested `searchCatalogueSubset`: true in, false out, null when
321
88
  * the graph cannot say. Absent when no narrowing scope was asked for. It
322
89
  * flags, it never removes (MG-2292).
323
90
  */
@@ -335,10 +102,10 @@ export type CospecPairing = {
335
102
  * How this companion relates to the seed — the distinction raw order evidence
336
103
  * does not carry, and the one that decides what you may claim.
337
104
  *
338
- * - `companion` — a DIFFERENT BRAND entirely. This is the cross-brand claim
105
+ * - `other_brand` — a DIFFERENT BRAND entirely. This is the cross-brand claim
339
106
  * the rail exists to make.
340
107
  * - `brand_sibling` — the seed's own brand, a different product.
341
- * - `colourway` — the seed's own product, restyled.
108
+ * - `colorway` — the seed's own product, restyled.
342
109
  *
343
110
  * **70.6% of edges are same-brand**, so a consumer that ignores this reports
344
111
  * the seed's own family as cross-brand intelligence (MG-1007).
@@ -346,7 +113,7 @@ export type CospecPairing = {
346
113
  * Optional because responses captured before the field existed are genuinely
347
114
  * in the wild; absent means "from an older version", never "unrelated".
348
115
  */
349
- relation?: "companion" | "brand_sibling" | "colourway";
116
+ relation?: "other_brand" | "brand_sibling" | "colorway";
350
117
  category: string | null;
351
118
  projectCount: number;
352
119
  customerCount: number;
@@ -354,41 +121,41 @@ export type CospecPairing = {
354
121
  liftLabel: string;
355
122
  demand: CompanionDemand | null;
356
123
  topProjectTypes: ProjectTypeShare[];
357
- /** Graph-owned colour ranking facts, present only for an explicitly ordered rail. */
124
+ /** Graph-owned color ranking facts, present only for an explicitly ordered rail. */
358
125
  ranking?: PairingRanking;
359
126
  };
360
127
  /**
361
- * A pairing item topped up from colour palette-similarity — a LOOK-ALIKE, not
128
+ * A pairing item topped up from color palette-similarity — a LOOK-ALIKE, not
362
129
  * evidence. Nobody ordered these together and nobody boarded them together; the
363
130
  * palettes sit near each other in OKLab. Say "shares a palette with" and never
364
131
  * "specified together" or "often paired with".
365
132
  */
366
- export type ColourPairing = {
367
- source: "colour";
133
+ export type ColorPairing = {
134
+ source: "color";
368
135
  score: number;
369
136
  /**
370
- * Membership of the requested `corpusScope`: true in, false out, null when
137
+ * Membership of the requested `searchCatalogueSubset`: true in, false out, null when
371
138
  * the graph cannot say. Absent when no narrowing scope was asked for. It
372
139
  * flags, it never removes (MG-2292).
373
140
  */
374
141
  inScope?: boolean | null;
375
- /** Graph-owned colour ranking facts, present only for an explicitly ordered rail. */
142
+ /** Graph-owned color ranking facts, present only for an explicitly ordered rail. */
376
143
  ranking?: PairingRanking;
377
144
  } & MaterialTile;
378
145
  export type PairingOrderMode = "relevance" | "lightness_asc" | "lightness_desc";
379
146
  export type PairingRanking = {
380
- anchorColourSimilarity: number | null;
147
+ anchorColorSimilarity: number | null;
381
148
  lightness: number | null;
382
- anchorColourSimilarityMeasurable: boolean;
149
+ anchorColorSimilarityMeasurable: boolean;
383
150
  lightnessMeasurable: boolean;
384
- basis: OpenEnum<"dominant_colour_oklab">;
151
+ basis: OpenEnum<"dominant_color_oklab">;
385
152
  };
386
153
  export type PairingOrdering = {
387
154
  requested: OpenEnum<PairingOrderMode>;
388
155
  applied: OpenEnum<PairingOrderMode>;
389
156
  direction: OpenEnum<"ascending" | "descending">;
390
- anchorColourBasis: OpenEnum<"one_minus_oklab_euclidean_distance">;
391
- colourSpace: OpenEnum<"oklab">;
157
+ anchorColorBasis: OpenEnum<"one_minus_oklab_euclidean_distance">;
158
+ colorSpace: OpenEnum<"oklab">;
392
159
  lightnessRange: {
393
160
  min: 0;
394
161
  max: 1;
@@ -398,17 +165,17 @@ export type PairingOrdering = {
398
165
  };
399
166
  /**
400
167
  * One pairing, discriminated by `source` into three tiers of very different
401
- * strength: `cospec` (real orders) > `considered` (board activity) > `colour`
168
+ * strength: `cospec` (real orders) > `considered` (board activity) > `color`
402
169
  * (palette look-alike). Branch on `source` before you write a sentence about it.
403
170
  */
404
- export type PairingItem = CospecPairing | ColourPairing;
171
+ export type PairingItem = CospecPairing | ColorPairing;
405
172
  export type PairingsResponse = {
406
173
  seed: MaterialTile;
407
174
  /** A rail, not a ranked corpus — mixed evidence tiers, capped by `limit`. */
408
175
  items: PairingItem[];
409
176
  /**
410
177
  * How many `items` came from real ORDER evidence (`source: "cospec"`). Zero
411
- * with a full rail means every item is board activity or colour look-alike —
178
+ * with a full rail means every item is board activity or color look-alike —
412
179
  * check it before presenting the rail as demand evidence.
413
180
  */
414
181
  cospecCount: number;
@@ -481,43 +248,24 @@ export type ListAttributesResult = {
481
248
  };
482
249
  };
483
250
  export type * from "./catalog-discovery-types.js";
484
- export type * from "./resolve-colour-types.js";
251
+ export type * from "./resolve-color-types.js";
485
252
  export type * from "./by-spec-types.js";
486
253
  export type WhoamiResponse = {
487
254
  principal: string;
488
255
  scopes: string[];
489
256
  };
490
- /** A single colour entry in a search palette — exactly one of `hex` or `name`. */
491
- export type ColourInput = {
492
- hex?: string;
493
- name?: string;
494
- weight?: number;
495
- role?: OpenEnum<"any" | "is" | "accent">;
496
- sourceName?: string;
497
- };
498
- /** A raw incoming image to search by. */
499
- export type SearchImageInput = {
500
- type: "url";
501
- url: string;
502
- } | {
503
- type: "base64";
504
- data: string;
505
- };
506
- export type { SearchGetParams, SearchIndexFilter, SearchInput, } from "./search-request-types.js";
507
- export type { SearchIndexPlan, SearchPlanReceipt, } from "./search-index-plan-types.js";
508
- export type { ColourRegionRange } from "./colour-region-types.js";
509
257
  /** Options for a pairings read. */
510
258
  export type PairingsOptions = {
511
259
  /** Comma-separated coarse category display names; filters both arms. */
512
260
  categories?: string;
513
261
  /** Page size, 1–24 inclusive (default 12). The server rejects (400), not clamps, values outside this range. */
514
262
  limit?: number;
515
- /** Return sampled order evidence only, without board or colour fallback. */
263
+ /** Return sampled order evidence only, without board or color fallback. */
516
264
  sampledOnly?: boolean;
517
- /** Graph-owned order applied within fixed cospec → considered → colour tiers. */
265
+ /** Graph-owned order applied within fixed cospec → considered → color tiers. */
518
266
  order?: PairingOrderMode;
519
267
  /** Flags each item's `inScope` against this scope; never narrows the rail. */
520
- corpusScope?: CorpusScope;
268
+ searchCatalogueSubset?: SearchCatalogueSubset;
521
269
  /** The previous response's `nextCursor`, passed back verbatim. */
522
270
  cursor?: Cursor;
523
271
  };