@instruments/materialgraph 0.26.0 → 0.27.1
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/association-types.d.ts +18 -7
- package/attribution-types.d.ts +2 -2
- package/brand-types.d.ts +8 -0
- package/brands-client.d.ts +5 -36
- package/brands-client.js +7 -33
- package/by-colour-types.d.ts +40 -13
- package/by-spec-types.d.ts +0 -67
- package/by-spec-types.js +7 -5
- package/catalog-discovery-types.d.ts +1 -16
- package/catalogue-client.d.ts +12 -31
- package/catalogue-client.js +10 -46
- package/catalogue-types.d.ts +40 -0
- package/categories-client.d.ts +63 -0
- package/categories-client.js +48 -0
- package/category-types.d.ts +114 -0
- package/client-config.d.ts +2 -4
- package/client-config.js +2 -4
- package/client-vocabulary.d.ts +2 -4
- package/client-vocabulary.js +11 -9
- package/client.d.ts +103 -203
- package/client.js +128 -251
- package/codes-types.d.ts +21 -0
- package/colours-client.d.ts +4 -9
- package/colours-client.js +0 -8
- package/errors.d.ts +1 -1
- package/errors.js +1 -0
- package/identify-types.d.ts +49 -39
- package/identify-types.js +5 -3
- package/index.d.ts +11 -21
- package/index.js +4 -4
- package/list-variant-facets-types.d.ts +60 -0
- package/list-variants-types.d.ts +149 -0
- package/list-variants-types.js +12 -0
- package/match-stream-types.d.ts +13 -0
- package/match-stream-types.js +2 -0
- package/match-stream.js +12 -8
- package/match-types.d.ts +24 -0
- package/material-asset-types.d.ts +69 -49
- package/material-block-types.d.ts +2 -0
- package/material-composition-client.d.ts +21 -0
- package/material-composition-client.js +13 -0
- package/material-composition-types.d.ts +403 -0
- package/material-query-plan-types.d.ts +57 -141
- package/material-resources-client.d.ts +9 -7
- package/material-resources-client.js +19 -4
- package/material-types.d.ts +6 -14
- package/operations.d.ts +1 -1
- package/operations.js +20 -117
- package/package.json +5 -1
- package/palette-search-types.d.ts +59 -24
- package/query-understanding-types.d.ts +27 -5
- package/resolve-client.d.ts +6 -10
- package/resolve-client.js +5 -7
- package/response-schemas.d.ts +20 -0
- package/response-schemas.generated.d.ts +5 -0
- package/response-schemas.generated.js +160 -0
- package/response-schemas.js +113 -0
- package/run-types.d.ts +12 -93
- package/runs.d.ts +1 -37
- package/runs.js +2 -79
- package/schema-resource-types.d.ts +14 -1
- package/search-card-types.d.ts +22 -0
- package/search-index-plan-types.d.ts +70 -0
- package/search-refinement-types.d.ts +8 -38
- package/search-refinement-types.js +1 -0
- package/search-request-types.d.ts +117 -200
- package/search-response-receipt-types.d.ts +12 -268
- package/search-result-validation.d.ts +0 -16
- package/search-result-validation.js +22 -180
- package/spec-facet-types.d.ts +3 -8
- package/spec-types.d.ts +17 -10
- package/spec-types.js +8 -7
- package/specifications.d.ts +13 -55
- package/specifications.js +29 -73
- package/suggest-types.d.ts +4 -10
- package/suggest-types.js +1 -1
- package/types.d.ts +135 -338
- package/webmcp.d.ts +1 -1
- package/webmcp.generated.js +10 -94
- package/brand-most-sampled-types.d.ts +0 -33
- package/browse-types.d.ts +0 -236
- package/client-browse.d.ts +0 -78
- package/client-browse.js +0 -71
- package/client-exploration.d.ts +0 -9
- package/client-exploration.js +0 -5
- package/compare-types.d.ts +0 -89
- package/composition-client.d.ts +0 -33
- package/composition-client.js +0 -28
- package/composition-types.d.ts +0 -455
- package/composition-types.js +0 -10
- package/correction-types.d.ts +0 -88
- package/exploration-types.d.ts +0 -118
- package/exploration-types.js +0 -1
- package/fan-deck-types.d.ts +0 -42
- package/fan-deck-types.js +0 -9
- package/match-rejection-types.d.ts +0 -43
- package/match-rejection-types.js +0 -7
- package/palette-completion-types.d.ts +0 -115
- package/palette-completion-types.js +0 -1
- package/render-assets-types.d.ts +0 -65
- package/render-assets-types.js +0 -1
- package/search-collapse-types.d.ts +0 -69
- package/search-collapse-types.js +0 -1
- package/search-mood-types.d.ts +0 -6
- package/search-mood-types.js +0 -1
- package/search-window-types.d.ts +0 -25
- package/search-window-types.js +0 -1
- package/slot-pool-types.d.ts +0 -372
- package/slot-pool-types.js +0 -1
- /package/{brand-most-sampled-types.js → category-types.js} +0 -0
- /package/{browse-types.js → list-variant-facets-types.js} +0 -0
- /package/{compare-types.js → material-composition-types.js} +0 -0
- /package/{correction-types.js → search-index-plan-types.js} +0 -0
package/client.js
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import * as assoc from "./associations-client.js";
|
|
2
|
-
import {
|
|
2
|
+
import { listBrands } from "./brands-client.js";
|
|
3
3
|
import { CatalogueClient } from "./catalogue-client.js";
|
|
4
|
-
import {
|
|
5
|
-
import { DEFAULT_BASE_URL
|
|
6
|
-
import { createExplorationNamespace } from "./client-exploration.js";
|
|
4
|
+
import { CategoriesClient } from "./categories-client.js";
|
|
5
|
+
import { DEFAULT_BASE_URL } from "./client-config.js";
|
|
7
6
|
import { createSourceMaterialCommentsNamespace } from "./client-source-material-comments.js";
|
|
8
|
-
import {
|
|
7
|
+
import { listAttributes as listAttributesCall, resolveColour as resolveColourCall, suggest as suggestCall, suggestColour as suggestColourCall, } from "./client-vocabulary.js";
|
|
9
8
|
import { CodesClient } from "./codes-client.js";
|
|
10
9
|
import { ColoursClient } from "./colours-client.js";
|
|
11
|
-
import { slotPools as slotPoolsCall, varyScheme as varySchemeCall, } from "./composition-client.js";
|
|
12
10
|
import { coordinate as coordinateCall } from "./coordinate-client.js";
|
|
13
11
|
import { MaterialGraphError } from "./errors.js";
|
|
14
12
|
import { HttpTransport } from "./http-transport.js";
|
|
15
13
|
import { matchStream as matchStreamCall } from "./match-stream.js";
|
|
16
|
-
import {
|
|
14
|
+
import { compositionReport as compositionReportCall } from "./material-composition-client.js";
|
|
15
|
+
import { listVariantAssets as listVariantAssetsCall, materialBlocks as materialBlocksCall, materialDocuments as materialDocumentsCall, } from "./material-resources-client.js";
|
|
17
16
|
// Aliased to `operation` — the local name every call site here already used.
|
|
18
17
|
import { resolveOperation as operation } from "./operations.js";
|
|
19
18
|
import { resolvePhrasesRequest as resolvePhrases, searchVocabularyRequest, } from "./resolve-client.js";
|
|
20
|
-
import {
|
|
19
|
+
import { createRun, drainRunEvents, getRun, getSourceMaterialBatch as getSourceMaterialBatchCall, streamRunEvents, uploadSourceMaterial, } from "./runs.js";
|
|
21
20
|
import { SCHEMA_RESOURCE_OPERATION_IDS } from "./schema-resource-types.js";
|
|
22
21
|
// Re-exported whole so `index.ts` and every existing importer keep their path.
|
|
23
22
|
export * from "./client-config.js";
|
|
@@ -31,13 +30,13 @@ export * from "./client-config.js";
|
|
|
31
30
|
* | --- | --- |
|
|
32
31
|
* | "find me something like this / matching these words" | {@link MaterialGraphClient.search} |
|
|
33
32
|
* | "what's IN this photo?" | {@link MaterialGraphClient.match} / {@link MaterialGraphClient.matchStream} |
|
|
34
|
-
* | "which materials, from anyone, meet this hard spec?" | {@link MaterialGraphClient.
|
|
33
|
+
* | "which materials, from anyone, meet this hard spec?" | {@link MaterialGraphClient.listVariants} |
|
|
35
34
|
* | "what's near this colour / this palette / this gradient?" | {@link MaterialGraphClient.byColour}, `colours.palette()`, `colours.gradient()` |
|
|
36
|
-
* | "what goes with THIS material?" | {@link MaterialGraphClient.pairings}, {@link MaterialGraphClient.
|
|
35
|
+
* | "what goes with THIS material?" | {@link MaterialGraphClient.pairings}, {@link MaterialGraphClient.listVariants} (same product, other colourways), {@link MaterialGraphClient.coordinate} |
|
|
37
36
|
* | "what does the manufacturer say is AVAILABLE on this?" | {@link MaterialGraphClient.associatedFinishes}, {@link MaterialGraphClient.associatedProducts} |
|
|
38
37
|
* | "how much is this specified?" | {@link MaterialGraphClient.demand}, {@link MaterialGraphClient.demandBatch} |
|
|
39
38
|
* | "is this allowed in an A-2 corridor?" | `codes.*` |
|
|
40
|
-
* | "what can I even ask for?" | {@link MaterialGraphClient.specificationFacets},
|
|
39
|
+
* | "what can I even ask for?" | {@link MaterialGraphClient.specificationFacets}, `categories.list()` |
|
|
41
40
|
* | "I have words — what do they mean in YOUR vocabulary?" | {@link MaterialGraphClient.resolve} |
|
|
42
41
|
* | "do the whole job for me, async" | {@link MaterialGraphClient.createRun} + `mg:compose` |
|
|
43
42
|
*
|
|
@@ -59,30 +58,26 @@ export * from "./client-config.js";
|
|
|
59
58
|
export class MaterialGraphClient extends CatalogueClient {
|
|
60
59
|
baseUrl;
|
|
61
60
|
/**
|
|
62
|
-
* THE QUESTION: "
|
|
63
|
-
*
|
|
64
|
-
* `
|
|
61
|
+
* THE QUESTION: "what kinds of thing exist, and what are their keys?"
|
|
62
|
+
* `categories.list()` pages the tree (roots, one node's children, or a
|
|
63
|
+
* name/meaning search), and `categories.get(id)` reads one node in full:
|
|
64
|
+
* its place, the names that find it, and the canonical taxonomy concepts it
|
|
65
|
+
* relates to. The source of every category id the other lenses accept
|
|
66
|
+
* (`search({ category })`, `listVariants({ filters })`, `colours.profiles()`).
|
|
65
67
|
*
|
|
66
|
-
*
|
|
67
|
-
* the
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* Instead use {@link MaterialGraphClient.byColour} for materials NEAR a colour,
|
|
72
|
-
* {@link MaterialGraphClient.search} when there is anything to rank against,
|
|
73
|
-
* and `colours.profiles()` for a category's palette rather than its members.
|
|
74
|
-
*
|
|
75
|
-
* @example A category listing that can state its own size
|
|
76
|
-
* const page = await client.browse({ types: "tile_surfaces", count: true });
|
|
68
|
+
* A category id is a TREE KEY, never a canonical `product_category` value:
|
|
69
|
+
* the two look alike and are not interchangeable. Discover ids here, never
|
|
70
|
+
* hand-write them. Replaces the retired `taxonomy()` (MG-2226), with no
|
|
71
|
+
* shim.
|
|
77
72
|
*/
|
|
78
|
-
|
|
73
|
+
categories;
|
|
79
74
|
/**
|
|
80
75
|
* THE QUESTION: "is this material allowed HERE?" — a jurisdiction + occupancy +
|
|
81
76
|
* finish location, not a property of the material alone. `codes.context()`
|
|
82
77
|
* discovers the vocabulary, `codes.requirements()` states a space,
|
|
83
78
|
* `codes.advisory()` badges candidates.
|
|
84
79
|
*
|
|
85
|
-
* Instead use {@link MaterialGraphClient.
|
|
80
|
+
* Instead use {@link MaterialGraphClient.listVariants} to find materials by a
|
|
86
81
|
* classification you already know you need; codes is what tells you WHICH one
|
|
87
82
|
* the space demands. ADVISORY ONLY: model code as adopted, never a
|
|
88
83
|
* determination for an authority having jurisdiction.
|
|
@@ -92,15 +87,14 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
92
87
|
* THE QUESTION: "what colours exist here?" (`profiles`, `stats`) and "what
|
|
93
88
|
* matches this whole SET of colours?" (`palette`, `gradient`).
|
|
94
89
|
*
|
|
95
|
-
* Instead use {@link MaterialGraphClient.byColour} for ONE colour
|
|
96
|
-
* {@link MaterialGraphClient.
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
90
|
+
* Instead use {@link MaterialGraphClient.byColour} for ONE colour and
|
|
91
|
+
* {@link MaterialGraphClient.listVariants} to page through a colour's family.
|
|
92
|
+
* Colour is usually derived from product imagery, which is visual likeness,
|
|
93
|
+
* not a colour specification. A maker's declared colour, such as a paint's
|
|
94
|
+
* published hex, wins where MaterialGraph holds one, and each value's
|
|
95
|
+
* source basis says which.
|
|
100
96
|
*/
|
|
101
97
|
colours;
|
|
102
|
-
/** Discover honest graph questions, then traverse one explicit operator. */
|
|
103
|
-
exploration;
|
|
104
98
|
/**
|
|
105
99
|
* THE QUESTION: "what did people say about this uploaded file, and record what
|
|
106
100
|
* they said." Names on these remarks are REPORTED, never verified — see
|
|
@@ -120,7 +114,9 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
120
114
|
super(transport);
|
|
121
115
|
this.#transport = transport;
|
|
122
116
|
this.baseUrl = transport.baseUrl;
|
|
123
|
-
this.
|
|
117
|
+
this.categories = new CategoriesClient({
|
|
118
|
+
get: async (operationId, query, signal, pathParams) => await this.#get(operationId, query, signal, pathParams),
|
|
119
|
+
});
|
|
124
120
|
this.codes = new CodesClient({
|
|
125
121
|
get: async (operationId, query, signal) => await this.#get(operationId, query, signal),
|
|
126
122
|
post: async (operationId, body, signal) => await this.#post(operationId, body, signal),
|
|
@@ -129,7 +125,6 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
129
125
|
get: async (operationId, query, signal) => await this.#get(operationId, query, signal),
|
|
130
126
|
post: async (operationId, body, signal) => await this.#post(operationId, body, signal),
|
|
131
127
|
});
|
|
132
|
-
this.exploration = createExplorationNamespace(this.#transport);
|
|
133
128
|
this.sourceMaterialComments = createSourceMaterialCommentsNamespace(this.#transport);
|
|
134
129
|
}
|
|
135
130
|
get lastRateLimit() {
|
|
@@ -149,21 +144,6 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
149
144
|
signal,
|
|
150
145
|
});
|
|
151
146
|
}
|
|
152
|
-
/**
|
|
153
|
-
* THE QUESTION: "find me materials like this" — a RANKED answer over words, a
|
|
154
|
-
* seed image, colours and a mood, optionally gated by hard specs.
|
|
155
|
-
*
|
|
156
|
-
* Instead use {@link MaterialGraphClient.bySpec} when the spec IS the question
|
|
157
|
-
* (ranking would be noise), {@link MaterialGraphClient.byColour} /
|
|
158
|
-
* `colours.palette()` for visual colour intent (`text` matches WORDS — "sage
|
|
159
|
-
* green" finds materials that SAY sage, not ones that look it),
|
|
160
|
-
* {@link MaterialGraphClient.match} for a photo, {@link MaterialGraphClient.pairings}
|
|
161
|
-
* for what goes WITH something, {@link MaterialGraphClient.suggest} for typeahead.
|
|
162
|
-
*
|
|
163
|
-
* The full POST search primitive. `input.filters` layers typed hard
|
|
164
|
-
* specification gates onto ranked colour/visual/lexical/seed search. Build
|
|
165
|
-
* them with `certificationProgramme`, `specTerm`, or `hasSpec`.
|
|
166
|
-
*/
|
|
167
147
|
async search(input, signal) {
|
|
168
148
|
return await this.#post("search_materials_post", input, signal);
|
|
169
149
|
}
|
|
@@ -171,12 +151,6 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
171
151
|
async planQuery(query, signal) {
|
|
172
152
|
return await this.#post("plan_material_query", { query }, signal);
|
|
173
153
|
}
|
|
174
|
-
/**
|
|
175
|
-
* The same question as {@link MaterialGraphClient.search}; the difference is
|
|
176
|
-
* transport — a cacheable, link-shareable GET for queries that must survive in
|
|
177
|
-
* a URL. `filters`, `preferences`, `verdicts`, image bytes and `weights` have
|
|
178
|
-
* no query-string form, so anything using those must go through `search`.
|
|
179
|
-
*/
|
|
180
154
|
async searchGet(params, signal) {
|
|
181
155
|
return await this.#get("search_materials_get", { ...params }, signal);
|
|
182
156
|
}
|
|
@@ -229,7 +203,7 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
229
203
|
* Instead use `colours.palette()` for N colours at stated proportions (a
|
|
230
204
|
* different ranking, not a wrapper), `colours.gradient()` for a transition,
|
|
231
205
|
* {@link MaterialGraphClient.resolveColour} first if you hold a NAME,
|
|
232
|
-
* {@link MaterialGraphClient.
|
|
206
|
+
* {@link MaterialGraphClient.listVariants} to page through a category or a brand, and
|
|
233
207
|
* {@link MaterialGraphClient.search} when colour is one signal among many.
|
|
234
208
|
* Report matches as visual likeness, never a stated colour specification.
|
|
235
209
|
* `family` gates to ONE colorscope family (a gated page can be short); `brand` gates to named brands — one brand's nearest colour to another's.
|
|
@@ -249,6 +223,7 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
249
223
|
maxColourDistance: params.maxColourDistance,
|
|
250
224
|
order: params.order,
|
|
251
225
|
q: params.q,
|
|
226
|
+
variantId: params.variantId,
|
|
252
227
|
}, signal);
|
|
253
228
|
}
|
|
254
229
|
// ── Materials ──────────────────────────────────────────────────────────────
|
|
@@ -257,35 +232,34 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
257
232
|
* `/api/v1/materials/{variantId}`).
|
|
258
233
|
*
|
|
259
234
|
* The hinge of the API: every relationship lens
|
|
260
|
-
* ({@link MaterialGraphClient.
|
|
235
|
+
* ({@link MaterialGraphClient.listVariants}, {@link MaterialGraphClient.pairings},
|
|
261
236
|
* {@link MaterialGraphClient.demand}, `codes.advisory()`) hangs off a
|
|
262
|
-
* `variantId`. Instead use {@link MaterialGraphClient.
|
|
263
|
-
*
|
|
264
|
-
*
|
|
237
|
+
* `variantId`. Instead use {@link MaterialGraphClient.listVariants} for many
|
|
238
|
+
* ids, or for the other colourways of the same product (MG-2222 retired
|
|
239
|
+
* this route's `?include=siblings`; ask `listVariants` with a `product in`
|
|
240
|
+
* filter instead), {@link MaterialGraphClient.materialBlocks} to tell "no
|
|
241
|
+
* value" from "not checked", {@link MaterialGraphClient.search} when you
|
|
242
|
+
* have no id yet.
|
|
265
243
|
*
|
|
266
244
|
* @param variantId Opaque — pass back exactly what you were given.
|
|
267
245
|
*/
|
|
268
|
-
async material(variantId,
|
|
269
|
-
return await this.#get("get_material",
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* THE QUESTION: "what other colourways and finishes does this product come in?"
|
|
273
|
-
* — the SAME product, different variants.
|
|
274
|
-
*
|
|
275
|
-
* Instead use {@link MaterialGraphClient.pairings} for a DIFFERENT product that
|
|
276
|
-
* goes with this one, and `search({ similarTo })` for one that LOOKS like it.
|
|
277
|
-
*/
|
|
278
|
-
async siblings(variantId, signal) {
|
|
279
|
-
return await this.#get("get_material_siblings", undefined, signal, { variantId });
|
|
246
|
+
async material(variantId, signal) {
|
|
247
|
+
return await this.#get("get_material", undefined, signal, { variantId });
|
|
280
248
|
}
|
|
281
249
|
async materialBlocks(variantId, signal) {
|
|
282
250
|
return await materialBlocksCall(this.#transport, variantId, signal);
|
|
283
251
|
}
|
|
284
|
-
|
|
285
|
-
|
|
252
|
+
/**
|
|
253
|
+
* THE QUESTION: "which product types can composition be answered for yet?"
|
|
254
|
+
*
|
|
255
|
+
* Coverage per product type, in Live SKUs as well as tuples. Read the SKU
|
|
256
|
+
* figure: one tuple can carry a hundred thousand SKUs or one.
|
|
257
|
+
*/
|
|
258
|
+
async compositionReport(signal) {
|
|
259
|
+
return await compositionReportCall(this.#transport, signal);
|
|
286
260
|
}
|
|
287
|
-
async
|
|
288
|
-
return await
|
|
261
|
+
async listVariantAssets(variantId, options, signal) {
|
|
262
|
+
return await listVariantAssetsCall(this.#transport, variantId, options, signal);
|
|
289
263
|
}
|
|
290
264
|
async materialDocuments(variantId, options, signal) {
|
|
291
265
|
return await materialDocumentsCall(this.#transport, variantId, options, signal);
|
|
@@ -294,8 +268,7 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
294
268
|
* THE QUESTION: "what does the schema DEFINE?" — the governed static registry.
|
|
295
269
|
*
|
|
296
270
|
* Definitions, not data: use {@link MaterialGraphClient.specificationFacets} for
|
|
297
|
-
* what the corpus actually holds
|
|
298
|
-
* explain one term.
|
|
271
|
+
* what the corpus actually holds.
|
|
299
272
|
*/
|
|
300
273
|
async schema(resource, signal) {
|
|
301
274
|
return await this.#get(SCHEMA_RESOURCE_OPERATION_IDS[resource], undefined, signal);
|
|
@@ -305,9 +278,9 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
305
278
|
* off one resolved material (GET `/api/v1/materials/{variantId}/pairings`).
|
|
306
279
|
*
|
|
307
280
|
* Instead use {@link MaterialGraphClient.coordinate} for a whole palette of
|
|
308
|
-
* picks, {@link MaterialGraphClient.
|
|
309
|
-
* colour, and `search({ similarTo })` for a LIKE-THIS substitute —
|
|
310
|
-
* complementary are opposite questions.
|
|
281
|
+
* picks, {@link MaterialGraphClient.listVariants} for the same product in
|
|
282
|
+
* another colour, and `search({ similarTo })` for a LIKE-THIS substitute —
|
|
283
|
+
* similar and complementary are opposite questions.
|
|
311
284
|
*
|
|
312
285
|
* READ `item.source` BEFORE PHRASING: the rail mixes three tiers of evidence —
|
|
313
286
|
* `cospec` (real orders), `considered` (board activity), `colour` (palette
|
|
@@ -319,10 +292,6 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
319
292
|
variantId,
|
|
320
293
|
});
|
|
321
294
|
}
|
|
322
|
-
/** Find bounded, exact-spec substitutes for one opaque variant id. */
|
|
323
|
-
async findSubstitutes(variantId, options = {}, signal) {
|
|
324
|
-
return await this.#post("find_material_substitutes", options, signal, { variantId });
|
|
325
|
-
}
|
|
326
295
|
/** "What finishes does the manufacturer declare for THIS product?" — {@link declaredFinishes}. */
|
|
327
296
|
async associatedFinishes(variantId, options, signal) {
|
|
328
297
|
return await assoc.declaredFinishes(this.#transport, variantId, options, signal);
|
|
@@ -334,29 +303,52 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
334
303
|
async applicability(options, signal) {
|
|
335
304
|
return await assoc.applicability(this.#transport, options, signal);
|
|
336
305
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
306
|
+
/**
|
|
307
|
+
* THE QUESTION: "eligible variants matching these hard filters, ranked and
|
|
308
|
+
* cursor-paged" (GET `/api/v1/variants`, MG-2222) — the one read behind
|
|
309
|
+
* `list_variants`, hydrating ids you already hold (`{terms: [{field:
|
|
310
|
+
* "variant", op: "in", values: [...]}]}`, up to 100), the other colourways
|
|
311
|
+
* of a product (`{terms: [{field: "product", op: "in", values:
|
|
312
|
+
* [productId]}, {field: "variant", op: "not_in", values: [variantId]}]}` —
|
|
313
|
+
* `filters` is kind-grouped, `{terms, numbers, presence}`, every condition
|
|
314
|
+
* across all three ANDed), or a category/maker listing. An explicit
|
|
315
|
+
* `variant in` id that does not resolve to an eligible variant is
|
|
316
|
+
* reported in `unresolvedVariantIds`, never silently dropped from the
|
|
317
|
+
* page.
|
|
318
|
+
*
|
|
319
|
+
* `filters`/`optionalFilters`/`orderBy` are sent as URL-encoded JSON;
|
|
320
|
+
* `attributes`/`include` as comma-separated lists — the server validates,
|
|
321
|
+
* this method does not duplicate that check.
|
|
322
|
+
*/
|
|
323
|
+
async listVariants(input, signal) {
|
|
324
|
+
return await this.#get("list_variants", {
|
|
325
|
+
attributes: input.attributes?.length
|
|
326
|
+
? input.attributes.join(",")
|
|
327
|
+
: undefined,
|
|
328
|
+
collapse: input.collapse,
|
|
329
|
+
corpusScope: input.corpusScope,
|
|
330
|
+
cursor: input.cursor,
|
|
331
|
+
filters: input.filters ? JSON.stringify(input.filters) : undefined,
|
|
332
|
+
include: input.include?.length ? input.include.join(",") : undefined,
|
|
333
|
+
limit: input.limit,
|
|
334
|
+
name: input.name,
|
|
335
|
+
optionalFilters: input.optionalFilters
|
|
336
|
+
? JSON.stringify(input.optionalFilters)
|
|
337
|
+
: undefined,
|
|
338
|
+
orderBy: input.orderBy?.length
|
|
339
|
+
? JSON.stringify(input.orderBy)
|
|
340
|
+
: undefined,
|
|
355
341
|
}, signal);
|
|
356
342
|
}
|
|
357
|
-
/**
|
|
358
|
-
|
|
359
|
-
|
|
343
|
+
/**
|
|
344
|
+
* THE QUESTION: "which values do the variants matching these filters carry,
|
|
345
|
+
* and how many of each" (POST `/api/v1/variants/facets`, MG-2294) — the
|
|
346
|
+
* options and counts a filter UI shows beside a {@link
|
|
347
|
+
* MaterialGraphClient.listVariants} page. `filters` is the same tree
|
|
348
|
+
* `listVariants()` takes; the server validates both.
|
|
349
|
+
*/
|
|
350
|
+
async listVariantFacets(input, signal) {
|
|
351
|
+
return await this.#post("list_variant_facets", input, signal);
|
|
360
352
|
}
|
|
361
353
|
/** Resolve a colour name to its hex, and the named paint's catalogue variant. */
|
|
362
354
|
async resolveColour(name, signal, options) {
|
|
@@ -371,13 +363,17 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
371
363
|
return await suggestCall(this.#transport, q, options, signal);
|
|
372
364
|
}
|
|
373
365
|
// ── Catalog ──────────────────────────────────────────────────────────────────
|
|
374
|
-
/**
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
366
|
+
/**
|
|
367
|
+
* THE QUESTION: "which attribute means this phrase?" or "what could a
|
|
368
|
+
* <category> be specified by?" (GET `/api/v1/catalog/attributes`). One page of
|
|
369
|
+
* attribute definitions, browsed or found by id, name, misspelling or
|
|
370
|
+
* meaning, scoped by `categoryIds`, `blockIds` and `filterableOnly`. Pass an
|
|
371
|
+
* item's `id` with one of its `filterOps` to
|
|
372
|
+
* {@link MaterialGraphClient.listVariants}; follow `pageInfo.endCursor` for
|
|
373
|
+
* the next page.
|
|
374
|
+
*/
|
|
375
|
+
async listAttributes(options, signal) {
|
|
376
|
+
return await listAttributesCall(this.#transport, options, signal);
|
|
381
377
|
}
|
|
382
378
|
/** "Is this phrase an attribute or a value?" — see {@link searchVocabularyRequest}. */
|
|
383
379
|
async searchVocabulary(query, options, signal) {
|
|
@@ -387,45 +383,16 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
387
383
|
async resolve(phrases, scope) {
|
|
388
384
|
return await resolvePhrases(this.#transport, phrases, scope);
|
|
389
385
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
* unresolvable criterion means the query NEVER RAN and the response says so —
|
|
401
|
-
* read {@link BySpecResponse.unresolvedCriteria} before treating an empty
|
|
402
|
-
* `matches` as "nothing qualifies". A PAGE, NOT A CORPUS, unusually so:
|
|
403
|
-
* `totalMatches` is the returned sample's size under `limit`, not a corpus
|
|
404
|
-
* count. Each `spec` is an `attribute:value` criterion built with
|
|
405
|
-
* `specCriterion` / `certificationProgrammeCriterion`; a variant must satisfy
|
|
406
|
-
* ALL of them, and each match carries per-criterion trust (`confidence` +
|
|
407
|
-
* `sourceBasis`). NUMBER-valued specs take BOUNDS, not values — pass
|
|
408
|
-
* `options.ranges` built with `specRangeCriterion("light_reflectance_value",
|
|
409
|
-
* { min: 60 })`; bounds are inclusive, at least one is required, and a
|
|
410
|
-
* non-numeric stored value is excluded rather than read as zero.
|
|
411
|
-
*/
|
|
412
|
-
async bySpec(spec, options, signal) {
|
|
413
|
-
return await this.#get("catalog_by_spec", {
|
|
414
|
-
spec: Array.isArray(spec) ? spec : [spec],
|
|
415
|
-
...(options?.ranges?.length ? { specRange: options.ranges } : {}),
|
|
416
|
-
limit: options?.limit,
|
|
417
|
-
}, signal);
|
|
418
|
-
}
|
|
419
|
-
// ── Taxonomy + meta ──────────────────────────────────────────────────────────
|
|
420
|
-
/**
|
|
421
|
-
* THE QUESTION: "what kinds of thing exist, and what are their canonical keys?"
|
|
422
|
-
* The source of every `categoryKey` / product-type id the other lenses accept
|
|
423
|
-
* (`search({ category })`, `colours.profiles()`,
|
|
424
|
-
* {@link MaterialGraphClient.applicableSpecs}). Discover, never hand-write.
|
|
425
|
-
*/
|
|
426
|
-
async taxonomy(signal) {
|
|
427
|
-
return await this.#get("get_category_taxonomy", undefined, signal);
|
|
428
|
-
}
|
|
386
|
+
// Hard-specification search (formerly `bySpec()`, GET
|
|
387
|
+
// `/api/v1/search/specifications`) moved to {@link MaterialGraphClient.listVariants}
|
|
388
|
+
// (MG-2222): each `attribute:value` criterion is one `filters` condition —
|
|
389
|
+
// `{field: attributeId, op: "in", values: [...]}` in `filters.terms` for a
|
|
390
|
+
// term, `{field: attributeId, op: "gte"/"lte"/etc, value}` in
|
|
391
|
+
// `filters.numbers` for a number — and every condition across the
|
|
392
|
+
// kind-grouped `filters` object must match. Use `codes.advisory()` when
|
|
393
|
+
// the real question is what a space demands, and {@link specificationFacets}
|
|
394
|
+
// first if you do not know the canonical tokens.
|
|
395
|
+
// ── Coverage + meta ──────────────────────────────────────────────────────────
|
|
429
396
|
/**
|
|
430
397
|
* Exact current primary-type status counts by retained source category path.
|
|
431
398
|
* `comparisonThreshold` is a reporting cutoff only: it never promotes a
|
|
@@ -513,13 +480,12 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
513
480
|
async extractPalette(input, signal) {
|
|
514
481
|
return await this.#post("extract_image_palette", input, signal);
|
|
515
482
|
}
|
|
516
|
-
// ── Catalog (facets /
|
|
483
|
+
// ── Catalog (facets / glossary) ─────────────────────────────────────────────
|
|
517
484
|
/**
|
|
518
485
|
* THE QUESTION: "what can I even filter on, and what values does the corpus
|
|
519
486
|
* actually hold?" The start of every spec-driven workflow. Instead use
|
|
520
|
-
* {@link MaterialGraphClient.
|
|
521
|
-
* {@link MaterialGraphClient.
|
|
522
|
-
* carry, {@link MaterialGraphClient.schema} for the definition.
|
|
487
|
+
* {@link MaterialGraphClient.listAttributes} with `categoryIds` for what a
|
|
488
|
+
* product type COULD carry, {@link MaterialGraphClient.schema} for the definition.
|
|
523
489
|
*
|
|
524
490
|
* Discover the live specification vocabulary (GET
|
|
525
491
|
* `/api/v1/catalog/specifications/facets`) — the facetable spec attributes that
|
|
@@ -528,58 +494,19 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
528
494
|
* exists: LEED, GREENGUARD, ASTM E84 Class A, FloorScore, EPDs all surface here
|
|
529
495
|
* with counts. Includes the virtual `certification_programme` facet (its values
|
|
530
496
|
* are programme names). Learn an `attribute:value` here, then reuse it as a
|
|
531
|
-
* {@link
|
|
497
|
+
* {@link MaterialGraphClient.listVariants} filter leaf or a {@link search}
|
|
498
|
+
* `filters` entry. NOTE: continuous
|
|
532
499
|
* numeric specs (e.g. `slip_resistance_dcof`, a 0–1 DCOF range) are NOT
|
|
533
500
|
* token-facetable and do not appear here — read those per-attribute.
|
|
534
501
|
*/
|
|
535
502
|
async specificationFacets(signal) {
|
|
536
503
|
return await this.#get("list_spec_facets", undefined, signal);
|
|
537
504
|
}
|
|
538
|
-
/**
|
|
539
|
-
* List the live value distribution for ONE canonical attribute (GET
|
|
540
|
-
* `/api/v1/catalog/attributes/{attributeId}/values`), ordered by cross-brand
|
|
541
|
-
* reach with variant + brand counts. The drill-in companion to
|
|
542
|
-
* {@link specificationFacets} — e.g. every certification programme on record,
|
|
543
|
-
* or every fire classification and how many variants carry each.
|
|
544
|
-
*/
|
|
545
|
-
async attributeValues(attributeId, signal) {
|
|
546
|
-
return await this.#get("list_attribute_values", undefined, signal, {
|
|
547
|
-
attributeId,
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
/**
|
|
551
|
-
* THE QUESTION: "what specs COULD a material of this type carry?" — the
|
|
552
|
-
* template, not the data. Use it to know which absences are meaningful; instead
|
|
553
|
-
* use {@link MaterialGraphClient.specificationFacets} for what the corpus holds
|
|
554
|
-
* and {@link MaterialGraphClient.materialBlocks} for one material.
|
|
555
|
-
*
|
|
556
|
-
* @param key A canonical taxonomy key from {@link MaterialGraphClient.taxonomy}.
|
|
557
|
-
*/
|
|
558
|
-
async applicableSpecs(key, signal) {
|
|
559
|
-
return await this.#get("get_applicable_specs", undefined, signal, { key });
|
|
560
|
-
}
|
|
561
|
-
/**
|
|
562
|
-
* THE QUESTION: "what does 'DCOF' MEAN?" — one term explained, for a human.
|
|
563
|
-
* Explanation, not retrieval: instead use
|
|
564
|
-
* {@link MaterialGraphClient.searchVocabulary} to FIND a token and
|
|
565
|
-
* {@link MaterialGraphClient.attributeValues} for the values it takes.
|
|
566
|
-
*/
|
|
567
|
-
async glossary(term, signal) {
|
|
568
|
-
return await this.#get("explain_material_term", undefined, signal, { term });
|
|
569
|
-
}
|
|
570
505
|
// ── Brands ───────────────────────────────────────────────────────────────────
|
|
571
506
|
/** "Whose catalogs are in here, and how big is each?" — {@link listBrands}. */
|
|
572
507
|
async brands(options = {}, signal) {
|
|
573
508
|
return await listBrands(this.#transport, options, signal);
|
|
574
509
|
}
|
|
575
|
-
/** "Who is this brand, and what do they claim?" — {@link brandProfile}. */
|
|
576
|
-
async brand(brandSlug, options, signal) {
|
|
577
|
-
return await brandProfile(this.#transport, brandSlug, options, signal);
|
|
578
|
-
}
|
|
579
|
-
/** "This maker's whole range, the way a fan deck reads" — {@link brandColourDeck}. */
|
|
580
|
-
async brandColourDeck(brandSlug, signal) {
|
|
581
|
-
return await brandColourDeck(this.#transport, brandSlug, signal);
|
|
582
|
-
}
|
|
583
510
|
// ── Runs (the hosted agentic surface) ────────────────────────────────────────
|
|
584
511
|
/**
|
|
585
512
|
* Upload one atomic source-material batch (POST `/api/v1/source-material`,
|
|
@@ -601,22 +528,11 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
601
528
|
async sourceMaterialBatch(batchId, signal) {
|
|
602
529
|
return await getSourceMaterialBatchCall(this.#transport, batchId, signal);
|
|
603
530
|
}
|
|
604
|
-
/**
|
|
605
|
-
* THE QUESTION: "what can the hosted agentic surface be asked to DO?" Start
|
|
606
|
-
* here before {@link MaterialGraphClient.createRun}: each capability carries its
|
|
607
|
-
* live input/output schemas, scopes and side-effect class, so nothing about a
|
|
608
|
-
* Run need be hardcoded.
|
|
609
|
-
*/
|
|
610
|
-
async capabilities(signal) {
|
|
611
|
-
return await capabilitiesRun(this.#transport, signal);
|
|
612
|
-
}
|
|
613
531
|
/**
|
|
614
532
|
* THE QUESTION: "do a whole job for me" — the ASYNC lens. Everything else here
|
|
615
533
|
* answers in one round trip; a Run may suspend for human input and finish
|
|
616
534
|
* minutes later. Follow with {@link MaterialGraphClient.streamRunEvents} (live)
|
|
617
|
-
* or {@link MaterialGraphClient.run} (poll)
|
|
618
|
-
* {@link MaterialGraphClient.resumeRun}; pick the capability from
|
|
619
|
-
* {@link MaterialGraphClient.capabilities}. Pass `idempotencyKey` so a retry
|
|
535
|
+
* or {@link MaterialGraphClient.run} (poll). Pass `idempotencyKey` so a retry
|
|
620
536
|
* replays rather than duplicates.
|
|
621
537
|
*/
|
|
622
538
|
async createRun(body, signal) {
|
|
@@ -625,54 +541,15 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
625
541
|
/**
|
|
626
542
|
* THE QUESTION: "where has this Run got to?" — the caller-owned, redacted
|
|
627
543
|
* ledger record. Polling; prefer {@link MaterialGraphClient.streamRunEvents}
|
|
628
|
-
* when a human is waiting.
|
|
629
|
-
* asking you something (see {@link MaterialGraphClient.resumeRun}).
|
|
544
|
+
* when a human is waiting.
|
|
630
545
|
*/
|
|
631
546
|
async run(runId, signal) {
|
|
632
547
|
return await getRun(this.#transport, runId, signal);
|
|
633
548
|
}
|
|
634
|
-
/**
|
|
635
|
-
* THE QUESTION: "which Runs are mine?" — your own Runs, newest first, paged
|
|
636
|
-
* (GET `/api/v1/runs`). This is how a client that lost a Run id finds its work
|
|
637
|
-
* again: `{ status: "suspended" }` returns the Runs still waiting on an
|
|
638
|
-
* answer, whose ids you can then feed to {@link MaterialGraphClient.resumeRun}.
|
|
639
|
-
*
|
|
640
|
-
* Only your own Runs are ever returned. Page by following `nextCursor` until
|
|
641
|
-
* it is ABSENT — a full page is not the signal to stop.
|
|
642
|
-
*/
|
|
643
|
-
async listRuns(options) {
|
|
644
|
-
return await listRuns(this.#transport, options);
|
|
645
|
-
}
|
|
646
549
|
/** Follow a verdict cell's citation handle to its canonical claim source. */
|
|
647
550
|
async citation(citationId, signal) {
|
|
648
551
|
return await this.#get("get_verdict_citation", undefined, signal, { citationId });
|
|
649
552
|
}
|
|
650
|
-
/**
|
|
651
|
-
* Resume a suspended Run (POST `/api/v1/runs/{runId}/resume`).
|
|
652
|
-
*
|
|
653
|
-
* The third argument accepts either an `AbortSignal` (the published form) or
|
|
654
|
-
* {@link ResumeRunOptions}, whose `stepId` guards against resuming a step the
|
|
655
|
-
* run has already moved past — the API answers 409 on a mismatch (MG-608).
|
|
656
|
-
*/
|
|
657
|
-
async resumeRun(runId, resumeData, signalOrOptions) {
|
|
658
|
-
return await resumeRun(this.#transport, runId, resumeData, signalOrOptions);
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* THE QUESTION: "stop this Run." Requests DURABLE cancellation — the Run
|
|
662
|
-
* acknowledges and unwinds; the call returning does not mean it has stopped.
|
|
663
|
-
* Poll {@link MaterialGraphClient.run} for the terminal state.
|
|
664
|
-
*/
|
|
665
|
-
async cancelRun(runId, signal) {
|
|
666
|
-
return await cancelRun(this.#transport, runId, signal);
|
|
667
|
-
}
|
|
668
|
-
/** Option B from a composed Run, along one axis. See {@link varySchemeCall}. */
|
|
669
|
-
async varyScheme(body, signal) {
|
|
670
|
-
return await varySchemeCall(this.#transport, body, signal);
|
|
671
|
-
}
|
|
672
|
-
/** Ranked candidate pools for up to 24 slots. See {@link slotPoolsCall}. */
|
|
673
|
-
async slotPools(body, signal) {
|
|
674
|
-
return await slotPoolsCall(this.#transport, body, signal);
|
|
675
|
-
}
|
|
676
553
|
/**
|
|
677
554
|
* Stream ordered Run events as an async iterator (GET
|
|
678
555
|
* `/api/v1/runs/{runId}/events`, SSE). Iterates until the stream ends or the
|
package/codes-types.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* model code as adopted, never a legal determination.
|
|
15
15
|
*/
|
|
16
16
|
import type { VariantId } from "./identifiers.js";
|
|
17
|
+
import type { FilterGroups } from "./list-variants-types.js";
|
|
17
18
|
import type { OpenEnum } from "./types.js";
|
|
18
19
|
/**
|
|
19
20
|
* A code context: the space the registry is queried for.
|
|
@@ -292,6 +293,14 @@ export type CodeRequirementListing = {
|
|
|
292
293
|
/** One readable line, e.g. `"Class A or better · ASTM E84 / UL 723"`. */
|
|
293
294
|
label: string;
|
|
294
295
|
};
|
|
296
|
+
/** Why one requirement the context imposes could not become a `list_variants` filter predicate. */
|
|
297
|
+
export type CodeSpecFilterUnexpressible = {
|
|
298
|
+
requirementId: string;
|
|
299
|
+
dimension: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
|
|
300
|
+
reason: OpenEnum<"no_matchable_tokens" | "unsupported_scale" | "no_floor">;
|
|
301
|
+
/** One readable line naming why, e.g. `"Scale ... has no ordinal definition..."`. */
|
|
302
|
+
detail: string;
|
|
303
|
+
};
|
|
295
304
|
/**
|
|
296
305
|
* The `GET /api/v1/codes/requirements` envelope. An empty `requirements` array
|
|
297
306
|
* means the registry holds nothing for this context — it is not a pass.
|
|
@@ -300,5 +309,17 @@ export type CodeRequirementsResponse = {
|
|
|
300
309
|
jurisdiction: CodeJurisdictionSummary;
|
|
301
310
|
context: ResolvedCodeContext;
|
|
302
311
|
requirements: CodeRequirementListing[];
|
|
312
|
+
/**
|
|
313
|
+
* The same requirements as a ready-to-use `list_variants` `filters` group
|
|
314
|
+
* (MG-2222, `{terms, numbers, presence}`) — pass it straight through as
|
|
315
|
+
* `listVariants({ filters })`.
|
|
316
|
+
* `null` when nothing here is filterable; read `specFilterUnexpressible`
|
|
317
|
+
* for why, not an empty `requirements` list.
|
|
318
|
+
*/
|
|
319
|
+
specFilter: FilterGroups | null;
|
|
320
|
+
/** The dimensions `specFilter` actually narrows on — a subset of `requirements`' dimensions. */
|
|
321
|
+
specFilterApplied: OpenEnum<"fire" | "floor" | "slip" | "decorative">[];
|
|
322
|
+
/** Requirements that could not be turned into a filter predicate, and why. */
|
|
323
|
+
specFilterUnexpressible: CodeSpecFilterUnexpressible[];
|
|
303
324
|
caveat: string;
|
|
304
325
|
};
|