@instruments/materialgraph 0.7.1 → 0.9.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/association-types.d.ts +92 -0
- package/association-types.js +1 -0
- package/associations-client.d.ts +51 -0
- package/associations-client.js +40 -0
- package/by-colour-types.d.ts +139 -0
- package/by-colour-types.js +1 -0
- package/client-config.d.ts +17 -0
- package/client-config.js +9 -0
- package/client.d.ts +35 -22
- package/client.js +46 -32
- package/composition-types.d.ts +31 -1
- package/index.d.ts +4 -1
- package/index.js +1 -1
- package/match-stream-types.d.ts +85 -1
- package/match-stream-types.js +8 -0
- package/match-stream.d.ts +1 -1
- package/match-types.d.ts +101 -0
- package/match-types.js +1 -0
- package/material-types.d.ts +8 -0
- package/operations.d.ts +7 -1
- package/operations.js +16 -0
- package/package.json +1 -1
- package/resolve-client.d.ts +71 -0
- package/resolve-client.js +51 -0
- package/resolve-types.d.ts +102 -0
- package/resolve-types.js +17 -0
- package/run-events.d.ts +11 -0
- package/run-types.d.ts +25 -0
- package/runs.d.ts +15 -1
- package/runs.js +24 -0
- package/schema-resource-types.d.ts +17 -14
- package/schema-resource-types.js +17 -1
- package/search-result-validation.js +8 -0
- package/types.d.ts +83 -104
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary lens (MG-913) — the hand-mirrored wire types for
|
|
3
|
+
* `POST /api/v1/resolve`.
|
|
4
|
+
*
|
|
5
|
+
* Split out of `types.ts` for the repository's source-file size budget, the same
|
|
6
|
+
* reason `suggest-types.ts` sits beside it; both are re-exported from `types.ts`,
|
|
7
|
+
* so importers see no difference.
|
|
8
|
+
*
|
|
9
|
+
* THE LENS: it turns WORDS into vocabulary, never into materials.
|
|
10
|
+
* `mg.search()` answers "I want materials" and `mg.answer` questions want prose;
|
|
11
|
+
* this answers "what do these words mean in your vocabulary?" — the question you
|
|
12
|
+
* ask BEFORE you spend a token on a filter, a facet or a brand slug. It is the
|
|
13
|
+
* reason you should not keep a hand-written table mapping your users' words onto
|
|
14
|
+
* this API's tokens: that table is a copy of a registry you do not own, and it
|
|
15
|
+
* drifts silently the moment the registry moves.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Which controlled vocabulary a reading came from — and therefore what `id`
|
|
19
|
+
* means and where you can spend it.
|
|
20
|
+
*
|
|
21
|
+
* - `brand` — a brand slug, for `mg.brand()` or a brand filter.
|
|
22
|
+
* - `paint_identity` — one exact vendor paint (a library colour), by its vendor
|
|
23
|
+
* code where the library has one.
|
|
24
|
+
* - `colour` — a canonical colour-family term, namespaced (`colour_family.green`).
|
|
25
|
+
* - `category` — a product-category taxonomy node key.
|
|
26
|
+
* - `attribute` — a registry attribute id: the LEFT half of an `attribute:value`
|
|
27
|
+
* pair, for `mg.bySpec()` or `search({ filters })`.
|
|
28
|
+
* - `attribute_value` — a value-dictionary term, namespaced (`ms_sheen.matte`):
|
|
29
|
+
* the RIGHT half.
|
|
30
|
+
*/
|
|
31
|
+
export type ResolutionKind = "brand" | "paint_identity" | "colour" | "category" | "attribute" | "attribute_value";
|
|
32
|
+
/**
|
|
33
|
+
* How the phrase matched, strongest first. `exact` — it IS the canonical token;
|
|
34
|
+
* `alias` — it IS a recorded alternative name for it; `lexical` — it resembles
|
|
35
|
+
* one closely enough to clear the resolver's own confidence floor.
|
|
36
|
+
*
|
|
37
|
+
* There is deliberately no `semantic` rung on this lens. Every tier that would
|
|
38
|
+
* cost an embedding round-trip is switched off, which is what keeps resolution
|
|
39
|
+
* millisecond-class; a phrase only a semantic match could have caught comes back
|
|
40
|
+
* unresolved rather than slow.
|
|
41
|
+
*/
|
|
42
|
+
export type ResolutionRung = "exact" | "alias" | "lexical";
|
|
43
|
+
/** One thing a phrase turned out to mean. */
|
|
44
|
+
export interface ResolvedMeaning {
|
|
45
|
+
kind: ResolutionKind;
|
|
46
|
+
/**
|
|
47
|
+
* The canonical token. OPAQUE — hand it back verbatim, never parse, tidy or
|
|
48
|
+
* construct one. The single thing worth knowing about the SHAPE: a
|
|
49
|
+
* value-dictionary term id is unique only within its dictionary, so `colour`
|
|
50
|
+
* and `attribute_value` ids arrive namespaced as `<dictionaryId>.<termId>`.
|
|
51
|
+
*/
|
|
52
|
+
id: string;
|
|
53
|
+
/** The canonical display term — what to show a human, not what to send back. */
|
|
54
|
+
term: string;
|
|
55
|
+
rung: ResolutionRung;
|
|
56
|
+
/**
|
|
57
|
+
* The resolver's own confidence in [0,1], present only when the underlying
|
|
58
|
+
* resolver produces one. Its ABSENCE is meaningful rather than missing data:
|
|
59
|
+
* the rung is then the whole statement, because an exact paint identity is an
|
|
60
|
+
* equality and not a score.
|
|
61
|
+
*/
|
|
62
|
+
confidence?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Present only when the resolver reported a runner-up. A small `gap` means the
|
|
65
|
+
* answer was nearly something else; when the resolver refused to break the tie,
|
|
66
|
+
* every tied reading is listed and `gap` is 0.
|
|
67
|
+
*/
|
|
68
|
+
ambiguity?: {
|
|
69
|
+
gap: number;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** What one phrase resolved to — including, explicitly, nothing. */
|
|
73
|
+
export interface PhraseResolution {
|
|
74
|
+
/** The phrase exactly as you sent it, so you can rejoin the batch by value. */
|
|
75
|
+
phrase: string;
|
|
76
|
+
/**
|
|
77
|
+
* The phrase in the one normalised form the resolvers fold to — the same
|
|
78
|
+
* `normalise` stage a search query passes through. Empty when the phrase was
|
|
79
|
+
* all punctuation.
|
|
80
|
+
*/
|
|
81
|
+
normalised: string;
|
|
82
|
+
/**
|
|
83
|
+
* Everything the phrase means here, best first. A phrase can legitimately mean
|
|
84
|
+
* several things at once ("sage" is two vendor paints AND a colour family), so
|
|
85
|
+
* this is a list rather than a winner.
|
|
86
|
+
*/
|
|
87
|
+
resolved: ResolvedMeaning[];
|
|
88
|
+
/**
|
|
89
|
+
* TRUE when nothing resolved. Read this rather than inferring it from
|
|
90
|
+
* `resolved.length`: the redundancy is deliberate, because a consumer that
|
|
91
|
+
* cannot SEE what failed to resolve cannot tell a word this vocabulary does
|
|
92
|
+
* not have from a word something quietly dropped. Absent is not false — an
|
|
93
|
+
* unresolved phrase is a statement about this vocabulary, never about the
|
|
94
|
+
* material world.
|
|
95
|
+
*/
|
|
96
|
+
unresolved: boolean;
|
|
97
|
+
}
|
|
98
|
+
/** The resolve response: one entry per phrase, in the order you sent them. */
|
|
99
|
+
export interface ResolveResponse {
|
|
100
|
+
/** One per phrase, in request order — never reordered, never deduplicated. */
|
|
101
|
+
resolutions: PhraseResolution[];
|
|
102
|
+
}
|
package/resolve-types.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary lens (MG-913) — the hand-mirrored wire types for
|
|
3
|
+
* `POST /api/v1/resolve`.
|
|
4
|
+
*
|
|
5
|
+
* Split out of `types.ts` for the repository's source-file size budget, the same
|
|
6
|
+
* reason `suggest-types.ts` sits beside it; both are re-exported from `types.ts`,
|
|
7
|
+
* so importers see no difference.
|
|
8
|
+
*
|
|
9
|
+
* THE LENS: it turns WORDS into vocabulary, never into materials.
|
|
10
|
+
* `mg.search()` answers "I want materials" and `mg.answer` questions want prose;
|
|
11
|
+
* this answers "what do these words mean in your vocabulary?" — the question you
|
|
12
|
+
* ask BEFORE you spend a token on a filter, a facet or a brand slug. It is the
|
|
13
|
+
* reason you should not keep a hand-written table mapping your users' words onto
|
|
14
|
+
* this API's tokens: that table is a copy of a registry you do not own, and it
|
|
15
|
+
* drifts silently the moment the registry moves.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
package/run-events.d.ts
CHANGED
|
@@ -50,7 +50,18 @@ export interface CompositionReviewCriteriaSuggestion {
|
|
|
50
50
|
export interface CompositionReviewCriteriaUnresolved {
|
|
51
51
|
slotId: string;
|
|
52
52
|
raw: string;
|
|
53
|
+
/**
|
|
54
|
+
* Ranked corrections, best first. MG-994: every one of these is an attribute
|
|
55
|
+
* the slot's own category holds accepted values for, so accepting the top
|
|
56
|
+
* suggestion can never leave the slot unfillable.
|
|
57
|
+
*/
|
|
53
58
|
suggestions: CompositionReviewCriteriaSuggestion[];
|
|
59
|
+
/**
|
|
60
|
+
* MG-994: attribute ids ranked for this phrase and then withheld — the slot's
|
|
61
|
+
* category holds no accepted value for them. ABSENT when nothing was withheld
|
|
62
|
+
* (absent means nothing was removed, not that nothing could be).
|
|
63
|
+
*/
|
|
64
|
+
withheld?: string[];
|
|
54
65
|
}
|
|
55
66
|
/**
|
|
56
67
|
* Payload of a `composition.review-criteria` / `material-box.review-criteria`
|
package/run-types.d.ts
CHANGED
|
@@ -233,6 +233,31 @@ export interface CreatePublicRunResponse {
|
|
|
233
233
|
export interface PublicRunResponse {
|
|
234
234
|
run: PublicRun;
|
|
235
235
|
}
|
|
236
|
+
/** Query for `GET /api/v1/runs` ({@link MaterialGraphClient.listRuns}). */
|
|
237
|
+
export interface ListRunsOptions {
|
|
238
|
+
/**
|
|
239
|
+
* Return only Runs in this status. `"suspended"` is the recovery question:
|
|
240
|
+
* which of my Runs is still waiting on an answer?
|
|
241
|
+
*/
|
|
242
|
+
status?: PublicRunStatus;
|
|
243
|
+
/** Page size, 1–100. Defaults to 20 server-side. */
|
|
244
|
+
limit?: number;
|
|
245
|
+
/**
|
|
246
|
+
* The previous page's {@link PublicRunListResponse.nextCursor}, round-tripped
|
|
247
|
+
* VERBATIM. It is opaque — never parse or construct one.
|
|
248
|
+
*/
|
|
249
|
+
cursor?: string;
|
|
250
|
+
signal?: AbortSignal;
|
|
251
|
+
}
|
|
252
|
+
/** The `GET /api/v1/runs` response envelope — one page, newest first. */
|
|
253
|
+
export interface PublicRunListResponse {
|
|
254
|
+
runs: PublicRun[];
|
|
255
|
+
/**
|
|
256
|
+
* Pass as {@link ListRunsOptions.cursor} for the next page. ABSENT — not
|
|
257
|
+
* empty-string, not null — once the last page has been served.
|
|
258
|
+
*/
|
|
259
|
+
nextCursor?: string;
|
|
260
|
+
}
|
|
236
261
|
/** Body for `POST /api/v1/runs/{runId}/resume`. */
|
|
237
262
|
export interface ResumePublicRunRequest {
|
|
238
263
|
/** The resume payload, validated against the suspended step's resume schema. */
|
package/runs.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HttpTransport } from "./http-transport.js";
|
|
2
2
|
import type { FinishScheduleResponse, GetRunScheduleOptions } from "./schedule-types.js";
|
|
3
|
-
import { type CapabilityManifestResponse, type CreatePublicRunRequest, type CreatePublicRunResponse, type DrainRunEventsOptions, type PublicRunResponse, type ResumeRunOptions, type RunEvent, type SourceMaterialBatchDetail, type SourceMaterialBatchReceipt, type StreamRunEventsOptions, type UploadSourceMaterialInput } from "./run-types.js";
|
|
3
|
+
import { type CapabilityManifestResponse, type CreatePublicRunRequest, type CreatePublicRunResponse, type DrainRunEventsOptions, type ListRunsOptions, type PublicRunListResponse, type PublicRunResponse, type ResumeRunOptions, type RunEvent, type SourceMaterialBatchDetail, type SourceMaterialBatchReceipt, type StreamRunEventsOptions, type UploadSourceMaterialInput } from "./run-types.js";
|
|
4
4
|
/**
|
|
5
5
|
* Upload one atomic source-material batch (`POST /api/v1/source-material`).
|
|
6
6
|
* Accepts the pile-of-files shape (`{ files }` → repeatable `files` field) or
|
|
@@ -25,6 +25,20 @@ export declare function getSourceMaterialBatch(transport: HttpTransport, batchId
|
|
|
25
25
|
export declare function capabilities(transport: HttpTransport, signal?: AbortSignal): Promise<CapabilityManifestResponse>;
|
|
26
26
|
/** Start an asynchronous capability Run (`POST /api/v1/runs`). */
|
|
27
27
|
export declare function createRun(transport: HttpTransport, body: CreatePublicRunRequest, signal?: AbortSignal): Promise<CreatePublicRunResponse>;
|
|
28
|
+
/**
|
|
29
|
+
* List your own Runs, newest first (`GET /api/v1/runs`).
|
|
30
|
+
*
|
|
31
|
+
* The recovery path for a client that still holds its API key but has lost a Run
|
|
32
|
+
* id — a reinstalled app, or a crash before the id from {@link createRun} was
|
|
33
|
+
* persisted. `{ status: "suspended" }` finds the Run still waiting on an answer.
|
|
34
|
+
*
|
|
35
|
+
* Only the calling principal's own Runs are ever returned; there is no
|
|
36
|
+
* cross-principal listing.
|
|
37
|
+
*
|
|
38
|
+
* Page by following `nextCursor` until it is ABSENT — a full page is not the
|
|
39
|
+
* signal to stop, and an absent cursor is the only one.
|
|
40
|
+
*/
|
|
41
|
+
export declare function listRuns(transport: HttpTransport, options?: ListRunsOptions): Promise<PublicRunListResponse>;
|
|
28
42
|
/** Get a Run's current state (`GET /api/v1/runs/{runId}`). */
|
|
29
43
|
export declare function getRun(transport: HttpTransport, runId: string, signal?: AbortSignal): Promise<PublicRunResponse>;
|
|
30
44
|
/**
|
package/runs.js
CHANGED
|
@@ -99,6 +99,30 @@ export function createRun(transport, body, signal) {
|
|
|
99
99
|
signal,
|
|
100
100
|
});
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* List your own Runs, newest first (`GET /api/v1/runs`).
|
|
104
|
+
*
|
|
105
|
+
* The recovery path for a client that still holds its API key but has lost a Run
|
|
106
|
+
* id — a reinstalled app, or a crash before the id from {@link createRun} was
|
|
107
|
+
* persisted. `{ status: "suspended" }` finds the Run still waiting on an answer.
|
|
108
|
+
*
|
|
109
|
+
* Only the calling principal's own Runs are ever returned; there is no
|
|
110
|
+
* cross-principal listing.
|
|
111
|
+
*
|
|
112
|
+
* Page by following `nextCursor` until it is ABSENT — a full page is not the
|
|
113
|
+
* signal to stop, and an absent cursor is the only one.
|
|
114
|
+
*/
|
|
115
|
+
export function listRuns(transport, options = {}) {
|
|
116
|
+
const query = {
|
|
117
|
+
...(options.status === undefined ? {} : { status: options.status }),
|
|
118
|
+
...(options.limit === undefined ? {} : { limit: options.limit }),
|
|
119
|
+
...(options.cursor === undefined ? {} : { cursor: options.cursor }),
|
|
120
|
+
};
|
|
121
|
+
return transport.requestJson(runOperation("list_public_runs"), {
|
|
122
|
+
...(Object.keys(query).length > 0 ? { query } : {}),
|
|
123
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
102
126
|
/** Get a Run's current state (`GET /api/v1/runs/{runId}`). */
|
|
103
127
|
export function getRun(transport, runId, signal) {
|
|
104
128
|
return transport.requestJson(runOperation("get_public_run"), {
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wire types for the governed STATIC schema (`GET /api/v1/schema/*`).
|
|
3
|
-
*
|
|
4
|
-
* THE LENS: "what does the registry DEFINE?" — blocks, attributes, units, value
|
|
5
|
-
* dictionaries, product types and their external mappings. Definitions, not
|
|
6
|
-
* data: nothing here says how many materials carry an attribute or which values
|
|
7
|
-
* the corpus holds. For that, use `mg.specificationFacets()` and
|
|
8
|
-
* `mg.attributeValues()`; for one term explained in prose, `mg.glossary()`; for
|
|
9
|
-
* what one material has been determined on, `mg.materialBlocks()`.
|
|
10
|
-
*
|
|
11
|
-
* Every id here (`attributeId`, `blockId`, product-type keys) is canonical
|
|
12
|
-
* VOCABULARY, not an opaque token — this is where you discover it rather than
|
|
13
|
-
* invent it.
|
|
14
|
-
*/
|
|
15
1
|
import type { OpenEnum } from "./types.js";
|
|
16
2
|
/** The nine governed static resources. */
|
|
17
3
|
export type SchemaResource = "blocks" | "attributes" | "verticals" | "productTypes" | "units" | "valueDictionaries" | "sourceRequirements" | "productTypeTemplates" | "mappings";
|
|
4
|
+
/**
|
|
5
|
+
* Resource → the operation that serves it. Internal to the SDK (`index.ts`
|
|
6
|
+
* re-exports this module's TYPES only), and it lives beside
|
|
7
|
+
* {@link SchemaResource} rather than in the client so the mapping and the union
|
|
8
|
+
* it must cover cannot drift apart.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SCHEMA_RESOURCE_OPERATION_IDS: {
|
|
11
|
+
readonly blocks: "list_schema_blocks";
|
|
12
|
+
readonly attributes: "list_schema_attributes";
|
|
13
|
+
readonly verticals: "list_schema_verticals";
|
|
14
|
+
readonly productTypes: "list_schema_product_types";
|
|
15
|
+
readonly units: "list_schema_units";
|
|
16
|
+
readonly valueDictionaries: "list_schema_value_dictionaries";
|
|
17
|
+
readonly sourceRequirements: "list_schema_source_requirements";
|
|
18
|
+
readonly productTypeTemplates: "list_schema_product_type_templates";
|
|
19
|
+
readonly mappings: "list_schema_mappings";
|
|
20
|
+
};
|
|
18
21
|
export type SchemaResourceRequest = SchemaResource;
|
|
19
22
|
export interface SchemaLifecycle {
|
|
20
23
|
status: OpenEnum<"draft" | "active" | "deprecated">;
|
package/schema-resource-types.js
CHANGED
|
@@ -1 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Resource → the operation that serves it. Internal to the SDK (`index.ts`
|
|
3
|
+
* re-exports this module's TYPES only), and it lives beside
|
|
4
|
+
* {@link SchemaResource} rather than in the client so the mapping and the union
|
|
5
|
+
* it must cover cannot drift apart.
|
|
6
|
+
*/
|
|
7
|
+
export const SCHEMA_RESOURCE_OPERATION_IDS = {
|
|
8
|
+
blocks: "list_schema_blocks",
|
|
9
|
+
attributes: "list_schema_attributes",
|
|
10
|
+
verticals: "list_schema_verticals",
|
|
11
|
+
productTypes: "list_schema_product_types",
|
|
12
|
+
units: "list_schema_units",
|
|
13
|
+
valueDictionaries: "list_schema_value_dictionaries",
|
|
14
|
+
sourceRequirements: "list_schema_source_requirements",
|
|
15
|
+
productTypeTemplates: "list_schema_product_type_templates",
|
|
16
|
+
mappings: "list_schema_mappings",
|
|
17
|
+
};
|
|
@@ -143,6 +143,14 @@ export function checkSearchResultItem(value, what, fail) {
|
|
|
143
143
|
requireString(record.product, `${what}.product`, fail);
|
|
144
144
|
requireNullableString(record.brandSlug, `${what}.brandSlug`, fail);
|
|
145
145
|
requireNullableString(record.productId, `${what}.productId`, fail);
|
|
146
|
+
// `z.string().optional()` — absent, or a string. NOT nullable: the emit path
|
|
147
|
+
// omits the key rather than sending null when no MB identity is known.
|
|
148
|
+
requireOptionalString(record.materialBankId, `${what}.materialBankId`, fail);
|
|
149
|
+
// Same shape as `materialBankId`: absent, or a string. The emit path omits
|
|
150
|
+
// the key when no primary product type is on record rather than sending null,
|
|
151
|
+
// so "we don't know what this is" and "this is a null kind of thing" cannot
|
|
152
|
+
// be confused.
|
|
153
|
+
requireOptionalString(record.productType, `${what}.productType`, fail);
|
|
146
154
|
requireNullableString(record.color, `${what}.color`, fail);
|
|
147
155
|
requireNullableString(record.variantName, `${what}.variantName`, fail);
|
|
148
156
|
requireNullableString(record.imageUrl, `${what}.imageUrl`, fail);
|
package/types.d.ts
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* match set. Follow the cursor for the rest, and never report a page size as
|
|
26
26
|
* a total.
|
|
27
27
|
*/
|
|
28
|
+
import type { ByColourSwatch } from "./by-colour-types.js";
|
|
28
29
|
import type { BySpecVerdict } from "./by-spec-types.js";
|
|
29
30
|
import type { Cursor, ProductId, VariantId } from "./identifiers.js";
|
|
30
31
|
import type { MaterialDetail, MaterialTile } from "./material-types.js";
|
|
@@ -39,12 +40,22 @@ export type OpenEnum<T extends string> = T | (string & {});
|
|
|
39
40
|
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
40
41
|
[key: string]: JsonValue;
|
|
41
42
|
};
|
|
42
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* One palette swatch of a material's main image — colour DERIVED FROM PRODUCT
|
|
45
|
+
* IMAGERY, so read it as visual likeness, never as a declared colour spec.
|
|
46
|
+
*/
|
|
43
47
|
export interface PaletteSwatch {
|
|
44
48
|
hex: string;
|
|
45
49
|
proportion: number;
|
|
46
50
|
/** Stored human colour name for the swatch, when one was extracted. */
|
|
47
51
|
name?: string | null;
|
|
52
|
+
/**
|
|
53
|
+
* `hex` in OKLab, derived server-side so you can sort or measure swatches
|
|
54
|
+
* without converting them yourself. Additive, so optional: the colour lane and
|
|
55
|
+
* the material record carry it, other surfaces may not, and absent means "not
|
|
56
|
+
* derived here" — never "achromatic".
|
|
57
|
+
*/
|
|
58
|
+
oklab?: OklabTriple;
|
|
48
59
|
}
|
|
49
60
|
/** Loose colour metadata for a search result's main image; every field is independently optional. */
|
|
50
61
|
export interface ColourMeta {
|
|
@@ -109,6 +120,30 @@ export interface SearchResultItem {
|
|
|
109
120
|
product: string;
|
|
110
121
|
brandSlug: string | null;
|
|
111
122
|
productId: ProductId | null;
|
|
123
|
+
/**
|
|
124
|
+
* The variant's Material Bank SKU — the same `materialBankId` the material
|
|
125
|
+
* record carries, so a match card can render straight from a result instead of
|
|
126
|
+
* paying a per-result lookup. Populated on the MATCH lanes (`match()` and
|
|
127
|
+
* `matchStream()` frames).
|
|
128
|
+
*
|
|
129
|
+
* ABSENT means no MB identity is known for the variant — never that the SKU
|
|
130
|
+
* cannot be ordered. Presence is the orderability tell; absence is silence.
|
|
131
|
+
*/
|
|
132
|
+
materialBankId?: string;
|
|
133
|
+
/**
|
|
134
|
+
* What kind of material this is — the variant's primary product type, as a
|
|
135
|
+
* display name (`"Wall Tile"`, `"Modular Carpet"`, `"Interior Paint"`).
|
|
136
|
+
*
|
|
137
|
+
* This is the field that lets you tell a wall tile from a bar stool without
|
|
138
|
+
* parsing `product`. Before it existed, callers grading Match results had to
|
|
139
|
+
* regex the product's NAME, and one such grader scored pendant lights and
|
|
140
|
+
* electrical sockets as correct answers for a wall — because they are neither
|
|
141
|
+
* a stool nor a chair.
|
|
142
|
+
*
|
|
143
|
+
* ABSENT means no primary type is on record for the variant. Unknown, never
|
|
144
|
+
* "uncategorised" and never "not a material".
|
|
145
|
+
*/
|
|
146
|
+
productType?: string;
|
|
112
147
|
color: string | null;
|
|
113
148
|
variantName: string | null;
|
|
114
149
|
imageUrl: string | null;
|
|
@@ -117,6 +152,35 @@ export interface SearchResultItem {
|
|
|
117
152
|
colourMeta?: ColourMeta;
|
|
118
153
|
/** Fused score — range [0, sum of active weights]; see `maxScore`. */
|
|
119
154
|
score: number;
|
|
155
|
+
/**
|
|
156
|
+
* Match only. How close this material is to the query, as an ABSOLUTE
|
|
157
|
+
* image-space similarity in [0,1] adjusted for colour — the same unit as
|
|
158
|
+
* `qualityFloor` and `comparison.bestRejected`, so all three sit on one axis.
|
|
159
|
+
*
|
|
160
|
+
* Never normalised against the page, so the same material reads the same in
|
|
161
|
+
* two different searches. It is the TRUE value and MAY reach 1 — rendering it
|
|
162
|
+
* so that no card claims "100%" is yours to do (round toward zero, or show
|
|
163
|
+
* "99%+"); `Math.round` prints 100% from 0.995 up. Capping it server-side
|
|
164
|
+
* would put it on a different ceiling from `comparison.bestRejected`, which
|
|
165
|
+
* publishes the same unit uncapped, and the two are meant to be comparable.
|
|
166
|
+
* `score` is the ranking value and lives on a different scale.
|
|
167
|
+
*/
|
|
168
|
+
confidence?: number;
|
|
169
|
+
/**
|
|
170
|
+
* `confidence` as a word — `strong` >= 0.85, `good` >= 0.72, `fair` >= 0.55
|
|
171
|
+
* (the default quality floor), `weak` below it and only reachable under
|
|
172
|
+
* `includeBelowFloor`. Present exactly when `confidence` is.
|
|
173
|
+
*/
|
|
174
|
+
confidenceBand?: "strong" | "good" | "fair" | "weak";
|
|
175
|
+
/**
|
|
176
|
+
* Match only. Proportion-weighted OKLab distance from the query crop's palette
|
|
177
|
+
* to this material's. `null` means the material carries no stored palette —
|
|
178
|
+
* UNKNOWN colour, never "no colour" and never "distant". Absent when the
|
|
179
|
+
* colour stage did not run.
|
|
180
|
+
*/
|
|
181
|
+
colourDistance?: number | null;
|
|
182
|
+
/** `colourDistance` as a word, or null when unmeasurable. */
|
|
183
|
+
colourBand?: string | null;
|
|
120
184
|
signals?: SearchResultSignals;
|
|
121
185
|
retired?: boolean;
|
|
122
186
|
/**
|
|
@@ -223,69 +287,6 @@ export interface SearchCardResponse extends Omit<SearchResponse, "view" | "resul
|
|
|
223
287
|
view: OpenEnum<"card">;
|
|
224
288
|
results: SearchCardItem[];
|
|
225
289
|
}
|
|
226
|
-
/** An inferred product-type category from the query image, with its boost. */
|
|
227
|
-
export interface MatchCategoryInference {
|
|
228
|
-
productTypeId: string;
|
|
229
|
-
displayName: string;
|
|
230
|
-
boost: number;
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* The Material Match response (non-streaming form).
|
|
234
|
-
*
|
|
235
|
-
* `poolSize` is the candidate pool the floor was applied to, NOT a corpus total
|
|
236
|
-
* and NOT "how many materials look like this". An empty `results` with
|
|
237
|
-
* `droppedBelowFloor > 0` means "candidates existed and none was good enough" —
|
|
238
|
-
* a different answer from `poolSize: 0`, which means the image found nothing to
|
|
239
|
-
* search against at all.
|
|
240
|
-
*/
|
|
241
|
-
export interface MatchResponse {
|
|
242
|
-
/** THIS answer, capped by `limit`. Not a total. */
|
|
243
|
-
results: SearchResultItem[];
|
|
244
|
-
inferredCategories: MatchCategoryInference[];
|
|
245
|
-
qualityFloor: number;
|
|
246
|
-
poolSize: number;
|
|
247
|
-
droppedBelowFloor: number;
|
|
248
|
-
droppedAsDuplicateProduct: number;
|
|
249
|
-
note?: string;
|
|
250
|
-
}
|
|
251
|
-
/** Coarse group + fine type of a colour match's primary product type. */
|
|
252
|
-
export interface MatchCategory {
|
|
253
|
-
group: string | null;
|
|
254
|
-
type: string | null;
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* One colour-palette match.
|
|
258
|
-
*
|
|
259
|
-
* `hex`, `distance` and `closeness` are OKLab geometry over colour EXTRACTED
|
|
260
|
-
* FROM PRODUCT IMAGERY, not declared by the manufacturer. Report it as visual
|
|
261
|
-
* likeness ("reads close to this colour"), never as a stated colour
|
|
262
|
-
* specification.
|
|
263
|
-
*/
|
|
264
|
-
export interface ByColourMatch {
|
|
265
|
-
/** Opaque — round-trip verbatim. */
|
|
266
|
-
variantId: VariantId;
|
|
267
|
-
brand: string;
|
|
268
|
-
product: string;
|
|
269
|
-
color: string | null;
|
|
270
|
-
variantName: string | null;
|
|
271
|
-
imageUrl: string | null;
|
|
272
|
-
hex: string | null;
|
|
273
|
-
distance: number;
|
|
274
|
-
closeness: number;
|
|
275
|
-
category: MatchCategory | null;
|
|
276
|
-
oklab: OklabTriple | null;
|
|
277
|
-
}
|
|
278
|
-
/** The `search/colours` response. */
|
|
279
|
-
export interface ByColourResponse {
|
|
280
|
-
query: {
|
|
281
|
-
colours: string[];
|
|
282
|
-
categories?: string[];
|
|
283
|
-
oklab?: OklabTriple;
|
|
284
|
-
};
|
|
285
|
-
/** `matches.length` — THIS PAGE. Not a corpus total; this surface states none. */
|
|
286
|
-
count: number;
|
|
287
|
-
matches: ByColourMatch[];
|
|
288
|
-
}
|
|
289
290
|
/** One item from the pure colour-scalar browse. */
|
|
290
291
|
export interface BrowseResultItem {
|
|
291
292
|
/** Opaque — round-trip verbatim. */
|
|
@@ -303,6 +304,17 @@ export interface BrowseResultItem {
|
|
|
303
304
|
vibrancy: number | null;
|
|
304
305
|
};
|
|
305
306
|
retired?: boolean;
|
|
307
|
+
/**
|
|
308
|
+
* The row's own image palette — the FULL stored palette of the SAME image
|
|
309
|
+
* `imageUrl` serves, uncapped and in stored order, each swatch with `oklab`
|
|
310
|
+
* (see {@link ByColourSwatch}), so a card can render, sort or group colours
|
|
311
|
+
* without a second call. A one-swatch palette means the extractor found one
|
|
312
|
+
* colour (solid-colour materials are genuinely monochrome), not that the list
|
|
313
|
+
* was trimmed. ABSENT when no palette is on record for that image: absent
|
|
314
|
+
* means unknown, NOT that the material has no colours. These swatches come
|
|
315
|
+
* from product imagery, not from the manufacturer.
|
|
316
|
+
*/
|
|
317
|
+
palette?: ByColourSwatch[];
|
|
306
318
|
}
|
|
307
319
|
/**
|
|
308
320
|
* A single page of the colour browse — the SDK's one genuinely cursor-paginated
|
|
@@ -508,6 +520,7 @@ export interface ResolveAttributeResponse {
|
|
|
508
520
|
candidates: AttributeCandidate[];
|
|
509
521
|
}
|
|
510
522
|
export type * from "./catalog-discovery-types.js";
|
|
523
|
+
export type * from "./resolve-types.js";
|
|
511
524
|
export type * from "./suggest-types.js";
|
|
512
525
|
export type * from "./by-spec-types.js";
|
|
513
526
|
export interface WhoamiResponse {
|
|
@@ -626,20 +639,6 @@ export interface SearchGetParams {
|
|
|
626
639
|
/** Result projection — `"full"` (default) or the lean `"card"` grid form. */
|
|
627
640
|
view?: OpenEnum<"full" | "card">;
|
|
628
641
|
}
|
|
629
|
-
/** Params for `search/colours`. */
|
|
630
|
-
export interface ByColourParams {
|
|
631
|
-
/** A single hex (with or without `#`). */
|
|
632
|
-
hex?: string;
|
|
633
|
-
/** A palette — repeated as one `colour` query entry each. */
|
|
634
|
-
colour?: string | string[];
|
|
635
|
-
/** OKLab triple (all-or-none). */
|
|
636
|
-
l?: number;
|
|
637
|
-
a?: number;
|
|
638
|
-
b?: number;
|
|
639
|
-
/** HARD category filter — display names or taxonomy ids (repeatable). */
|
|
640
|
-
category?: string | string[];
|
|
641
|
-
limit?: number;
|
|
642
|
-
}
|
|
643
642
|
/** Params for the pure colour `search/browse` surface. */
|
|
644
643
|
export interface BrowseParams {
|
|
645
644
|
sort: OpenEnum<"light" | "dark" | "vivid" | "muted" | "colourful">;
|
|
@@ -650,34 +649,14 @@ export interface BrowseParams {
|
|
|
650
649
|
cursor?: Cursor;
|
|
651
650
|
/** Page size. */
|
|
652
651
|
limit?: number;
|
|
653
|
-
/** Comma-separated product-type ids to restrict the browse to. */
|
|
654
|
-
types?: string;
|
|
655
|
-
}
|
|
656
|
-
/** The Material Match request body — exactly one of `imageBase64`, `imageUrl`, `vector`. */
|
|
657
|
-
export interface MatchInput {
|
|
658
|
-
imageBase64?: string;
|
|
659
|
-
imageUrl?: string;
|
|
660
|
-
vector?: string;
|
|
661
|
-
limit?: number;
|
|
662
|
-
qualityFloor?: number;
|
|
663
|
-
categoryTopK?: number;
|
|
664
|
-
categoryMinBoost?: number;
|
|
665
|
-
detectionLabel?: string;
|
|
666
|
-
materialFunction?: string;
|
|
667
|
-
detectionCategory?: string;
|
|
668
652
|
/**
|
|
669
|
-
*
|
|
670
|
-
*
|
|
671
|
-
* `
|
|
653
|
+
* Comma-separated taxonomy keys to restrict the browse to, at EITHER tier of
|
|
654
|
+
* `/api/v1/taxonomy/categories`. A GROUP key (`tile_surfaces`) expands to
|
|
655
|
+
* every product type under it, a TYPE key (`ceramic_porcelain_tile`) stands
|
|
656
|
+
* for itself, and a mixed list is their deduplicated union. A key in neither
|
|
657
|
+
* tier is a 400 listing the valid keys — never an empty page.
|
|
672
658
|
*/
|
|
673
|
-
|
|
674
|
-
requestedType?: OpenEnum<"swatch" | "installation" | "flatplan">;
|
|
675
|
-
/** Hard spec gates forwarded verbatim to the inner search — term match or presence, each with an optional trust bar. */
|
|
676
|
-
filters?: SearchFilter[];
|
|
677
|
-
/** Soft criteria (rank-never-exclude). A miss demotes within the page but never removes a hit. */
|
|
678
|
-
preferences?: SearchFilter[];
|
|
679
|
-
/** Opt in to seed-relative verdict cells; inert unless the inner search carries a variant seed. */
|
|
680
|
-
verdicts?: boolean;
|
|
659
|
+
types?: string;
|
|
681
660
|
}
|
|
682
661
|
/** Options for a batch material read. */
|
|
683
662
|
export interface MaterialsBatchOptions {
|