@instruments/materialgraph 0.31.0 → 0.32.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.
- package/client.d.ts +2 -43
- package/client.js +1 -45
- package/index.d.ts +0 -3
- package/index.js +0 -1
- package/ndjson.d.ts +1 -1
- package/ndjson.js +1 -1
- package/operations.d.ts +1 -1
- package/operations.js +0 -1
- package/package.json +1 -1
- package/response-schemas.generated.d.ts +1 -1
- package/response-schemas.generated.js +24 -33
- package/search-result-validation.d.ts +0 -2
- package/search-result-validation.js +1 -17
- package/types.d.ts +2 -63
- package/validation.d.ts +0 -14
- package/validation.js +0 -38
- package/webmcp.generated.js +5 -6
- package/match-stream-types.d.ts +0 -306
- package/match-stream-types.js +0 -65
- package/match-stream.d.ts +0 -22
- package/match-stream.js +0 -308
- package/match-types.d.ts +0 -138
- package/match-types.js +0 -1
|
@@ -7,5 +7,3 @@ import type { ValidationFailure } from "./validation.js";
|
|
|
7
7
|
* normalisation the wire never had).
|
|
8
8
|
*/
|
|
9
9
|
export declare const checkSearchResultItem: (value: unknown, what: string, fail: ValidationFailure) => void;
|
|
10
|
-
/** Prove every entry of a `results[]` array is a {@link SearchResultItem}. */
|
|
11
|
-
export declare const checkSearchResultItems: (items: readonly unknown[], what: string, fail: ValidationFailure) => void;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*
|
|
18
18
|
* ENUMS ARE CLOSED, matching the canonical `z.enum(…)` members.
|
|
19
19
|
*/
|
|
20
|
-
import { requireArray, requireBoolean, requireEnum,
|
|
20
|
+
import { requireArray, requireBoolean, requireEnum, requireNullableString, requireNumber, requireOptionalBoolean, requireOptionalString, requireRecord, requireString, } from "./validation.js";
|
|
21
21
|
// ── Leaf checks ──────────────────────────────────────────────────────────────
|
|
22
22
|
/** `palette[]` — `{ hex, proportion, name: string.nullable().optional() }`. */
|
|
23
23
|
const checkPaletteSwatch = (value, what, fail) => {
|
|
@@ -36,11 +36,6 @@ const checkColourMeta = (value, what, fail) => {
|
|
|
36
36
|
requireOptionalString(record.dominantFamily, `${what}.dominantFamily`, fail);
|
|
37
37
|
requireOptionalString(record.dominantHex, `${what}.dominantHex`, fail);
|
|
38
38
|
};
|
|
39
|
-
/** `searchResultSignalsSchema` — the Match image cosine, nullable. */
|
|
40
|
-
const checkSignals = (value, what, fail) => {
|
|
41
|
-
const record = requireRecord(value, what, fail);
|
|
42
|
-
requireNullableNumber(record.image, `${what}.image`, fail);
|
|
43
|
-
};
|
|
44
39
|
// ── The item ─────────────────────────────────────────────────────────────────
|
|
45
40
|
/**
|
|
46
41
|
* Prove one wire value is a {@link SearchResultItem}, raising through `fail` at
|
|
@@ -83,17 +78,6 @@ export const checkSearchResultItem = (value, what, fail) => {
|
|
|
83
78
|
if (record.colourMeta !== undefined) {
|
|
84
79
|
checkColourMeta(record.colourMeta, `${what}.colourMeta`, fail);
|
|
85
80
|
}
|
|
86
|
-
if (record.signals !== undefined) {
|
|
87
|
-
checkSignals(record.signals, `${what}.signals`, fail);
|
|
88
|
-
}
|
|
89
81
|
requireOptionalBoolean(record.retired, `${what}.retired`, fail);
|
|
90
82
|
requireOptionalBoolean(record.sampleable, `${what}.sampleable`, fail);
|
|
91
|
-
requireOptionalBoolean(record.belowFloor, `${what}.belowFloor`, fail);
|
|
92
|
-
requireOptionalBoolean(record.admittedOnColour, `${what}.admittedOnColour`, fail);
|
|
93
|
-
};
|
|
94
|
-
/** Prove every entry of a `results[]` array is a {@link SearchResultItem}. */
|
|
95
|
-
export const checkSearchResultItems = (items, what, fail) => {
|
|
96
|
-
items.forEach((item, index) => {
|
|
97
|
-
checkSearchResultItem(item, `${what}[${index}]`, fail);
|
|
98
|
-
});
|
|
99
83
|
};
|
package/types.d.ts
CHANGED
|
@@ -78,13 +78,6 @@ export type MaterialColour = {
|
|
|
78
78
|
/** Where the served image came from. */
|
|
79
79
|
export type ImageSource = OpenEnum<"r2" | "origin">;
|
|
80
80
|
export type { MaterialAvailability, 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 = {
|
|
86
|
-
image: number | null;
|
|
87
|
-
};
|
|
88
81
|
/**
|
|
89
82
|
* One material in a search / match result list.
|
|
90
83
|
*
|
|
@@ -110,7 +103,7 @@ export type SearchResultItem = {
|
|
|
110
103
|
* against Material Bank's own catalogue, so a result list can be joined to a
|
|
111
104
|
* cart, an order or an analytics event without a per-result lookup.
|
|
112
105
|
* Populated on EVERY lane: `search()` (all four arms), `match()`, and
|
|
113
|
-
*
|
|
106
|
+
* full search responses.
|
|
114
107
|
* ABSENT means no MB identity is known for the variant — never that the SKU
|
|
115
108
|
* cannot be ordered. Presence identifies the source; it does not establish sampleability.
|
|
116
109
|
*/
|
|
@@ -165,63 +158,9 @@ export type SearchResultItem = {
|
|
|
165
158
|
colourChip?: {
|
|
166
159
|
hex: string;
|
|
167
160
|
};
|
|
168
|
-
/**
|
|
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).
|
|
172
|
-
*/
|
|
161
|
+
/** Selected mode's weighted primary score; see maxScore. */
|
|
173
162
|
score: number;
|
|
174
|
-
/**
|
|
175
|
-
* Match only. How close this material is to the query, as an ABSOLUTE
|
|
176
|
-
* image-space similarity in [0,1], not adjusted for colour — the same unit as
|
|
177
|
-
* `qualityFloor` and `comparison.bestRejected`, so all three sit on one axis.
|
|
178
|
-
*
|
|
179
|
-
* Never normalised against the page, so the same material reads the same in
|
|
180
|
-
* two different searches. It is the TRUE value and MAY reach 1 — rendering it
|
|
181
|
-
* so that no card claims "100%" is yours to do (round toward zero, or show
|
|
182
|
-
* "99%+"); `Math.round` prints 100% from 0.995 up. Capping it server-side
|
|
183
|
-
* would put it on a different ceiling from `comparison.bestRejected`, which
|
|
184
|
-
* publishes the same unit uncapped, and the two are meant to be comparable.
|
|
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.
|
|
188
|
-
*/
|
|
189
|
-
confidence?: number;
|
|
190
|
-
/**
|
|
191
|
-
* `confidence` as a word — `strong` >= 0.85, `good` >= 0.72, `fair` >= 0.55
|
|
192
|
-
* (the default quality floor), `weak` below it and only reachable under
|
|
193
|
-
* `includeBelowFloor`. Present exactly when `confidence` is.
|
|
194
|
-
*/
|
|
195
|
-
confidenceBand?: "strong" | "good" | "fair" | "weak";
|
|
196
|
-
/**
|
|
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 —
|
|
200
|
-
* UNKNOWN colour, never "no colour" and never "distant". Absent when the
|
|
201
|
-
* colour stage did not run.
|
|
202
|
-
*/
|
|
203
|
-
colourDistance?: number | null;
|
|
204
|
-
/** `colourDistance` as a word, or null when unmeasurable. */
|
|
205
|
-
colourBand?: string | null;
|
|
206
|
-
signals?: SearchResultSignals;
|
|
207
163
|
retired?: boolean;
|
|
208
|
-
/**
|
|
209
|
-
* Present (true) only on quality-floor rejects returned under `includeBelowFloor`
|
|
210
|
-
* (match only) — the item failed the match image-quality floor and would be
|
|
211
|
-
* dropped by default. Appended after the kept results.
|
|
212
|
-
*/
|
|
213
|
-
belowFloor?: boolean;
|
|
214
|
-
/**
|
|
215
|
-
* Present (true) only on Match rows the colour probe admitted (match only):
|
|
216
|
-
* this material is as close in colour as the catalogue gets, and its image
|
|
217
|
-
* similarity is BELOW the match quality floor by design. It is on the page
|
|
218
|
-
* because nothing image-retrieved is nearer in colour, not because it cleared
|
|
219
|
-
* the floor — present it as a colour answer or suppress it knowingly. Absent
|
|
220
|
-
* (never `false`) on rows that came through image retrieval. Distinct from
|
|
221
|
-
* {@link SearchResultItem.belowFloor}, which marks rejects returned only under
|
|
222
|
-
* `includeBelowFloor`.
|
|
223
|
-
*/
|
|
224
|
-
admittedOnColour?: boolean;
|
|
225
164
|
/**
|
|
226
165
|
* Resolved values for the attribute ids sent in `returnAttributes`, keyed by
|
|
227
166
|
* attribute id. ABSENT entirely when `returnAttributes` was not sent.
|
package/validation.d.ts
CHANGED
|
@@ -37,21 +37,7 @@ export declare const requireOptionalBoolean: (value: unknown, what: string, fail
|
|
|
37
37
|
* out of JSON as `Infinity`, so finiteness is a reachable check.
|
|
38
38
|
*/
|
|
39
39
|
export declare const requireNumber: (value: unknown, what: string, fail: ValidationFailure) => number;
|
|
40
|
-
/** A `z.number().nullable()`. */
|
|
41
|
-
export declare const requireNullableNumber: (value: unknown, what: string, fail: ValidationFailure) => number | null;
|
|
42
|
-
/**
|
|
43
|
-
* A `z.number().min(min).max(max)`. `NaN` and both infinities fail the
|
|
44
|
-
* comparisons, exactly as the bounded Zod schema rejects them.
|
|
45
|
-
*/
|
|
46
|
-
export declare const requireBoundedNumber: (value: unknown, what: string, fail: ValidationFailure, min: number, max: number) => number;
|
|
47
|
-
/** A `z.number().int().min(min)` — the integer constraint AND the lower bound. */
|
|
48
|
-
export declare const requireIntegerAtLeast: (value: unknown, what: string, fail: ValidationFailure, min: number) => number;
|
|
49
40
|
/** A `z.enum([…])` — CLOSED, exactly like the canonical member list. */
|
|
50
41
|
export declare const requireEnum: (value: unknown, allowed: readonly string[], what: string, fail: ValidationFailure) => string;
|
|
51
42
|
/** A `z.array(…)`. */
|
|
52
43
|
export declare const requireArray: (value: unknown, what: string, fail: ValidationFailure) => unknown[];
|
|
53
|
-
/**
|
|
54
|
-
* A `z.array(…).default([])`: an OMITTED array is valid and materialises as
|
|
55
|
-
* `[]`; a present non-array is not.
|
|
56
|
-
*/
|
|
57
|
-
export declare const requireDefaultedArray: (value: unknown, what: string, fail: ValidationFailure) => unknown[];
|
package/validation.js
CHANGED
|
@@ -51,34 +51,6 @@ export const requireNumber = (value, what, fail) => {
|
|
|
51
51
|
}
|
|
52
52
|
return value;
|
|
53
53
|
};
|
|
54
|
-
/** A `z.number().nullable()`. */
|
|
55
|
-
export const requireNullableNumber = (value, what, fail) => {
|
|
56
|
-
if (value === null) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
return requireNumber(value, what, fail);
|
|
60
|
-
};
|
|
61
|
-
/**
|
|
62
|
-
* A `z.number().min(min).max(max)`. `NaN` and both infinities fail the
|
|
63
|
-
* comparisons, exactly as the bounded Zod schema rejects them.
|
|
64
|
-
*/
|
|
65
|
-
export const requireBoundedNumber = (value, what, fail, min, max) => {
|
|
66
|
-
if (typeof value !== "number" || !(value >= min && value <= max)) {
|
|
67
|
-
throw fail(`${what} is not a number within [${min}, ${max}]`);
|
|
68
|
-
}
|
|
69
|
-
return value;
|
|
70
|
-
};
|
|
71
|
-
/** A `z.number().int().min(min)` — the integer constraint AND the lower bound. */
|
|
72
|
-
export const requireIntegerAtLeast = (value, what, fail, min) => {
|
|
73
|
-
const numeric = requireNumber(value, what, fail);
|
|
74
|
-
if (!Number.isInteger(numeric)) {
|
|
75
|
-
throw fail(`${what} is not an integer`);
|
|
76
|
-
}
|
|
77
|
-
if (numeric < min) {
|
|
78
|
-
throw fail(`${what} is below the minimum of ${min}`);
|
|
79
|
-
}
|
|
80
|
-
return numeric;
|
|
81
|
-
};
|
|
82
54
|
/** A `z.enum([…])` — CLOSED, exactly like the canonical member list. */
|
|
83
55
|
export const requireEnum = (value, allowed, what, fail) => {
|
|
84
56
|
const text = requireString(value, what, fail);
|
|
@@ -94,16 +66,6 @@ export const requireArray = (value, what, fail) => {
|
|
|
94
66
|
}
|
|
95
67
|
return value;
|
|
96
68
|
};
|
|
97
|
-
/**
|
|
98
|
-
* A `z.array(…).default([])`: an OMITTED array is valid and materialises as
|
|
99
|
-
* `[]`; a present non-array is not.
|
|
100
|
-
*/
|
|
101
|
-
export const requireDefaultedArray = (value, what, fail) => {
|
|
102
|
-
if (value === undefined) {
|
|
103
|
-
return [];
|
|
104
|
-
}
|
|
105
|
-
return requireArray(value, what, fail);
|
|
106
|
-
};
|
|
107
69
|
/**
|
|
108
70
|
* A `z.json()` — a JSON value, recursively: `string | number | boolean | null`,
|
|
109
71
|
* an array of them, or an object of them, at ANY depth.
|
package/webmcp.generated.js
CHANGED
|
@@ -3,11 +3,10 @@ const json = [
|
|
|
3
3
|
",\"prefixItems\":[{\"type\":\"number\"},{\"type\":\"number\"}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"hex\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"A Variant whose own colour to order by: materials the same colour as this one. A Variant with no colour on the index is a 400.\"}},\"required\":[\"variantId\"],\"additionalProperties\":false}],\"description\":\"Order the page by a colour: `hex`, or `variantId` for the colour of an existing Variant. The page is the colour's family, nearest first by OKLab distance; it replaces any colour read from `text`.\"},\"corpusScope\":{\"type\":\"string\",\"enum\":[\"all\",\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"],\"description\":\"Which corpus to answer from. samplizeCatalogue admits published US paint colours regardless of stock. ABSENT (or 'all') means the WHOLE GRAPH — every material MaterialGraph knows, orderable or not; absence is never a narrower default. Three transaction-oriented scopes gate on governed CHANNEL membership: 'materialBankNaOrderable' (Material Bank North America, ~277,000 materials), 'materialBankEuOrderable' (Material Bank Europe, ~65,000) and 'designShopOrderable' (DesignShop, ~34,000, with the products DesignShop hides from browse excluded). 'materialBankNaAndCatalogue' is the one-query union of Material Bank North America and the governed Catalogue editorial discovery corpus; that union does not by itself claim every member is transactable. 'materialBankOrderable' is NOT one of them: it selects materials carrying a Material Bank identity, which is a provenance signal rather than a channel one, and it admits 98.4% of the graph — use it to exclude crawl-sourced brands, not to mean orderable. A scope answers membership in its named governed corpus, never suitability: scope on channel, then refine on sector. Every scope is applied INSIDE the query: `limit` fills with eligible rows and `counts` describe the scoped corpus, which post-filtering a page cannot give you. An unknown value is a 400 naming it, never a silent fall back to the whole graph.\"},\"cursor\":{\"description\":\"Opaque cursor from a previous response's `cursor`, for the next page of the same query. A cursor from another query is a 409 telling the caller to restart from the first page.\",\"type\":\"string\",\"minLength\":1},\"explain\":{\"description\":\"Add `timings`: wall-clock milliseconds for reading the plan (`planMs`), the index query (`indexMs`) and hydrating the page (`pageMs`), and `explain.plan`: the receipt of the plan the search ran on, with the plan itself.\",\"type\":\"boolean\"},\"filters\":{\"minItems\":1,\"maxItems\":24,\"type\":\"array\",\"items\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"An attribute the search index carries, e.g. \\\"primary_material\\\" or \\\"colour_mood\\\", or \\\"certification_programme\\\" for any certification programme.\"},\"term\":{\"type\":\"string\",\"minLength\":1,\"description\":\"The canonical term the value must carry, e.g. \\\"textile\\\".\"}},\"required\":[\"attributeId\",\"term\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"An attribute the search index carries, e.g. \\\"primary_material\\\" or \\\"colour_mood\\\", or \\\"certification_programme\\\" for any certification programme.\"},\"terms\":{\"minItems\":1,\"maxItems\":50,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Canonical terms, any one of which satisfies the filter.\"}},\"required\":[\"attributeId\",\"terms\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"An attribute the search index carries, e.g. \\\"primary_material\\\" or \\\"colour_mood\\\", or \\\"certification_programme\\\" for any certification programme.\"},\"excludesTerm\":{\"type\":\"string\",\"minLength\":1,\"description\":\"A canonical term the value must not carry. A material ",
|
|
4
4
|
"that states nothing for the attribute passes.\"}},\"required\":[\"attributeId\",\"excludesTerm\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"An attribute the search index carries, e.g. \\\"primary_material\\\" or \\\"colour_mood\\\", or \\\"certification_programme\\\" for any certification programme.\"},\"hasValue\":{\"type\":\"boolean\",\"const\":true,\"description\":\"Require any stated value.\"}},\"required\":[\"attributeId\",\"hasValue\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\",\"minLength\":1,\"description\":\"A number the search index cuts into bands: a numeric attribute such as \\\"nrc_rating\\\" or \\\"lead_time_days\\\", a dimensions lens such as \\\"dimensions.thickness_mm\\\" or \\\"dimensions.short_side_mm\\\" (millimetres), or a composition share such as \\\"composition_weighted.wool\\\" (0 to 1).\"},\"max\":{\"description\":\"Inclusive upper bound. Omit for a floor.\",\"type\":\"number\"},\"min\":{\"description\":\"Inclusive lower bound. Omit for a ceiling.\",\"type\":\"number\"}},\"required\":[\"attributeId\"],\"additionalProperties\":false}]},\"description\":\"Attribute filters, every one hard, run as token sets on the search index. Each entry is a term (`attributeId` + `term`), a term set (`attributeId` + `terms`, any one satisfies it), an exclusion (`attributeId` + `excludesTerm`; a material stating nothing passes), a presence check (`attributeId` + `hasValue: true`) or a numeric range (`attributeId` + `min` and/or `max`, inclusive), read as the number bands wholly inside it, so a bound between two band edges is read inward and a value the index cannot prove in range is left out. Entries on one attribute are OR'd; entries on different attributes AND together. They are added to what the words were read as and change no ordering. An attribute or term the index does not carry is a 400 naming it.\"},\"image\":{\"description\":\"A query image to search by (URL or base64). It is embedded, and the page is its nearest materials in the image namespace; `text`, when also given, narrows them.\",\"oneOf\":[{\"type\":\"object\",\"properties\":{\"type\":{\"type\":\"string\",\"const\":\"url\"},\"url\":{\"type\":\"string\",\"format\":\"uri\",\"description\":\"HTTP(S) URL of the query image. MUST be an https URL.\"}},\"required\":[\"type\",\"url\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"data\":{\"type\":\"string\",\"minLength\":1,\"description\":\"Base64-encoded image bytes (or a data URI) the caller already holds — e.g. a form upload — so there is nothing to host.\"},\"type\":{\"type\":\"string\",\"const\":\"base64\"}},\"required\":[\"data\",\"type\"],\"additionalProperties\":false}],\"type\":\"object\"},\"limit\":{\"description\":\"Products per page (max 60). Absent, the search's own page size.\",\"type\":\"integer\",\"minimum\":1,\"maximum\":60},\"returnAttributes\":{\"maxItems\":8,\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1},\"description\":\"Attribute ids whose RESOLVED VALUE should ride each result, under `attributes` keyed by attribute id. Values resolve with the normal variant-over-product precedence. THREE STATES, and they are different: an attribute you did not ask for is not a key at all; an attribute you asked for that this variant carries no accepted value for is `null`; anything else is the value (a string, or an array for a multi-valued attribute). An unknown attribute id is refused naming it, never silently dropped. Pair it with `filters` to show what you filtered on.\"},\"similarTo\":{\"description\":\"Find materials that LOOK like a seed: `variantId` seeds from an existing Variant's image, `materialBankId` from a Material Bank SKU (an unknown SKU is a 400 naming it), `vector` from a pre-computed image-space halfvec literal (`[a,b,...]`). `text`, when also given, narrows the look-alikes.\",\"anyOf\":[{\"type\":\"object\",\"properties\":{\"variantId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"variantId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"materialBankId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"materialBankId\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"vector\":{\"type\":\"string\",\"minLength\":1}},\"",
|
|
5
5
|
"required\":[\"vector\"],\"additionalProperties\":false}]},\"text\":{\"description\":\"Free-text query (\\\"oak flooring\\\", \\\"sage green velvet\\\", \\\"Kvadrat wool\\\"). Brands, categories, colours and attributes are read from the words unless `brands`, `category`, `colour` or `filters` says so instead; a word no material carries may be corrected, and the response says so under `correctedQuery`. May be omitted when `brands`, `category` or `filters` says what to find.\",\"type\":\"string\",\"maxLength\":500},\"view\":{\"default\":\"full\",\"description\":\"Result projection. \\\"full\\\" (default) returns the complete result items; \\\"card\\\" the lean grid projection. The response echoes `view`, which discriminates the `results` union.\",\"type\":\"string\",\"enum\":[\"full\",\"card\"]}},\"additionalProperties\":false}}},\"method\":\"POST\",\"name\":\"search\",\"operationId\":\"search_materials_post\",\"outputSchema\":{\"$ref\":\"#/$defs/schema0\",\"$defs\":{\"schema0\":{\"type\":\"object\",\"properties\":{\"data\":{\"oneOf\":[{\"type\":\"object\",\"properties\":{\"results\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"resultId\":{\"type\":\"string\",\"description\":\"This exact hit's attribution key — `<responseId>:<rank>`, where rank is 1-based within THIS response. Unique across pages, because each page is its own response with its own id. Store it verbatim next to the outcome; it is opaque, and `attribution.responseId` is the field to group by.\"},\"variantId\":{\"type\":\"string\"},\"brand\":{\"type\":\"string\"},\"product\":{\"type\":\"string\"},\"brandSlug\":{\"type\":[\"string\",\"null\"]},\"productId\":{\"type\":[\"string\",\"null\"]},\"materialBankId\":{\"description\":\"The variant's Material Bank SKU — the ONE EXTERNAL identifier on a result, in contrast to the OPAQUE `variantId`: it is the id you are expected to match against Material Bank's own catalogue (carts, ordering and analytics all key on it), so a result list can be joined without a second lookup. Populated by the search and by Match alike. ABSENT means no MB identity is known for the variant, never that the SKU cannot be ordered; presence is the orderability tell, absence is silence.\",\"type\":\"string\"},\"manufacturerCode\":{\"description\":\"The governed manufacturer/source-native item code from `variants.code` (for example \\\"GADLC02\\\"). Populated when the full-result retrieval source carries that canonical field; Match always hydrates it from the variant row. ABSENT means unknown or an older external serving projection, never an invented code. Independent of materialBankId.\",\"type\":\"string\"},\"projectSaveCapability\":{\"description\":\"Explicit source-governed permission to save this exact source-coded variant to a Project. ABSENT means unknown/not allowed; it is never inferred from the brand or name.\",\"type\":\"string\",\"const\":\"allowed\"},\"sampleable\":{\"description\":\"Whether a physical sample is explicitly known to be orderable. Absent means unknown, never unavailable.\",\"type\":\"boolean\"},\"sourceStatus\":{\"description\":\"The governed data status supporting Project capture. ABSENT means unknown. Provisional data remains source-backed but is not a verified catalogue claim.\",\"type\":\"string\",\"const\":\"provisional\"},\"productType\":{\"type\":\"string\"},\"color\":{\"type\":[\"string\",\"null\"]},\"variantName\":{\"type\":[\"string\",\"null\"]},\"imageUrl\":{\"type\":[\"string\",\"null\"]},\"imageSha256\":{\"description\":\"SHA-256 of the displayed image `imageUrl` points at, as a hex digest. Lets a caller key a cache or dedupe on the asset rather than its URL. ABSENT means no hash is on record, never that there is no image.\",\"type\":\"string\"},\"hasImage\":{\"type\":\"boolean\"},\"variantCount\":{\"description\":\"How many eligible variants the row's product has, the same set `list_variants` pages, so a follow-up list of the product returns this many: the card's \\\"12 colourways\\\". ABSENT means the product is unknown or has no eligible variant.\",\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"palette\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"description\":\"`#rrggbb` of the swatch, as extracted from the product image.\"},\"name\":{\"description\":\"Stored human colour name for the swatch, when on",
|
|
6
|
-
"e was extracted.\",\"type\":[\"string\",\"null\"]},\"proportion\":{\"type\":\"number\",\"description\":\"The swatch's share of the analysed image, in [0,1].\"}},\"required\":[\"hex\",\"proportion\"],\"additionalProperties\":false},\"description\":\"The material's main-image palette — the FULL stored palette, uncapped and in stored order. Colours are EXTRACTED FROM PRODUCT IMAGERY (visual likeness, never a declared colour specification). A one-swatch palette means the extractor found one colour — solid-colour materials are genuinely monochrome — never that the list was trimmed. This array is always present: EMPTY means nothing is on record (unknown), not 'this material has no colours'.\"},\"colourMeta\":{\"type\":\"object\",\"properties\":{\"dominantFamily\":{\"type\":\"string\"},\"dominantHex\":{\"type\":\"string\"},\"mood\":{\"type\":\"string\"}},\"additionalProperties\":false},\"colourChip\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"description\":\"`#rrggbb` to fill the chip with. EXTRACTED FROM the product image (a visual likeness), never a manufacturer-declared colour specification — surfaces must not present it as one.\"}},\"required\":[\"hex\"],\"additionalProperties\":false,\"description\":\"Present when this material's product imagery carries nothing beyond one colour, so a surface should render that colour rather than the photograph. Set for COATINGS (paints, primers, stains, floor coatings) whose stored palette quantises to a single swatch — a paint's photo is a flat fill, and the colour we hold structured beats a JPEG of it. ABSENT means the question was not established (unknown product type, unextracted or multi-swatch palette) and the image should be rendered; it NEVER asserts 'this is not a coating'.\"},\"attributes\":{\"description\":\"Resolved attribute values for the ids sent in `returnAttributes`, keyed by attribute id. ABSENT entirely when `returnAttributes` was not sent. A key present with `null` means this variant carries no accepted value for that attribute — absent is not empty and neither is null: the three states are distinct on purpose. Values resolve with the normal variant-over-product precedence.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"array\",\"items\":{\"type\":\"string\"}},{\"type\":\"null\"}]}},\"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"\",\"strength\",\"terms\"],\"additionalProperties\":false}}},\"required\":[\"brands\",\"category\",\"colour\",\"excludeBrands\",\"excludes\",\"leftover\",\"normalised\",\"softAlone\",\"vendorColourNames\",\"wants\"],\"additionalProperties\":false,\"description\":\"The plan the search ran on: the category, brands, wanted and excluded attribute terms (each hard or soft, with its fill), the colour, the words left for text, and the caller's corpus scope when one was sent.\"},\"signature\":{\"type\":\"string\"},\"source\":{\"type\":\"string\",\"enum\":[\"dictionary\",\"jev\"]},\"version\":{\"type\":\"string\"}},\"required\":[\"cached\",\"plan\",\"signature\",\"source\",\"version\"],\"additionalProperties\":false}},\"required\":[\"plan\"],\"additionalProperties\":false},\"query\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}},\"seed\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}}},\"required\":[\"results\",\"view\",\"suggestions\",\"mode\",\"maxScore\",\"query\"],\"additionalProperties\":false}],\"type\":\"object\"},\"links\":{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false},\"meta\":{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"enum\":[\"search_materials_post\",\"search_materials_get\"]},\"requestId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"operationId\",\"requestId\"],\"additionalProperties\":false},\"warnings\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}}},\"required\":[\"data\",\"links\",\"meta\",\"warnings\"],\"additionalProperties\":false}}},\"path\":\"/api/v1/search\",\"requiredHeaders\":[],\"requiresVisibleConfirmation\":false}]",
|
|
6
|
+
"e was extracted.\",\"type\":[\"string\",\"null\"]},\"proportion\":{\"type\":\"number\",\"description\":\"The swatch's share of the analysed image, in [0,1].\"}},\"required\":[\"hex\",\"proportion\"],\"additionalProperties\":false},\"description\":\"The material's main-image palette — the FULL stored palette, uncapped and in stored order. Colours are EXTRACTED FROM PRODUCT IMAGERY (visual likeness, never a declared colour specification). A one-swatch palette means the extractor found one colour — solid-colour materials are genuinely monochrome — never that the list was trimmed. This array is always present: EMPTY means nothing is on record (unknown), not 'this material has no colours'.\"},\"colourMeta\":{\"type\":\"object\",\"properties\":{\"dominantFamily\":{\"type\":\"string\"},\"dominantHex\":{\"type\":\"string\"},\"mood\":{\"type\":\"string\"}},\"additionalProperties\":false},\"colourChip\":{\"type\":\"object\",\"properties\":{\"hex\":{\"type\":\"string\",\"description\":\"`#rrggbb` to fill the chip with. EXTRACTED FROM the product image (a visual likeness), never a manufacturer-declared colour specification — surfaces must not present it as one.\"}},\"required\":[\"hex\"],\"additionalProperties\":false,\"description\":\"Present when this material's product imagery carries nothing beyond one colour, so a surface should render that colour rather than the photograph. Set for COATINGS (paints, primers, stains, floor coatings) whose stored palette quantises to a single swatch — a paint's photo is a flat fill, and the colour we hold structured beats a JPEG of it. ABSENT means the question was not established (unknown product type, unextracted or multi-swatch palette) and the image should be rendered; it NEVER asserts 'this is not a coating'.\"},\"attributes\":{\"description\":\"Resolved attribute values for the ids sent in `returnAttributes`, keyed by attribute id. ABSENT entirely when `returnAttributes` was not sent. A key present with `null` means this variant carries no accepted value for that attribute — absent is not empty and neither is null: the three states are distinct on purpose. Values resolve with the normal variant-over-product precedence.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"array\",\"items\":{\"type\":\"string\"}},{\"type\":\"null\"}]}},\"score\":{\"type\":\"number\",\"description\":\"The selected mode's weighted primary score on the [0, maxScore] scale.\"},\"retired\":{\"description\":\"MB lists the SKU as not visible/retired; demoted in ranking. Omitted when false.\",\"type\":\"boolean\"}},\"required\":[\"variantId\",\"brand\",\"product\",\"brandSlug\",\"productId\",\"color\",\"variantName\",\"imageUrl\",\"hasImage\",\"palette\",\"score\"],\"additionalProperties\":false}},\"view\":{\"default\":\"full\",\"description\":\"Projection echo — always 'full' on this envelope variant.\",\"type\":\"string\",\"const\":\"full\"},\"correctedQuery\":{\"description\":\"Present when a word no material carries was corrected before the search ran: `from` is the query as typed (normalised), `to` the phrasing the page shows results for.\",\"type\":\"object\",\"properties\":{\"from\":{\"type\":\"string\"},\"to\":{\"type\":\"string\"}},\"required\":[\"from\",\"to\"],\"additionalProperties\":false},\"attribution\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"lexical\",\"agent\",\"attribute\",\"match\"],\"description\":\"The ranking basis. On /api/v1/search this equals the envelope's `mode`. The historical match basis remains readable for stored outcomes.\"},\"issuedAt\":{\"type\":\"string\",\"description\":\"ISO-8601 timestamp of when this response was assembled, so a stored outcome carries its own age without a second lookup.\"},\"responseId\":{\"type\":\"string\",\"description\":\"This response's stable id — the same value as the `X-Request-Id` header. Store it next to the outcome (an order, a save, a click) to join that outcome back to the ranking that produced it. Opaque: round-trip it verbatim, never parse it.\"}},\"required\":[\"basis\",\"issuedAt\",\"responseId\"],\"additionalProperties\":false,\"description\":\"Result attribution receipt. Present on every successful ranked response. Nothing on our side retains it — what you store IS the record.\"},\"s",
|
|
7
|
+
"uggestions\":{\"default\":[],\"description\":\"Always empty: the one search offers no refinements. Read nothing from it.\",\"maxItems\":0,\"type\":\"array\",\"items\":{\"not\":{}}},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"lexical\",\"agent\",\"attribute\"],\"description\":\"What ordered the page: 'image' (a look-alike: image similarity to the seed), 'colour' (the words or the caller named a colour: colour distance inside that colour's family), 'lexical' (the query's words, ranked by text rank on the index), 'attribute' (no words left to rank on: the category and filters define the page), or 'agent' (the search agent chose these variants and their order; scores are rank-based, 1 for the first result falling linearly, and `maxScore` is 1).\"},\"maxScore\":{\"type\":\"number\"},\"note\":{\"type\":\"string\"},\"cursor\":{\"type\":\"string\"},\"timings\":{\"description\":\"Wall-clock milliseconds per step of the search, keyed by step id. The key set is open and depends on the path a query took, so read it by iterating rather than by naming a key. Present only when `explain` is set: diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"number\"}},\"explain\":{\"description\":\"Present only under `explain`: the receipt of the plan the search ran on. Diagnostic, like `timings`.\",\"type\":\"object\",\"properties\":{\"plan\":{\"type\":\"object\",\"properties\":{\"cached\":{\"type\":\"boolean\"},\"plan\":{\"type\":\"object\",\"properties\":{\"brands\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"ids\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"names\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"phrase\":{\"type\":\"string\"}},\"required\":[\"ids\",\"names\",\"phrase\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"category\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\"},\"phrase\":{\"type\":\"string\"},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"label\",\"phrase\",\"productTypeIds\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"colour\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"family12\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\"},\"oklab\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\"},{\"type\":\"number\"},{\"type\":\"number\"}],\"items\":false,\"minItems\":3,\"maxItems\":3},\"phrase\":{\"type\":\"string\"},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\"},{\"type\":\"number\"}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\"},{\"type\":\"number\"}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\"},{\"type\":\"number\"}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"family12\",\"familyId\",\"hex\",\"oklab\",\"phrase\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"corpusScope\":{\"type\":\"string\",\"enum\":[\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"excludeBrands\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"excludes\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"phrase\":{\"type\":\"string\"},\"term\":{\"type\":\"string\"}},\"required\":[\"attributeId\",\"phrase\",\"term\"],\"additionalProperties\":false}},\"leftover\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"normalised\":{\"type\":\"string\"},\"softAlone\":{\"type\":\"boolean\"},\"vendorColourNames\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"wants\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"fill\":{\"type\":\"number\"},\"phrase\":{\"type\":\"string\"},\"presence\":{\"type\":\"boolean\",\"const\":true},\"strength\":{\"type\":\"string\",\"enum\":[\"hard\",\"soft\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"attributeId\",\"fill\",\"phrase\",\"strength\",\"terms\"],\"additionalProperties\":false}}},\"required\":[\"brands\",\"category\",\"colour\",\"excludeBrands\",\"excludes\",\"leftover\",\"normalised\",\"softAlone\",\"vendorColourNames\",\"wants\"],\"additionalProperties\":false",
|
|
8
|
+
",\"description\":\"The plan the search ran on: the category, brands, wanted and excluded attribute terms (each hard or soft, with its fill), the colour, the words left for text, and the caller's corpus scope when one was sent.\"},\"signature\":{\"type\":\"string\"},\"source\":{\"type\":\"string\",\"enum\":[\"dictionary\",\"jev\"]},\"version\":{\"type\":\"string\"}},\"required\":[\"cached\",\"plan\",\"signature\",\"source\",\"version\"],\"additionalProperties\":false}},\"required\":[\"plan\"],\"additionalProperties\":false},\"query\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}},\"seed\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}}},\"required\":[\"results\",\"view\",\"suggestions\",\"mode\",\"maxScore\",\"query\"],\"additionalProperties\":false},{\"type\":\"object\",\"properties\":{\"results\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributes\":{\"description\":\"Resolved attribute values for the ids sent in `returnAttributes`, keyed by attribute id. ABSENT entirely when `returnAttributes` was not sent. A key present with `null` means this variant carries no accepted value for that attribute — absent is not empty and neither is null: the three states are distinct on purpose. Values resolve with the normal variant-over-product precedence.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"anyOf\":[{\"type\":\"string\"},{\"type\":\"array\",\"items\":{\"type\":\"string\"}},{\"type\":\"null\"}]}},\"brand\":{\"type\":\"string\"},\"brandSlug\":{\"type\":[\"string\",\"null\"]},\"imageSha256\":{\"description\":\"SHA-256 of the displayed image `imageUrl` points at, as a hex digest. Lets a caller key a cache or dedupe on the asset rather than its URL. ABSENT means no hash is on record, never that there is no image.\",\"type\":\"string\"},\"imageUrl\":{\"type\":[\"string\",\"null\"]},\"manufacturerCode\":{\"description\":\"The governed manufacturer/source-native item code from `variants.code` (for example \\\"K-7265-BV\\\") — the same key and the same value the full item publishes. It is what separates two cards a reader cannot otherwise tell apart: brand, product name and variant name are shared by 280,334 of 371,515 cardable variants, because `name` is the PRODUCT name and a paint product is a whole collection. ABSENT means no code is recorded for the variant, never an invented one.\",\"type\":\"string\"},\"name\":{\"type\":\"string\",\"description\":\"Product name (the full item's `product`).\"},\"productId\":{\"type\":[\"string\",\"null\"]},\"projectSaveCapability\":{\"type\":\"string\",\"const\":\"allowed\"},\"resultId\":{\"type\":\"string\",\"description\":\"This exact hit's attribution key — `<responseId>:<rank>`, where rank is 1-based within THIS response. Unique across pages, because each page is its own response with its own id. Store it verbatim next to the outcome; it is opaque, and `attribution.responseId` is the field to group by.\"},\"sampleable\":{\"description\":\"Whether a physical sample is explicitly known to be orderable. Absent means unknown, never unavailable.\",\"type\":\"boolean\"},\"score\":{\"type\":\"number\"},\"sourceStatus\":{\"type\":\"string\",\"const\":\"provisional\"},\"variantCount\":{\"description\":\"How many eligible variants the row's product has, the same set `list_variants` pages, so a follow-up list of the product returns this many: the card's \\\"12 colourways\\\". ABSENT means the product is unknown or has no eligible variant.\",\"type\":\"integer\",\"exclusiveMinimum\":0,\"maximum\":9007199254740991},\"variantId\":{\"type\":\"string\"},\"variantName\":{\"type\":[\"string\",\"null\"]}},\"required\":[\"brand\",\"brandSlug\",\"imageUrl\",\"name\",\"productId\",\"score\",\"variantId\",\"variantName\"],\"additionalProperties\":false}},\"view\":{\"type\":\"string\",\"const\":\"card\",\"description\":\"Projection echo — always 'card' on this envelope variant.\"},\"correctedQuery\":{\"description\":\"Present when a word no material carries was corrected before the search ran: `from` is the query as typed (normalised), `to` the phrasing the page shows results for.\",\"type\":\"object\",\"properties\":{\"from\":{\"type\":\"string\"},\"to\":{\"type\":\"string\"}},\"required\":[\"from\",\"to\"],\"additionalProperties\":false},\"attribution\":{\"type\":\"object\",\"properties\":{\"basis\":{\"type\":\"string\",",
|
|
9
|
+
"\"enum\":[\"image\",\"colour\",\"lexical\",\"agent\",\"attribute\",\"match\"],\"description\":\"The ranking basis. On /api/v1/search this equals the envelope's `mode`. The historical match basis remains readable for stored outcomes.\"},\"issuedAt\":{\"type\":\"string\",\"description\":\"ISO-8601 timestamp of when this response was assembled, so a stored outcome carries its own age without a second lookup.\"},\"responseId\":{\"type\":\"string\",\"description\":\"This response's stable id — the same value as the `X-Request-Id` header. Store it next to the outcome (an order, a save, a click) to join that outcome back to the ranking that produced it. Opaque: round-trip it verbatim, never parse it.\"}},\"required\":[\"basis\",\"issuedAt\",\"responseId\"],\"additionalProperties\":false,\"description\":\"Result attribution receipt. Present on every successful ranked response. Nothing on our side retains it — what you store IS the record.\"},\"suggestions\":{\"default\":[],\"description\":\"Always empty: the one search offers no refinements. Read nothing from it.\",\"maxItems\":0,\"type\":\"array\",\"items\":{\"not\":{}}},\"mode\":{\"type\":\"string\",\"enum\":[\"image\",\"colour\",\"lexical\",\"agent\",\"attribute\"],\"description\":\"What ordered the page: 'image' (a look-alike: image similarity to the seed), 'colour' (the words or the caller named a colour: colour distance inside that colour's family), 'lexical' (the query's words, ranked by text rank on the index), 'attribute' (no words left to rank on: the category and filters define the page), or 'agent' (the search agent chose these variants and their order; scores are rank-based, 1 for the first result falling linearly, and `maxScore` is 1).\"},\"maxScore\":{\"type\":\"number\"},\"note\":{\"type\":\"string\"},\"cursor\":{\"type\":\"string\"},\"timings\":{\"description\":\"Wall-clock milliseconds per step of the search, keyed by step id. The key set is open and depends on the path a query took, so read it by iterating rather than by naming a key. Present only when `explain` is set: diagnostic, not part of the ranking contract.\",\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{\"type\":\"number\"}},\"explain\":{\"description\":\"Present only under `explain`: the receipt of the plan the search ran on. Diagnostic, like `timings`.\",\"type\":\"object\",\"properties\":{\"plan\":{\"type\":\"object\",\"properties\":{\"cached\":{\"type\":\"boolean\"},\"plan\":{\"type\":\"object\",\"properties\":{\"brands\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"ids\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"names\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"phrase\":{\"type\":\"string\"}},\"required\":[\"ids\",\"names\",\"phrase\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"category\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"label\":{\"type\":\"string\"},\"phrase\":{\"type\":\"string\"},\"productTypeIds\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"label\",\"phrase\",\"productTypeIds\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"colour\":{\"anyOf\":[{\"type\":\"object\",\"properties\":{\"family12\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"familyId\":{\"type\":\"string\"},\"hex\":{\"type\":\"string\"},\"oklab\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\"},{\"type\":\"number\"},{\"type\":\"number\"}],\"items\":false,\"minItems\":3,\"maxItems\":3},\"phrase\":{\"type\":\"string\"},\"range\":{\"type\":\"object\",\"properties\":{\"chroma\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\"},{\"type\":\"number\"}],\"items\":false,\"minItems\":2,\"maxItems\":2},\"hue\":{\"anyOf\":[{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\"},{\"type\":\"number\"}],\"items\":false,\"minItems\":2,\"maxItems\":2},{\"type\":\"null\"}]},\"lightness\":{\"type\":\"array\",\"prefixItems\":[{\"type\":\"number\"},{\"type\":\"number\"}],\"items\":false,\"minItems\":2,\"maxItems\":2}},\"required\":[\"chroma\",\"hue\",\"lightness\"],\"additionalProperties\":false}},\"required\":[\"family12\",\"familyId\",\"hex\",\"oklab\",\"phrase\"],\"additionalProperties\":false},{\"type\":\"null\"}]},\"corpusScope\":{\"type\":\"string\",\"enum\":[\"samplizeCatalogue\",\"materialBankOrderable\",\"materialBankNaOrderable\",\"materialBankNaAndCatalogue\",\"materialBankEuOrderable\",\"designShopOrderable\"]},\"excludeBrands\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"excludes\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"pr",
|
|
10
|
+
"operties\":{\"attributeId\":{\"type\":\"string\"},\"phrase\":{\"type\":\"string\"},\"term\":{\"type\":\"string\"}},\"required\":[\"attributeId\",\"phrase\",\"term\"],\"additionalProperties\":false}},\"leftover\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"normalised\":{\"type\":\"string\"},\"softAlone\":{\"type\":\"boolean\"},\"vendorColourNames\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"wants\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"attributeId\":{\"type\":\"string\"},\"fill\":{\"type\":\"number\"},\"phrase\":{\"type\":\"string\"},\"presence\":{\"type\":\"boolean\",\"const\":true},\"strength\":{\"type\":\"string\",\"enum\":[\"hard\",\"soft\"]},\"terms\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"attributeId\",\"fill\",\"phrase\",\"strength\",\"terms\"],\"additionalProperties\":false}}},\"required\":[\"brands\",\"category\",\"colour\",\"excludeBrands\",\"excludes\",\"leftover\",\"normalised\",\"softAlone\",\"vendorColourNames\",\"wants\"],\"additionalProperties\":false,\"description\":\"The plan the search ran on: the category, brands, wanted and excluded attribute terms (each hard or soft, with its fill), the colour, the words left for text, and the caller's corpus scope when one was sent.\"},\"signature\":{\"type\":\"string\"},\"source\":{\"type\":\"string\",\"enum\":[\"dictionary\",\"jev\"]},\"version\":{\"type\":\"string\"}},\"required\":[\"cached\",\"plan\",\"signature\",\"source\",\"version\"],\"additionalProperties\":false}},\"required\":[\"plan\"],\"additionalProperties\":false},\"query\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}},\"seed\":{\"type\":\"object\",\"propertyNames\":{\"type\":\"string\"},\"additionalProperties\":{}}},\"required\":[\"results\",\"view\",\"suggestions\",\"mode\",\"maxScore\",\"query\"],\"additionalProperties\":false}],\"type\":\"object\"},\"links\":{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false},\"meta\":{\"type\":\"object\",\"properties\":{\"operationId\":{\"type\":\"string\",\"enum\":[\"search_materials_post\",\"search_materials_get\"]},\"requestId\":{\"type\":\"string\",\"minLength\":1}},\"required\":[\"operationId\",\"requestId\"],\"additionalProperties\":false},\"warnings\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"minLength\":1}}},\"required\":[\"data\",\"links\",\"meta\",\"warnings\"],\"additionalProperties\":false}}},\"path\":\"/api/v1/search\",\"requiredHeaders\":[],\"requiresVisibleConfirmation\":false}]",
|
|
12
11
|
].join("");
|
|
13
12
|
export const WEBMCP_TOOLS = JSON.parse(json);
|