@instruments/materialgraph 0.25.0 → 0.26.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 +20 -20
- package/associations-client.d.ts +4 -4
- package/associations-client.js +3 -10
- package/attribution-types.d.ts +3 -3
- package/brand-most-sampled-types.d.ts +4 -4
- package/brand-types.d.ts +6 -6
- package/brands-client.d.ts +4 -4
- package/brands-client.js +9 -13
- package/browse-types.d.ts +8 -8
- package/by-colour-types.d.ts +18 -18
- package/by-spec-types.d.ts +14 -14
- package/catalog-discovery-types.d.ts +14 -14
- package/catalogue-client.d.ts +4 -4
- package/catalogue-client.js +38 -37
- package/catalogue-types.d.ts +4 -4
- package/citation-types.d.ts +4 -4
- package/client-browse.d.ts +4 -4
- package/client-browse.js +24 -25
- package/client-config.d.ts +2 -2
- package/client-exploration.d.ts +3 -3
- package/client-exploration.js +4 -6
- package/client-source-material-comments.d.ts +4 -4
- package/client-source-material-comments.js +4 -38
- package/client-vocabulary.d.ts +8 -8
- package/client-vocabulary.js +20 -30
- package/client.d.ts +11 -45
- package/client.js +143 -188
- package/codes-client.d.ts +2 -2
- package/codes-client.js +8 -8
- package/codes-types.d.ts +34 -34
- package/colour-aggregate-types.d.ts +24 -24
- package/colours-client.d.ts +2 -2
- package/colours-client.js +15 -15
- package/compare-types.d.ts +10 -10
- package/composition-client.d.ts +3 -3
- package/composition-client.js +8 -7
- package/composition-types.d.ts +12 -12
- package/coordinate-client.d.ts +2 -2
- package/coordinate-client.js +1 -13
- package/corpus-scope-types.d.ts +3 -3
- package/corpus-scope-types.js +1 -25
- package/correction-types.d.ts +10 -10
- package/demand-batch-types.d.ts +18 -18
- package/demand-types.d.ts +24 -24
- package/errors.d.ts +12 -12
- package/errors.js +53 -52
- package/exploration-types.d.ts +22 -22
- package/fan-deck-types.d.ts +6 -6
- package/http-transport.d.ts +13 -21
- package/http-transport.js +31 -28
- package/identify-types.d.ts +10 -10
- package/index.d.ts +0 -6
- package/index.js +0 -2
- package/match-rejection-types.d.ts +6 -6
- package/match-stream-types.d.ts +32 -32
- package/match-stream.d.ts +2 -2
- package/match-stream.js +55 -41
- package/match-types.d.ts +8 -8
- package/material-asset-types.d.ts +26 -26
- package/material-block-types.d.ts +6 -6
- package/material-query-plan-types.d.ts +26 -26
- package/material-resources-client.d.ts +5 -5
- package/material-resources-client.js +10 -20
- package/material-types.d.ts +12 -12
- package/ndjson.js +16 -8
- package/operations.d.ts +6 -6
- package/operations.js +9 -39
- package/package.json +1 -1
- package/palette-completion-types.d.ts +22 -22
- package/palette-search-types.d.ts +28 -28
- package/query-understanding-types.d.ts +38 -35
- package/render-assets-types.d.ts +8 -8
- package/resolve-client.d.ts +2 -2
- package/resolve-client.js +4 -6
- package/resolve-colour-types.d.ts +8 -8
- package/resolve-types.d.ts +10 -10
- package/run-types.d.ts +70 -70
- package/runs.d.ts +13 -26
- package/runs.js +77 -89
- package/samplize-catalogue-types.d.ts +18 -18
- package/samplize-colorvec.d.ts +20 -20
- package/samplize-colorvec.js +66 -42
- package/schema-resource-types.d.ts +38 -38
- package/schema-resource-types.js +5 -5
- package/search-card-types.d.ts +4 -4
- package/search-collapse-types.d.ts +4 -4
- package/search-mood-types.d.ts +2 -2
- package/search-refinement-types.d.ts +8 -8
- package/search-request-types.d.ts +5 -5
- package/search-response-receipt-types.d.ts +58 -42
- package/search-response-types.d.ts +2 -2
- package/search-result-validation.d.ts +3 -3
- package/search-result-validation.js +53 -36
- package/search-window-types.d.ts +2 -2
- package/slot-pool-types.d.ts +20 -20
- package/source-material-validation.d.ts +3 -3
- package/source-material-validation.js +35 -39
- package/spec-facet-types.d.ts +10 -10
- package/spec-types.d.ts +6 -6
- package/specifications.d.ts +9 -25
- package/specifications.js +15 -21
- package/sse.d.ts +2 -2
- package/sse.js +32 -16
- package/substitute-types.d.ts +22 -22
- package/suggest-types.d.ts +7 -7
- package/suggest-types.js +3 -3
- package/taxonomy-coverage-types.d.ts +8 -8
- package/types.d.ts +119 -119
- package/validation.d.ts +26 -26
- package/validation.js +97 -92
- package/webmcp.d.ts +2 -2
- package/webmcp.generated.js +94 -93
- package/project-types.d.ts +0 -79
- package/project-types.js +0 -9
- package/projects.d.ts +0 -13
- package/projects.js +0 -46
- package/run-events.d.ts +0 -106
- package/run-events.js +0 -32
- package/schedule-reference-types.d.ts +0 -46
- package/schedule-reference-types.js +0 -1
- package/schedule-references-client.d.ts +0 -14
- package/schedule-references-client.js +0 -26
- package/schedule-types.d.ts +0 -261
- package/schedule-types.js +0 -1
package/match-stream-types.d.ts
CHANGED
|
@@ -42,14 +42,14 @@ export type MatchSurfaceStatus = (typeof MATCH_SURFACE_STATUSES)[number];
|
|
|
42
42
|
export declare const MATCH_STREAM_EVENT_TYPES: readonly ["step", "classified", "surfaces", "surface", "heartbeat", "complete", "error"];
|
|
43
43
|
export type MatchStreamEventType = (typeof MATCH_STREAM_EVENT_TYPES)[number];
|
|
44
44
|
/** Where a surface sits in the image, in Gemini-style 0–1000 image space. */
|
|
45
|
-
export
|
|
45
|
+
export type MatchBoundingBox = {
|
|
46
46
|
x0: number;
|
|
47
47
|
y0: number;
|
|
48
48
|
x1: number;
|
|
49
49
|
y1: number;
|
|
50
|
-
}
|
|
50
|
+
};
|
|
51
51
|
/** One surface the decompose step found in the image. */
|
|
52
|
-
export
|
|
52
|
+
export type MatchDetectedSurface = {
|
|
53
53
|
id: string;
|
|
54
54
|
label: string;
|
|
55
55
|
materialFunction: MatchMaterialFunction;
|
|
@@ -61,7 +61,7 @@ export interface MatchDetectedSurface {
|
|
|
61
61
|
boundingBox: MatchBoundingBox;
|
|
62
62
|
/** JPEG data-URI crop used for Match retrieval — the thumbnail to show. */
|
|
63
63
|
cropDataUri?: string;
|
|
64
|
-
}
|
|
64
|
+
};
|
|
65
65
|
/**
|
|
66
66
|
* One surface's matching state and its ranked results. The same `surface.id`
|
|
67
67
|
* arrives repeatedly as the run progresses (`matching` → `complete` / `error`);
|
|
@@ -86,7 +86,7 @@ export interface MatchDetectedSurface {
|
|
|
86
86
|
* [0,1] — the same unit as each result's `confidence` — so they belong on one
|
|
87
87
|
* axis. `poolSize` and `droppedBelowFloor` are counts and belong on neither.
|
|
88
88
|
*/
|
|
89
|
-
export
|
|
89
|
+
export type MatchComparison = {
|
|
90
90
|
/** Candidates retrieved before the quality floor and product dedup. */
|
|
91
91
|
poolSize: number;
|
|
92
92
|
/** How many of those the quality floor rejected. */
|
|
@@ -98,8 +98,8 @@ export interface MatchComparison {
|
|
|
98
98
|
* nothing was rejected, or when the rejects carried no readable signal.
|
|
99
99
|
*/
|
|
100
100
|
bestRejected?: number;
|
|
101
|
-
}
|
|
102
|
-
export
|
|
101
|
+
};
|
|
102
|
+
export type MatchSurfaceResult = {
|
|
103
103
|
surface: MatchDetectedSurface;
|
|
104
104
|
status: MatchSurfaceStatus;
|
|
105
105
|
results: SearchResultItem[];
|
|
@@ -150,10 +150,10 @@ export interface MatchSurfaceResult {
|
|
|
150
150
|
*/
|
|
151
151
|
colour?: MatchColourReading[];
|
|
152
152
|
/** The segment's own extracted palette — what the colour stage matched against. */
|
|
153
|
-
segmentPalette?:
|
|
153
|
+
segmentPalette?: {
|
|
154
154
|
hex: string;
|
|
155
155
|
proportion: number;
|
|
156
|
-
}
|
|
156
|
+
}[];
|
|
157
157
|
/**
|
|
158
158
|
* One sentence of provenance a consumer may render verbatim, when the server
|
|
159
159
|
* sent one — e.g. the `colour_fallback` explanation carrying the refused
|
|
@@ -162,13 +162,13 @@ export interface MatchSurfaceResult {
|
|
|
162
162
|
note?: string;
|
|
163
163
|
/** Present only on `status: "error"` — why this one surface failed. */
|
|
164
164
|
error?: string;
|
|
165
|
-
}
|
|
165
|
+
};
|
|
166
166
|
/** The verifier's reading of one candidate, keyed back to its variant. */
|
|
167
|
-
export
|
|
167
|
+
export type MatchSenseCheckCell = {
|
|
168
168
|
variantId: string;
|
|
169
169
|
verdict: MatchSenseCheckVerdict;
|
|
170
170
|
reason: string;
|
|
171
|
-
}
|
|
171
|
+
};
|
|
172
172
|
export declare const MATCH_SENSE_CHECK_VERDICTS: readonly ["confirmed", "rejected", "abstained"];
|
|
173
173
|
export type MatchSenseCheckVerdict = (typeof MATCH_SENSE_CHECK_VERDICTS)[number];
|
|
174
174
|
/** The terminal Match workflow result — every surface in its final state. */
|
|
@@ -179,7 +179,7 @@ export type MatchSenseCheckVerdict = (typeof MATCH_SENSE_CHECK_VERDICTS)[number]
|
|
|
179
179
|
*/
|
|
180
180
|
export declare const MATCH_OUTCOMES: readonly ["matched", "no_surfaces"];
|
|
181
181
|
export type MatchOutcome = (typeof MATCH_OUTCOMES)[number];
|
|
182
|
-
export
|
|
182
|
+
export type MatchWorkflowResult = {
|
|
183
183
|
/**
|
|
184
184
|
* Result attribution (MG-1131) — `responseId` (the same value as the run's
|
|
185
185
|
* `X-Request-Id` header), `basis: "match"`, and `issuedAt`. Every item in
|
|
@@ -214,10 +214,10 @@ export interface MatchWorkflowResult {
|
|
|
214
214
|
*/
|
|
215
215
|
paletteMatches?: {
|
|
216
216
|
results: SearchResultItem[];
|
|
217
|
-
palette:
|
|
217
|
+
palette: {
|
|
218
218
|
hex: string;
|
|
219
219
|
proportion: number;
|
|
220
|
-
}
|
|
220
|
+
}[];
|
|
221
221
|
note: string;
|
|
222
222
|
};
|
|
223
223
|
/**
|
|
@@ -225,23 +225,23 @@ export interface MatchWorkflowResult {
|
|
|
225
225
|
* nowhere else to put this. A scene run reports per surface instead.
|
|
226
226
|
*/
|
|
227
227
|
comparison?: MatchComparison;
|
|
228
|
-
}
|
|
228
|
+
};
|
|
229
229
|
/** Progress narration for the phase the workflow just entered. */
|
|
230
|
-
export
|
|
230
|
+
export type MatchStreamStepEvent = {
|
|
231
231
|
type: "step";
|
|
232
232
|
step: MatchWorkflowStep;
|
|
233
233
|
message: string;
|
|
234
|
-
}
|
|
234
|
+
};
|
|
235
235
|
/** The image type the classify step settled on. */
|
|
236
|
-
export
|
|
236
|
+
export type MatchStreamClassifiedEvent = {
|
|
237
237
|
type: "classified";
|
|
238
238
|
imageType: MatchImageType;
|
|
239
|
-
}
|
|
239
|
+
};
|
|
240
240
|
/** Every surface the decompose step found, before any of them has matched. */
|
|
241
|
-
export
|
|
241
|
+
export type MatchStreamSurfacesEvent = {
|
|
242
242
|
type: "surfaces";
|
|
243
243
|
surfaces: MatchDetectedSurface[];
|
|
244
|
-
}
|
|
244
|
+
};
|
|
245
245
|
/**
|
|
246
246
|
* The run's tally at the moment a `surface` event was emitted, so a consumer
|
|
247
247
|
* reads the server's arithmetic rather than reimplementing it.
|
|
@@ -252,7 +252,7 @@ export interface MatchStreamSurfacesEvent {
|
|
|
252
252
|
* `matchesSoFar`. `surfacesResolved === surfacesDetected` is therefore the
|
|
253
253
|
* correct "the run is done" test, and never stalls at 3/4 on a failure.
|
|
254
254
|
*/
|
|
255
|
-
export
|
|
255
|
+
export type MatchRunTotals = {
|
|
256
256
|
/** Surfaces the decompose step found. The denominator; fixed for the run. */
|
|
257
257
|
surfacesDetected: number;
|
|
258
258
|
/** Surfaces that reached a terminal state — `complete` OR `error`. */
|
|
@@ -261,14 +261,14 @@ export interface MatchRunTotals {
|
|
|
261
261
|
surfacesFailed: number;
|
|
262
262
|
/** Result items accumulated across every surface resolved so far. */
|
|
263
263
|
matchesSoFar: number;
|
|
264
|
-
}
|
|
264
|
+
};
|
|
265
265
|
/** One surface's state changed — the frame that makes Match progressive. */
|
|
266
|
-
export
|
|
266
|
+
export type MatchStreamSurfaceEvent = {
|
|
267
267
|
type: "surface";
|
|
268
268
|
surface: MatchSurfaceResult;
|
|
269
269
|
/** The run's tally as of this frame. Always present on a `surface` event. */
|
|
270
270
|
totals: MatchRunTotals;
|
|
271
|
-
}
|
|
271
|
+
};
|
|
272
272
|
/**
|
|
273
273
|
* CONNECTION LIVENESS ONLY — never progress. Proxies and load balancers kill
|
|
274
274
|
* idle connections, so the server sends this when the stream has been quiet, and
|
|
@@ -276,18 +276,18 @@ export interface MatchStreamSurfaceEvent {
|
|
|
276
276
|
* consumer should treat it as a state change: do not advance a step, reset a
|
|
277
277
|
* no-progress watchdog, or count it towards anything.
|
|
278
278
|
*/
|
|
279
|
-
export
|
|
279
|
+
export type MatchStreamHeartbeatEvent = {
|
|
280
280
|
type: "heartbeat";
|
|
281
|
-
}
|
|
281
|
+
};
|
|
282
282
|
/** Terminal: the whole run finished and carries its assembled result. */
|
|
283
|
-
export
|
|
283
|
+
export type MatchStreamCompleteEvent = {
|
|
284
284
|
type: "complete";
|
|
285
285
|
result: MatchWorkflowResult;
|
|
286
|
-
}
|
|
286
|
+
};
|
|
287
287
|
/** Terminal: the run failed as a whole (a single surface failing is not this). */
|
|
288
|
-
export
|
|
288
|
+
export type MatchStreamErrorEvent = {
|
|
289
289
|
type: "error";
|
|
290
290
|
message: string;
|
|
291
|
-
}
|
|
291
|
+
};
|
|
292
292
|
/** One frame of the Match progress stream, discriminated by `type`. */
|
|
293
293
|
export type MatchStreamEvent = MatchStreamStepEvent | MatchStreamClassifiedEvent | MatchStreamSurfacesEvent | MatchStreamSurfaceEvent | MatchStreamHeartbeatEvent | MatchStreamCompleteEvent | MatchStreamErrorEvent;
|
package/match-stream.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HttpTransport } from "./http-transport.js";
|
|
2
|
-
import {
|
|
2
|
+
import type { MatchStreamEvent } from "./match-stream-types.js";
|
|
3
3
|
import type { MatchInput } from "./match-types.js";
|
|
4
4
|
/**
|
|
5
5
|
* Decode one NDJSON line into a validated {@link MatchStreamEvent}, raising a
|
|
@@ -7,7 +7,7 @@ import type { MatchInput } from "./match-types.js";
|
|
|
7
7
|
* for `contract-parity.test.ts`, which re-checks it against the real Zod schema;
|
|
8
8
|
* it is not part of the published surface.
|
|
9
9
|
*/
|
|
10
|
-
export declare
|
|
10
|
+
export declare const decodeMatchStreamEvent: (line: string) => MatchStreamEvent;
|
|
11
11
|
/**
|
|
12
12
|
* Stream Match progress as an async iterator. Iterates until the run reaches a
|
|
13
13
|
* `complete` or an `error` event, and raises if the connection ends before one
|
package/match-stream.js
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
import { MaterialGraphError } from "./errors.js";
|
|
58
58
|
import { MATCH_IMAGE_TYPES, MATCH_MATERIAL_FUNCTIONS, MATCH_STREAM_EVENT_TYPES, MATCH_SURFACE_STATUSES, MATCH_WORKFLOW_STEPS, } from "./match-stream-types.js";
|
|
59
59
|
import { readNdjsonLines } from "./ndjson.js";
|
|
60
|
-
import { findSdkOperation
|
|
60
|
+
import { findSdkOperation } from "./operations.js";
|
|
61
61
|
import { checkSearchResultItems } from "./search-result-validation.js";
|
|
62
62
|
import { requireArray, requireBoundedNumber, requireDefaultedArray, requireEnum, requireIntegerAtLeast, requireNumber, requireOptionalString, requireRecord, requireString, } from "./validation.js";
|
|
63
63
|
/** The NDJSON media type the Match route negotiates the scene workflow on. */
|
|
@@ -70,34 +70,37 @@ const BOUNDING_BOX_CORNERS = Object.freeze([
|
|
|
70
70
|
"x1",
|
|
71
71
|
"y1",
|
|
72
72
|
]);
|
|
73
|
-
|
|
73
|
+
const operation = (operationId) => {
|
|
74
74
|
const found = findSdkOperation(operationId);
|
|
75
|
-
if (!found)
|
|
75
|
+
if (!found) {
|
|
76
76
|
throw MaterialGraphError.unexpected(`Unknown SDK operation: ${operationId}`);
|
|
77
|
+
}
|
|
77
78
|
return found;
|
|
78
|
-
}
|
|
79
|
+
};
|
|
79
80
|
/**
|
|
80
81
|
* The failure shape every check in this module raises: one message naming the
|
|
81
82
|
* offending path and quoting a bounded slice of the frame.
|
|
82
83
|
*/
|
|
83
|
-
|
|
84
|
+
const frameFailure = (line) => {
|
|
84
85
|
const preview = line.length > MAX_FRAME_PREVIEW
|
|
85
86
|
? `${line.slice(0, MAX_FRAME_PREVIEW)}…`
|
|
86
87
|
: line;
|
|
87
88
|
return (reason) => MaterialGraphError.unexpected(`MaterialGraph match stream sent a malformed NDJSON frame: ${reason}. Frame: ${preview}`);
|
|
88
|
-
}
|
|
89
|
+
};
|
|
89
90
|
/** Check one `MatchCategoryInference` item. */
|
|
90
|
-
|
|
91
|
+
const checkCategoryInference = (value, what, fail) => {
|
|
91
92
|
const record = requireRecord(value, what, fail);
|
|
92
93
|
requireString(record.productTypeId, `${what}.productTypeId`, fail);
|
|
93
94
|
requireString(record.displayName, `${what}.displayName`, fail);
|
|
94
95
|
requireNumber(record.boost, `${what}.boost`, fail);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
items.forEach((item, index) =>
|
|
98
|
-
}
|
|
96
|
+
};
|
|
97
|
+
const checkCategoryInferences = (items, what, fail) => {
|
|
98
|
+
items.forEach((item, index) => {
|
|
99
|
+
checkCategoryInference(item, `${what}[${index}]`, fail);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
99
102
|
/** Check one detected surface — identity, function, confidence, and its box. */
|
|
100
|
-
|
|
103
|
+
const checkDetectedSurface = (value, what, fail) => {
|
|
101
104
|
const record = requireRecord(value, what, fail);
|
|
102
105
|
requireString(record.id, `${what}.id`, fail);
|
|
103
106
|
requireString(record.label, `${what}.label`, fail);
|
|
@@ -110,12 +113,12 @@ function checkDetectedSurface(value, what, fail) {
|
|
|
110
113
|
for (const corner of BOUNDING_BOX_CORNERS) {
|
|
111
114
|
requireNumber(box[corner], `${what}.boundingBox.${corner}`, fail);
|
|
112
115
|
}
|
|
113
|
-
}
|
|
116
|
+
};
|
|
114
117
|
/**
|
|
115
118
|
* Check one surface's matching state and fill the canonical `.default([])`
|
|
116
119
|
* arrays, so a consumer never meets `undefined` where the type promises an array.
|
|
117
120
|
*/
|
|
118
|
-
|
|
121
|
+
const readSurfaceResult = (value, what, fail) => {
|
|
119
122
|
const record = requireRecord(value, what, fail);
|
|
120
123
|
checkDetectedSurface(record.surface, `${what}.surface`, fail);
|
|
121
124
|
requireEnum(record.status, MATCH_SURFACE_STATUSES, `${what}.status`, fail);
|
|
@@ -124,8 +127,8 @@ function readSurfaceResult(value, what, fail) {
|
|
|
124
127
|
checkSearchResultItems(results, `${what}.results`, fail);
|
|
125
128
|
const inferredCategories = requireDefaultedArray(record.inferredCategories, `${what}.inferredCategories`, fail);
|
|
126
129
|
checkCategoryInferences(inferredCategories, `${what}.inferredCategories`, fail);
|
|
127
|
-
return { ...record,
|
|
128
|
-
}
|
|
130
|
+
return { ...record, inferredCategories, results };
|
|
131
|
+
};
|
|
129
132
|
/**
|
|
130
133
|
* `matchRunTotalsSchema` — four non-negative integers plus the TWO cross-field
|
|
131
134
|
* invariants its `.superRefine()` enforces. Relational rather than field-wise,
|
|
@@ -135,7 +138,7 @@ function readSurfaceResult(value, what, fail) {
|
|
|
135
138
|
* `matchesSoFar` is deliberately unbounded against the surface counts — one
|
|
136
139
|
* surface can carry many results and another none, so no ratio is principled.
|
|
137
140
|
*/
|
|
138
|
-
|
|
141
|
+
const checkRunTotals = (value, what, fail) => {
|
|
139
142
|
const record = requireRecord(value, what, fail);
|
|
140
143
|
const detected = requireIntegerAtLeast(record.surfacesDetected, `${what}.surfacesDetected`, fail, 0);
|
|
141
144
|
const resolved = requireIntegerAtLeast(record.surfacesResolved, `${what}.surfacesResolved`, fail, 0);
|
|
@@ -149,9 +152,9 @@ function checkRunTotals(value, what, fail) {
|
|
|
149
152
|
if (resolved > detected) {
|
|
150
153
|
throw fail(`${what}.surfacesResolved (${resolved}) exceeds ${what}.surfacesDetected (${detected}) — a run cannot resolve more surfaces than it found`);
|
|
151
154
|
}
|
|
152
|
-
}
|
|
155
|
+
};
|
|
153
156
|
/** Check the terminal workflow result — image type, surfaces, optional roll-ups. */
|
|
154
|
-
|
|
157
|
+
const readWorkflowResult = (value, what, fail) => {
|
|
155
158
|
const record = requireRecord(value, what, fail);
|
|
156
159
|
requireEnum(record.imageType, MATCH_IMAGE_TYPES, `${what}.imageType`, fail);
|
|
157
160
|
const surfaces = requireArray(record.surfaces, `${what}.surfaces`, fail).map((surface, index) => readSurfaceResult(surface, `${what}.surfaces[${index}]`, fail));
|
|
@@ -164,26 +167,30 @@ function readWorkflowResult(value, what, fail) {
|
|
|
164
167
|
checkCategoryInferences(requireArray(record.inferredCategories, `${what}.inferredCategories`, fail), `${what}.inferredCategories`, fail);
|
|
165
168
|
}
|
|
166
169
|
return { ...record, surfaces };
|
|
167
|
-
}
|
|
170
|
+
};
|
|
168
171
|
/**
|
|
169
172
|
* Validate one frame against the canonical contract and apply the schema's
|
|
170
173
|
* `.default([])` fills, returning the event as a plain record. Unknown keys ride
|
|
171
174
|
* through untouched — see the pass-through note in the module header.
|
|
172
175
|
*/
|
|
173
|
-
|
|
176
|
+
const readFrame = (value, fail) => {
|
|
174
177
|
const record = requireRecord(value, "the frame", fail);
|
|
175
178
|
const type = requireString(record.type, "`type`", fail);
|
|
176
179
|
switch (type) {
|
|
177
|
-
case "step":
|
|
180
|
+
case "step": {
|
|
178
181
|
requireEnum(record.step, MATCH_WORKFLOW_STEPS, "`step`", fail);
|
|
179
182
|
requireString(record.message, "`message`", fail);
|
|
180
183
|
return record;
|
|
181
|
-
|
|
184
|
+
}
|
|
185
|
+
case "classified": {
|
|
182
186
|
requireEnum(record.imageType, MATCH_IMAGE_TYPES, "`imageType`", fail);
|
|
183
187
|
return record;
|
|
188
|
+
}
|
|
184
189
|
case "surfaces": {
|
|
185
190
|
const surfaces = requireArray(record.surfaces, "`surfaces`", fail);
|
|
186
|
-
surfaces.forEach((surface, index) =>
|
|
191
|
+
surfaces.forEach((surface, index) => {
|
|
192
|
+
checkDetectedSurface(surface, `surfaces[${index}]`, fail);
|
|
193
|
+
});
|
|
187
194
|
return record;
|
|
188
195
|
}
|
|
189
196
|
case "surface": {
|
|
@@ -193,37 +200,41 @@ function readFrame(value, fail) {
|
|
|
193
200
|
checkRunTotals(record.totals, "`totals`", fail);
|
|
194
201
|
return { ...record, surface };
|
|
195
202
|
}
|
|
196
|
-
case "heartbeat":
|
|
203
|
+
case "heartbeat": {
|
|
197
204
|
// Payload-free by contract — there is nothing else to check.
|
|
198
205
|
return record;
|
|
199
|
-
|
|
206
|
+
}
|
|
207
|
+
case "complete": {
|
|
200
208
|
return {
|
|
201
209
|
...record,
|
|
202
210
|
result: readWorkflowResult(record.result, "`result`", fail),
|
|
203
211
|
};
|
|
204
|
-
|
|
212
|
+
}
|
|
213
|
+
case "error": {
|
|
205
214
|
requireString(record.message, "`message`", fail);
|
|
206
215
|
return record;
|
|
207
|
-
|
|
216
|
+
}
|
|
217
|
+
default: {
|
|
208
218
|
throw fail(`\`type\` is not a known Match stream event (${type})`);
|
|
219
|
+
}
|
|
209
220
|
}
|
|
210
|
-
}
|
|
221
|
+
};
|
|
211
222
|
/**
|
|
212
223
|
* The single narrowing boundary of this module. {@link readFrame} has already
|
|
213
224
|
* proven every field the union's shape depends on; re-checking the discriminant
|
|
214
225
|
* here keeps the assertion from being vacuous.
|
|
215
226
|
*/
|
|
216
|
-
|
|
227
|
+
const assertMatchStreamEvent = (frame, fail) => {
|
|
217
228
|
const record = requireRecord(frame, "the frame", fail);
|
|
218
229
|
requireEnum(record.type, MATCH_STREAM_EVENT_TYPES, "`type`", fail);
|
|
219
|
-
}
|
|
230
|
+
};
|
|
220
231
|
/**
|
|
221
232
|
* Decode one NDJSON line into a validated {@link MatchStreamEvent}, raising a
|
|
222
233
|
* {@link MaterialGraphError} on anything the contract does not allow. Exported
|
|
223
234
|
* for `contract-parity.test.ts`, which re-checks it against the real Zod schema;
|
|
224
235
|
* it is not part of the published surface.
|
|
225
236
|
*/
|
|
226
|
-
export
|
|
237
|
+
export const decodeMatchStreamEvent = (line) => {
|
|
227
238
|
const fail = frameFailure(line);
|
|
228
239
|
let parsed;
|
|
229
240
|
try {
|
|
@@ -235,10 +246,8 @@ export function decodeMatchStreamEvent(line) {
|
|
|
235
246
|
const frame = readFrame(parsed, fail);
|
|
236
247
|
assertMatchStreamEvent(frame, fail);
|
|
237
248
|
return frame;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return event.type === "complete" || event.type === "error";
|
|
241
|
-
}
|
|
249
|
+
};
|
|
250
|
+
const isTerminal = (event) => event.type === "complete" || event.type === "error";
|
|
242
251
|
/**
|
|
243
252
|
* Stream Match progress as an async iterator. Iterates until the run reaches a
|
|
244
253
|
* `complete` or an `error` event, and raises if the connection ends before one
|
|
@@ -251,33 +260,38 @@ function isTerminal(event) {
|
|
|
251
260
|
* it cancelled, so the truncated-stream error is not raised for it.
|
|
252
261
|
*/
|
|
253
262
|
export async function* matchStream(transport, input, signal) {
|
|
254
|
-
if (signal?.aborted === true)
|
|
263
|
+
if (signal?.aborted === true) {
|
|
255
264
|
return;
|
|
265
|
+
}
|
|
256
266
|
// Our own controller, so abandoning the iterator cancels the request too.
|
|
257
267
|
const controller = new AbortController();
|
|
258
|
-
const forwardAbort = () =>
|
|
268
|
+
const forwardAbort = () => {
|
|
269
|
+
controller.abort();
|
|
270
|
+
};
|
|
259
271
|
signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
260
272
|
try {
|
|
261
273
|
let body;
|
|
262
274
|
try {
|
|
263
275
|
body = await transport.openStream(operation("match_material"), {
|
|
276
|
+
accept: MATCH_STREAM_ACCEPT,
|
|
264
277
|
body: input,
|
|
265
278
|
signal: controller.signal,
|
|
266
|
-
accept: MATCH_STREAM_ACCEPT,
|
|
267
279
|
subject: "match stream",
|
|
268
280
|
});
|
|
269
281
|
}
|
|
270
282
|
catch (error) {
|
|
271
283
|
// Cancelled before the response headers landed — the caller's own stop.
|
|
272
|
-
if (controller.signal.aborted)
|
|
284
|
+
if (controller.signal.aborted) {
|
|
273
285
|
return;
|
|
286
|
+
}
|
|
274
287
|
throw error;
|
|
275
288
|
}
|
|
276
289
|
for await (const line of readNdjsonLines(body, controller.signal)) {
|
|
277
290
|
const event = decodeMatchStreamEvent(line);
|
|
278
291
|
yield event;
|
|
279
|
-
if (isTerminal(event))
|
|
292
|
+
if (isTerminal(event)) {
|
|
280
293
|
return;
|
|
294
|
+
}
|
|
281
295
|
}
|
|
282
296
|
if (!controller.signal.aborted) {
|
|
283
297
|
throw MaterialGraphError.unexpected("MaterialGraph match stream ended without a `complete` or `error` event — the connection was truncated");
|
package/match-types.d.ts
CHANGED
|
@@ -12,11 +12,11 @@ import type { CorpusScope } from "./corpus-scope-types.js";
|
|
|
12
12
|
import type { SearchFilter } from "./search-refinement-types.js";
|
|
13
13
|
import type { OpenEnum, PaletteSwatch, SearchResultItem } from "./types.js";
|
|
14
14
|
/** An inferred product-type category from the query image, with its boost. */
|
|
15
|
-
export
|
|
15
|
+
export type MatchCategoryInference = {
|
|
16
16
|
productTypeId: string;
|
|
17
17
|
displayName: string;
|
|
18
18
|
boost: number;
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
20
|
/**
|
|
21
21
|
* The Material Match response (non-streaming form).
|
|
22
22
|
*
|
|
@@ -31,13 +31,13 @@ export interface MatchCategoryInference {
|
|
|
31
31
|
* means the material carries no palette on record — UNKNOWN, never "no colour"
|
|
32
32
|
* and never "the wrong colour".
|
|
33
33
|
*/
|
|
34
|
-
export
|
|
34
|
+
export type MatchColourReading = {
|
|
35
35
|
variantId: string;
|
|
36
36
|
colourDistance: number | null;
|
|
37
37
|
colourBand: string | null;
|
|
38
38
|
measurable: boolean;
|
|
39
|
-
}
|
|
40
|
-
export
|
|
39
|
+
};
|
|
40
|
+
export type MatchResponse = {
|
|
41
41
|
/**
|
|
42
42
|
* Result attribution (MG-1131). `basis` is always `"match"` here — this route
|
|
43
43
|
* has no `mode`, so this is the field that lets a Match outcome be grouped
|
|
@@ -73,9 +73,9 @@ export interface MatchResponse {
|
|
|
73
73
|
/** The query crop's own extracted palette — what the colour stage compared against. */
|
|
74
74
|
segmentPalette?: PaletteSwatch[] | null;
|
|
75
75
|
note?: string;
|
|
76
|
-
}
|
|
76
|
+
};
|
|
77
77
|
/** The Material Match request body — exactly one of `imageBase64`, `imageUrl`, `vector`. */
|
|
78
|
-
export
|
|
78
|
+
export type MatchInput = {
|
|
79
79
|
imageBase64?: string;
|
|
80
80
|
imageUrl?: string;
|
|
81
81
|
vector?: string;
|
|
@@ -111,4 +111,4 @@ export interface MatchInput {
|
|
|
111
111
|
preferences?: SearchFilter[];
|
|
112
112
|
/** Opt in to seed-relative verdict cells; inert unless the inner search carries a variant seed. */
|
|
113
113
|
verdicts?: boolean;
|
|
114
|
-
}
|
|
114
|
+
};
|
|
@@ -18,7 +18,7 @@ export type MaterialAssetVisualRole = OpenEnum<"grid" | "detail" | "scene" | "sw
|
|
|
18
18
|
* - `retired_placeholder` — a stand-in for a withdrawn asset.
|
|
19
19
|
*/
|
|
20
20
|
export type MaterialAssetStatus = OpenEnum<"downloaded" | "linked_not_downloaded" | "failed" | "retired_placeholder">;
|
|
21
|
-
export
|
|
21
|
+
export type MaterialAssetProjectionImage = {
|
|
22
22
|
key: string;
|
|
23
23
|
ordinal: number;
|
|
24
24
|
proxyPath: string;
|
|
@@ -28,8 +28,8 @@ export interface MaterialAssetProjectionImage {
|
|
|
28
28
|
fileExtension: string | null;
|
|
29
29
|
filename: string | null;
|
|
30
30
|
byteSize: number | null;
|
|
31
|
-
}
|
|
32
|
-
export
|
|
31
|
+
};
|
|
32
|
+
export type MaterialAssetProjectionDownload = {
|
|
33
33
|
ordinal: number;
|
|
34
34
|
url: string;
|
|
35
35
|
label: string | null;
|
|
@@ -38,20 +38,20 @@ export interface MaterialAssetProjectionDownload {
|
|
|
38
38
|
contentType: string | null;
|
|
39
39
|
fileExtension: string | null;
|
|
40
40
|
filename: string | null;
|
|
41
|
-
}
|
|
41
|
+
};
|
|
42
42
|
export type MaterialDocumentStatus = OpenEnum<"downloaded" | "linked_not_downloaded" | "unavailable_source" | "dead_link" | "failed_ingest">;
|
|
43
43
|
export type MaterialDocumentCoverageState = OpenEnum<"available" | "none_declared" | "not_ingested" | "unavailable_source" | "dead_link" | "failed_ingest">;
|
|
44
|
-
export
|
|
44
|
+
export type MaterialDocumentScope = {
|
|
45
45
|
kind: "product" | "variant";
|
|
46
46
|
id: string;
|
|
47
|
-
}
|
|
48
|
-
export
|
|
47
|
+
};
|
|
48
|
+
export type MaterialDocumentSource = {
|
|
49
49
|
/** Opaque, public identifier for one source collection; never a PIM/DAM path. */
|
|
50
50
|
id: string;
|
|
51
51
|
authority: string;
|
|
52
52
|
revision: string;
|
|
53
|
-
}
|
|
54
|
-
export
|
|
53
|
+
};
|
|
54
|
+
export type MaterialDocumentProjectionItem = {
|
|
55
55
|
id: string;
|
|
56
56
|
scope: MaterialDocumentScope & {
|
|
57
57
|
inherited: boolean;
|
|
@@ -61,12 +61,12 @@ export interface MaterialDocumentProjectionItem {
|
|
|
61
61
|
/** True only for a Product declaration or this exact requested Variant. */
|
|
62
62
|
appliesToRequestedVariant: boolean;
|
|
63
63
|
/** Every exact source-grain declaration collapsed into this URL identity. */
|
|
64
|
-
declarations:
|
|
64
|
+
declarations: {
|
|
65
65
|
scope: MaterialDocumentScope;
|
|
66
66
|
source: MaterialDocumentSource;
|
|
67
67
|
status: MaterialDocumentStatus;
|
|
68
68
|
readable: boolean;
|
|
69
|
-
}
|
|
69
|
+
}[];
|
|
70
70
|
documentType: string | null;
|
|
71
71
|
originalName: string;
|
|
72
72
|
status: MaterialDocumentStatus;
|
|
@@ -75,46 +75,46 @@ export interface MaterialDocumentProjectionItem {
|
|
|
75
75
|
contentType: string | null;
|
|
76
76
|
fileExtension: string | null;
|
|
77
77
|
source: MaterialDocumentSource;
|
|
78
|
-
}
|
|
79
|
-
export
|
|
78
|
+
};
|
|
79
|
+
export type MaterialDocumentCoverageSource = {
|
|
80
80
|
scope: MaterialDocumentScope;
|
|
81
81
|
state: MaterialDocumentCoverageState;
|
|
82
82
|
exact: boolean;
|
|
83
83
|
declaredCount: number | null;
|
|
84
84
|
readableCount: number;
|
|
85
85
|
source: MaterialDocumentSource;
|
|
86
|
-
}
|
|
87
|
-
export
|
|
86
|
+
};
|
|
87
|
+
export type MaterialDocumentCoverage = {
|
|
88
88
|
productUnion: MaterialDocumentCoverageSummary;
|
|
89
89
|
requestedVariant: MaterialDocumentCoverageSummary;
|
|
90
90
|
sources: MaterialDocumentCoverageSource[];
|
|
91
|
-
}
|
|
92
|
-
export
|
|
91
|
+
};
|
|
92
|
+
export type MaterialDocumentCoverageSummary = {
|
|
93
93
|
state: MaterialDocumentCoverageState;
|
|
94
94
|
exact: boolean;
|
|
95
95
|
declaredCount: number | null;
|
|
96
96
|
readableCount: number;
|
|
97
|
-
}
|
|
98
|
-
export
|
|
97
|
+
};
|
|
98
|
+
export type MaterialDocumentPageOptions = {
|
|
99
99
|
cursor?: string;
|
|
100
100
|
limit?: number;
|
|
101
|
-
}
|
|
102
|
-
export
|
|
101
|
+
};
|
|
102
|
+
export type MaterialDocumentPage = {
|
|
103
103
|
variantId: VariantId;
|
|
104
104
|
documents: MaterialDocumentProjectionItem[];
|
|
105
105
|
coverage: MaterialDocumentCoverage;
|
|
106
106
|
totalDocuments: number;
|
|
107
107
|
nextCursor: string | null;
|
|
108
|
-
}
|
|
108
|
+
};
|
|
109
109
|
/** Tallies over what this projection returned — never a claim about the brand's own library. */
|
|
110
|
-
export
|
|
110
|
+
export type MaterialAssetProjectionSummary = {
|
|
111
111
|
imageCount: number;
|
|
112
112
|
downloadCount: number;
|
|
113
113
|
documentCount: number;
|
|
114
114
|
readableDocumentCount: number;
|
|
115
115
|
downloadedImageCount: number;
|
|
116
|
-
}
|
|
117
|
-
export
|
|
116
|
+
};
|
|
117
|
+
export type MaterialAssetProjection = {
|
|
118
118
|
/** Opaque — echoed from the request. */
|
|
119
119
|
variantId: VariantId;
|
|
120
120
|
images: MaterialAssetProjectionImage[];
|
|
@@ -122,4 +122,4 @@ export interface MaterialAssetProjection {
|
|
|
122
122
|
documents: MaterialDocumentProjectionItem[];
|
|
123
123
|
documentCoverage: MaterialDocumentCoverage;
|
|
124
124
|
summary: MaterialAssetProjectionSummary;
|
|
125
|
-
}
|
|
125
|
+
};
|
|
@@ -45,7 +45,7 @@ export type MaterialBlockFieldValue = string | number | boolean | unknown[] | Re
|
|
|
45
45
|
* `status: "verified"` is a real negative you may publish; the same `false`
|
|
46
46
|
* under `missing` is a placeholder for an unanswered question.
|
|
47
47
|
*/
|
|
48
|
-
export
|
|
48
|
+
export type MaterialBlockProjectionField = {
|
|
49
49
|
/** Canonical vocabulary, not an opaque id — the same token `bySpec` accepts. */
|
|
50
50
|
attributeId: string;
|
|
51
51
|
label: string;
|
|
@@ -56,21 +56,21 @@ export interface MaterialBlockProjectionField {
|
|
|
56
56
|
sourceBasis?: string;
|
|
57
57
|
/** Claim confidence supplied by the graph; absence means no confidence was emitted. */
|
|
58
58
|
confidence?: number;
|
|
59
|
-
}
|
|
60
|
-
export
|
|
59
|
+
};
|
|
60
|
+
export type MaterialBlockProjectionItem = {
|
|
61
61
|
canonicalBlockId: string;
|
|
62
62
|
canonicalBlockName: string;
|
|
63
63
|
status: MaterialBlockStatus;
|
|
64
64
|
fields: MaterialBlockProjectionField[];
|
|
65
65
|
/** The work queue: attributes with `status: "missing"`. UNKNOWN, never "none". */
|
|
66
66
|
missingAttributeIds: string[];
|
|
67
|
-
}
|
|
67
|
+
};
|
|
68
68
|
/**
|
|
69
69
|
* Every canonical block for one material, each field carrying whether it is
|
|
70
70
|
* determined. The whole surface, not a page — there is no cursor here.
|
|
71
71
|
*/
|
|
72
|
-
export
|
|
72
|
+
export type MaterialBlockProjection = {
|
|
73
73
|
/** Opaque — echoed from the request. */
|
|
74
74
|
variantId: VariantId;
|
|
75
75
|
blocks: MaterialBlockProjectionItem[];
|
|
76
|
-
}
|
|
76
|
+
};
|