@instruments/materialgraph 0.28.0 → 0.30.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/brands-client.d.ts +1 -1
- package/by-colour-types.d.ts +4 -4
- package/catalogue-client.d.ts +3 -3
- package/catalogue-client.js +3 -3
- package/catalogue-types.d.ts +1 -1
- package/client-vocabulary.d.ts +3 -5
- package/client-vocabulary.js +9 -6
- package/client.d.ts +73 -174
- package/client.js +85 -211
- package/colour-region-types.d.ts +18 -0
- package/colours-client.d.ts +10 -83
- package/colours-client.js +1 -85
- package/find-variants-by-colour-types.d.ts +197 -0
- package/find-variants-by-vector-types.d.ts +67 -0
- package/find-variants-by-vector-types.js +5 -0
- package/get-variant-types.d.ts +50 -0
- package/get-variant-types.js +10 -0
- package/identifiers.d.ts +1 -1
- package/identifiers.js +1 -1
- package/image-reference-types.d.ts +18 -0
- package/index.d.ts +8 -7
- package/index.js +1 -1
- package/list-enum-values-types.d.ts +58 -0
- package/list-variants-types.d.ts +6 -2
- package/material-asset-types.d.ts +4 -3
- package/material-types.d.ts +11 -150
- package/operations.d.ts +1 -1
- package/operations.js +26 -88
- package/package.json +1 -1
- package/palette-search-types.d.ts +3 -49
- package/resolve-colour-types.d.ts +5 -5
- package/resolve-variant-types.d.ts +75 -0
- package/{identify-types.js → resolve-variant-types.js} +2 -2
- package/response-schemas.generated.d.ts +1 -1
- package/response-schemas.generated.js +45 -84
- package/run-types.d.ts +23 -216
- package/runs.d.ts +10 -58
- package/runs.js +19 -106
- package/schema-resource-types.d.ts +2 -139
- package/schema-resource-types.js +0 -8
- package/search-card-types.d.ts +1 -1
- package/search-index-plan-types.d.ts +4 -1
- package/search-request-types.d.ts +10 -2
- package/spec-types.d.ts +4 -4
- package/spec-types.js +2 -2
- package/sse.d.ts +2 -2
- package/sse.js +2 -2
- package/types.d.ts +4 -38
- package/webmcp.generated.js +3 -3
- package/workflows-client.d.ts +54 -0
- package/workflows-client.js +41 -0
- package/client-source-material-comments.d.ts +0 -51
- package/client-source-material-comments.js +0 -6
- package/codes-client.d.ts +0 -118
- package/codes-client.js +0 -99
- package/codes-types.d.ts +0 -325
- package/colour-aggregate-types.d.ts +0 -163
- package/coordinate-client.d.ts +0 -23
- package/coordinate-client.js +0 -13
- package/demand-batch-types.d.ts +0 -118
- package/demand-types.d.ts +0 -154
- package/identify-types.d.ts +0 -82
- package/material-composition-client.d.ts +0 -21
- package/material-composition-client.js +0 -13
- package/material-composition-types.d.ts +0 -403
- package/material-composition-types.js +0 -1
- package/source-material-validation.d.ts +0 -7
- package/source-material-validation.js +0 -214
- package/spec-facet-types.d.ts +0 -48
- package/spec-facet-types.js +0 -7
- package/substitute-types.d.ts +0 -102
- package/substitute-types.js +0 -1
- package/suggest-types.d.ts +0 -67
- package/suggest-types.js +0 -28
- package/taxonomy-coverage-types.d.ts +0 -34
- package/taxonomy-coverage-types.js +0 -1
- /package/{codes-types.js → colour-region-types.js} +0 -0
- /package/{colour-aggregate-types.js → find-variants-by-colour-types.js} +0 -0
- /package/{demand-batch-types.js → image-reference-types.js} +0 -0
- /package/{demand-types.js → list-enum-values-types.js} +0 -0
package/client.js
CHANGED
|
@@ -3,20 +3,17 @@ import { listBrands } from "./brands-client.js";
|
|
|
3
3
|
import { CatalogueClient } from "./catalogue-client.js";
|
|
4
4
|
import { CategoriesClient } from "./categories-client.js";
|
|
5
5
|
import { DEFAULT_BASE_URL } from "./client-config.js";
|
|
6
|
-
import {
|
|
7
|
-
import { listAttributes as listAttributesCall, resolveColour as resolveColourCall, suggest as suggestCall, suggestColour as suggestColourCall, } from "./client-vocabulary.js";
|
|
8
|
-
import { CodesClient } from "./codes-client.js";
|
|
6
|
+
import { listAttributes as listAttributesCall, listEnumValues as listEnumValuesCall, resolveColour as resolveColourCall, } from "./client-vocabulary.js";
|
|
9
7
|
import { ColoursClient } from "./colours-client.js";
|
|
10
|
-
import { coordinate as coordinateCall } from "./coordinate-client.js";
|
|
11
8
|
import { MaterialGraphError } from "./errors.js";
|
|
12
9
|
import { HttpTransport } from "./http-transport.js";
|
|
13
10
|
import { matchStream as matchStreamCall } from "./match-stream.js";
|
|
14
|
-
import { compositionReport as compositionReportCall } from "./material-composition-client.js";
|
|
15
11
|
import { listVariantAssets as listVariantAssetsCall, materialBlocks as materialBlocksCall, materialDocuments as materialDocumentsCall, } from "./material-resources-client.js";
|
|
16
12
|
// Aliased to `operation` — the local name every call site here already used.
|
|
17
13
|
import { resolveOperation as operation } from "./operations.js";
|
|
18
|
-
import {
|
|
14
|
+
import { uploadSourceMaterial } from "./runs.js";
|
|
19
15
|
import { SCHEMA_RESOURCE_OPERATION_IDS } from "./schema-resource-types.js";
|
|
16
|
+
import { WorkflowsClient } from "./workflows-client.js";
|
|
20
17
|
// Re-exported whole so `index.ts` and every existing importer keep their path.
|
|
21
18
|
export * from "./client-config.js";
|
|
22
19
|
/**
|
|
@@ -28,16 +25,15 @@ export * from "./client-config.js";
|
|
|
28
25
|
* | The question | The lens |
|
|
29
26
|
* | --- | --- |
|
|
30
27
|
* | "find me something like this / matching these words" | {@link MaterialGraphClient.search} |
|
|
28
|
+
* | "tell me about THIS variant" | {@link MaterialGraphClient.getVariant} |
|
|
31
29
|
* | "what's IN this photo?" | {@link MaterialGraphClient.match} / {@link MaterialGraphClient.matchStream} |
|
|
32
30
|
* | "which materials, from anyone, meet this hard spec?" | {@link MaterialGraphClient.listVariants} |
|
|
33
|
-
* | "what's near this colour / this palette
|
|
34
|
-
* | "what goes with THIS material?" | {@link MaterialGraphClient.pairings}, {@link MaterialGraphClient.listVariants} (same product, other colourways)
|
|
31
|
+
* | "what's near this colour / this palette?" | {@link MaterialGraphClient.byColour}, `colours.palette()` |
|
|
32
|
+
* | "what goes with THIS material?" | {@link MaterialGraphClient.pairings}, {@link MaterialGraphClient.listVariants} (same product, other colourways) |
|
|
35
33
|
* | "what does the manufacturer say is AVAILABLE on this?" | {@link MaterialGraphClient.associatedFinishes}, {@link MaterialGraphClient.associatedProducts} |
|
|
36
|
-
* | "
|
|
37
|
-
* | "is this allowed in an A-2 corridor?" | `codes.*` |
|
|
38
|
-
* | "what can I even ask for?" | {@link MaterialGraphClient.specificationFacets}, `categories.list()` |
|
|
34
|
+
* | "what can I even ask for?" | {@link MaterialGraphClient.listVariantFacets}, `categories.list()` |
|
|
39
35
|
* | "what is this word called here?" | {@link MaterialGraphClient.listAttributes} |
|
|
40
|
-
* | "do the whole job for me, async" |
|
|
36
|
+
* | "do the whole job for me, async" | `workflows.start()` + `mg:compose` |
|
|
41
37
|
*
|
|
42
38
|
* Three structural rules that fall out of that table:
|
|
43
39
|
* - **Colour is a separate lens.** {@link MaterialGraphClient.search} matches
|
|
@@ -71,35 +67,25 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
71
67
|
*/
|
|
72
68
|
categories;
|
|
73
69
|
/**
|
|
74
|
-
* THE QUESTION: "
|
|
75
|
-
*
|
|
76
|
-
* discovers the vocabulary, `codes.requirements()` states a space,
|
|
77
|
-
* `codes.advisory()` badges candidates.
|
|
78
|
-
*
|
|
79
|
-
* Instead use {@link MaterialGraphClient.listVariants} to find materials by a
|
|
80
|
-
* classification you already know you need; codes is what tells you WHICH one
|
|
81
|
-
* the space demands. ADVISORY ONLY: model code as adopted, never a
|
|
82
|
-
* determination for an authority having jurisdiction.
|
|
83
|
-
*/
|
|
84
|
-
codes;
|
|
85
|
-
/**
|
|
86
|
-
* THE QUESTION: "what colours exist here?" (`profiles`, `stats`) and "what
|
|
87
|
-
* matches this whole SET of colours?" (`palette`, `gradient`).
|
|
70
|
+
* THE QUESTION: "what colours exist here?" and "what matches this whole SET
|
|
71
|
+
* of colours?" (`palette`).
|
|
88
72
|
*
|
|
89
73
|
* Instead use {@link MaterialGraphClient.byColour} for ONE colour and
|
|
90
74
|
* {@link MaterialGraphClient.listVariants} to page through a colour's family.
|
|
91
75
|
* Colour is usually derived from product imagery, which is visual likeness,
|
|
92
|
-
* not a colour specification. A
|
|
76
|
+
* not a colour specification. A brand's declared colour, such as a paint's
|
|
93
77
|
* published hex, wins where MaterialGraph holds one, and each value's
|
|
94
78
|
* source basis says which.
|
|
95
79
|
*/
|
|
96
80
|
colours;
|
|
97
81
|
/**
|
|
98
|
-
* THE QUESTION: "
|
|
99
|
-
*
|
|
100
|
-
*
|
|
82
|
+
* THE QUESTION: "do a whole job for me" — the ASYNC lens. `start()` begins a
|
|
83
|
+
* Run of one registered workflow, `run()` polls it, `streamRunEvents()` and
|
|
84
|
+
* `drainRunEvents()` follow it live. Everything else on this client answers
|
|
85
|
+
* in one round trip; a Run may suspend for human input and finish minutes
|
|
86
|
+
* later. Replaces the flat `createRun()` / `run()` family, with no shim.
|
|
101
87
|
*/
|
|
102
|
-
|
|
88
|
+
workflows;
|
|
103
89
|
#transport;
|
|
104
90
|
constructor(options) {
|
|
105
91
|
if (!options.apiKey) {
|
|
@@ -116,15 +102,11 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
116
102
|
this.categories = new CategoriesClient({
|
|
117
103
|
get: async (operationId, query, signal, pathParams) => await this.#get(operationId, query, signal, pathParams),
|
|
118
104
|
});
|
|
119
|
-
this.codes = new CodesClient({
|
|
120
|
-
get: async (operationId, query, signal) => await this.#get(operationId, query, signal),
|
|
121
|
-
post: async (operationId, body, signal) => await this.#post(operationId, body, signal),
|
|
122
|
-
});
|
|
123
105
|
this.colours = new ColoursClient({
|
|
124
106
|
get: async (operationId, query, signal) => await this.#get(operationId, query, signal),
|
|
125
107
|
post: async (operationId, body, signal) => await this.#post(operationId, body, signal),
|
|
126
108
|
});
|
|
127
|
-
this.
|
|
109
|
+
this.workflows = new WorkflowsClient({ transport });
|
|
128
110
|
}
|
|
129
111
|
get lastRateLimit() {
|
|
130
112
|
return this.#transport.lastRateLimit;
|
|
@@ -200,7 +182,7 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
200
182
|
* palette), ranked by OKLab closeness to colour extracted from product imagery.
|
|
201
183
|
*
|
|
202
184
|
* Instead use `colours.palette()` for N colours at stated proportions (a
|
|
203
|
-
* different ranking, not a wrapper),
|
|
185
|
+
* different ranking, not a wrapper),
|
|
204
186
|
* {@link MaterialGraphClient.resolveColour} first if you hold a NAME,
|
|
205
187
|
* {@link MaterialGraphClient.listVariants} to page through a category or a brand, and
|
|
206
188
|
* {@link MaterialGraphClient.search} when colour is one signal among many.
|
|
@@ -227,36 +209,33 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
227
209
|
}
|
|
228
210
|
// ── Materials ──────────────────────────────────────────────────────────────
|
|
229
211
|
/**
|
|
230
|
-
* THE QUESTION: "tell me
|
|
231
|
-
* `/api/v1/
|
|
212
|
+
* THE QUESTION: "tell me about THIS variant" (GET
|
|
213
|
+
* `/api/v1/variants/{variantId}`, `get_variant`).
|
|
232
214
|
*
|
|
233
215
|
* The hinge of the API: every relationship lens
|
|
234
|
-
* ({@link MaterialGraphClient.listVariants}, {@link MaterialGraphClient.pairings}
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
216
|
+
* ({@link MaterialGraphClient.listVariants}, {@link MaterialGraphClient.pairings})
|
|
217
|
+
* hangs off a `variantId`. The answer is compact on purpose: name, code,
|
|
218
|
+
* product, brand, category, image and page. Name `attributes` and `blocks`
|
|
219
|
+
* to add their values under `specifications`; a null value is unknown, never
|
|
220
|
+
* false or zero. Instead use {@link MaterialGraphClient.listVariants} for many
|
|
221
|
+
* ids, or for the other colourways of the same product (ask `listVariants`
|
|
222
|
+
* with a `product in` filter),
|
|
223
|
+
* {@link MaterialGraphClient.materialBlocks} to tell "no value" from "not
|
|
224
|
+
* checked", {@link MaterialGraphClient.search} when you have no id yet.
|
|
242
225
|
*
|
|
243
226
|
* @param variantId Opaque — pass back exactly what you were given.
|
|
244
227
|
*/
|
|
245
|
-
async
|
|
246
|
-
return await this.#get("
|
|
228
|
+
async getVariant(variantId, options, signal) {
|
|
229
|
+
return await this.#get("get_variant", {
|
|
230
|
+
attributes: options?.attributes?.length
|
|
231
|
+
? options.attributes.join(",")
|
|
232
|
+
: undefined,
|
|
233
|
+
blocks: options?.blocks?.length ? options.blocks.join(",") : undefined,
|
|
234
|
+
}, signal, { variantId });
|
|
247
235
|
}
|
|
248
236
|
async materialBlocks(variantId, signal) {
|
|
249
237
|
return await materialBlocksCall(this.#transport, variantId, signal);
|
|
250
238
|
}
|
|
251
|
-
/**
|
|
252
|
-
* THE QUESTION: "which product types can composition be answered for yet?"
|
|
253
|
-
*
|
|
254
|
-
* Coverage per product type, in Live SKUs as well as tuples. Read the SKU
|
|
255
|
-
* figure: one tuple can carry a hundred thousand SKUs or one.
|
|
256
|
-
*/
|
|
257
|
-
async compositionReport(signal) {
|
|
258
|
-
return await compositionReportCall(this.#transport, signal);
|
|
259
|
-
}
|
|
260
239
|
async listVariantAssets(variantId, options, signal) {
|
|
261
240
|
return await listVariantAssetsCall(this.#transport, variantId, options, signal);
|
|
262
241
|
}
|
|
@@ -266,7 +245,7 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
266
245
|
/**
|
|
267
246
|
* THE QUESTION: "what does the schema DEFINE?" — the governed static registry.
|
|
268
247
|
*
|
|
269
|
-
* Definitions, not data: use {@link MaterialGraphClient.
|
|
248
|
+
* Definitions, not data: use {@link MaterialGraphClient.listVariantFacets} for
|
|
270
249
|
* what the corpus actually holds.
|
|
271
250
|
*/
|
|
272
251
|
async schema(resource, signal) {
|
|
@@ -276,8 +255,7 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
276
255
|
* THE QUESTION: "what goes WITH this material?" — the companionship rail, hung
|
|
277
256
|
* off one resolved material (GET `/api/v1/materials/{variantId}/pairings`).
|
|
278
257
|
*
|
|
279
|
-
* Instead use {@link MaterialGraphClient.
|
|
280
|
-
* picks, {@link MaterialGraphClient.listVariants} for the same product in
|
|
258
|
+
* Instead use {@link MaterialGraphClient.listVariants} for the same product in
|
|
281
259
|
* another colour, and `search({ similarTo })` for a LIKE-THIS substitute —
|
|
282
260
|
* similar and complementary are opposite questions.
|
|
283
261
|
*
|
|
@@ -310,10 +288,11 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
310
288
|
* of a product (`{terms: [{field: "product", op: "in", values:
|
|
311
289
|
* [productId]}, {field: "variant", op: "not_in", values: [variantId]}]}` —
|
|
312
290
|
* `filters` is kind-grouped, `{terms, numbers, presence}`, every condition
|
|
313
|
-
* across all three ANDed), or a category/
|
|
291
|
+
* across all three ANDed), or a category/brand listing. An explicit
|
|
314
292
|
* `variant in` id that does not resolve to an eligible variant is
|
|
315
|
-
* reported in `unresolvedVariantIds`,
|
|
316
|
-
*
|
|
293
|
+
* reported in `unresolvedVariantIds`, or in `excludedByCorpusScope` when
|
|
294
|
+
* it names a real variant outside the materials catalogue, never silently
|
|
295
|
+
* dropped from the page.
|
|
317
296
|
*
|
|
318
297
|
* `filters`/`optionalFilters`/`orderBy` are sent as URL-encoded JSON;
|
|
319
298
|
* `attributes`/`include` as comma-separated lists — the server validates,
|
|
@@ -349,18 +328,35 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
349
328
|
async listVariantFacets(input, signal) {
|
|
350
329
|
return await this.#post("list_variant_facets", input, signal);
|
|
351
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* THE QUESTION: "which variants hold, resemble, or can be ranked by, this
|
|
333
|
+
* colour?" (POST `/api/v1/variants/by-colour`, MG-2217/MG-2230/MG-2247).
|
|
334
|
+
* One required `mode`: `contains` (a swatch for every required `colours[]`
|
|
335
|
+
* condition), `palette` (whole-palette closeness to `colours[]`, an
|
|
336
|
+
* image's extracted palette, or `targetVariantId`'s own palette) or
|
|
337
|
+
* `rerank` (reorder a held `variantIds` set by colour closeness). Use
|
|
338
|
+
* {@link MaterialGraphClient.byColour} instead for ONE colour against the
|
|
339
|
+
* search index rather than the canonical variant graph.
|
|
340
|
+
*/
|
|
341
|
+
async findVariantsByColour(input, signal) {
|
|
342
|
+
return await this.#post("find_variants_by_colour", input, signal);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* THE QUESTION: "what looks or feels like this?" (POST
|
|
346
|
+
* `/api/v1/variants/by-vector`, MG-2231). Eligible variants ranked by
|
|
347
|
+
* semantic or visual similarity to a text description (mood words count),
|
|
348
|
+
* an image reference, or both fused into one embedding. `filters` is the
|
|
349
|
+
* same tree {@link MaterialGraphClient.listVariants} takes. `query.mode`
|
|
350
|
+
* values other than `"text"` embed through Voyage and spend the shared
|
|
351
|
+
* per-key `embed` rate budget.
|
|
352
|
+
*/
|
|
353
|
+
async findVariantsByVector(input, signal) {
|
|
354
|
+
return await this.#post("find_variants_by_vector", input, signal);
|
|
355
|
+
}
|
|
352
356
|
/** Resolve a colour name to its hex, and the named paint's catalogue variant. */
|
|
353
357
|
async resolveColour(name, signal, options) {
|
|
354
358
|
return await resolveColourCall(this.#transport, name, signal, options);
|
|
355
359
|
}
|
|
356
|
-
/** Ranked colour-name typeahead (GET `/api/v1/search/colours/suggest`). */
|
|
357
|
-
async suggestColour(q, options, signal) {
|
|
358
|
-
return await suggestColourCall(this.#transport, q, options, signal);
|
|
359
|
-
}
|
|
360
|
-
/** Entity typeahead. Navigate using the selected `refId`; use `search()` for materials. */
|
|
361
|
-
async suggest(q, options, signal) {
|
|
362
|
-
return await suggestCall(this.#transport, q, options, signal);
|
|
363
|
-
}
|
|
364
360
|
// ── Catalog ──────────────────────────────────────────────────────────────────
|
|
365
361
|
/**
|
|
366
362
|
* THE QUESTION: "which attribute means this phrase?" or "what could a
|
|
@@ -374,24 +370,28 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
374
370
|
async listAttributes(options, signal) {
|
|
375
371
|
return await listAttributesCall(this.#transport, options, signal);
|
|
376
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* THE QUESTION: "which value means this phrase?" or "what may this attribute
|
|
375
|
+
* be set to?" (GET `/api/v1/catalog/enum-values`). One page of a value
|
|
376
|
+
* dictionary's allowed values, browsed by `dictionaryId` (from a
|
|
377
|
+
* {@link MaterialGraphClient.listAttributes} item) or found by name, alias,
|
|
378
|
+
* misspelling or meaning across every dictionary. Pass an item's `valueId`
|
|
379
|
+
* with one of its `attributeIds` to {@link MaterialGraphClient.listVariants};
|
|
380
|
+
* an item with no `attributeIds` cannot be filtered on. Follow
|
|
381
|
+
* `pageInfo.endCursor` for the next page.
|
|
382
|
+
*/
|
|
383
|
+
async listEnumValues(options, signal) {
|
|
384
|
+
return await listEnumValuesCall(this.#transport, options, signal);
|
|
385
|
+
}
|
|
377
386
|
// Hard-specification search (formerly `bySpec()`, GET
|
|
378
387
|
// `/api/v1/search/specifications`) moved to {@link MaterialGraphClient.listVariants}
|
|
379
388
|
// (MG-2222): each `attribute:value` criterion is one `filters` condition —
|
|
380
389
|
// `{field: attributeId, op: "in", values: [...]}` in `filters.terms` for a
|
|
381
390
|
// term, `{field: attributeId, op: "gte"/"lte"/etc, value}` in
|
|
382
391
|
// `filters.numbers` for a number — and every condition across the
|
|
383
|
-
// kind-grouped `filters` object must match.
|
|
384
|
-
// the real question is what a space demands, and {@link specificationFacets}
|
|
392
|
+
// kind-grouped `filters` object must match. See {@link listVariantFacets}
|
|
385
393
|
// first if you do not know the canonical tokens.
|
|
386
394
|
// ── Coverage + meta ──────────────────────────────────────────────────────────
|
|
387
|
-
/**
|
|
388
|
-
* Exact current primary-type status counts by retained source category path.
|
|
389
|
-
* `comparisonThreshold` is a reporting cutoff only: it never promotes a
|
|
390
|
-
* proposal into an accepted category.
|
|
391
|
-
*/
|
|
392
|
-
async productTypeCoverage(options, signal) {
|
|
393
|
-
return await this.#get("get_product_type_status_coverage", options, signal);
|
|
394
|
-
}
|
|
395
395
|
/**
|
|
396
396
|
* THE QUESTION: "what is this key allowed to do?" Check `scopes` before
|
|
397
397
|
* offering a capability: the write surfaces need `mg:ingest` / `mg:compose`,
|
|
@@ -400,66 +400,6 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
400
400
|
async whoami(signal) {
|
|
401
401
|
return await this.#get("whoami", undefined, signal);
|
|
402
402
|
}
|
|
403
|
-
// ── Pairings / demand ────────────────────────────────────────────────────────
|
|
404
|
-
/** What completes a scheme already part-chosen. See {@link coordinateCall}. */
|
|
405
|
-
async coordinate(input, signal) {
|
|
406
|
-
return await coordinateCall(this.#transport, input, signal);
|
|
407
|
-
}
|
|
408
|
-
/**
|
|
409
|
-
* THE QUESTION: "how much is this material actually specified, and what gets
|
|
410
|
-
* specified alongside it?" — real order behaviour, trailing 36 months.
|
|
411
|
-
*
|
|
412
|
-
* Instead use {@link MaterialGraphClient.demandBatch} for many ids (this one
|
|
413
|
-
* also resolves free text, which the batch deliberately does not) and
|
|
414
|
-
* {@link MaterialGraphClient.pairings} for a presentable rail. A null `demand`
|
|
415
|
-
* means UNREAD, never zero. Provide EXACTLY ONE of `variantId` or `material`.
|
|
416
|
-
*/
|
|
417
|
-
async demand(params, signal) {
|
|
418
|
-
return await this.#get("get_material_demand", { material: params.material, variantId: params.variantId }, signal);
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* THE QUESTION: "for each of these picks I already hold, how much momentum does
|
|
422
|
-
* it have?" Enrichment, never discovery or ranking. Instead use
|
|
423
|
-
* {@link MaterialGraphClient.demand} for one material named in free text and
|
|
424
|
-
* {@link MaterialGraphClient.search} to find candidates at all. Only
|
|
425
|
-
* `state: "available"` carries numbers; the other three mean "no number", never
|
|
426
|
-
* "no demand".
|
|
427
|
-
*
|
|
428
|
-
* Enrich a bounded set of ALREADY-CHOSEN candidate ids with their real
|
|
429
|
-
* specification demand (POST `/api/v1/demand/batch`, 1–48 ids). The bounded
|
|
430
|
-
* batch complement to {@link demand}: no fuzzy resolution, ranking, or
|
|
431
|
-
* reordering — each id yields one discriminated `state` (`available` /
|
|
432
|
-
* `insufficient_data` / `no_signal` / `unknown_variant`) in input order and
|
|
433
|
-
* cardinality. This is the Desk's "show demand momentum for every pick in this
|
|
434
|
-
* scheme" read; an `mg:admin` key additionally sees per-result `evidence`.
|
|
435
|
-
*
|
|
436
|
-
* @example
|
|
437
|
-
* // Rank a shortlist by real specification momentum before locking the scheme.
|
|
438
|
-
* const { results } = await mg.demandBatch({
|
|
439
|
-
* variantIds: scheme.picks.map((p) => p.variantId),
|
|
440
|
-
* signal: "sampled",
|
|
441
|
-
* context: { projectType: "hospitality" },
|
|
442
|
-
* });
|
|
443
|
-
* const rising = results.filter(
|
|
444
|
-
* (r) => r.state === "available" && r.momentum.state === "rising",
|
|
445
|
-
* );
|
|
446
|
-
*/
|
|
447
|
-
async demandBatch(input, signal) {
|
|
448
|
-
return await this.#post("get_demand_batch", input, signal);
|
|
449
|
-
}
|
|
450
|
-
// ── Codes (advisory building-code compliance) ────────────────────────────────
|
|
451
|
-
/**
|
|
452
|
-
* THE QUESTION: "may I put these materials in an A-2 corridor in Texas?"
|
|
453
|
-
*
|
|
454
|
-
* Badge materials against the advisory building-code registry (POST
|
|
455
|
-
* `/api/v1/codes/advisory`). Identical to {@link CodesClient.advisory}, kept
|
|
456
|
-
* flat because it shipped before the `codes` namespace existed — prefer
|
|
457
|
-
* `mg.codes.advisory()` in new code, and start at `mg.codes.context()` to
|
|
458
|
-
* discover every accepted context value. `unverified` is not a pass.
|
|
459
|
-
*/
|
|
460
|
-
async codeAdvisory(input, signal) {
|
|
461
|
-
return await this.codes.advisory(input, signal);
|
|
462
|
-
}
|
|
463
403
|
// ── Colour helpers (extract) ─────────────────────────────────────────────────
|
|
464
404
|
/**
|
|
465
405
|
* THE QUESTION: "what colours are in this image?" — the palette only, no
|
|
@@ -471,89 +411,23 @@ export class MaterialGraphClient extends CatalogueClient {
|
|
|
471
411
|
async extractPalette(input, signal) {
|
|
472
412
|
return await this.#post("extract_image_palette", input, signal);
|
|
473
413
|
}
|
|
474
|
-
// ── Catalog (facets / glossary) ─────────────────────────────────────────────
|
|
475
|
-
/**
|
|
476
|
-
* THE QUESTION: "what can I even filter on, and what values does the corpus
|
|
477
|
-
* actually hold?" The start of every spec-driven workflow. Instead use
|
|
478
|
-
* {@link MaterialGraphClient.listAttributes} with `categoryIds` for what a
|
|
479
|
-
* product type COULD carry, {@link MaterialGraphClient.schema} for the definition.
|
|
480
|
-
*
|
|
481
|
-
* Discover the live specification vocabulary (GET
|
|
482
|
-
* `/api/v1/catalog/specifications/facets`) — the facetable spec attributes that
|
|
483
|
-
* have real product-graph data, each with its top canonical values and the
|
|
484
|
-
* variants/brands behind them. This is where a caller LEARNS that the data
|
|
485
|
-
* exists: LEED, GREENGUARD, ASTM E84 Class A, FloorScore, EPDs all surface here
|
|
486
|
-
* with counts. Includes the virtual `certification_programme` facet (its values
|
|
487
|
-
* are programme names). Learn an `attribute:value` here, then reuse it as a
|
|
488
|
-
* {@link MaterialGraphClient.listVariants} filter leaf or a {@link search}
|
|
489
|
-
* `filters` entry. NOTE: continuous
|
|
490
|
-
* numeric specs (e.g. `slip_resistance_dcof`, a 0–1 DCOF range) are NOT
|
|
491
|
-
* token-facetable and do not appear here — read those per-attribute.
|
|
492
|
-
*/
|
|
493
|
-
async specificationFacets(signal) {
|
|
494
|
-
return await this.#get("list_spec_facets", undefined, signal);
|
|
495
|
-
}
|
|
496
414
|
// ── Brands ───────────────────────────────────────────────────────────────────
|
|
497
415
|
/** "Whose catalogs are in here, and how big is each?" — {@link listBrands}. */
|
|
498
416
|
async brands(options = {}, signal) {
|
|
499
417
|
return await listBrands(this.#transport, options, signal);
|
|
500
418
|
}
|
|
501
|
-
// ──
|
|
419
|
+
// ── Source material ──────────────────────────────────────────────────────────
|
|
502
420
|
/**
|
|
503
421
|
* Upload one atomic source-material batch (POST `/api/v1/source-material`,
|
|
504
422
|
* multipart). Pass a heterogeneous pile via `{ files }` (any mix of PDF,
|
|
505
|
-
* text/markdown, and images — the server classifies each)
|
|
506
|
-
* `
|
|
423
|
+
* text/markdown, and images — the server classifies each). Requires the
|
|
424
|
+
* `mg:source` scope, which a `source`, `ingest` or `admin` key carries.
|
|
507
425
|
*/
|
|
508
426
|
async uploadSourceMaterial(input, signal) {
|
|
509
427
|
return await uploadSourceMaterial(this.#transport, input, signal);
|
|
510
428
|
}
|
|
511
|
-
/**
|
|
512
|
-
* Read back an uploaded batch (GET `/api/v1/source-material/{batchId}`): every
|
|
513
|
-
* file with a fetchable `url` and its analyses inlined. `analyses` is always
|
|
514
|
-
* an array — `[]` means nothing has been computed yet. Analyses are shared by
|
|
515
|
-
* content hash, so re-uploading an already-analysed image returns its analysis
|
|
516
|
-
* immediately. A batch you do not own raises the same 404 as one that does not
|
|
517
|
-
* exist. Requires the `mg:compose` scope.
|
|
518
|
-
*/
|
|
519
|
-
async sourceMaterialBatch(batchId, signal) {
|
|
520
|
-
return await getSourceMaterialBatchCall(this.#transport, batchId, signal);
|
|
521
|
-
}
|
|
522
|
-
/**
|
|
523
|
-
* THE QUESTION: "do a whole job for me" — the ASYNC lens. Everything else here
|
|
524
|
-
* answers in one round trip; a Run may suspend for human input and finish
|
|
525
|
-
* minutes later. Follow with {@link MaterialGraphClient.streamRunEvents} (live)
|
|
526
|
-
* or {@link MaterialGraphClient.run} (poll). Pass `idempotencyKey` so a retry
|
|
527
|
-
* replays rather than duplicates.
|
|
528
|
-
*/
|
|
529
|
-
async createRun(body, signal) {
|
|
530
|
-
return await createRun(this.#transport, body, signal);
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* THE QUESTION: "where has this Run got to?" — the caller-owned, redacted
|
|
534
|
-
* ledger record. Polling; prefer {@link MaterialGraphClient.streamRunEvents}
|
|
535
|
-
* when a human is waiting.
|
|
536
|
-
*/
|
|
537
|
-
async run(runId, signal) {
|
|
538
|
-
return await getRun(this.#transport, runId, signal);
|
|
539
|
-
}
|
|
540
429
|
/** Follow a verdict cell's citation handle to its canonical claim source. */
|
|
541
430
|
async citation(citationId, signal) {
|
|
542
431
|
return await this.#get("get_verdict_citation", undefined, signal, { citationId });
|
|
543
432
|
}
|
|
544
|
-
/**
|
|
545
|
-
* Stream ordered Run events as an async iterator (GET
|
|
546
|
-
* `/api/v1/runs/{runId}/events`, SSE). Iterates until the stream ends or the
|
|
547
|
-
* signal aborts; pass `after` to resume from the last observed sequence.
|
|
548
|
-
*/
|
|
549
|
-
streamRunEvents(runId, options) {
|
|
550
|
-
return streamRunEvents(this.#transport, runId, options);
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Collect Run events until a terminal event, the deadline, or the stream ends
|
|
554
|
-
* — the bounded events-log pattern (a single pass over {@link streamRunEvents}).
|
|
555
|
-
*/
|
|
556
|
-
async drainRunEvents(runId, options) {
|
|
557
|
-
return await drainRunEvents(this.#transport, runId, options);
|
|
558
|
-
}
|
|
559
433
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A colour word's region in OKLCh: what a name like "sage" covers, rather than
|
|
3
|
+
* one point. A swatch counts inside it, so it is a box (lightness, chroma, hue)
|
|
4
|
+
* and not a distance. Search echoes it on the plan's colour and accepts it back
|
|
5
|
+
* on the request's `colour`, and the colour lookups carry it on each resolved
|
|
6
|
+
* region.
|
|
7
|
+
*/
|
|
8
|
+
export type ColourRegionRange = {
|
|
9
|
+
/** Inclusive OKLab lightness bounds, 0 to 1. */
|
|
10
|
+
lightness: [number, number];
|
|
11
|
+
/** Inclusive OKLCh chroma bounds. */
|
|
12
|
+
chroma: [number, number];
|
|
13
|
+
/**
|
|
14
|
+
* The hue arc in degrees, 0 to 360, inclusive. It wraps when its start is past
|
|
15
|
+
* its end (reds). `null` means any hue, which is what a neutral is.
|
|
16
|
+
*/
|
|
17
|
+
hue: [number, number] | null;
|
|
18
|
+
};
|
package/colours-client.d.ts
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
1
|
-
import type { CategoryColourProfilesResponse, ColourProfilesParams, ColourStatsParams, ColourStatsResponse } from "./colour-aggregate-types.js";
|
|
2
1
|
import type { QueryParams } from "./http-transport.js";
|
|
3
2
|
import type { SdkOperationId } from "./operations.js";
|
|
4
3
|
/**
|
|
5
|
-
* The colour surface, namespaced as `mg.colours` —
|
|
6
|
-
* (`
|
|
4
|
+
* The colour surface, namespaced as `mg.colours` — MULTI-COLOUR search
|
|
5
|
+
* (`palette`).
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* colours" (`palette`), and "what moves from this colour to that one"
|
|
12
|
-
* (`gradient`). `mg.codes` sets the precedent for a namespace when a
|
|
13
|
-
* capability is a family rather than a single verb.
|
|
7
|
+
* A namespace rather than a flat method on an already-large client facade:
|
|
8
|
+
* "what goes with these colours" (`palette`). `mg.codes` set the precedent for
|
|
9
|
+
* a namespace when a capability is a family rather than a single verb.
|
|
14
10
|
*
|
|
15
11
|
* The namespace holds no transport of its own: the client hands it a bound
|
|
16
12
|
* `get`/`post`, so auth, rate-limit accounting and error shaping stay in one
|
|
17
13
|
* place.
|
|
18
14
|
*
|
|
19
|
-
*
|
|
20
|
-
* materialized view refreshed on a schedule, not on write, so a caller can and
|
|
21
|
-
* should check how much of the corpus the answer actually covers before
|
|
22
|
-
* treating it as complete. The two SEARCH responses are live and uncached.
|
|
15
|
+
* `palette`'s response is live and uncached.
|
|
23
16
|
*
|
|
24
|
-
* ROUTING OUT OF THIS NAMESPACE. Colour is a lens, and
|
|
25
|
-
*
|
|
17
|
+
* ROUTING OUT OF THIS NAMESPACE. Colour is a lens, and this is not the only
|
|
18
|
+
* door into it:
|
|
26
19
|
* - ONE colour → `mg.byColour({ hex })`. This namespace's `palette` is for N
|
|
27
20
|
* colours at stated proportions, which is a different ranking, not a
|
|
28
21
|
* convenience wrapper.
|
|
@@ -35,7 +28,7 @@ import type { SdkOperationId } from "./operations.js";
|
|
|
35
28
|
* FROM PRODUCT IMAGERY, not declared by the manufacturer. Report visual
|
|
36
29
|
* likeness, never a stated colour specification.
|
|
37
30
|
*/
|
|
38
|
-
import type {
|
|
31
|
+
import type { SearchPaletteInput, SearchPaletteResponse } from "./palette-search-types.js";
|
|
39
32
|
/** The client's own request primitives, handed down so this file owns no transport. */
|
|
40
33
|
export type ColoursTransport = {
|
|
41
34
|
get<T>(operationId: SdkOperationId, query?: QueryParams, signal?: AbortSignal): Promise<T>;
|
|
@@ -44,64 +37,11 @@ export type ColoursTransport = {
|
|
|
44
37
|
export declare class ColoursClient {
|
|
45
38
|
#private;
|
|
46
39
|
constructor(transport: ColoursTransport);
|
|
47
|
-
/**
|
|
48
|
-
* THE QUESTION: "what colours does this CATEGORY come in?" — the representative
|
|
49
|
-
* palette of 1–8 categories in ONE call (GET `/api/v1/colours/profiles`).
|
|
50
|
-
*
|
|
51
|
-
* An aggregate over a category, not a list of materials: use `mg.byColour()` or
|
|
52
|
-
* `mg.search()` when you want the materials themselves, and
|
|
53
|
-
* {@link ColoursClient.stats} for coverage across the whole corpus rather than
|
|
54
|
-
* one category's palette. Check `basis` — this reads a scheduled materialized
|
|
55
|
-
* view, so it can lag the catalog.
|
|
56
|
-
*
|
|
57
|
-
* `groupBy: "group"` takes product-type GROUP keys and merges every leaf type
|
|
58
|
-
* under a group into one histogram BEFORE ranking — a group's palette, not the
|
|
59
|
-
* union of its leaves' heads. `select: "diverse"` returns visually spread
|
|
60
|
-
* swatches (greedy max-min over OKLab, 0.02 minimum separation, backfilled by
|
|
61
|
-
* frequency if fewer survive) instead of the most frequent ones; a client
|
|
62
|
-
* cannot recover that spread from a truncated frequency list, which is why it
|
|
63
|
-
* is a server mode rather than a local sort.
|
|
64
|
-
*
|
|
65
|
-
* `groupBy: "merged"` merges across the KEYS as well and returns ONE profile
|
|
66
|
-
* over the union of their leaves — the palette of a caller-defined facet such
|
|
67
|
-
* as "Wood" (`wood_surface` + `wood_flooring`) that the taxonomy holds no
|
|
68
|
-
* single node for, and that no combination of per-key responses reconstructs.
|
|
69
|
-
* Its keys may sit at either tier, `categoryKey` is those keys sorted and
|
|
70
|
-
* joined with `+`, and `members` hands them back so nothing has to split it.
|
|
71
|
-
*
|
|
72
|
-
* A selected category with no colour data comes back as a zero-count profile,
|
|
73
|
-
* not a missing key.
|
|
74
|
-
*/
|
|
75
|
-
profiles(params: ColourProfilesParams, signal?: AbortSignal): Promise<CategoryColourProfilesResponse>;
|
|
76
|
-
/**
|
|
77
|
-
* THE QUESTION: "how much colour does the graph hold AT ALL?" — corpus-level
|
|
78
|
-
* coverage, before you trust any colour answer.
|
|
79
|
-
*
|
|
80
|
-
* The sanity check under every other colour call. Use
|
|
81
|
-
* {@link ColoursClient.profiles} for one category's palette and
|
|
82
|
-
* `mg.colourFacets()` for the vocabulary. `basis.omissions` lists what this
|
|
83
|
-
* call deliberately does not answer — read it rather than assuming silence.
|
|
84
|
-
*
|
|
85
|
-
* Corpus colour coverage (GET `/api/v1/colours/stats`): how much of the graph
|
|
86
|
-
* carries colour at all, the split across the canonical colour families, and
|
|
87
|
-
* coverage per product-type group. `basis` states the source and freshness of
|
|
88
|
-
* each section plus `basis.omissions` — what this call deliberately does not
|
|
89
|
-
* answer, listed rather than silently absent.
|
|
90
|
-
*
|
|
91
|
-
* Pass `{ groupBy: "brand,category" }` for the whole brand x category x
|
|
92
|
-
* family matrix in one call, or a `brandSlug`/`categoryKey` scope for a slice
|
|
93
|
-
* of it — either way it arrives as `rows`, each row carrying `brandId` and
|
|
94
|
-
* `brandSlug` to join on. Without any of those parameters the payload is the
|
|
95
|
-
* platform-wide one and `rows` is absent. `families` is never scoped by them.
|
|
96
|
-
*/
|
|
97
|
-
stats(params?: ColourStatsParams, signal?: AbortSignal): Promise<ColourStatsResponse>;
|
|
98
40
|
/**
|
|
99
41
|
* THE QUESTION: "what goes with this WHOLE PALETTE?" — N colours at once, at
|
|
100
42
|
* stated proportions, not one colour repeated (POST `/api/v1/search/palettes`).
|
|
101
43
|
*
|
|
102
|
-
* Use `mg.byColour()` for a single colour
|
|
103
|
-
* the colours form an ordered TRANSITION rather than a set, and
|
|
104
|
-
* `mg.coordinate()` when the scheme is made of materials rather than colours.
|
|
44
|
+
* Use `mg.byColour()` for a single colour.
|
|
105
45
|
*
|
|
106
46
|
* Each colour scores `sim × prop`: `sim` is OKLab closeness on the same
|
|
107
47
|
* mapping `byColour` ranks with, and `prop` rewards a candidate whose own
|
|
@@ -127,17 +67,4 @@ export declare class ColoursClient {
|
|
|
127
67
|
* });
|
|
128
68
|
*/
|
|
129
69
|
palette(input: SearchPaletteInput, signal?: AbortSignal): Promise<SearchPaletteResponse>;
|
|
130
|
-
/**
|
|
131
|
-
* THE QUESTION: "what moves from this colour to that one?" — materials that
|
|
132
|
-
* travel ALONG a transition, in order (POST `/api/v1/search/gradients`).
|
|
133
|
-
*
|
|
134
|
-
* Order is the whole point: use {@link ColoursClient.palette} when the colours
|
|
135
|
-
* are a set rather than a path, and `mg.byColour()` for a single colour.
|
|
136
|
-
*
|
|
137
|
-
* Not a palette: the score is multiplied by `coverage`, the share of the path
|
|
138
|
-
* the candidate actually spans, so a material owning only one end ranks below
|
|
139
|
-
* one that travels the whole way, and a flat single-colour material is
|
|
140
|
-
* rejected outright however close its hue sits to the path.
|
|
141
|
-
*/
|
|
142
|
-
gradient(input: SearchGradientInput, signal?: AbortSignal): Promise<SearchGradientResponse>;
|
|
143
70
|
}
|