@instruments/materialgraph 0.2.0 → 0.4.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/by-spec-types.d.ts +73 -0
- package/by-spec-types.js +7 -0
- package/client-browse.d.ts +46 -0
- package/client-browse.js +42 -0
- package/client.d.ts +83 -28
- package/client.js +99 -47
- package/codes-client.d.ts +82 -0
- package/codes-client.js +73 -0
- package/codes-types.d.ts +154 -9
- package/colour-aggregate-types.d.ts +93 -0
- package/colour-aggregate-types.js +1 -0
- package/colours-client.d.ts +52 -0
- package/colours-client.js +39 -0
- package/composition-types.d.ts +228 -2
- package/http-transport.d.ts +19 -2
- package/http-transport.js +72 -2
- package/index.d.ts +7 -0
- package/index.js +3 -0
- package/match-stream-types.d.ts +150 -0
- package/match-stream-types.js +28 -0
- package/match-stream.d.ts +22 -0
- package/match-stream.js +280 -0
- package/material-asset-types.d.ts +1 -1
- package/material-block-types.d.ts +23 -2
- package/material-types.d.ts +92 -0
- package/material-types.js +1 -0
- package/ndjson.d.ts +46 -0
- package/ndjson.js +103 -0
- package/operations.d.ts +1 -1
- package/operations.js +7 -0
- package/package.json +1 -1
- package/run-types.d.ts +65 -1
- package/runs.d.ts +45 -3
- package/runs.js +82 -43
- package/schedule-types.d.ts +88 -0
- package/schedule-types.js +12 -0
- package/search-refinement-types.d.ts +13 -0
- package/search-result-validation.d.ts +27 -0
- package/search-result-validation.js +174 -0
- package/source-material-validation.d.ts +3 -0
- package/source-material-validation.js +109 -0
- package/spec-types.d.ts +4 -0
- package/spec-types.js +4 -0
- package/specifications.d.ts +9 -0
- package/specifications.js +9 -0
- package/suggest-types.d.ts +58 -0
- package/suggest-types.js +21 -0
- package/types.d.ts +60 -73
- package/validation.d.ts +119 -0
- package/validation.js +241 -0
package/match-stream.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Material Match PROGRESS STREAM — the streaming twin of the buffered
|
|
3
|
+
* `match()` read. `POST /api/v1/search/match` with `Accept:
|
|
4
|
+
* application/x-ndjson` runs classify → decompose → one match per surface and
|
|
5
|
+
* emits an event per line as each surface resolves, so a `/match` UI can fill in
|
|
6
|
+
* progressively instead of showing a spinner until the whole run lands.
|
|
7
|
+
*
|
|
8
|
+
* FAIL-FAST BY CONTRACT. Every non-blank line is validated before it is yielded;
|
|
9
|
+
* a line that is not JSON, is not an object, carries an unknown `type`, an
|
|
10
|
+
* out-of-range number, a value outside a closed enum, or a field of the wrong
|
|
11
|
+
* kind raises a {@link MaterialGraphError} rather than being skipped. A stream
|
|
12
|
+
* that ends without a `complete` or an `error` event was truncated, and raises
|
|
13
|
+
* too — a short run and a dropped connection must never look alike. There is no
|
|
14
|
+
* retry or reconnect.
|
|
15
|
+
*
|
|
16
|
+
* HEARTBEATS ARE NOT PROGRESS. A scene match goes quiet for long stretches
|
|
17
|
+
* between surfaces, and proxies kill idle connections, so the server sends a
|
|
18
|
+
* payload-free `{ type: "heartbeat" }` when the stream has been silent. It is
|
|
19
|
+
* yielded like any other event — the connection being alive is worth knowing —
|
|
20
|
+
* but it carries nothing and marks no state change: never advance a step, reset
|
|
21
|
+
* a no-progress watchdog, or count anything on one. It is not terminal, and none
|
|
22
|
+
* arrives after `complete` or `error`.
|
|
23
|
+
*
|
|
24
|
+
* ACCEPTANCE PARITY WITH THE CANONICAL SCHEMA. The SDK cannot import
|
|
25
|
+
* `matchStreamEventSchema` at runtime (it publishes with zero dependencies), so
|
|
26
|
+
* this validator mirrors it by hand and `contract-parity.test.ts` proves the two
|
|
27
|
+
* ACCEPT and REJECT the same frames — with the enum members derived from the
|
|
28
|
+
* canonical schemas, so a server-side addition fails that test.
|
|
29
|
+
*
|
|
30
|
+
* OUTPUT IS PASS-THROUGH, NOT `parse` OUTPUT — a deliberate divergence. Zod
|
|
31
|
+
* objects strip unknown keys, so `matchStreamEventSchema.parse(frame)` DROPS any
|
|
32
|
+
* field the schema does not name. This reader keeps the frame as sent. Three
|
|
33
|
+
* reasons:
|
|
34
|
+
* 1. Nothing on the emit path parses. The route stringifies the workflow's own
|
|
35
|
+
* objects (`createMatchNdjsonStream`), so `parse` output is not what the wire
|
|
36
|
+
* carries; reshaping to match it would invent a normalisation that never
|
|
37
|
+
* happened.
|
|
38
|
+
* 2. Stripping needs the COMPLETE key inventory of `searchResultItemSchema`,
|
|
39
|
+
* whose closure reaches `candidateVerdictSchema` in `@materialgraph/contracts`
|
|
40
|
+
* — a recursive hand-mirror that would have to stay in step forever, and a
|
|
41
|
+
* wider surface than the validation it would serve.
|
|
42
|
+
* 3. An additive server field then reaches consumers without an SDK release,
|
|
43
|
+
* which matters while new EVENT types still require one (MG-658).
|
|
44
|
+
* The ONE exception is `.default([])`: an omitted `results` / `inferredCategories`
|
|
45
|
+
* is FILLED to `[]`, because {@link MatchSurfaceResult} types both as always
|
|
46
|
+
* present and the SDK must not hand back a type it has falsified.
|
|
47
|
+
*
|
|
48
|
+
* VALIDATION DEPTH — FULL. The frame envelope, every closed enum, every bounded
|
|
49
|
+
* number, every non-negative integer in a `surface` event's `totals`, every
|
|
50
|
+
* optional string, `MatchCategoryInference` items, and every
|
|
51
|
+
* `SearchResultItem` in `results[]` are checked exhaustively. Item validation is
|
|
52
|
+
* the shared `checkSearchResultItem` (`search-result-validation.ts`), which
|
|
53
|
+
* mirrors `searchResultItemSchema` and its whole closure down to
|
|
54
|
+
* `candidateVerdictSchema` — including both of its `.superRefine()` cross-field
|
|
55
|
+
* invariants.
|
|
56
|
+
*/
|
|
57
|
+
import { MaterialGraphError } from "./errors.js";
|
|
58
|
+
import { findSdkOperation } from "./operations.js";
|
|
59
|
+
import { MATCH_IMAGE_TYPES, MATCH_MATERIAL_FUNCTIONS, MATCH_STREAM_EVENT_TYPES, MATCH_SURFACE_STATUSES, MATCH_WORKFLOW_STEPS, } from "./match-stream-types.js";
|
|
60
|
+
import { readNdjsonLines } from "./ndjson.js";
|
|
61
|
+
import { checkSearchResultItems } from "./search-result-validation.js";
|
|
62
|
+
import { requireArray, requireBoundedNumber, requireDefaultedArray, requireEnum, requireIntegerAtLeast, requireNumber, requireOptionalString, requireRecord, requireString, } from "./validation.js";
|
|
63
|
+
/** The NDJSON media type the Match route negotiates the scene workflow on. */
|
|
64
|
+
const MATCH_STREAM_ACCEPT = "application/x-ndjson";
|
|
65
|
+
/** How much of an offending frame a diagnostic quotes (crops can be megabytes). */
|
|
66
|
+
const MAX_FRAME_PREVIEW = 200;
|
|
67
|
+
const BOUNDING_BOX_CORNERS = Object.freeze(["x0", "y0", "x1", "y1"]);
|
|
68
|
+
function operation(operationId) {
|
|
69
|
+
const found = findSdkOperation(operationId);
|
|
70
|
+
if (!found)
|
|
71
|
+
throw MaterialGraphError.unexpected(`Unknown SDK operation: ${operationId}`);
|
|
72
|
+
return found;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The failure shape every check in this module raises: one message naming the
|
|
76
|
+
* offending path and quoting a bounded slice of the frame.
|
|
77
|
+
*/
|
|
78
|
+
function frameFailure(line) {
|
|
79
|
+
const preview = line.length > MAX_FRAME_PREVIEW ? `${line.slice(0, MAX_FRAME_PREVIEW)}…` : line;
|
|
80
|
+
return (reason) => MaterialGraphError.unexpected(`MaterialGraph match stream sent a malformed NDJSON frame: ${reason}. Frame: ${preview}`);
|
|
81
|
+
}
|
|
82
|
+
/** Check one `MatchCategoryInference` item. */
|
|
83
|
+
function checkCategoryInference(value, what, fail) {
|
|
84
|
+
const record = requireRecord(value, what, fail);
|
|
85
|
+
requireString(record.productTypeId, `${what}.productTypeId`, fail);
|
|
86
|
+
requireString(record.displayName, `${what}.displayName`, fail);
|
|
87
|
+
requireNumber(record.boost, `${what}.boost`, fail);
|
|
88
|
+
}
|
|
89
|
+
function checkCategoryInferences(items, what, fail) {
|
|
90
|
+
items.forEach((item, index) => checkCategoryInference(item, `${what}[${index}]`, fail));
|
|
91
|
+
}
|
|
92
|
+
/** Check one detected surface — identity, function, confidence, and its box. */
|
|
93
|
+
function checkDetectedSurface(value, what, fail) {
|
|
94
|
+
const record = requireRecord(value, what, fail);
|
|
95
|
+
requireString(record.id, `${what}.id`, fail);
|
|
96
|
+
requireString(record.label, `${what}.label`, fail);
|
|
97
|
+
requireEnum(record.materialFunction, MATCH_MATERIAL_FUNCTIONS, `${what}.materialFunction`, fail);
|
|
98
|
+
requireOptionalString(record.categoryHint, `${what}.categoryHint`, fail);
|
|
99
|
+
requireOptionalString(record.description, `${what}.description`, fail);
|
|
100
|
+
requireOptionalString(record.cropDataUri, `${what}.cropDataUri`, fail);
|
|
101
|
+
requireBoundedNumber(record.confidence, `${what}.confidence`, fail, 0, 1);
|
|
102
|
+
const box = requireRecord(record.boundingBox, `${what}.boundingBox`, fail);
|
|
103
|
+
for (const corner of BOUNDING_BOX_CORNERS) {
|
|
104
|
+
requireNumber(box[corner], `${what}.boundingBox.${corner}`, fail);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Check one surface's matching state and fill the canonical `.default([])`
|
|
109
|
+
* arrays, so a consumer never meets `undefined` where the type promises an array.
|
|
110
|
+
*/
|
|
111
|
+
function readSurfaceResult(value, what, fail) {
|
|
112
|
+
const record = requireRecord(value, what, fail);
|
|
113
|
+
checkDetectedSurface(record.surface, `${what}.surface`, fail);
|
|
114
|
+
requireEnum(record.status, MATCH_SURFACE_STATUSES, `${what}.status`, fail);
|
|
115
|
+
requireOptionalString(record.error, `${what}.error`, fail);
|
|
116
|
+
const results = requireDefaultedArray(record.results, `${what}.results`, fail);
|
|
117
|
+
checkSearchResultItems(results, `${what}.results`, fail);
|
|
118
|
+
const inferredCategories = requireDefaultedArray(record.inferredCategories, `${what}.inferredCategories`, fail);
|
|
119
|
+
checkCategoryInferences(inferredCategories, `${what}.inferredCategories`, fail);
|
|
120
|
+
return { ...record, results, inferredCategories };
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* `matchRunTotalsSchema` — four non-negative integers plus the TWO cross-field
|
|
124
|
+
* invariants its `.superRefine()` enforces. Relational rather than field-wise,
|
|
125
|
+
* which is no obstacle here: they are two comparisons, and without them a frame
|
|
126
|
+
* claiming "7 of 2 surfaces done" would reach a consumer that trusted the tally.
|
|
127
|
+
*
|
|
128
|
+
* `matchesSoFar` is deliberately unbounded against the surface counts — one
|
|
129
|
+
* surface can carry many results and another none, so no ratio is principled.
|
|
130
|
+
*/
|
|
131
|
+
function checkRunTotals(value, what, fail) {
|
|
132
|
+
const record = requireRecord(value, what, fail);
|
|
133
|
+
const detected = requireIntegerAtLeast(record.surfacesDetected, `${what}.surfacesDetected`, fail, 0);
|
|
134
|
+
const resolved = requireIntegerAtLeast(record.surfacesResolved, `${what}.surfacesResolved`, fail, 0);
|
|
135
|
+
const failed = requireIntegerAtLeast(record.surfacesFailed, `${what}.surfacesFailed`, fail, 0);
|
|
136
|
+
requireIntegerAtLeast(record.matchesSoFar, `${what}.matchesSoFar`, fail, 0);
|
|
137
|
+
// superRefine invariant 1 — a failed surface is also a resolved one.
|
|
138
|
+
if (failed > resolved) {
|
|
139
|
+
throw fail(`${what}.surfacesFailed (${failed}) exceeds ${what}.surfacesResolved (${resolved}) — a failed surface is also a resolved one`);
|
|
140
|
+
}
|
|
141
|
+
// superRefine invariant 2 — a run cannot resolve more surfaces than it found.
|
|
142
|
+
if (resolved > detected) {
|
|
143
|
+
throw fail(`${what}.surfacesResolved (${resolved}) exceeds ${what}.surfacesDetected (${detected}) — a run cannot resolve more surfaces than it found`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Check the terminal workflow result — image type, surfaces, optional roll-ups. */
|
|
147
|
+
function readWorkflowResult(value, what, fail) {
|
|
148
|
+
const record = requireRecord(value, what, fail);
|
|
149
|
+
requireEnum(record.imageType, MATCH_IMAGE_TYPES, `${what}.imageType`, fail);
|
|
150
|
+
const surfaces = requireArray(record.surfaces, `${what}.surfaces`, fail).map((surface, index) => readSurfaceResult(surface, `${what}.surfaces[${index}]`, fail));
|
|
151
|
+
// These two are `.optional()` on the workflow result, NOT `.default([])`:
|
|
152
|
+
// absent stays absent, matching the swatch path that skips decompose.
|
|
153
|
+
if (record.results !== undefined) {
|
|
154
|
+
checkSearchResultItems(requireArray(record.results, `${what}.results`, fail), `${what}.results`, fail);
|
|
155
|
+
}
|
|
156
|
+
if (record.inferredCategories !== undefined) {
|
|
157
|
+
checkCategoryInferences(requireArray(record.inferredCategories, `${what}.inferredCategories`, fail), `${what}.inferredCategories`, fail);
|
|
158
|
+
}
|
|
159
|
+
return { ...record, surfaces };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Validate one frame against the canonical contract and apply the schema's
|
|
163
|
+
* `.default([])` fills, returning the event as a plain record. Unknown keys ride
|
|
164
|
+
* through untouched — see the pass-through note in the module header.
|
|
165
|
+
*/
|
|
166
|
+
function readFrame(value, fail) {
|
|
167
|
+
const record = requireRecord(value, "the frame", fail);
|
|
168
|
+
const type = requireString(record.type, "`type`", fail);
|
|
169
|
+
switch (type) {
|
|
170
|
+
case "step":
|
|
171
|
+
requireEnum(record.step, MATCH_WORKFLOW_STEPS, "`step`", fail);
|
|
172
|
+
requireString(record.message, "`message`", fail);
|
|
173
|
+
return record;
|
|
174
|
+
case "classified":
|
|
175
|
+
requireEnum(record.imageType, MATCH_IMAGE_TYPES, "`imageType`", fail);
|
|
176
|
+
return record;
|
|
177
|
+
case "surfaces": {
|
|
178
|
+
const surfaces = requireArray(record.surfaces, "`surfaces`", fail);
|
|
179
|
+
surfaces.forEach((surface, index) => checkDetectedSurface(surface, `surfaces[${index}]`, fail));
|
|
180
|
+
return record;
|
|
181
|
+
}
|
|
182
|
+
case "surface": {
|
|
183
|
+
// Payload before tally, so a malformed surface reports itself rather than
|
|
184
|
+
// being masked by whatever the totals looked like.
|
|
185
|
+
const surface = readSurfaceResult(record.surface, "`surface`", fail);
|
|
186
|
+
checkRunTotals(record.totals, "`totals`", fail);
|
|
187
|
+
return { ...record, surface };
|
|
188
|
+
}
|
|
189
|
+
case "heartbeat":
|
|
190
|
+
// Payload-free by contract — there is nothing else to check.
|
|
191
|
+
return record;
|
|
192
|
+
case "complete":
|
|
193
|
+
return { ...record, result: readWorkflowResult(record.result, "`result`", fail) };
|
|
194
|
+
case "error":
|
|
195
|
+
requireString(record.message, "`message`", fail);
|
|
196
|
+
return record;
|
|
197
|
+
default:
|
|
198
|
+
throw fail(`\`type\` is not a known Match stream event (${type})`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The single narrowing boundary of this module. {@link readFrame} has already
|
|
203
|
+
* proven every field the union's shape depends on; re-checking the discriminant
|
|
204
|
+
* here keeps the assertion from being vacuous.
|
|
205
|
+
*/
|
|
206
|
+
function assertMatchStreamEvent(frame, fail) {
|
|
207
|
+
const record = requireRecord(frame, "the frame", fail);
|
|
208
|
+
requireEnum(record.type, MATCH_STREAM_EVENT_TYPES, "`type`", fail);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Decode one NDJSON line into a validated {@link MatchStreamEvent}, raising a
|
|
212
|
+
* {@link MaterialGraphError} on anything the contract does not allow. Exported
|
|
213
|
+
* for `contract-parity.test.ts`, which re-checks it against the real Zod schema;
|
|
214
|
+
* it is not part of the published surface.
|
|
215
|
+
*/
|
|
216
|
+
export function decodeMatchStreamEvent(line) {
|
|
217
|
+
const fail = frameFailure(line);
|
|
218
|
+
let parsed;
|
|
219
|
+
try {
|
|
220
|
+
parsed = JSON.parse(line);
|
|
221
|
+
}
|
|
222
|
+
catch (cause) {
|
|
223
|
+
throw fail(`it is not valid JSON (${String(cause)})`);
|
|
224
|
+
}
|
|
225
|
+
const frame = readFrame(parsed, fail);
|
|
226
|
+
assertMatchStreamEvent(frame, fail);
|
|
227
|
+
return frame;
|
|
228
|
+
}
|
|
229
|
+
function isTerminal(event) {
|
|
230
|
+
return event.type === "complete" || event.type === "error";
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Stream Match progress as an async iterator. Iterates until the run reaches a
|
|
234
|
+
* `complete` or an `error` event, and raises if the connection ends before one
|
|
235
|
+
* arrives.
|
|
236
|
+
*
|
|
237
|
+
* `signal` genuinely cancels: it is linked to the AbortController that owns the
|
|
238
|
+
* in-flight request, so aborting (or simply `break`ing out of the loop) tears
|
|
239
|
+
* down the HTTP connection rather than merely stopping the yields. A
|
|
240
|
+
* caller-requested abort ends the iteration CLEANLY — the caller already knows
|
|
241
|
+
* it cancelled, so the truncated-stream error is not raised for it.
|
|
242
|
+
*/
|
|
243
|
+
export async function* matchStream(transport, input, signal) {
|
|
244
|
+
if (signal?.aborted === true)
|
|
245
|
+
return;
|
|
246
|
+
// Our own controller, so abandoning the iterator cancels the request too.
|
|
247
|
+
const controller = new AbortController();
|
|
248
|
+
const forwardAbort = () => controller.abort();
|
|
249
|
+
signal?.addEventListener("abort", forwardAbort, { once: true });
|
|
250
|
+
try {
|
|
251
|
+
let body;
|
|
252
|
+
try {
|
|
253
|
+
body = await transport.openStream(operation("match_material"), {
|
|
254
|
+
body: input,
|
|
255
|
+
signal: controller.signal,
|
|
256
|
+
accept: MATCH_STREAM_ACCEPT,
|
|
257
|
+
subject: "match stream",
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
// Cancelled before the response headers landed — the caller's own stop.
|
|
262
|
+
if (controller.signal.aborted)
|
|
263
|
+
return;
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
for await (const line of readNdjsonLines(body, controller.signal)) {
|
|
267
|
+
const event = decodeMatchStreamEvent(line);
|
|
268
|
+
yield event;
|
|
269
|
+
if (isTerminal(event))
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (!controller.signal.aborted) {
|
|
273
|
+
throw MaterialGraphError.unexpected("MaterialGraph match stream ended without a `complete` or `error` event — the connection was truncated");
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
signal?.removeEventListener("abort", forwardAbort);
|
|
278
|
+
controller.abort();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { OpenEnum } from "./types.js";
|
|
2
2
|
export type MaterialAssetVisualRole = OpenEnum<"grid" | "detail" | "scene" | "swatch" | "technical" | "unknown">;
|
|
3
|
-
export type MaterialAssetStatus = OpenEnum<"downloaded" | "linked_not_downloaded" | "failed">;
|
|
3
|
+
export type MaterialAssetStatus = OpenEnum<"downloaded" | "linked_not_downloaded" | "failed" | "retired_placeholder">;
|
|
4
4
|
export interface MaterialAssetProjectionImage {
|
|
5
5
|
key: string;
|
|
6
6
|
ordinal: number;
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
import type { OpenEnum } from "./types.js";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Coverage status, shared by blocks and fields.
|
|
4
|
+
*
|
|
5
|
+
* `verified` — determined. For a field a value is known, INCLUDING `false`, `0`
|
|
6
|
+
* and `[]`, which are answers meaning "we looked and there is none". For a
|
|
7
|
+
* block, every field is determined.
|
|
8
|
+
* `partial` — determined for some fields but not all.
|
|
9
|
+
* `missing` — not determined yet. The work queue, and the only status that
|
|
10
|
+
* appears in `missingAttributeIds`.
|
|
11
|
+
* `not_applicable` — cannot apply to this product type. Never work, never a gap.
|
|
12
|
+
*
|
|
13
|
+
* BREAKING in this release: blocks previously reported `populated`/`pending`
|
|
14
|
+
* while fields reported `verified`/`missing` — two spellings of one concept
|
|
15
|
+
* that disagreed with each other on the same variant. Map `populated ->
|
|
16
|
+
* verified` and `pending -> missing`. Both types are retained as aliases so
|
|
17
|
+
* existing code keeps compiling; they now describe the same set.
|
|
18
|
+
*
|
|
19
|
+
* The authoritative list is served at runtime by
|
|
20
|
+
* `get-schema { kind: "coverageStatuses" }`.
|
|
21
|
+
*/
|
|
22
|
+
export type MaterialCoverageStatus = OpenEnum<"verified" | "partial" | "missing" | "not_applicable">;
|
|
23
|
+
export type MaterialBlockStatus = MaterialCoverageStatus;
|
|
24
|
+
export type MaterialBlockFieldStatus = MaterialCoverageStatus;
|
|
4
25
|
export type MaterialBlockFieldValue = string | number | boolean | unknown[] | Record<string, unknown> | null;
|
|
5
26
|
export interface MaterialBlockProjectionField {
|
|
6
27
|
attributeId: string;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-mirrored wire types for the material projections — the grid-card tile,
|
|
3
|
+
* the detail record, and the availability/successor block they carry.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `./types.ts` (which re-exports everything here) so that module
|
|
6
|
+
* stays within the repository's source-size budget. Same contract, same
|
|
7
|
+
* tolerant-parsing rules: response enums are OPEN, so `MaterialAvailability` is
|
|
8
|
+
* widened — a new server state still type-checks.
|
|
9
|
+
*/
|
|
10
|
+
import type { ImageSource, MaterialColour, OpenEnum, PaletteSwatch } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Whether a material can still be bought.
|
|
13
|
+
*
|
|
14
|
+
* - `available` — listed in the catalog of record.
|
|
15
|
+
* - `discontinued` — WITHDRAWN from that catalog. This is the source's own
|
|
16
|
+
* listing state, not a reviewed manufacturer-discontinuation claim; do not
|
|
17
|
+
* present it as the manufacturer having discontinued the product.
|
|
18
|
+
* - `superseded` — a reviewed product relationship names a successor and the
|
|
19
|
+
* material is no longer listed. See `MaterialDetail.supersededBy`.
|
|
20
|
+
* - `unknown` — never checked. A real answer, not an error.
|
|
21
|
+
*/
|
|
22
|
+
export type MaterialAvailability = OpenEnum<"available" | "discontinued" | "superseded" | "unknown">;
|
|
23
|
+
/**
|
|
24
|
+
* The variant a reviewed product relationship names as replacing another.
|
|
25
|
+
* `availability` is the successor's OWN state, resolved ONE HOP only — a
|
|
26
|
+
* successor that has itself been superseded reports `discontinued`/`unknown`
|
|
27
|
+
* here; follow `variantId` for the next link in the chain.
|
|
28
|
+
*/
|
|
29
|
+
export interface MaterialSuccessor {
|
|
30
|
+
variantId: string;
|
|
31
|
+
brandSlug: string;
|
|
32
|
+
productId: string;
|
|
33
|
+
brand: string;
|
|
34
|
+
product: string;
|
|
35
|
+
variant: string;
|
|
36
|
+
color: string | null;
|
|
37
|
+
availability: MaterialAvailability;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The grid-card projection of a material. Tiles carry `availability` but not the
|
|
41
|
+
* successor record — read the detail projection for that.
|
|
42
|
+
*/
|
|
43
|
+
export interface MaterialTile {
|
|
44
|
+
variantId: string;
|
|
45
|
+
brand: string;
|
|
46
|
+
product: string;
|
|
47
|
+
variantName: string | null;
|
|
48
|
+
imageUrl: string | null;
|
|
49
|
+
imageSource: ImageSource | null;
|
|
50
|
+
category: string | null;
|
|
51
|
+
colour: MaterialColour;
|
|
52
|
+
availability: MaterialAvailability;
|
|
53
|
+
}
|
|
54
|
+
/** A canonical attribute/term pair carried on a detail record. */
|
|
55
|
+
export interface MaterialFacet {
|
|
56
|
+
attributeId: string;
|
|
57
|
+
term: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* One dimensional configuration of a material. Numeric fields are millimetres
|
|
61
|
+
* (mass in kilograms); string fields are the source's own descriptors. The
|
|
62
|
+
* index signature preserves the corpus's long tail of rarer keys.
|
|
63
|
+
*/
|
|
64
|
+
export interface DimensionEntry {
|
|
65
|
+
label?: string;
|
|
66
|
+
tile_size?: string;
|
|
67
|
+
measurement_basis?: string;
|
|
68
|
+
width_mm?: number;
|
|
69
|
+
length_mm?: number;
|
|
70
|
+
height_mm?: number;
|
|
71
|
+
depth_mm?: number;
|
|
72
|
+
thickness_mm?: number;
|
|
73
|
+
diameter_mm?: number;
|
|
74
|
+
visible_projection_mm?: number;
|
|
75
|
+
weight_kg?: number;
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
}
|
|
78
|
+
/** The record projection of a material — the tile plus detail-only fields. */
|
|
79
|
+
export interface MaterialDetail extends MaterialTile {
|
|
80
|
+
brandSlug: string;
|
|
81
|
+
productId: string;
|
|
82
|
+
color: string | null;
|
|
83
|
+
materialBankId: string | null;
|
|
84
|
+
installationImageUrl: string | null;
|
|
85
|
+
displayCategory: string | null;
|
|
86
|
+
categoryPath: string[];
|
|
87
|
+
palette: PaletteSwatch[];
|
|
88
|
+
facets: MaterialFacet[];
|
|
89
|
+
dimensions: DimensionEntry[] | null;
|
|
90
|
+
/** The declared successor, or null when no reviewed relationship names one. */
|
|
91
|
+
supersededBy: MaterialSuccessor | null;
|
|
92
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/ndjson.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A minimal, dependency-free NDJSON (newline-delimited JSON) line reader — the
|
|
3
|
+
* primitive behind {@link MaterialGraphClient.matchStream}. Hand-rolled so the
|
|
4
|
+
* SDK stays zero-runtime-dep and works unchanged inside Node route handlers,
|
|
5
|
+
* where `fetch` yields a byte `ReadableStream`.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately a SIBLING of `sse.ts`, not a reuse of it. NDJSON and SSE agree on
|
|
8
|
+
* nothing but "text arrives in chunks": SSE frames are `field: value` lines
|
|
9
|
+
* terminated by a BLANK line, with comment lines and multi-line `data:`
|
|
10
|
+
* accumulation, whereas an NDJSON frame is exactly one line of JSON. Feeding
|
|
11
|
+
* NDJSON through the SSE tokenizer would read `{"type":"step",…}` as a field
|
|
12
|
+
* named `{"type"` and silently discard every frame. The two also want OPPOSITE
|
|
13
|
+
* end-of-stream policies: SSE dispatches a trailing partial frame as valid,
|
|
14
|
+
* while a truncated NDJSON line must surface as an error (enforced by the
|
|
15
|
+
* caller, which parses whatever `flush` returns rather than dropping it).
|
|
16
|
+
*
|
|
17
|
+
* Line endings: the reader splits on `\n` only, per the NDJSON contract. A `\r`
|
|
18
|
+
* left by a CRLF-normalising proxy stays as trailing whitespace on the line,
|
|
19
|
+
* which `JSON.parse` ignores.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* A push-based NDJSON line assembler. Feed it decoded text with {@link push};
|
|
23
|
+
* it returns the lines that text COMPLETED, terminators removed. Any partial
|
|
24
|
+
* trailing line is held in an internal buffer until a later chunk terminates it,
|
|
25
|
+
* or until {@link flush} surrenders it at end of stream.
|
|
26
|
+
*/
|
|
27
|
+
export declare class NdjsonDecoder {
|
|
28
|
+
#private;
|
|
29
|
+
/** Absorb one decoded chunk and return the lines it completed, in order. */
|
|
30
|
+
push(text: string): string[];
|
|
31
|
+
/**
|
|
32
|
+
* Surrender the unterminated remainder at end of stream — a well-formed
|
|
33
|
+
* NDJSON stream's final line need not carry a trailing newline. The remainder
|
|
34
|
+
* is RETURNED, never dropped: a truncated frame is the caller's error to
|
|
35
|
+
* raise, not this decoder's fragment to discard.
|
|
36
|
+
*/
|
|
37
|
+
flush(): string[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Iterate the NDJSON lines of a byte stream. Only lines with non-whitespace
|
|
41
|
+
* content are yielded — a blank separator line carries no frame and must never
|
|
42
|
+
* reach `JSON.parse`, where it would become an empty object. Stops cleanly when
|
|
43
|
+
* the stream ends or `signal` aborts (mid-read abort rejections are swallowed
|
|
44
|
+
* once the signal is set).
|
|
45
|
+
*/
|
|
46
|
+
export declare function readNdjsonLines(body: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncGenerator<string, void, unknown>;
|
package/ndjson.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A minimal, dependency-free NDJSON (newline-delimited JSON) line reader — the
|
|
3
|
+
* primitive behind {@link MaterialGraphClient.matchStream}. Hand-rolled so the
|
|
4
|
+
* SDK stays zero-runtime-dep and works unchanged inside Node route handlers,
|
|
5
|
+
* where `fetch` yields a byte `ReadableStream`.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately a SIBLING of `sse.ts`, not a reuse of it. NDJSON and SSE agree on
|
|
8
|
+
* nothing but "text arrives in chunks": SSE frames are `field: value` lines
|
|
9
|
+
* terminated by a BLANK line, with comment lines and multi-line `data:`
|
|
10
|
+
* accumulation, whereas an NDJSON frame is exactly one line of JSON. Feeding
|
|
11
|
+
* NDJSON through the SSE tokenizer would read `{"type":"step",…}` as a field
|
|
12
|
+
* named `{"type"` and silently discard every frame. The two also want OPPOSITE
|
|
13
|
+
* end-of-stream policies: SSE dispatches a trailing partial frame as valid,
|
|
14
|
+
* while a truncated NDJSON line must surface as an error (enforced by the
|
|
15
|
+
* caller, which parses whatever `flush` returns rather than dropping it).
|
|
16
|
+
*
|
|
17
|
+
* Line endings: the reader splits on `\n` only, per the NDJSON contract. A `\r`
|
|
18
|
+
* left by a CRLF-normalising proxy stays as trailing whitespace on the line,
|
|
19
|
+
* which `JSON.parse` ignores.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* A push-based NDJSON line assembler. Feed it decoded text with {@link push};
|
|
23
|
+
* it returns the lines that text COMPLETED, terminators removed. Any partial
|
|
24
|
+
* trailing line is held in an internal buffer until a later chunk terminates it,
|
|
25
|
+
* or until {@link flush} surrenders it at end of stream.
|
|
26
|
+
*/
|
|
27
|
+
export class NdjsonDecoder {
|
|
28
|
+
#buffer = "";
|
|
29
|
+
/** Absorb one decoded chunk and return the lines it completed, in order. */
|
|
30
|
+
push(text) {
|
|
31
|
+
this.#buffer += text;
|
|
32
|
+
const lines = [];
|
|
33
|
+
for (;;) {
|
|
34
|
+
const index = this.#buffer.indexOf("\n");
|
|
35
|
+
if (index === -1)
|
|
36
|
+
break;
|
|
37
|
+
lines.push(this.#buffer.slice(0, index));
|
|
38
|
+
this.#buffer = this.#buffer.slice(index + 1);
|
|
39
|
+
}
|
|
40
|
+
return lines;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Surrender the unterminated remainder at end of stream — a well-formed
|
|
44
|
+
* NDJSON stream's final line need not carry a trailing newline. The remainder
|
|
45
|
+
* is RETURNED, never dropped: a truncated frame is the caller's error to
|
|
46
|
+
* raise, not this decoder's fragment to discard.
|
|
47
|
+
*/
|
|
48
|
+
flush() {
|
|
49
|
+
if (this.#buffer.length === 0)
|
|
50
|
+
return [];
|
|
51
|
+
const remainder = this.#buffer;
|
|
52
|
+
this.#buffer = "";
|
|
53
|
+
return [remainder];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Iterate the NDJSON lines of a byte stream. Only lines with non-whitespace
|
|
58
|
+
* content are yielded — a blank separator line carries no frame and must never
|
|
59
|
+
* reach `JSON.parse`, where it would become an empty object. Stops cleanly when
|
|
60
|
+
* the stream ends or `signal` aborts (mid-read abort rejections are swallowed
|
|
61
|
+
* once the signal is set).
|
|
62
|
+
*/
|
|
63
|
+
export async function* readNdjsonLines(body, signal) {
|
|
64
|
+
const reader = body.getReader();
|
|
65
|
+
const decoder = new TextDecoder();
|
|
66
|
+
const ndjson = new NdjsonDecoder();
|
|
67
|
+
try {
|
|
68
|
+
for (;;) {
|
|
69
|
+
if (signal?.aborted)
|
|
70
|
+
return;
|
|
71
|
+
let result;
|
|
72
|
+
try {
|
|
73
|
+
result = await reader.read();
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (signal?.aborted)
|
|
77
|
+
return;
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
if (result.done) {
|
|
81
|
+
// `decode()` with no argument flushes any dangling multi-byte sequence.
|
|
82
|
+
for (const line of ndjson.push(decoder.decode())) {
|
|
83
|
+
if (line.trim().length > 0)
|
|
84
|
+
yield line;
|
|
85
|
+
}
|
|
86
|
+
for (const line of ndjson.flush()) {
|
|
87
|
+
if (line.trim().length > 0)
|
|
88
|
+
yield line;
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
for (const line of ndjson.push(decoder.decode(result.value, { stream: true }))) {
|
|
93
|
+
if (signal?.aborted)
|
|
94
|
+
return;
|
|
95
|
+
if (line.trim().length > 0)
|
|
96
|
+
yield line;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
reader.releaseLock();
|
|
102
|
+
}
|
|
103
|
+
}
|
package/operations.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const MATERIALGRAPH_API_VERSION = "0.7.0";
|
|
2
|
-
declare const OPERATION_DEFINITIONS: readonly [readonly ["search", "POST", "/api/v1/search", "search_materials_post"], readonly ["searchGet", "GET", "/api/v1/search", "search_materials_get"], readonly ["match", "POST", "/api/v1/search/match", "match_material"], readonly ["byColour", "GET", "/api/v1/search/colours", "search_by_colour"], readonly ["material", "GET", "/api/v1/materials/{variantId}", "get_material"], readonly ["siblings", "GET", "/api/v1/materials/{variantId}/siblings", "get_material_siblings"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["materialAssets", "GET", "/api/v1/materials/{variantId}/assets", "list_material_assets"], readonly ["schema", "GET", "/api/v1/schema/blocks", "list_schema_blocks"], readonly ["schema", "GET", "/api/v1/schema/attributes", "list_schema_attributes"], readonly ["schema", "GET", "/api/v1/schema/verticals", "list_schema_verticals"], readonly ["schema", "GET", "/api/v1/schema/product-types", "list_schema_product_types"], readonly ["schema", "GET", "/api/v1/schema/units", "list_schema_units"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["schema", "GET", "/api/v1/schema/source-requirements", "list_schema_source_requirements"], readonly ["schema", "GET", "/api/v1/schema/product-type-templates", "list_schema_product_type_templates"], readonly ["schema", "GET", "/api/v1/schema/mappings", "list_schema_mappings"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"], readonly ["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"], readonly ["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"], readonly ["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"], readonly ["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"], readonly ["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"], readonly ["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"], readonly ["resolveAttributeValue", "GET", "/api/v1/catalog/attributes/{attributeId}/values/resolve", "catalog_resolve_value"], readonly ["searchVocabulary", "GET", "/api/v1/catalog/vocabulary/search", "catalog_vocabulary_search"], readonly ["bySpec", "GET", "/api/v1/search/specifications", "catalog_by_spec"], readonly ["taxonomy", "GET", "/api/v1/taxonomy/categories", "get_category_taxonomy"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"], readonly ["demand", "GET", "/api/v1/demand", "get_material_demand"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"], readonly ["attributeValues", "GET", "/api/v1/catalog/attributes/{attributeId}/values", "list_attribute_values"], readonly ["applicableSpecs", "GET", "/api/v1/taxonomy/categories/{key}/specs", "get_applicable_specs"], readonly ["glossary", "GET", "/api/v1/glossary/{term}", "explain_material_term"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"], readonly ["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"], readonly ["createRun", "POST", "/api/v1/runs", "create_public_run"], readonly ["run", "GET", "/api/v1/runs/{runId}", "get_public_run"], readonly ["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"], readonly ["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"], readonly ["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"], readonly ["varyScheme", "POST", "/api/v1/schemes/vary", "vary_schemes"], readonly ["createProject", "POST", "/api/v1/projects", "create_box_project"], readonly ["listProjects", "GET", "/api/v1/projects", "list_box_projects"], readonly ["getProject", "GET", "/api/v1/projects/{projectId}", "get_box_project"], readonly ["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"], readonly ["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"], readonly ["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"]];
|
|
2
|
+
declare const OPERATION_DEFINITIONS: readonly [readonly ["search", "POST", "/api/v1/search", "search_materials_post"], readonly ["searchGet", "GET", "/api/v1/search", "search_materials_get"], readonly ["match", "POST", "/api/v1/search/match", "match_material"], readonly ["byColour", "GET", "/api/v1/search/colours", "search_by_colour"], readonly ["material", "GET", "/api/v1/materials/{variantId}", "get_material"], readonly ["siblings", "GET", "/api/v1/materials/{variantId}/siblings", "get_material_siblings"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["materialAssets", "GET", "/api/v1/materials/{variantId}/assets", "list_material_assets"], readonly ["schema", "GET", "/api/v1/schema/blocks", "list_schema_blocks"], readonly ["schema", "GET", "/api/v1/schema/attributes", "list_schema_attributes"], readonly ["schema", "GET", "/api/v1/schema/verticals", "list_schema_verticals"], readonly ["schema", "GET", "/api/v1/schema/product-types", "list_schema_product_types"], readonly ["schema", "GET", "/api/v1/schema/units", "list_schema_units"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["schema", "GET", "/api/v1/schema/source-requirements", "list_schema_source_requirements"], readonly ["schema", "GET", "/api/v1/schema/product-type-templates", "list_schema_product_type_templates"], readonly ["schema", "GET", "/api/v1/schema/mappings", "list_schema_mappings"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"], readonly ["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"], readonly ["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"], readonly ["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"], readonly ["colours.profiles", "GET", "/api/v1/colours/profiles", "get_category_colour_profiles"], readonly ["colours.stats", "GET", "/api/v1/colours/stats", "get_colour_stats"], readonly ["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"], readonly ["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"], readonly ["suggest", "GET", "/api/v1/search/suggest", "search_suggest"], readonly ["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"], readonly ["resolveAttributeValue", "GET", "/api/v1/catalog/attributes/{attributeId}/values/resolve", "catalog_resolve_value"], readonly ["searchVocabulary", "GET", "/api/v1/catalog/vocabulary/search", "catalog_vocabulary_search"], readonly ["bySpec", "GET", "/api/v1/search/specifications", "catalog_by_spec"], readonly ["taxonomy", "GET", "/api/v1/taxonomy/categories", "get_category_taxonomy"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"], readonly ["demand", "GET", "/api/v1/demand", "get_material_demand"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"], readonly ["attributeValues", "GET", "/api/v1/catalog/attributes/{attributeId}/values", "list_attribute_values"], readonly ["applicableSpecs", "GET", "/api/v1/taxonomy/categories/{key}/specs", "get_applicable_specs"], readonly ["glossary", "GET", "/api/v1/glossary/{term}", "explain_material_term"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"], readonly ["sourceMaterialBatch", "GET", "/api/v1/source-material/{batchId}", "get_source_material_batch"], readonly ["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"], readonly ["createRun", "POST", "/api/v1/runs", "create_public_run"], readonly ["run", "GET", "/api/v1/runs/{runId}", "get_public_run"], readonly ["runSchedule", "GET", "/api/v1/runs/{runId}/schedule", "get_run_finish_schedule"], readonly ["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"], readonly ["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"], readonly ["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"], readonly ["varyScheme", "POST", "/api/v1/schemes/vary", "vary_schemes"], readonly ["createProject", "POST", "/api/v1/projects", "create_box_project"], readonly ["listProjects", "GET", "/api/v1/projects", "list_box_projects"], readonly ["getProject", "GET", "/api/v1/projects/{projectId}", "get_box_project"], readonly ["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"], readonly ["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"], readonly ["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"], readonly ["codes.context", "GET", "/api/v1/codes/context", "list_code_context"], readonly ["codes.requirements", "GET", "/api/v1/codes/requirements", "list_code_requirements"]];
|
|
3
3
|
export type SdkOperationId = (typeof OPERATION_DEFINITIONS)[number][3];
|
|
4
4
|
export type SdkOperation = Readonly<{
|
|
5
5
|
clientMethod: (typeof OPERATION_DEFINITIONS)[number][0];
|
package/operations.js
CHANGED
|
@@ -22,8 +22,11 @@ const OPERATION_DEFINITIONS = [
|
|
|
22
22
|
["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"],
|
|
23
23
|
["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"],
|
|
24
24
|
["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"],
|
|
25
|
+
["colours.profiles", "GET", "/api/v1/colours/profiles", "get_category_colour_profiles"],
|
|
26
|
+
["colours.stats", "GET", "/api/v1/colours/stats", "get_colour_stats"],
|
|
25
27
|
["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"],
|
|
26
28
|
["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"],
|
|
29
|
+
["suggest", "GET", "/api/v1/search/suggest", "search_suggest"],
|
|
27
30
|
["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"],
|
|
28
31
|
[
|
|
29
32
|
"resolveAttributeValue",
|
|
@@ -50,9 +53,11 @@ const OPERATION_DEFINITIONS = [
|
|
|
50
53
|
["brands", "GET", "/api/v1/brands", "list_brands"],
|
|
51
54
|
["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"],
|
|
52
55
|
["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"],
|
|
56
|
+
["sourceMaterialBatch", "GET", "/api/v1/source-material/{batchId}", "get_source_material_batch"],
|
|
53
57
|
["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"],
|
|
54
58
|
["createRun", "POST", "/api/v1/runs", "create_public_run"],
|
|
55
59
|
["run", "GET", "/api/v1/runs/{runId}", "get_public_run"],
|
|
60
|
+
["runSchedule", "GET", "/api/v1/runs/{runId}/schedule", "get_run_finish_schedule"],
|
|
56
61
|
["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"],
|
|
57
62
|
["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"],
|
|
58
63
|
["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"],
|
|
@@ -63,6 +68,8 @@ const OPERATION_DEFINITIONS = [
|
|
|
63
68
|
["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"],
|
|
64
69
|
["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"],
|
|
65
70
|
["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"],
|
|
71
|
+
["codes.context", "GET", "/api/v1/codes/context", "list_code_context"],
|
|
72
|
+
["codes.requirements", "GET", "/api/v1/codes/requirements", "list_code_requirements"],
|
|
66
73
|
];
|
|
67
74
|
export const SDK_OPERATIONS = Object.freeze(OPERATION_DEFINITIONS.map(([clientMethod, method, path, operationId]) => Object.freeze({ clientMethod, method, path, operationId })));
|
|
68
75
|
/** Look up a registered operation by its OpenAPI `operationId`. */
|