@instruments/materialgraph 0.12.1 → 0.14.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/browse-types.d.ts +84 -3
- package/by-colour-types.d.ts +16 -1
- package/client-browse.d.ts +15 -0
- package/client-browse.js +19 -0
- package/client.d.ts +6 -9
- package/client.js +9 -9
- package/demand-types.d.ts +20 -0
- package/errors.d.ts +1 -1
- package/errors.js +3 -0
- package/index.d.ts +2 -0
- package/material-query-plan-types.d.ts +154 -0
- package/material-query-plan-types.js +1 -0
- package/operations.d.ts +1 -1
- package/operations.js +1 -0
- package/package.json +1 -1
- package/query-understanding-types.d.ts +38 -0
- package/query-understanding-types.js +1 -0
- package/search-request-types.d.ts +49 -2
- package/search-result-validation.js +7 -0
- package/types.d.ts +45 -6
package/browse-types.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type { ByColourSwatch } from "./by-colour-types.js";
|
|
|
15
15
|
*/
|
|
16
16
|
import type { CorpusScope, CorpusScopeReceipt } from "./corpus-scope-types.js";
|
|
17
17
|
import type { Cursor, ProductId, VariantId } from "./identifiers.js";
|
|
18
|
-
import type { CountedTotal, OpenEnum } from "./types.js";
|
|
18
|
+
import type { CountedTotal, OpenEnum, SearchFacets } from "./types.js";
|
|
19
19
|
/** One item from the pure colour-scalar browse. */
|
|
20
20
|
export interface BrowseResultItem {
|
|
21
21
|
/** Opaque — round-trip verbatim. */
|
|
@@ -44,9 +44,20 @@ export interface BrowseResultItem {
|
|
|
44
44
|
* from product imagery, not from the manufacturer.
|
|
45
45
|
*/
|
|
46
46
|
palette?: ByColourSwatch[];
|
|
47
|
+
/**
|
|
48
|
+
* Resolved values for the attribute ids sent in `returnAttributes`, keyed by
|
|
49
|
+
* attribute id. ABSENT entirely when `returnAttributes` was not sent; a key
|
|
50
|
+
* whose value is `null` means this material carries no accepted value for that
|
|
51
|
+
* attribute. Three states, all different.
|
|
52
|
+
*/
|
|
53
|
+
attributes?: Record<string, string | string[] | null>;
|
|
47
54
|
}
|
|
48
|
-
/**
|
|
49
|
-
|
|
55
|
+
/**
|
|
56
|
+
* The browse orders. `name` (A→Z) is the default, `name_desc` the same index
|
|
57
|
+
* read Z→A, `recent` newest first; the rest are the colour ramps. `/search`
|
|
58
|
+
* takes all eight too — one word, one order, on both lanes.
|
|
59
|
+
*/
|
|
60
|
+
export type BrowseSort = OpenEnum<"name" | "name_desc" | "recent" | "light" | "dark" | "vivid" | "muted" | "colourful">;
|
|
50
61
|
/**
|
|
51
62
|
* The browse feed's total — how many materials the browse DESCRIBES, which is
|
|
52
63
|
* never `results.length`.
|
|
@@ -79,11 +90,37 @@ export interface BrowseResponse {
|
|
|
79
90
|
* to label its own listing.
|
|
80
91
|
*/
|
|
81
92
|
sort: BrowseSort;
|
|
93
|
+
/**
|
|
94
|
+
* The attribute ladder this page was ordered by — present ONLY when `orderBy`
|
|
95
|
+
* was sent, in which case it SUPERSEDED `sort` (still echoed above, and still
|
|
96
|
+
* describing nothing this page was ordered by).
|
|
97
|
+
*/
|
|
98
|
+
orderBy?: {
|
|
99
|
+
attributeId: string;
|
|
100
|
+
direction: OpenEnum<"asc" | "desc">;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Reader prose about how the request was interpreted — today, only that
|
|
104
|
+
* `orderBy` superseded a STATED `sort`. Absent means nothing needed saying.
|
|
105
|
+
*/
|
|
106
|
+
note?: string;
|
|
82
107
|
/**
|
|
83
108
|
* The total, present ONLY when `count: true` was passed. Absent means it was
|
|
84
109
|
* not asked for — never that the set is empty.
|
|
85
110
|
*/
|
|
86
111
|
counts?: BrowseCounts;
|
|
112
|
+
/**
|
|
113
|
+
* Per-facet value counts, present ONLY when `facetCounts` was asked for.
|
|
114
|
+
*
|
|
115
|
+
* Counted over this browse's OWN match set — the same predicate the page and
|
|
116
|
+
* `counts` ran under — so every value is `basis:"exact"` and `poolSize` is
|
|
117
|
+
* ABSENT. That differs from {@link SearchResponse}'s facets, which are
|
|
118
|
+
* normally counted over a ranked candidate pool and are therefore floors:
|
|
119
|
+
* browse retrieves no pool, so there is nothing for a figure to be a floor
|
|
120
|
+
* of. A facet the server could not count in time appears in `absent` with
|
|
121
|
+
* `reason:"timeout"` and the results are unaffected.
|
|
122
|
+
*/
|
|
123
|
+
facets?: SearchFacets;
|
|
87
124
|
/**
|
|
88
125
|
* The corpus this page was drawn from — present ONLY when the request
|
|
89
126
|
* narrowed it. ABSENT means the whole graph: every material MaterialGraph
|
|
@@ -133,6 +170,30 @@ export interface BrowseParams {
|
|
|
133
170
|
* brand's fan deck. An unknown brand is a 400 naming it, never an empty page.
|
|
134
171
|
*/
|
|
135
172
|
brands?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Typed attribute filters, in the SAME grammar `search` takes — a term, a
|
|
175
|
+
* term SET (OR within the entry), a presence check or a numeric range, each
|
|
176
|
+
* optionally carrying a `trust` bar. Entries AND together.
|
|
177
|
+
*
|
|
178
|
+
* Each entry is sent as ITS OWN `filters=` parameter, JSON-encoded, which is
|
|
179
|
+
* what the route reads. Pass the objects; the client does the encoding.
|
|
180
|
+
*
|
|
181
|
+
* Applied INSIDE the keyset scan and counted under the same predicate, so a
|
|
182
|
+
* filtered page still fills to `limit`, `cursor` still means another MATCHING
|
|
183
|
+
* row exists, and `counts.variants` describes the FILTERED set. An attribute
|
|
184
|
+
* id outside the registry is a 400 carrying ranked candidates, never an empty
|
|
185
|
+
* page.
|
|
186
|
+
*/
|
|
187
|
+
filters?: readonly Record<string, unknown>[];
|
|
188
|
+
/**
|
|
189
|
+
* Facet ids to count over this browse's match set — `brand`, `category`, or a
|
|
190
|
+
* spec attribute id. Sent as one comma-separated `facetCounts=` parameter.
|
|
191
|
+
*
|
|
192
|
+
* The response's `facets` block comes back `basis:"exact"` with no
|
|
193
|
+
* `poolSize`, because browse counts the whole predicate rather than a ranked
|
|
194
|
+
* pool. Omit it for no `facets` block and no counting cost.
|
|
195
|
+
*/
|
|
196
|
+
facetCounts?: readonly string[];
|
|
136
197
|
/**
|
|
137
198
|
* Which corpus to walk. ABSENT means the whole graph — every material
|
|
138
199
|
* MaterialGraph knows, orderable or not. `materialBankOrderable` restricts to
|
|
@@ -141,4 +202,24 @@ export interface BrowseParams {
|
|
|
141
202
|
* row exists.
|
|
142
203
|
*/
|
|
143
204
|
corpusScope?: CorpusScope;
|
|
205
|
+
/**
|
|
206
|
+
* Sort along ONE attribute's declared value ladder instead of a `sort` word —
|
|
207
|
+
* the same field, ladder and refusals `search` takes. Sent as one JSON-encoded
|
|
208
|
+
* `orderBy=` parameter; pass the object, the client encodes it.
|
|
209
|
+
*
|
|
210
|
+
* SUPERSEDES `sort` when both are given, and the response `note` says so.
|
|
211
|
+
* Ordering is DECLARED, never inferred from a value's spelling: an attribute
|
|
212
|
+
* with no stated ladder is a 400 carrying `orderable_attributes`. Materials
|
|
213
|
+
* off the ladder, or carrying no value, sort LAST in both directions.
|
|
214
|
+
*/
|
|
215
|
+
orderBy?: {
|
|
216
|
+
attributeId: string;
|
|
217
|
+
direction?: OpenEnum<"asc" | "desc">;
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Attribute ids whose resolved value rides each result under `attributes`, so
|
|
221
|
+
* a filtered listing can display what it filtered on. Sent as one
|
|
222
|
+
* comma-separated `returnAttributes=` parameter.
|
|
223
|
+
*/
|
|
224
|
+
returnAttributes?: readonly string[];
|
|
144
225
|
}
|
package/by-colour-types.d.ts
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* budget. Same tolerant-parsing contract as the rest of the mirror — response
|
|
7
7
|
* enums are widened so a new server value still type-checks.
|
|
8
8
|
*/
|
|
9
|
-
import type { CorpusScopeReceipt } from "./corpus-scope-types.js";
|
|
9
|
+
import type { CorpusScope, CorpusScopeReceipt } from "./corpus-scope-types.js";
|
|
10
10
|
import type { VariantId } from "./identifiers.js";
|
|
11
|
+
import type { QueryUnderstanding } from "./query-understanding-types.js";
|
|
11
12
|
import type { OklabTriple, OpenEnum, PaletteSwatch } from "./types.js";
|
|
12
13
|
/** Coarse group + fine type of a colour match's primary product type. */
|
|
13
14
|
export interface MatchCategory {
|
|
@@ -27,6 +28,12 @@ export interface ByColourMatch {
|
|
|
27
28
|
variantId: VariantId;
|
|
28
29
|
brand: string;
|
|
29
30
|
product: string;
|
|
31
|
+
/** Record-route brand identity. */
|
|
32
|
+
brandSlug?: string | null;
|
|
33
|
+
/** Opaque MaterialGraph product-family identity. */
|
|
34
|
+
productId?: string | null;
|
|
35
|
+
/** Material Bank cart/order join key; absent when not on record. */
|
|
36
|
+
materialBankId?: string;
|
|
30
37
|
color: string | null;
|
|
31
38
|
variantName: string | null;
|
|
32
39
|
imageUrl: string | null;
|
|
@@ -148,6 +155,8 @@ export interface ColourLaneReceipt {
|
|
|
148
155
|
/** The `search/colours` response. */
|
|
149
156
|
export interface ByColourResponse {
|
|
150
157
|
query: {
|
|
158
|
+
/** Raw query provenance, when supplied. */
|
|
159
|
+
q?: string;
|
|
151
160
|
colours: string[];
|
|
152
161
|
categories?: string[];
|
|
153
162
|
/**
|
|
@@ -188,9 +197,13 @@ export interface ByColourResponse {
|
|
|
188
197
|
* retrieval rather than over the page it produced.
|
|
189
198
|
*/
|
|
190
199
|
corpusScope?: CorpusScopeReceipt;
|
|
200
|
+
/** Shared standard-search query-understanding receipt. */
|
|
201
|
+
understanding?: QueryUnderstanding;
|
|
191
202
|
}
|
|
192
203
|
/** Params for `search/colours`. */
|
|
193
204
|
export interface ByColourParams {
|
|
205
|
+
/** Raw material-domain query provenance for shared query understanding. */
|
|
206
|
+
q?: string;
|
|
194
207
|
/** A single hex (with or without `#`). */
|
|
195
208
|
hex?: string;
|
|
196
209
|
/** A palette — repeated as one `colour` query entry each. */
|
|
@@ -232,5 +245,7 @@ export interface ByColourParams {
|
|
|
232
245
|
* drop.
|
|
233
246
|
*/
|
|
234
247
|
family?: string;
|
|
248
|
+
/** Narrow retrieval inside the server query; absent means the whole graph. */
|
|
249
|
+
corpusScope?: CorpusScope;
|
|
235
250
|
limit?: number;
|
|
236
251
|
}
|
package/client-browse.d.ts
CHANGED
|
@@ -33,6 +33,17 @@ export type BrowsePageFetcher = (params: BrowseParams, signal?: AbortSignal) =>
|
|
|
33
33
|
* route and never reached it from here, so the fan deck was unreachable through
|
|
34
34
|
* the published client while the server had supported it all along. When the
|
|
35
35
|
* route gains a param, it gains a line HERE too, or it is not shipped.
|
|
36
|
+
*
|
|
37
|
+
* It happened again, and twice: `filters` shipped with MG-1576 and `facetCounts`
|
|
38
|
+
* with MG-1590, and neither had a line here. Attribute filtering — the thing
|
|
39
|
+
* MG-1588 spent a materialized view making fast — was therefore unreachable
|
|
40
|
+
* through the published client for as long as it existed on the route. Both are
|
|
41
|
+
* enrolled below, along with MG-1593's `orderBy` and `returnAttributes`.
|
|
42
|
+
*
|
|
43
|
+
* `filters` is a REPEATED parameter and `facetCounts` a comma list, because that
|
|
44
|
+
* is what the route reads: the transport appends an array once per element, so
|
|
45
|
+
* the array shape and the comma-joined string are each doing exactly what the
|
|
46
|
+
* server expects rather than being two spellings of one idea.
|
|
36
47
|
*/
|
|
37
48
|
export declare function browseQuery(params: BrowseParams): {
|
|
38
49
|
sort: BrowseParams["sort"];
|
|
@@ -41,7 +52,11 @@ export declare function browseQuery(params: BrowseParams): {
|
|
|
41
52
|
count: BrowseParams["count"];
|
|
42
53
|
types: BrowseParams["types"];
|
|
43
54
|
brands: BrowseParams["brands"];
|
|
55
|
+
filters: string[] | undefined;
|
|
56
|
+
facetCounts: string | undefined;
|
|
44
57
|
corpusScope: BrowseParams["corpusScope"];
|
|
58
|
+
orderBy: string | undefined;
|
|
59
|
+
returnAttributes: string | undefined;
|
|
45
60
|
};
|
|
46
61
|
/**
|
|
47
62
|
* Walk every page from `params.cursor` onward.
|
package/client-browse.js
CHANGED
|
@@ -8,6 +8,17 @@
|
|
|
8
8
|
* route and never reached it from here, so the fan deck was unreachable through
|
|
9
9
|
* the published client while the server had supported it all along. When the
|
|
10
10
|
* route gains a param, it gains a line HERE too, or it is not shipped.
|
|
11
|
+
*
|
|
12
|
+
* It happened again, and twice: `filters` shipped with MG-1576 and `facetCounts`
|
|
13
|
+
* with MG-1590, and neither had a line here. Attribute filtering — the thing
|
|
14
|
+
* MG-1588 spent a materialized view making fast — was therefore unreachable
|
|
15
|
+
* through the published client for as long as it existed on the route. Both are
|
|
16
|
+
* enrolled below, along with MG-1593's `orderBy` and `returnAttributes`.
|
|
17
|
+
*
|
|
18
|
+
* `filters` is a REPEATED parameter and `facetCounts` a comma list, because that
|
|
19
|
+
* is what the route reads: the transport appends an array once per element, so
|
|
20
|
+
* the array shape and the comma-joined string are each doing exactly what the
|
|
21
|
+
* server expects rather than being two spellings of one idea.
|
|
11
22
|
*/
|
|
12
23
|
export function browseQuery(params) {
|
|
13
24
|
return {
|
|
@@ -17,7 +28,15 @@ export function browseQuery(params) {
|
|
|
17
28
|
count: params.count,
|
|
18
29
|
types: params.types,
|
|
19
30
|
brands: params.brands,
|
|
31
|
+
filters: params.filters?.map((entry) => JSON.stringify(entry)),
|
|
32
|
+
facetCounts: params.facetCounts?.join(","),
|
|
20
33
|
corpusScope: params.corpusScope,
|
|
34
|
+
// `orderBy` is JSON-encoded and `returnAttributes` comma-joined because that
|
|
35
|
+
// is what the route reads — the same two transports `filters` and
|
|
36
|
+
// `facetCounts` already use. Enrolled HERE at the same time they shipped on
|
|
37
|
+
// the route, which is the thing the three misses above did not do.
|
|
38
|
+
orderBy: params.orderBy ? JSON.stringify(params.orderBy) : undefined,
|
|
39
|
+
returnAttributes: params.returnAttributes?.join(","),
|
|
21
40
|
};
|
|
22
41
|
}
|
|
23
42
|
/**
|
package/client.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ import type { MatchStreamEvent } from "./match-stream-types.js";
|
|
|
19
19
|
import type { MatchInput, MatchResponse } from "./match-types.js";
|
|
20
20
|
import type { MaterialAssetProjection } from "./material-asset-types.js";
|
|
21
21
|
import type { MaterialBlockProjection } from "./material-block-types.js";
|
|
22
|
+
import type { MaterialQueryPlan } from "./material-query-plan-types.js";
|
|
22
23
|
import type { AppendProjectEventRequest, CreateProjectRequest, ProjectEventReceipt, ProjectListResponse, ProjectState } from "./project-types.js";
|
|
23
24
|
import { resolvePhrasesRequest as resolvePhrases } from "./resolve-client.js";
|
|
24
25
|
import type { CapabilityManifestResponse, CreatePublicRunRequest, CreatePublicRunResponse, DrainRunEventsOptions, ListRunsOptions, PublicRunListResponse, PublicRunResponse, ResumeRunOptions, RunEvent, SourceMaterialBatchDetail, SourceMaterialBatchReceipt, StreamRunEventsOptions, UploadSourceMaterialInput } from "./run-types.js";
|
|
@@ -127,17 +128,13 @@ export declare class MaterialGraphClient {
|
|
|
127
128
|
* {@link MaterialGraphClient.match} for a photo, {@link MaterialGraphClient.pairings}
|
|
128
129
|
* for what goes WITH something, {@link MaterialGraphClient.suggest} for typeahead.
|
|
129
130
|
*
|
|
130
|
-
* The full search primitive
|
|
131
|
-
* specification
|
|
132
|
-
*
|
|
133
|
-
* a term match (`{ attributeId, term }`), a presence check
|
|
134
|
-
* (`{ attributeId, hasValue: true }`), or a certification programme
|
|
135
|
-
* (`{ attributeId: "certification_programme", term }`). This is the seam for
|
|
136
|
-
* "a GREENGUARD-certified alternative to THIS material" (seed by `similarTo`,
|
|
137
|
-
* gate by `filters`) or "the same fire class, with an EPD on record". Build the
|
|
138
|
-
* filters with the `certificationProgramme` / `specTerm` / `hasSpec` helpers.
|
|
131
|
+
* The full POST search primitive. `input.filters` layers typed hard
|
|
132
|
+
* specification gates onto ranked colour/visual/lexical/seed search. Build
|
|
133
|
+
* them with `certificationProgramme`, `specTerm`, or `hasSpec`.
|
|
139
134
|
*/
|
|
140
135
|
search(input: SearchInput, signal?: AbortSignal): Promise<SearchResponse>;
|
|
136
|
+
/** Whole materials ask → inspectable meanings and complete safe search requests. */
|
|
137
|
+
planQuery(query: string, signal?: AbortSignal): Promise<MaterialQueryPlan>;
|
|
141
138
|
/**
|
|
142
139
|
* The same question as {@link MaterialGraphClient.search}; the difference is
|
|
143
140
|
* transport — a cacheable, link-shareable GET for queries that must survive in
|
package/client.js
CHANGED
|
@@ -157,19 +157,17 @@ export class MaterialGraphClient {
|
|
|
157
157
|
* {@link MaterialGraphClient.match} for a photo, {@link MaterialGraphClient.pairings}
|
|
158
158
|
* for what goes WITH something, {@link MaterialGraphClient.suggest} for typeahead.
|
|
159
159
|
*
|
|
160
|
-
* The full search primitive
|
|
161
|
-
* specification
|
|
162
|
-
*
|
|
163
|
-
* a term match (`{ attributeId, term }`), a presence check
|
|
164
|
-
* (`{ attributeId, hasValue: true }`), or a certification programme
|
|
165
|
-
* (`{ attributeId: "certification_programme", term }`). This is the seam for
|
|
166
|
-
* "a GREENGUARD-certified alternative to THIS material" (seed by `similarTo`,
|
|
167
|
-
* gate by `filters`) or "the same fire class, with an EPD on record". Build the
|
|
168
|
-
* filters with the `certificationProgramme` / `specTerm` / `hasSpec` helpers.
|
|
160
|
+
* The full POST search primitive. `input.filters` layers typed hard
|
|
161
|
+
* specification gates onto ranked colour/visual/lexical/seed search. Build
|
|
162
|
+
* them with `certificationProgramme`, `specTerm`, or `hasSpec`.
|
|
169
163
|
*/
|
|
170
164
|
search(input, signal) {
|
|
171
165
|
return this.#post("search_materials_post", input, signal);
|
|
172
166
|
}
|
|
167
|
+
/** Whole materials ask → inspectable meanings and complete safe search requests. */
|
|
168
|
+
planQuery(query, signal) {
|
|
169
|
+
return this.#post("plan_material_query", { query }, signal);
|
|
170
|
+
}
|
|
173
171
|
/**
|
|
174
172
|
* The same question as {@link MaterialGraphClient.search}; the difference is
|
|
175
173
|
* transport — a cacheable, link-shareable GET for queries that must survive in
|
|
@@ -235,6 +233,7 @@ export class MaterialGraphClient {
|
|
|
235
233
|
*/
|
|
236
234
|
byColour(params, signal) {
|
|
237
235
|
return this.#get("search_by_colour", {
|
|
236
|
+
q: params.q,
|
|
238
237
|
hex: params.hex,
|
|
239
238
|
colour: params.colour,
|
|
240
239
|
l: params.l,
|
|
@@ -243,6 +242,7 @@ export class MaterialGraphClient {
|
|
|
243
242
|
category: params.category,
|
|
244
243
|
brand: params.brand,
|
|
245
244
|
family: params.family,
|
|
245
|
+
corpusScope: params.corpusScope,
|
|
246
246
|
limit: params.limit,
|
|
247
247
|
}, signal);
|
|
248
248
|
}
|
package/demand-types.d.ts
CHANGED
|
@@ -95,6 +95,26 @@ export interface CoordinateCompanion {
|
|
|
95
95
|
imageUrl: string | null;
|
|
96
96
|
brandSlug: string;
|
|
97
97
|
productId: ProductId;
|
|
98
|
+
/**
|
|
99
|
+
* How this companion relates to the SEED SET — the closest relation that
|
|
100
|
+
* applies to any seed.
|
|
101
|
+
*
|
|
102
|
+
* `companion` is foreign to every seed: a maker the caller did not pick.
|
|
103
|
+
* `brand_sibling` shares a brand with some seed. `colourway` is a seed's own
|
|
104
|
+
* product restyled, and completes nothing.
|
|
105
|
+
*
|
|
106
|
+
* READ IT BEFORE CLAIMING BREADTH. Measured across the live edges (MG-1007),
|
|
107
|
+
* 70.6% are same-brand and 24.3% are the seed's own product in another
|
|
108
|
+
* colourway; on a two-seed rail, 27 of 40 companions came from the seeds' own
|
|
109
|
+
* two brands. A surface headed "makers you haven't picked" that does not
|
|
110
|
+
* filter on this is making a claim the evidence does not support — the
|
|
111
|
+
* measured case showed two distinct makers under a heading promising seven.
|
|
112
|
+
*
|
|
113
|
+
* The server has always sent this field; it was absent from this interface
|
|
114
|
+
* until 2026-08-23, so consumers could not see it and hand-rolled the same
|
|
115
|
+
* comparison against `brandSlug` themselves.
|
|
116
|
+
*/
|
|
117
|
+
relation: "companion" | "brand_sibling" | "colourway";
|
|
98
118
|
totalLift: number;
|
|
99
119
|
liftLabel: string;
|
|
100
120
|
seedMatchCount: number;
|
package/errors.d.ts
CHANGED
|
@@ -89,7 +89,7 @@ interface TransportErrorInput extends MaterialGraphRequestMetadata {
|
|
|
89
89
|
* member to a problem response means adding it here too, and
|
|
90
90
|
* `problem-extension-parity.test.ts` fails when the two disagree.
|
|
91
91
|
*/
|
|
92
|
-
export declare const MATERIALGRAPH_PROBLEM_EXTENSIONS: readonly ["content_length", "did_you_mean", "empty_brands", "limit_bytes", "missing_env", "populated_alternatives", "unknown_attributes", "unknown_brands", "valid_group_keys", "valid_type_keys"];
|
|
92
|
+
export declare const MATERIALGRAPH_PROBLEM_EXTENSIONS: readonly ["content_length", "did_you_mean", "empty_brands", "limit_bytes", "missing_env", "orderable_attributes", "populated_alternatives", "unknown_attributes", "unknown_brands", "unknown_fields", "unorderable_attribute", "valid_group_keys", "valid_type_keys"];
|
|
93
93
|
/** The one error class thrown by all SDK transport and HTTP failures. */
|
|
94
94
|
export declare class MaterialGraphError extends Error {
|
|
95
95
|
readonly kind: MaterialGraphErrorKind;
|
package/errors.js
CHANGED
|
@@ -28,9 +28,12 @@ export const MATERIALGRAPH_PROBLEM_EXTENSIONS = [
|
|
|
28
28
|
"empty_brands",
|
|
29
29
|
"limit_bytes",
|
|
30
30
|
"missing_env",
|
|
31
|
+
"orderable_attributes",
|
|
31
32
|
"populated_alternatives",
|
|
32
33
|
"unknown_attributes",
|
|
33
34
|
"unknown_brands",
|
|
35
|
+
"unknown_fields",
|
|
36
|
+
"unorderable_attribute",
|
|
34
37
|
"valid_group_keys",
|
|
35
38
|
"valid_type_keys",
|
|
36
39
|
];
|
package/index.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export { ColoursClient } from "./colours-client.js";
|
|
|
28
28
|
export type { ColoursTransport } from "./colours-client.js";
|
|
29
29
|
export type * from "./colour-aggregate-types.js";
|
|
30
30
|
export type * from "./palette-search-types.js";
|
|
31
|
+
export type * from "./query-understanding-types.js";
|
|
32
|
+
export type * from "./material-query-plan-types.js";
|
|
31
33
|
export type * from "./association-types.js";
|
|
32
34
|
export type * from "./brand-types.js";
|
|
33
35
|
export type * from "./fan-deck-types.js";
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/** Hand-mirrored wire types for `POST /api/v1/search/plan`. */
|
|
2
|
+
import type { QuerySense } from "./query-understanding-types.js";
|
|
3
|
+
import type { ResolvedMeaning } from "./resolve-types.js";
|
|
4
|
+
import type { SearchInput } from "./search-request-types.js";
|
|
5
|
+
import type { OpenEnum } from "./types.js";
|
|
6
|
+
export type MaterialQueryVocabularyKind = OpenEnum<"attribute" | "dictionary_term" | "inline_enum" | "product_type" | "product_type_group">;
|
|
7
|
+
export interface MaterialQuerySpan {
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
text: string;
|
|
11
|
+
}
|
|
12
|
+
export interface MaterialQueryOwner {
|
|
13
|
+
attributeId: string;
|
|
14
|
+
attributeName: string;
|
|
15
|
+
}
|
|
16
|
+
export interface MaterialQueryLifecycle {
|
|
17
|
+
status: string;
|
|
18
|
+
introducedIn?: string;
|
|
19
|
+
deprecatedIn?: string;
|
|
20
|
+
supersededBy?: string;
|
|
21
|
+
}
|
|
22
|
+
export type MaterialQueryOntologyContext = {
|
|
23
|
+
description: string;
|
|
24
|
+
valueKind: string;
|
|
25
|
+
valueDictionaryId?: string;
|
|
26
|
+
} | {
|
|
27
|
+
dictionaryId: string;
|
|
28
|
+
dictionaryName: string;
|
|
29
|
+
owningAttributes: Array<{
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
}>;
|
|
33
|
+
lifecycle: MaterialQueryLifecycle;
|
|
34
|
+
} | {
|
|
35
|
+
attributeId: string;
|
|
36
|
+
attributeName: string;
|
|
37
|
+
} | {
|
|
38
|
+
description: string;
|
|
39
|
+
lifecycle: MaterialQueryLifecycle;
|
|
40
|
+
parentProductTypeGroupId?: string;
|
|
41
|
+
} | {
|
|
42
|
+
description: string;
|
|
43
|
+
};
|
|
44
|
+
export interface MaterialQueryApplicability {
|
|
45
|
+
status: OpenEnum<"all" | "some" | "none" | "unknown">;
|
|
46
|
+
categoryId?: string;
|
|
47
|
+
productTypeIds: string[];
|
|
48
|
+
applicableProductTypeIds: string[];
|
|
49
|
+
}
|
|
50
|
+
export interface MaterialQueryCoverage {
|
|
51
|
+
variants: number;
|
|
52
|
+
basis: OpenEnum<"facet_index">;
|
|
53
|
+
scope: OpenEnum<"corpus">;
|
|
54
|
+
}
|
|
55
|
+
export interface MaterialQueryReachability {
|
|
56
|
+
status: OpenEnum<"positive" | "zero" | "unknown">;
|
|
57
|
+
categoryId: string;
|
|
58
|
+
basis: OpenEnum<"bounded_probe" | "unmeasured">;
|
|
59
|
+
}
|
|
60
|
+
export interface MaterialQueryCandidate {
|
|
61
|
+
kind: MaterialQueryVocabularyKind;
|
|
62
|
+
subjectType: MaterialQueryVocabularyKind;
|
|
63
|
+
subjectId: string;
|
|
64
|
+
label: string;
|
|
65
|
+
context: MaterialQueryOntologyContext;
|
|
66
|
+
score: number;
|
|
67
|
+
tier: OpenEnum<"exact_id" | "exact_alias" | "lexical" | "semantic">;
|
|
68
|
+
evidence: {
|
|
69
|
+
source: OpenEnum<"registry" | "query_domain_embedding">;
|
|
70
|
+
span: MaterialQuerySpan;
|
|
71
|
+
};
|
|
72
|
+
outcome: OpenEnum<"recognized" | "suggested" | "executable" | "ambiguous" | "refused">;
|
|
73
|
+
owners: MaterialQueryOwner[];
|
|
74
|
+
selectedOwner?: MaterialQueryOwner;
|
|
75
|
+
applicability?: MaterialQueryApplicability;
|
|
76
|
+
coverage?: MaterialQueryCoverage;
|
|
77
|
+
reachability?: MaterialQueryReachability;
|
|
78
|
+
reason?: string;
|
|
79
|
+
}
|
|
80
|
+
export interface MaterialQueryCriterion {
|
|
81
|
+
source: {
|
|
82
|
+
subjectType: MaterialQueryVocabularyKind;
|
|
83
|
+
subjectId: string;
|
|
84
|
+
span: MaterialQuerySpan;
|
|
85
|
+
};
|
|
86
|
+
attribute: {
|
|
87
|
+
id: string;
|
|
88
|
+
name: string;
|
|
89
|
+
};
|
|
90
|
+
ownerAlternatives: MaterialQueryOwner[];
|
|
91
|
+
operator: OpenEnum<"term" | "presence" | "numeric_range">;
|
|
92
|
+
term?: string;
|
|
93
|
+
min?: number;
|
|
94
|
+
max?: number;
|
|
95
|
+
coverage: MaterialQueryCoverage;
|
|
96
|
+
applicability: MaterialQueryApplicability;
|
|
97
|
+
reachability?: MaterialQueryReachability;
|
|
98
|
+
}
|
|
99
|
+
export type MaterialQueryStandardMeaning = ResolvedMeaning | QuerySense;
|
|
100
|
+
export type MaterialQueryAmbiguityAlternative = (MaterialQueryOwner & {
|
|
101
|
+
kind: OpenEnum<"owner">;
|
|
102
|
+
}) | {
|
|
103
|
+
kind: OpenEnum<"subject">;
|
|
104
|
+
subjectType: MaterialQueryVocabularyKind;
|
|
105
|
+
subjectId: string;
|
|
106
|
+
label: string;
|
|
107
|
+
} | {
|
|
108
|
+
kind: OpenEnum<"standard_meaning">;
|
|
109
|
+
meaning: MaterialQueryStandardMeaning;
|
|
110
|
+
};
|
|
111
|
+
export interface MaterialQueryPlan {
|
|
112
|
+
version: OpenEnum<"1">;
|
|
113
|
+
verdict: OpenEnum<"executable" | "partial" | "refused">;
|
|
114
|
+
rawQuery: string;
|
|
115
|
+
normalisedQuery: string;
|
|
116
|
+
registry: {
|
|
117
|
+
corpusSize: number;
|
|
118
|
+
contentHash: string;
|
|
119
|
+
};
|
|
120
|
+
candidates: MaterialQueryCandidate[];
|
|
121
|
+
category: {
|
|
122
|
+
key: string;
|
|
123
|
+
name: string;
|
|
124
|
+
level: OpenEnum<"group" | "type">;
|
|
125
|
+
parentKey: string | null;
|
|
126
|
+
source: {
|
|
127
|
+
subjectType: MaterialQueryVocabularyKind;
|
|
128
|
+
subjectId: string;
|
|
129
|
+
span: MaterialQuerySpan;
|
|
130
|
+
};
|
|
131
|
+
} | null;
|
|
132
|
+
criteria: MaterialQueryCriterion[];
|
|
133
|
+
colour: {
|
|
134
|
+
family?: string;
|
|
135
|
+
undertone?: string;
|
|
136
|
+
} | null;
|
|
137
|
+
residualText: string;
|
|
138
|
+
unspentSpans: Array<MaterialQuerySpan & {
|
|
139
|
+
reason: OpenEnum<"residual" | "ambiguous" | "refused">;
|
|
140
|
+
}>;
|
|
141
|
+
ambiguities: Array<{
|
|
142
|
+
subjectType: MaterialQueryVocabularyKind;
|
|
143
|
+
subjectId: string;
|
|
144
|
+
span: MaterialQuerySpan;
|
|
145
|
+
reason: string;
|
|
146
|
+
alternatives: MaterialQueryAmbiguityAlternative[];
|
|
147
|
+
}>;
|
|
148
|
+
standardMeanings: Array<{
|
|
149
|
+
span: MaterialQuerySpan;
|
|
150
|
+
meanings: MaterialQueryStandardMeaning[];
|
|
151
|
+
}>;
|
|
152
|
+
/** Complete ordinary-search requests authored by MaterialGraph. Execute verbatim. */
|
|
153
|
+
requests: SearchInput[];
|
|
154
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/operations.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const MATERIALGRAPH_API_VERSION = "0.7.0";
|
|
2
|
-
declare const OPERATION_DEFINITIONS: readonly [readonly ["search", "POST", "/api/v1/search", "search_materials_post"], readonly ["searchGet", "GET", "/api/v1/search", "search_materials_get"], readonly ["match", "POST", "/api/v1/search/match", "match_material"], readonly ["byColour", "GET", "/api/v1/search/colours", "search_by_colour"], readonly ["material", "GET", "/api/v1/materials/{variantId}", "get_material"], readonly ["siblings", "GET", "/api/v1/materials/{variantId}/siblings", "get_material_siblings"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["materialAssets", "GET", "/api/v1/materials/{variantId}/assets", "list_material_assets"], readonly ["exploration.operators", "GET", "/api/v1/materials/{variantId}/operators", "list_material_operators"], readonly ["exploration.candidates", "POST", "/api/v1/materials/{variantId}/candidates", "read_material_candidates"], readonly ["schema", "GET", "/api/v1/schema/blocks", "list_schema_blocks"], readonly ["schema", "GET", "/api/v1/schema/attributes", "list_schema_attributes"], readonly ["schema", "GET", "/api/v1/schema/verticals", "list_schema_verticals"], readonly ["schema", "GET", "/api/v1/schema/product-types", "list_schema_product_types"], readonly ["schema", "GET", "/api/v1/schema/units", "list_schema_units"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["schema", "GET", "/api/v1/schema/source-requirements", "list_schema_source_requirements"], readonly ["schema", "GET", "/api/v1/schema/product-type-templates", "list_schema_product_type_templates"], readonly ["schema", "GET", "/api/v1/schema/mappings", "list_schema_mappings"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["associatedFinishes", "GET", "/api/v1/associated/finishes", "list_associated_finishes"], readonly ["associatedProducts", "GET", "/api/v1/associated/products", "list_associated_products"], readonly ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"], readonly ["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"], readonly ["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"], readonly ["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"], readonly ["colours.profiles", "GET", "/api/v1/colours/profiles", "get_category_colour_profiles"], readonly ["colours.stats", "GET", "/api/v1/colours/stats", "get_colour_stats"], readonly ["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"], readonly ["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"], readonly ["suggest", "GET", "/api/v1/search/suggest", "search_suggest"], readonly ["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"], readonly ["resolveAttributeValue", "GET", "/api/v1/catalog/attributes/{attributeId}/values/resolve", "catalog_resolve_value"], readonly ["searchVocabulary", "GET", "/api/v1/catalog/vocabulary/search", "catalog_vocabulary_search"], readonly ["resolve", "POST", "/api/v1/resolve", "resolve_phrases"], readonly ["bySpec", "GET", "/api/v1/search/specifications", "catalog_by_spec"], readonly ["taxonomy", "GET", "/api/v1/taxonomy/categories", "get_category_taxonomy"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"], readonly ["demand", "GET", "/api/v1/demand", "get_material_demand"], readonly ["colours.palette", "POST", "/api/v1/search/palettes", "search_by_palette"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["colours.gradient", "POST", "/api/v1/search/gradients", "search_by_gradient"], readonly ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"], readonly ["attributeValues", "GET", "/api/v1/catalog/attributes/{attributeId}/values", "list_attribute_values"], readonly ["applicableSpecs", "GET", "/api/v1/taxonomy/categories/{key}/specs", "get_applicable_specs"], readonly ["glossary", "GET", "/api/v1/glossary/{term}", "explain_material_term"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"], readonly ["brandColourDeck", "GET", "/api/v1/brands/{brandSlug}/colours", "brand_colour_deck"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"], readonly ["sourceMaterialBatch", "GET", "/api/v1/source-material/{batchId}", "get_source_material_batch"], readonly ["sourceMaterialComments", "GET", "/api/v1/source-material/{batchId}/sources/{sourceId}/comments", "list_source_material_comments"], readonly ["sourceMaterialComments", "POST", "/api/v1/source-material/{batchId}/sources/{sourceId}/comments", "post_source_material_comment"], readonly ["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"], readonly ["createRun", "POST", "/api/v1/runs", "create_public_run"], readonly ["listRuns", "GET", "/api/v1/runs", "list_public_runs"], readonly ["run", "GET", "/api/v1/runs/{runId}", "get_public_run"], readonly ["runSchedule", "GET", "/api/v1/runs/{runId}/schedule", "get_run_finish_schedule"], readonly ["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"], readonly ["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"], readonly ["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"], readonly ["varyScheme", "POST", "/api/v1/schemes/vary", "vary_schemes"], readonly ["slotPools", "POST", "/api/v1/schemes/pools", "slot_pools"], readonly ["createProject", "POST", "/api/v1/projects", "create_box_project"], readonly ["listProjects", "GET", "/api/v1/projects", "list_box_projects"], readonly ["getProject", "GET", "/api/v1/projects/{projectId}", "get_box_project"], readonly ["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"], readonly ["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"], readonly ["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"], readonly ["codes.context", "GET", "/api/v1/codes/context", "list_code_context"], readonly ["codes.requirements", "GET", "/api/v1/codes/requirements", "list_code_requirements"]];
|
|
2
|
+
declare const OPERATION_DEFINITIONS: readonly [readonly ["search", "POST", "/api/v1/search", "search_materials_post"], readonly ["planQuery", "POST", "/api/v1/search/plan", "plan_material_query"], readonly ["searchGet", "GET", "/api/v1/search", "search_materials_get"], readonly ["match", "POST", "/api/v1/search/match", "match_material"], readonly ["byColour", "GET", "/api/v1/search/colours", "search_by_colour"], readonly ["material", "GET", "/api/v1/materials/{variantId}", "get_material"], readonly ["siblings", "GET", "/api/v1/materials/{variantId}/siblings", "get_material_siblings"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["materialAssets", "GET", "/api/v1/materials/{variantId}/assets", "list_material_assets"], readonly ["exploration.operators", "GET", "/api/v1/materials/{variantId}/operators", "list_material_operators"], readonly ["exploration.candidates", "POST", "/api/v1/materials/{variantId}/candidates", "read_material_candidates"], readonly ["schema", "GET", "/api/v1/schema/blocks", "list_schema_blocks"], readonly ["schema", "GET", "/api/v1/schema/attributes", "list_schema_attributes"], readonly ["schema", "GET", "/api/v1/schema/verticals", "list_schema_verticals"], readonly ["schema", "GET", "/api/v1/schema/product-types", "list_schema_product_types"], readonly ["schema", "GET", "/api/v1/schema/units", "list_schema_units"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["schema", "GET", "/api/v1/schema/source-requirements", "list_schema_source_requirements"], readonly ["schema", "GET", "/api/v1/schema/product-type-templates", "list_schema_product_type_templates"], readonly ["schema", "GET", "/api/v1/schema/mappings", "list_schema_mappings"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["associatedFinishes", "GET", "/api/v1/associated/finishes", "list_associated_finishes"], readonly ["associatedProducts", "GET", "/api/v1/associated/products", "list_associated_products"], readonly ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"], readonly ["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"], readonly ["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"], readonly ["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"], readonly ["colours.profiles", "GET", "/api/v1/colours/profiles", "get_category_colour_profiles"], readonly ["colours.stats", "GET", "/api/v1/colours/stats", "get_colour_stats"], readonly ["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"], readonly ["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"], readonly ["suggest", "GET", "/api/v1/search/suggest", "search_suggest"], readonly ["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"], readonly ["resolveAttributeValue", "GET", "/api/v1/catalog/attributes/{attributeId}/values/resolve", "catalog_resolve_value"], readonly ["searchVocabulary", "GET", "/api/v1/catalog/vocabulary/search", "catalog_vocabulary_search"], readonly ["resolve", "POST", "/api/v1/resolve", "resolve_phrases"], readonly ["bySpec", "GET", "/api/v1/search/specifications", "catalog_by_spec"], readonly ["taxonomy", "GET", "/api/v1/taxonomy/categories", "get_category_taxonomy"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"], readonly ["demand", "GET", "/api/v1/demand", "get_material_demand"], readonly ["colours.palette", "POST", "/api/v1/search/palettes", "search_by_palette"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["colours.gradient", "POST", "/api/v1/search/gradients", "search_by_gradient"], readonly ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"], readonly ["attributeValues", "GET", "/api/v1/catalog/attributes/{attributeId}/values", "list_attribute_values"], readonly ["applicableSpecs", "GET", "/api/v1/taxonomy/categories/{key}/specs", "get_applicable_specs"], readonly ["glossary", "GET", "/api/v1/glossary/{term}", "explain_material_term"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"], readonly ["brandColourDeck", "GET", "/api/v1/brands/{brandSlug}/colours", "brand_colour_deck"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"], readonly ["sourceMaterialBatch", "GET", "/api/v1/source-material/{batchId}", "get_source_material_batch"], readonly ["sourceMaterialComments", "GET", "/api/v1/source-material/{batchId}/sources/{sourceId}/comments", "list_source_material_comments"], readonly ["sourceMaterialComments", "POST", "/api/v1/source-material/{batchId}/sources/{sourceId}/comments", "post_source_material_comment"], readonly ["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"], readonly ["createRun", "POST", "/api/v1/runs", "create_public_run"], readonly ["listRuns", "GET", "/api/v1/runs", "list_public_runs"], readonly ["run", "GET", "/api/v1/runs/{runId}", "get_public_run"], readonly ["runSchedule", "GET", "/api/v1/runs/{runId}/schedule", "get_run_finish_schedule"], readonly ["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"], readonly ["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"], readonly ["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"], readonly ["varyScheme", "POST", "/api/v1/schemes/vary", "vary_schemes"], readonly ["slotPools", "POST", "/api/v1/schemes/pools", "slot_pools"], readonly ["createProject", "POST", "/api/v1/projects", "create_box_project"], readonly ["listProjects", "GET", "/api/v1/projects", "list_box_projects"], readonly ["getProject", "GET", "/api/v1/projects/{projectId}", "get_box_project"], readonly ["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"], readonly ["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"], readonly ["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"], readonly ["codes.context", "GET", "/api/v1/codes/context", "list_code_context"], readonly ["codes.requirements", "GET", "/api/v1/codes/requirements", "list_code_requirements"]];
|
|
3
3
|
export type SdkOperationId = (typeof OPERATION_DEFINITIONS)[number][3];
|
|
4
4
|
export type SdkOperation = Readonly<{
|
|
5
5
|
clientMethod: (typeof OPERATION_DEFINITIONS)[number][0];
|
package/operations.js
CHANGED
|
@@ -2,6 +2,7 @@ import { MaterialGraphError } from "./errors.js";
|
|
|
2
2
|
export const MATERIALGRAPH_API_VERSION = "0.7.0";
|
|
3
3
|
const OPERATION_DEFINITIONS = [
|
|
4
4
|
["search", "POST", "/api/v1/search", "search_materials_post"],
|
|
5
|
+
["planQuery", "POST", "/api/v1/search/plan", "plan_material_query"],
|
|
5
6
|
["searchGet", "GET", "/api/v1/search", "search_materials_get"],
|
|
6
7
|
["match", "POST", "/api/v1/search/match", "match_material"],
|
|
7
8
|
["byColour", "GET", "/api/v1/search/colours", "search_by_colour"],
|
package/package.json
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { OpenEnum } from "./types.js";
|
|
2
|
+
export type QuerySenseKind = OpenEnum<"brand" | "product" | "paint_identity" | "category" | "material" | "colour" | "attribute" | "attribute_value" | "certification">;
|
|
3
|
+
export interface QuerySense {
|
|
4
|
+
id: string;
|
|
5
|
+
kind: QuerySenseKind;
|
|
6
|
+
label: string;
|
|
7
|
+
outcome: OpenEnum<"applied" | "ambiguous" | "recognized" | "refused">;
|
|
8
|
+
effect: OpenEnum<"restrict" | "prioritize" | "rerank" | "offer" | "none">;
|
|
9
|
+
confidence: number;
|
|
10
|
+
evidence: {
|
|
11
|
+
source: string;
|
|
12
|
+
matchedText: string;
|
|
13
|
+
span?: {
|
|
14
|
+
start: number;
|
|
15
|
+
end: number;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
entity?: {
|
|
19
|
+
brandSlug?: string;
|
|
20
|
+
productId?: string;
|
|
21
|
+
variantId?: string;
|
|
22
|
+
materialBankId?: string;
|
|
23
|
+
productTypeId?: string;
|
|
24
|
+
paintCode?: string;
|
|
25
|
+
hex?: string;
|
|
26
|
+
};
|
|
27
|
+
reason?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Inspectable interpretation of the raw query by ordinary MaterialGraph search. */
|
|
30
|
+
export interface QueryUnderstanding {
|
|
31
|
+
version: OpenEnum<"1">;
|
|
32
|
+
rawQuery: string;
|
|
33
|
+
normalisedQuery: string;
|
|
34
|
+
residualText: string;
|
|
35
|
+
status: OpenEnum<"resolved" | "ambiguous" | "partial" | "unresolved">;
|
|
36
|
+
mode: OpenEnum<"image" | "colour" | "text" | "lexical" | "entity-browse">;
|
|
37
|
+
senses: QuerySense[];
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -31,7 +31,41 @@ export interface SearchInput {
|
|
|
31
31
|
};
|
|
32
32
|
};
|
|
33
33
|
mood?: string;
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Re-order the final scored results. `relevance` (default) keeps the fused
|
|
36
|
+
* score; `light` / `dark` / `vivid` / `muted` / `colourful` are the colour
|
|
37
|
+
* ramps; `name` (A→Z), `name_desc` (Z→A) and `recent` (newest first) are the
|
|
38
|
+
* catalogue orders — the same three `/search/browse` walks the corpus in, so
|
|
39
|
+
* one word means one order on both lanes.
|
|
40
|
+
*/
|
|
41
|
+
sort?: OpenEnum<"relevance" | "light" | "dark" | "vivid" | "muted" | "colourful" | "name" | "name_desc" | "recent">;
|
|
42
|
+
/**
|
|
43
|
+
* Sort along ONE attribute's declared value ladder — a SEPARATE axis from
|
|
44
|
+
* `sort`, which stays the closed relevance / colour / catalogue vocabulary.
|
|
45
|
+
*
|
|
46
|
+
* `direction` defaults to `"asc"`. Sent together with `sort`, `orderBy` wins
|
|
47
|
+
* and the response `note` says so — a UI holding both controls can send both.
|
|
48
|
+
*
|
|
49
|
+
* Ordering is DECLARED, never inferred from how a value is spelled: an
|
|
50
|
+
* attribute with no stated ladder is a 400 carrying the orderable set in
|
|
51
|
+
* `orderable_attributes`, not a quiet fall back to relevance. A material whose
|
|
52
|
+
* value is off the ladder, or which carries no value at all, sorts LAST in
|
|
53
|
+
* both directions — it is still a real material, it just has no rung.
|
|
54
|
+
*/
|
|
55
|
+
orderBy?: {
|
|
56
|
+
attributeId: string;
|
|
57
|
+
direction?: OpenEnum<"asc" | "desc">;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Attribute ids whose resolved value should ride each result under
|
|
61
|
+
* `attributes` — so a page you filtered by `price_tier` can DISPLAY the tier
|
|
62
|
+
* without a second call per material. Up to 8.
|
|
63
|
+
*
|
|
64
|
+
* Three response states, all different: no `attributes` key means you did not
|
|
65
|
+
* ask, a key with `null` means nothing is on record for that material, and
|
|
66
|
+
* anything else is the value. An unknown attribute id is a 400 naming it.
|
|
67
|
+
*/
|
|
68
|
+
returnAttributes?: string[];
|
|
35
69
|
/**
|
|
36
70
|
* Seed a visual search from an existing variant's image (`variantId`), from a
|
|
37
71
|
* Material Bank SKU (`materialBankId`), or from a pre-computed image vector
|
|
@@ -100,6 +134,11 @@ export interface SearchInput {
|
|
|
100
134
|
* `facets.absent`. Absent = no `facets` in the response, no counting cost.
|
|
101
135
|
*/
|
|
102
136
|
facetCounts?: string[];
|
|
137
|
+
/**
|
|
138
|
+
* Opaque five-minute receipt from a prior pool-derived faceted search.
|
|
139
|
+
* Resubmitting it may only narrow that exact finalized pool.
|
|
140
|
+
*/
|
|
141
|
+
refinementToken?: string;
|
|
103
142
|
/** Only return materials with at least one catalog image. Default false (full catalog). */
|
|
104
143
|
hasImages?: boolean;
|
|
105
144
|
/**
|
|
@@ -164,7 +203,7 @@ export interface SearchGetParams {
|
|
|
164
203
|
colours?: string;
|
|
165
204
|
logic?: OpenEnum<"all" | "any">;
|
|
166
205
|
mood?: string;
|
|
167
|
-
sort?: OpenEnum<"relevance" | "light" | "dark" | "vivid" | "muted" | "colourful">;
|
|
206
|
+
sort?: OpenEnum<"relevance" | "light" | "dark" | "vivid" | "muted" | "colourful" | "name" | "name_desc" | "recent">;
|
|
168
207
|
tol?: OpenEnum<"tight" | "normal" | "loose">;
|
|
169
208
|
/** Seed the search from this variant's image. Opaque — pass it through verbatim. */
|
|
170
209
|
variantId?: VariantId;
|
|
@@ -177,4 +216,12 @@ export interface SearchGetParams {
|
|
|
177
216
|
hasImages?: OpenEnum<"true">;
|
|
178
217
|
/** Result projection — `"full"` (default) or the lean `"card"` grid form. */
|
|
179
218
|
view?: OpenEnum<"full" | "card">;
|
|
219
|
+
/**
|
|
220
|
+
* One JSON OBJECT, e.g. `{"attributeId":"price_tier","direction":"desc"}` —
|
|
221
|
+
* the `orderBy` field of the POST body, JSON-encoded because a query string
|
|
222
|
+
* has no nested values. Same spelling as `filters=` on `/search/browse`.
|
|
223
|
+
*/
|
|
224
|
+
orderBy?: string;
|
|
225
|
+
/** Comma-separated attribute ids — the `returnAttributes` list. */
|
|
226
|
+
returnAttributes?: string;
|
|
180
227
|
}
|
|
@@ -162,6 +162,13 @@ export function checkSearchResultItem(value, what, fail) {
|
|
|
162
162
|
// `z.string().optional()` — absent, or a string. NOT nullable: the emit path
|
|
163
163
|
// omits the key rather than sending null when no MB identity is known.
|
|
164
164
|
requireOptionalString(record.materialBankId, `${what}.materialBankId`, fail);
|
|
165
|
+
requireOptionalString(record.manufacturerCode, `${what}.manufacturerCode`, fail);
|
|
166
|
+
if (record.projectSaveCapability !== undefined) {
|
|
167
|
+
requireEnum(record.projectSaveCapability, ["allowed"], `${what}.projectSaveCapability`, fail);
|
|
168
|
+
}
|
|
169
|
+
if (record.sourceStatus !== undefined) {
|
|
170
|
+
requireEnum(record.sourceStatus, ["provisional"], `${what}.sourceStatus`, fail);
|
|
171
|
+
}
|
|
165
172
|
// Same shape as `materialBankId`: absent, or a string. The emit path omits
|
|
166
173
|
// the key when no primary product type is on record rather than sending null,
|
|
167
174
|
// so "we don't know what this is" and "this is a null kind of thing" cannot
|
package/types.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ import type { BySpecVerdict } from "./by-spec-types.js";
|
|
|
29
29
|
import type { CorpusScopeReceipt } from "./corpus-scope-types.js";
|
|
30
30
|
import type { Cursor, ProductId, VariantId } from "./identifiers.js";
|
|
31
31
|
import type { MaterialDetail, MaterialTile } from "./material-types.js";
|
|
32
|
+
import type { QueryUnderstanding } from "./query-understanding-types.js";
|
|
32
33
|
import type { SearchDisambiguation, SearchSuggestion } from "./search-refinement-types.js";
|
|
33
34
|
/** A widened string-literal union: known members autocomplete, unknown values still type-check. */
|
|
34
35
|
export type OpenEnum<T extends string> = T | (string & {});
|
|
@@ -130,19 +131,19 @@ export interface SearchResultItem {
|
|
|
130
131
|
brandSlug: string | null;
|
|
131
132
|
productId: ProductId | null;
|
|
132
133
|
/**
|
|
133
|
-
* The variant's Material Bank SKU
|
|
134
|
-
*
|
|
135
|
-
* and means nothing anywhere else; this is the id you are expected to match
|
|
134
|
+
* The variant's Material Bank SKU, unlike our opaque `variantId`; this is the
|
|
135
|
+
* id you are expected to match
|
|
136
136
|
* against Material Bank's own catalogue, so a result list can be joined to a
|
|
137
137
|
* cart, an order or an analytics event without a per-result lookup.
|
|
138
|
-
*
|
|
139
138
|
* Populated on EVERY lane: `search()` (all four arms), `match()`, and
|
|
140
139
|
* `matchStream()` frames.
|
|
141
|
-
*
|
|
142
140
|
* ABSENT means no MB identity is known for the variant — never that the SKU
|
|
143
141
|
* cannot be ordered. Presence is the orderability tell; absence is silence.
|
|
144
142
|
*/
|
|
145
143
|
materialBankId?: string;
|
|
144
|
+
manufacturerCode?: string;
|
|
145
|
+
projectSaveCapability?: "allowed";
|
|
146
|
+
sourceStatus?: "provisional";
|
|
146
147
|
/**
|
|
147
148
|
* What kind of material this is — the variant's primary product type, as a
|
|
148
149
|
* display name (`"Wall Tile"`, `"Modular Carpet"`, `"Interior Paint"`).
|
|
@@ -254,6 +255,18 @@ export interface SearchResultItem {
|
|
|
254
255
|
* shared CandidateVerdict mirror; see {@link MaterialVerdict}.
|
|
255
256
|
*/
|
|
256
257
|
verdict?: BySpecVerdict;
|
|
258
|
+
/**
|
|
259
|
+
* Resolved values for the attribute ids sent in `returnAttributes`, keyed by
|
|
260
|
+
* attribute id. ABSENT entirely when `returnAttributes` was not sent.
|
|
261
|
+
*
|
|
262
|
+
* Three states, all different, and reading them as two is the mistake this
|
|
263
|
+
* shape exists to prevent: no `attributes` key means you did not ask; a key
|
|
264
|
+
* whose value is `null` means this material carries no accepted value for that
|
|
265
|
+
* attribute; anything else is what the graph holds. Values resolve with the
|
|
266
|
+
* normal variant-over-product precedence, and an array is a multi-valued
|
|
267
|
+
* attribute rather than a list of candidates.
|
|
268
|
+
*/
|
|
269
|
+
attributes?: Record<string, string | string[] | null>;
|
|
257
270
|
}
|
|
258
271
|
/**
|
|
259
272
|
* The lean `view:"card"` result item — identity + name + thumbnail + score.
|
|
@@ -273,7 +286,21 @@ export interface SearchCardItem {
|
|
|
273
286
|
name: string;
|
|
274
287
|
variantName: string | null;
|
|
275
288
|
imageUrl: string | null;
|
|
289
|
+
projectSaveCapability?: "allowed";
|
|
290
|
+
sourceStatus?: "provisional";
|
|
276
291
|
score: number;
|
|
292
|
+
/**
|
|
293
|
+
* Resolved values for the attribute ids sent in `returnAttributes`, keyed by
|
|
294
|
+
* attribute id. ABSENT entirely when `returnAttributes` was not sent.
|
|
295
|
+
*
|
|
296
|
+
* Three states, all different, and reading them as two is the mistake this
|
|
297
|
+
* shape exists to prevent: no `attributes` key means you did not ask; a key
|
|
298
|
+
* whose value is `null` means this material carries no accepted value for that
|
|
299
|
+
* attribute; anything else is what the graph holds. Values resolve with the
|
|
300
|
+
* normal variant-over-product precedence, and an array is a multi-valued
|
|
301
|
+
* attribute rather than a list of candidates.
|
|
302
|
+
*/
|
|
303
|
+
attributes?: Record<string, string | string[] | null>;
|
|
277
304
|
}
|
|
278
305
|
/**
|
|
279
306
|
* A feed total with its honesty basis — THE CLAIM ABOUT THE MATCH SET, which is
|
|
@@ -354,8 +381,16 @@ export interface SearchFacets {
|
|
|
354
381
|
absent: SearchAbsentFacet[];
|
|
355
382
|
counts: Record<string, SearchFacetCount>;
|
|
356
383
|
}
|
|
384
|
+
/** Inspectable metadata for an opaque, principal-bound search refinement receipt. */
|
|
385
|
+
export interface SearchRefinementReceipt {
|
|
386
|
+
token: string;
|
|
387
|
+
expiresAt: string;
|
|
388
|
+
poolSize: number;
|
|
389
|
+
schemaVersion: string;
|
|
390
|
+
projectionVersion: number;
|
|
391
|
+
}
|
|
357
392
|
/** The single candidate source a query resolved to. */
|
|
358
|
-
export type SearchMode = OpenEnum<"image" | "colour" | "text" | "lexical">;
|
|
393
|
+
export type SearchMode = OpenEnum<"image" | "colour" | "text" | "lexical" | "entity-browse">;
|
|
359
394
|
/** Rerank-term report (absent in colour mode — there colour IS the source). */
|
|
360
395
|
export interface SearchRerankReport {
|
|
361
396
|
colour?: OpenEnum<"ok" | "failed" | "skipped">;
|
|
@@ -401,6 +436,8 @@ export interface SearchResponse {
|
|
|
401
436
|
droppedColours?: string[];
|
|
402
437
|
/** Per-query facet counts — present ONLY when the request sent `facetCounts`. */
|
|
403
438
|
facets?: SearchFacets;
|
|
439
|
+
/** Receipt that narrows the exact finalized pool behind lower-bound facets. */
|
|
440
|
+
refinement?: SearchRefinementReceipt;
|
|
404
441
|
note?: string;
|
|
405
442
|
timings?: Record<string, number>;
|
|
406
443
|
/** A detected mood term overridden by a stronger colour reading. */
|
|
@@ -410,6 +447,8 @@ export interface SearchResponse {
|
|
|
410
447
|
* offers the other as a complete executable request.
|
|
411
448
|
*/
|
|
412
449
|
disambiguation?: SearchDisambiguation;
|
|
450
|
+
/** Structured receipt of the standard query-understanding decisions. */
|
|
451
|
+
understanding?: QueryUnderstanding;
|
|
413
452
|
/**
|
|
414
453
|
* The corpus this page AND its `counts` were drawn from — present ONLY when
|
|
415
454
|
* the request narrowed it. ABSENT means the whole graph: every material
|