@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/composition-types.d.ts
CHANGED
|
@@ -45,13 +45,95 @@ export interface VarySchemesRequest {
|
|
|
45
45
|
locks?: SchemeLock[];
|
|
46
46
|
exclusions?: SchemeExclusions;
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* One anchor (MG-613): a variant the designer has already pinned to a place in
|
|
50
|
+
* the programme, supplied on the `compose-material-schemes` run input's
|
|
51
|
+
* `anchors[]`. Exactly ONE of `variantId` (an exact catalog variant) or `query`
|
|
52
|
+
* (free-text "Brand Product Colour", resolved server-side) is required. An
|
|
53
|
+
* anchored slot is LOCKED — composition completes the OPEN slots in relation to
|
|
54
|
+
* it. Anchors that cannot be resolved leave the slot open and surface a run
|
|
55
|
+
* warning (never a failure).
|
|
56
|
+
*/
|
|
57
|
+
export interface ComposeAnchorInput {
|
|
58
|
+
/** The building element the anchor fills (flooring, wall, textile, …). */
|
|
59
|
+
element: SlotElement;
|
|
60
|
+
/** Optional named space to scope the anchor to (matched by name/slug). */
|
|
61
|
+
spaceName?: string;
|
|
62
|
+
/** An exact catalog variant id. Mutually exclusive with `query`. */
|
|
63
|
+
variantId?: string;
|
|
64
|
+
/** Free-text "Brand Product Colour". Mutually exclusive with `variantId`. */
|
|
65
|
+
query?: string;
|
|
66
|
+
/** Display name for slot naming and warnings. */
|
|
67
|
+
label?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* One standing rule in a project's compliance preset (MG-615): free-text
|
|
71
|
+
* `criteria` phrases that apply to a `scope` of slots. `scope: "all"` reaches
|
|
72
|
+
* every slot (including slots with no element and slots classified `other`); a
|
|
73
|
+
* named element reaches only slots carrying exactly that element, so an
|
|
74
|
+
* unclassified slot receives `"all"` rules only. Phrases are resolved
|
|
75
|
+
* server-side through the same vocabulary as brief criteria — sustainability
|
|
76
|
+
* wording ("LEED", "GREENGUARD Gold") resolves to a named certification
|
|
77
|
+
* programme. A phrase that grounds to nothing is dropped with a run warning.
|
|
78
|
+
*/
|
|
79
|
+
export interface CompliancePresetRule {
|
|
80
|
+
scope: "all" | SlotElement;
|
|
81
|
+
criteria: string[];
|
|
82
|
+
}
|
|
83
|
+
/** A project's standing rules, declared once on the compose run (max 12 rules). */
|
|
84
|
+
export interface CompliancePreset {
|
|
85
|
+
rules: CompliancePresetRule[];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The `inputJson` for a `compose-material-schemes` run. `batchId` is required;
|
|
89
|
+
* everything else has a server default. `anchors` (max 24) carry externally
|
|
90
|
+
* decided picks (MG-613); `compliancePreset` carries the project's standing
|
|
91
|
+
* rules (MG-615), which cascade into slots as PREFERRED criteria — they grade
|
|
92
|
+
* every candidate but never exclude one.
|
|
93
|
+
*/
|
|
94
|
+
export interface ComposeMaterialSchemesInput {
|
|
95
|
+
batchId: string;
|
|
96
|
+
count?: number;
|
|
97
|
+
seed?: number;
|
|
98
|
+
exclusions?: SchemeExclusions;
|
|
99
|
+
anchors?: ComposeAnchorInput[];
|
|
100
|
+
compliancePreset?: CompliancePreset;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The state of one criterion verdict cell (MG-627: the shared MaterialGraph
|
|
104
|
+
* verdict states). The six LIVE states are `matched` (accepted value satisfies
|
|
105
|
+
* the criterion), `deviated` (directionally off but named), `failed` (an
|
|
106
|
+
* accepted value exists and does not satisfy it), `unverified` (a real value
|
|
107
|
+
* that did not clear the trust bar), `missing` (no accepted value to evaluate),
|
|
108
|
+
* and `not_evaluated` (the engine did not check it). `mismatched`/`unknown` are
|
|
109
|
+
* the two LEGACY tokens old persisted runs still carry (never emitted again).
|
|
110
|
+
* Open per the response-enum policy: treat unknown states forward-compatibly.
|
|
111
|
+
*/
|
|
112
|
+
export type CompositionMatchState = OpenEnum<"matched" | "deviated" | "failed" | "unverified" | "missing" | "not_evaluated" | "mismatched" | "unknown">;
|
|
113
|
+
/** How a criterion cell was baselined — what the candidate was compared against. */
|
|
114
|
+
export type CompositionMatchBaseline = {
|
|
115
|
+
kind: "term";
|
|
116
|
+
term: string;
|
|
117
|
+
} | {
|
|
118
|
+
kind: "presence";
|
|
119
|
+
} | {
|
|
120
|
+
kind: "seed";
|
|
121
|
+
variantId: string;
|
|
122
|
+
};
|
|
123
|
+
/** One resolved criterion → material verdict cell, as graded during composition. */
|
|
50
124
|
export interface CompositionMatch {
|
|
51
125
|
attributeId: string;
|
|
52
126
|
state: CompositionMatchState;
|
|
53
127
|
confidence: number | null;
|
|
54
128
|
sourceBasis: string | null;
|
|
129
|
+
/** Required vs preferred criterion (MG-627). Absent on legacy persisted runs. */
|
|
130
|
+
strength?: "required" | "preferred";
|
|
131
|
+
/** What the cell was evaluated against (MG-627). Absent on legacy persisted runs. */
|
|
132
|
+
baseline?: CompositionMatchBaseline;
|
|
133
|
+
/** The candidate's own accepted value label, when the cell carries one (MG-627). */
|
|
134
|
+
value?: string | null;
|
|
135
|
+
/** Why an `unverified` cell missed the trust bar (MG-627), else null. */
|
|
136
|
+
trustGap?: "below_confidence" | "basis_unmet" | "proposed" | null;
|
|
55
137
|
}
|
|
56
138
|
/** One slot's chosen material plus the reason it was picked. */
|
|
57
139
|
export interface CompositionPick {
|
|
@@ -72,6 +154,12 @@ export interface CompositionPick {
|
|
|
72
154
|
matched: CompositionMatch[];
|
|
73
155
|
notes: string[];
|
|
74
156
|
};
|
|
157
|
+
/**
|
|
158
|
+
* True when this pick is an ANCHOR the designer pinned to the slot (MG-613):
|
|
159
|
+
* composition never replaced it and vary keeps it fixed. Absent for ordinary
|
|
160
|
+
* composed picks.
|
|
161
|
+
*/
|
|
162
|
+
locked?: boolean;
|
|
75
163
|
}
|
|
76
164
|
/** The multi-objective score for one scheme (each component in [0, 1]). */
|
|
77
165
|
export interface CompositionObjective {
|
|
@@ -115,3 +203,141 @@ export interface VarySchemesResponse {
|
|
|
115
203
|
provenance: VaryProvenance;
|
|
116
204
|
shortfall: CompositionShortfall | null;
|
|
117
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Whether a programme element (space, slot, or palette) was `stated` in a text
|
|
208
|
+
* source or `inferred` from imagery/mood. Only inferred elements raise a review
|
|
209
|
+
* question. Absent → treated as `stated`.
|
|
210
|
+
*/
|
|
211
|
+
export type ProgrammeProvenance = "stated" | "inferred";
|
|
212
|
+
/**
|
|
213
|
+
* The broad building element a slot fills — a pragmatically-extended Uniclass EF
|
|
214
|
+
* vocabulary. Widened with {@link OpenEnum} so a newer server term still
|
|
215
|
+
* type-checks.
|
|
216
|
+
*/
|
|
217
|
+
export type SlotElement = OpenEnum<"flooring" | "wall" | "ceiling" | "textile" | "surface" | "furniture" | "lighting" | "other">;
|
|
218
|
+
/** One colour cue: a hex swatch with an optional human name. */
|
|
219
|
+
export interface BriefColourCue {
|
|
220
|
+
hex: string;
|
|
221
|
+
name?: string;
|
|
222
|
+
}
|
|
223
|
+
/** A source anchor (document id + optional page) attributing derived content. */
|
|
224
|
+
export interface CompositionSourceRef {
|
|
225
|
+
sourceId: string;
|
|
226
|
+
page: number | null;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* One candidate colour direction the pile's colour evidence implies when no
|
|
230
|
+
* palette was explicitly committed. `directionId` is a stable kebab-case handle;
|
|
231
|
+
* `sourceRefs` attribute the direction to the palette source(s) it was drawn
|
|
232
|
+
* from (empty when unattributable).
|
|
233
|
+
*/
|
|
234
|
+
export interface PaletteDirection {
|
|
235
|
+
directionId: string;
|
|
236
|
+
name: string;
|
|
237
|
+
colours: BriefColourCue[];
|
|
238
|
+
sourceRefs: CompositionSourceRef[];
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Suspend payload for the compose `review-programme` step: the FULL programme
|
|
242
|
+
* picture — every space and slot tagged with its provenance — so the card can
|
|
243
|
+
* show the whole thing and highlight what was inferred.
|
|
244
|
+
*/
|
|
245
|
+
export interface ReviewProgrammeSuspendPayload {
|
|
246
|
+
spaces: Array<{
|
|
247
|
+
spaceId: string;
|
|
248
|
+
name: string;
|
|
249
|
+
spaceType?: string;
|
|
250
|
+
provenance: ProgrammeProvenance;
|
|
251
|
+
}>;
|
|
252
|
+
slots: Array<{
|
|
253
|
+
slotId: string;
|
|
254
|
+
name: string;
|
|
255
|
+
spaceId?: string;
|
|
256
|
+
element?: SlotElement;
|
|
257
|
+
provenance: ProgrammeProvenance;
|
|
258
|
+
}>;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Resume grammar for the `review-programme` step: drop a slot, drop a space
|
|
262
|
+
* (cascade-dropping its slots), or rename a space.
|
|
263
|
+
*/
|
|
264
|
+
export interface ReviewProgrammeResumeData {
|
|
265
|
+
corrections: Array<{
|
|
266
|
+
action: "drop-slot";
|
|
267
|
+
slotId: string;
|
|
268
|
+
} | {
|
|
269
|
+
action: "drop-space";
|
|
270
|
+
spaceId: string;
|
|
271
|
+
} | {
|
|
272
|
+
action: "rename-space";
|
|
273
|
+
spaceId: string;
|
|
274
|
+
name: string;
|
|
275
|
+
}>;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Suspend payload for the compose `commit-palette` step. Today only the
|
|
279
|
+
* `directions` mode ships; the shape carries `mode` so a later `ab-images` mode
|
|
280
|
+
* is an additive member, not a migration.
|
|
281
|
+
*/
|
|
282
|
+
export interface CommitPaletteSuspendPayload {
|
|
283
|
+
mode: OpenEnum<"directions">;
|
|
284
|
+
directions: PaletteDirection[];
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Resume grammar for the `commit-palette` step: commit to one direction (by id)
|
|
288
|
+
* or adopt the union of every direction's colours.
|
|
289
|
+
*/
|
|
290
|
+
export type CommitPaletteResumeData = {
|
|
291
|
+
choice: "direction";
|
|
292
|
+
directionId: string;
|
|
293
|
+
} | {
|
|
294
|
+
choice: "all";
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* Resume grammar for the `review-material-criteria` step: accept a resolved
|
|
298
|
+
* attribute/term for an unresolved phrase, or drop it.
|
|
299
|
+
*/
|
|
300
|
+
export interface ReviewCriteriaResumeData {
|
|
301
|
+
corrections: Array<{
|
|
302
|
+
action: "accept";
|
|
303
|
+
slotId: string;
|
|
304
|
+
raw: string;
|
|
305
|
+
attributeId: string;
|
|
306
|
+
term?: string;
|
|
307
|
+
} | {
|
|
308
|
+
action: "drop";
|
|
309
|
+
slotId: string;
|
|
310
|
+
raw: string;
|
|
311
|
+
}>;
|
|
312
|
+
}
|
|
313
|
+
/** The union of every socratic-step resume payload, keyed by the suspended step. */
|
|
314
|
+
export type InterviewResumeData = ReviewCriteriaResumeData | ReviewProgrammeResumeData | CommitPaletteResumeData;
|
|
315
|
+
/** The three socratic interview steps, in procedure order. */
|
|
316
|
+
export type InterviewStepId = "review-programme" | "commit-palette" | "review-material-criteria";
|
|
317
|
+
/**
|
|
318
|
+
* Payload of a `composition.answered` event — the pile answered a question with
|
|
319
|
+
* no human turn, or a suspension resolved. `answer` is a stable slug; `citation`
|
|
320
|
+
* attributes an answer to its source when the brief stated it.
|
|
321
|
+
*/
|
|
322
|
+
export interface CompositionAnsweredPayload {
|
|
323
|
+
step: InterviewStepId;
|
|
324
|
+
answer: string;
|
|
325
|
+
citation: CompositionSourceRef | null;
|
|
326
|
+
counts?: Record<string, number>;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Payload of a `composition.slot-pool` event — the candidate theater for one
|
|
330
|
+
* slot: how many were considered and the top picks (≤5) hydrated for identity.
|
|
331
|
+
*/
|
|
332
|
+
export interface SlotPoolEventPayload {
|
|
333
|
+
slotId: string;
|
|
334
|
+
slotName: string;
|
|
335
|
+
considered: number;
|
|
336
|
+
top: Array<{
|
|
337
|
+
variantId: string;
|
|
338
|
+
brand: string;
|
|
339
|
+
product: string;
|
|
340
|
+
imageUrl: string | null;
|
|
341
|
+
score: number;
|
|
342
|
+
}>;
|
|
343
|
+
}
|
package/http-transport.d.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* The shared low-level HTTP transport for {@link MaterialGraphClient}. It owns
|
|
3
3
|
* URL building, the bearer header, rate-limit capture, response reading, and the
|
|
4
4
|
* one error-mapping path — so the JSON read surface (`client.ts`), the multipart
|
|
5
|
-
* upload,
|
|
6
|
-
*
|
|
5
|
+
* upload, the SSE stream (`runs.ts`), and the NDJSON Match stream
|
|
6
|
+
* (`match-stream.ts`) all fail identically. Retry-free by design; a single
|
|
7
|
+
* failure surfaces as one {@link MaterialGraphError}.
|
|
7
8
|
*/
|
|
8
9
|
import { type RateLimit } from "./errors.js";
|
|
9
10
|
import { type SdkOperation } from "./operations.js";
|
|
@@ -15,6 +16,12 @@ export interface RequestOptions {
|
|
|
15
16
|
signal?: AbortSignal;
|
|
16
17
|
pathParams?: Readonly<Record<string, string>>;
|
|
17
18
|
}
|
|
19
|
+
export interface StreamRequestOptions extends RequestOptions {
|
|
20
|
+
/** The streaming media type to negotiate (`text/event-stream`, `application/x-ndjson`). */
|
|
21
|
+
accept: string;
|
|
22
|
+
/** Names this stream in the "returned no body" diagnostic, e.g. `"match stream"`. */
|
|
23
|
+
subject: string;
|
|
24
|
+
}
|
|
18
25
|
/** A read, status-checked HTTP response with its parsed body. */
|
|
19
26
|
export interface SentResponse {
|
|
20
27
|
response: Response;
|
|
@@ -51,6 +58,16 @@ export declare class HttpTransport {
|
|
|
51
58
|
* it consumes the whole body via `response.text()`.
|
|
52
59
|
*/
|
|
53
60
|
send(input: SendInput): Promise<SentResponse>;
|
|
61
|
+
/**
|
|
62
|
+
* Open a streaming operation and hand back its UNREAD byte body — the one
|
|
63
|
+
* entry point for every long-lived read (SSE run events, NDJSON Match
|
|
64
|
+
* progress). Everything up to the first byte behaves exactly like
|
|
65
|
+
* {@link send}: same URL building, same bearer header, same rate-limit
|
|
66
|
+
* capture, same {@link MaterialGraphError} mapping for a transport failure or
|
|
67
|
+
* a non-2xx (whose short error body IS read, since it is not a stream).
|
|
68
|
+
* Framing and cancellation belong to the caller.
|
|
69
|
+
*/
|
|
70
|
+
openStream(sdkOperation: SdkOperation, options: StreamRequestOptions): Promise<ReadableStream<Uint8Array>>;
|
|
54
71
|
/** Execute a registered JSON operation and return its typed body. */
|
|
55
72
|
requestJson<T>(sdkOperation: SdkOperation, options?: RequestOptions): Promise<T>;
|
|
56
73
|
}
|
package/http-transport.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* The shared low-level HTTP transport for {@link MaterialGraphClient}. It owns
|
|
3
3
|
* URL building, the bearer header, rate-limit capture, response reading, and the
|
|
4
4
|
* one error-mapping path — so the JSON read surface (`client.ts`), the multipart
|
|
5
|
-
* upload,
|
|
6
|
-
*
|
|
5
|
+
* upload, the SSE stream (`runs.ts`), and the NDJSON Match stream
|
|
6
|
+
* (`match-stream.ts`) all fail identically. Retry-free by design; a single
|
|
7
|
+
* failure surfaces as one {@link MaterialGraphError}.
|
|
7
8
|
*/
|
|
8
9
|
import { MaterialGraphError } from "./errors.js";
|
|
9
10
|
import { operationPath } from "./operations.js";
|
|
@@ -115,6 +116,75 @@ export class HttpTransport {
|
|
|
115
116
|
}
|
|
116
117
|
return { response, text, parsed };
|
|
117
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Open a streaming operation and hand back its UNREAD byte body — the one
|
|
121
|
+
* entry point for every long-lived read (SSE run events, NDJSON Match
|
|
122
|
+
* progress). Everything up to the first byte behaves exactly like
|
|
123
|
+
* {@link send}: same URL building, same bearer header, same rate-limit
|
|
124
|
+
* capture, same {@link MaterialGraphError} mapping for a transport failure or
|
|
125
|
+
* a non-2xx (whose short error body IS read, since it is not a stream).
|
|
126
|
+
* Framing and cancellation belong to the caller.
|
|
127
|
+
*/
|
|
128
|
+
async openStream(sdkOperation, options) {
|
|
129
|
+
const url = this.buildUrl(operationPath(sdkOperation, options.pathParams), options.query);
|
|
130
|
+
const requestUrl = new URL(url);
|
|
131
|
+
const metadata = {
|
|
132
|
+
method: sdkOperation.method,
|
|
133
|
+
origin: requestUrl.origin,
|
|
134
|
+
path: requestUrl.pathname,
|
|
135
|
+
};
|
|
136
|
+
const hasBody = options.body !== undefined;
|
|
137
|
+
let response;
|
|
138
|
+
try {
|
|
139
|
+
response = await this.fetch(url, {
|
|
140
|
+
method: sdkOperation.method,
|
|
141
|
+
headers: this.authHeaders({
|
|
142
|
+
Accept: options.accept,
|
|
143
|
+
...(hasBody ? { "Content-Type": "application/json" } : {}),
|
|
144
|
+
}),
|
|
145
|
+
body: hasBody ? JSON.stringify(options.body) : undefined,
|
|
146
|
+
signal: options.signal,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (cause) {
|
|
150
|
+
throw MaterialGraphError.fromTransport({ ...metadata, cause, callerSignal: options.signal });
|
|
151
|
+
}
|
|
152
|
+
const rateLimit = parseRateLimit(response.headers);
|
|
153
|
+
this.#lastRateLimit = rateLimit;
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
let text = "";
|
|
156
|
+
try {
|
|
157
|
+
text = await response.text();
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// The status IS the error; a body that will not read adds nothing and
|
|
161
|
+
// must not mask it. Report the failure with an empty detail instead.
|
|
162
|
+
text = "";
|
|
163
|
+
}
|
|
164
|
+
let parsed;
|
|
165
|
+
if (text.length > 0) {
|
|
166
|
+
try {
|
|
167
|
+
parsed = JSON.parse(text);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
parsed = text;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
throw MaterialGraphError.fromResponse({
|
|
174
|
+
...metadata,
|
|
175
|
+
status: response.status,
|
|
176
|
+
body: parsed,
|
|
177
|
+
responseText: text,
|
|
178
|
+
rateLimit,
|
|
179
|
+
apiKey: this.apiKey,
|
|
180
|
+
queryValues: [...requestUrl.searchParams.values()],
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (response.body === null) {
|
|
184
|
+
throw MaterialGraphError.unexpected(`MaterialGraph ${options.subject} returned no body`);
|
|
185
|
+
}
|
|
186
|
+
return response.body;
|
|
187
|
+
}
|
|
118
188
|
/** Execute a registered JSON operation and return its typed body. */
|
|
119
189
|
async requestJson(sdkOperation, options = {}) {
|
|
120
190
|
const url = this.buildUrl(operationPath(sdkOperation, options.pathParams), options.query);
|
package/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { MATERIALGRAPH_API_VERSION, SDK_OPERATIONS } from "./operations.js";
|
|
|
4
4
|
export type { SdkOperation, SdkOperationId } from "./operations.js";
|
|
5
5
|
export { TERMINAL_RUN_EVENT_TYPES } from "./run-types.js";
|
|
6
6
|
export type * from "./run-types.js";
|
|
7
|
+
export type * from "./schedule-types.js";
|
|
7
8
|
export { VARY_AXES } from "./composition-types.js";
|
|
8
9
|
export type * from "./composition-types.js";
|
|
9
10
|
export { PROJECT_EVENT_KINDS } from "./project-types.js";
|
|
@@ -13,7 +14,13 @@ export type * from "./run-events.js";
|
|
|
13
14
|
export { MaterialGraphError } from "./errors.js";
|
|
14
15
|
export type { MaterialGraphErrorInit, MaterialGraphErrorKind, MaterialGraphRequestMetadata, ProblemDetail, RateLimit, SanitizedErrorValue, SanitizedIssue, SimpleErrorBody, } from "./errors.js";
|
|
15
16
|
export type * from "./types.js";
|
|
17
|
+
export { MATCH_IMAGE_TYPES, MATCH_MATERIAL_FUNCTIONS, MATCH_STREAM_EVENT_TYPES, MATCH_SURFACE_STATUSES, MATCH_WORKFLOW_STEPS, } from "./match-stream-types.js";
|
|
18
|
+
export type * from "./match-stream-types.js";
|
|
19
|
+
export { CodesClient } from "./codes-client.js";
|
|
16
20
|
export type * from "./codes-types.js";
|
|
21
|
+
export { ColoursClient } from "./colours-client.js";
|
|
22
|
+
export type { ColoursTransport } from "./colours-client.js";
|
|
23
|
+
export type * from "./colour-aggregate-types.js";
|
|
17
24
|
export type * from "./brand-types.js";
|
|
18
25
|
export type * from "./material-asset-types.js";
|
|
19
26
|
export type * from "./material-block-types.js";
|
package/index.js
CHANGED
|
@@ -5,5 +5,8 @@ export { VARY_AXES } from "./composition-types.js";
|
|
|
5
5
|
export { PROJECT_EVENT_KINDS } from "./project-types.js";
|
|
6
6
|
export { COMPOSITION_PHASE_EVENT_TYPES, COMPOSITION_PROGRAMME_EVENT_TYPES, COMPOSITION_REVIEW_CRITERIA_EVENT_TYPES, parseRunEvent, } from "./run-events.js";
|
|
7
7
|
export { MaterialGraphError } from "./errors.js";
|
|
8
|
+
export { MATCH_IMAGE_TYPES, MATCH_MATERIAL_FUNCTIONS, MATCH_STREAM_EVENT_TYPES, MATCH_SURFACE_STATUSES, MATCH_WORKFLOW_STEPS, } from "./match-stream-types.js";
|
|
9
|
+
export { CodesClient } from "./codes-client.js";
|
|
10
|
+
export { ColoursClient } from "./colours-client.js";
|
|
8
11
|
export { CERTIFICATION_PROGRAMME_ATTRIBUTE_ID } from "./spec-types.js";
|
|
9
12
|
export { certificationProgramme, certificationProgrammeCriterion, hasSpec, specCriterion, specTerm, } from "./specifications.js";
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Material Match PROGRESS-STREAM contracts — the wire vocabulary of
|
|
3
|
+
* {@link MaterialGraphClient.matchStream} (`POST /api/v1/search/match` under
|
|
4
|
+
* `Accept: application/x-ndjson`). One JSON object per line; the run ends on a
|
|
5
|
+
* `complete` or an `error` event.
|
|
6
|
+
*
|
|
7
|
+
* These shapes are HAND-MIRRORED from `matchStreamEventSchema` in
|
|
8
|
+
* `@materialgraph/products` — the SDK publishes with zero runtime dependencies,
|
|
9
|
+
* so it cannot import the Zod schema itself. `contract-parity.test.ts` re-checks
|
|
10
|
+
* the mirror against the real schema, deriving the enum members FROM the
|
|
11
|
+
* canonical schemas so a server-side addition fails that test rather than
|
|
12
|
+
* silently widening the client.
|
|
13
|
+
*
|
|
14
|
+
* Every enum here is CLOSED, exactly as the canonical `z.enum(…)` members are.
|
|
15
|
+
* The value lists are exported so a consumer can build an exhaustive switch or a
|
|
16
|
+
* UI legend from the same source the validator uses.
|
|
17
|
+
*/
|
|
18
|
+
import type { MatchCategoryInference, SearchResultItem } from "./types.js";
|
|
19
|
+
/** How the workflow classified the uploaded image. */
|
|
20
|
+
export declare const MATCH_IMAGE_TYPES: readonly ["swatch", "installation", "flatplan"];
|
|
21
|
+
export type MatchImageType = (typeof MATCH_IMAGE_TYPES)[number];
|
|
22
|
+
/** What a detected surface is FOR — the element the material would serve. */
|
|
23
|
+
export declare const MATCH_MATERIAL_FUNCTIONS: readonly ["flooring", "wall_surface", "ceiling", "partition", "upholstery", "millwork", "countertop", "accent", "sample"];
|
|
24
|
+
export type MatchMaterialFunction = (typeof MATCH_MATERIAL_FUNCTIONS)[number];
|
|
25
|
+
/** The workflow's coarse progress phase, narrated by a `step` event. */
|
|
26
|
+
export declare const MATCH_WORKFLOW_STEPS: readonly ["classifying", "decomposing", "matching", "complete"];
|
|
27
|
+
export type MatchWorkflowStep = (typeof MATCH_WORKFLOW_STEPS)[number];
|
|
28
|
+
/** Where one surface stands in the run. */
|
|
29
|
+
export declare const MATCH_SURFACE_STATUSES: readonly ["pending", "matching", "complete", "error"];
|
|
30
|
+
export type MatchSurfaceStatus = (typeof MATCH_SURFACE_STATUSES)[number];
|
|
31
|
+
/** Every frame type the stream can carry — the union's discriminant values. */
|
|
32
|
+
export declare const MATCH_STREAM_EVENT_TYPES: readonly ["step", "classified", "surfaces", "surface", "heartbeat", "complete", "error"];
|
|
33
|
+
export type MatchStreamEventType = (typeof MATCH_STREAM_EVENT_TYPES)[number];
|
|
34
|
+
/** Where a surface sits in the image, in Gemini-style 0–1000 image space. */
|
|
35
|
+
export interface MatchBoundingBox {
|
|
36
|
+
x0: number;
|
|
37
|
+
y0: number;
|
|
38
|
+
x1: number;
|
|
39
|
+
y1: number;
|
|
40
|
+
}
|
|
41
|
+
/** One surface the decompose step found in the image. */
|
|
42
|
+
export interface MatchDetectedSurface {
|
|
43
|
+
id: string;
|
|
44
|
+
label: string;
|
|
45
|
+
materialFunction: MatchMaterialFunction;
|
|
46
|
+
/** The detector's catalog category answer, when it named one. */
|
|
47
|
+
categoryHint?: string;
|
|
48
|
+
/** Detector confidence, always within [0, 1]. */
|
|
49
|
+
confidence: number;
|
|
50
|
+
description?: string;
|
|
51
|
+
boundingBox: MatchBoundingBox;
|
|
52
|
+
/** JPEG data-URI crop used for Match retrieval — the thumbnail to show. */
|
|
53
|
+
cropDataUri?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One surface's matching state and its ranked results. The same `surface.id`
|
|
57
|
+
* arrives repeatedly as the run progresses (`matching` → `complete` / `error`);
|
|
58
|
+
* render by replacing on id rather than appending.
|
|
59
|
+
*
|
|
60
|
+
* `results` and `inferredCategories` are always present on a yielded event: the
|
|
61
|
+
* canonical schema defaults each to `[]`, and the reader fills them when the
|
|
62
|
+
* server omits them, so neither ever arrives as `undefined`.
|
|
63
|
+
*/
|
|
64
|
+
export interface MatchSurfaceResult {
|
|
65
|
+
surface: MatchDetectedSurface;
|
|
66
|
+
status: MatchSurfaceStatus;
|
|
67
|
+
results: SearchResultItem[];
|
|
68
|
+
inferredCategories: MatchCategoryInference[];
|
|
69
|
+
/** Present only on `status: "error"` — why this one surface failed. */
|
|
70
|
+
error?: string;
|
|
71
|
+
}
|
|
72
|
+
/** The terminal Match workflow result — every surface in its final state. */
|
|
73
|
+
export interface MatchWorkflowResult {
|
|
74
|
+
imageType: MatchImageType;
|
|
75
|
+
surfaces: MatchSurfaceResult[];
|
|
76
|
+
/** Present for swatch / single-surface runs that skip decompose. */
|
|
77
|
+
results?: SearchResultItem[];
|
|
78
|
+
inferredCategories?: MatchCategoryInference[];
|
|
79
|
+
/**
|
|
80
|
+
* How the run ended, counted rather than inferred. `surfacesFailed > 0` with
|
|
81
|
+
* no results is a broken run; zero failures with no results is a genuine
|
|
82
|
+
* "nothing above the floor". Do not fold `surfaces[]` to tell those apart.
|
|
83
|
+
*/
|
|
84
|
+
totals: MatchRunTotals;
|
|
85
|
+
}
|
|
86
|
+
/** Progress narration for the phase the workflow just entered. */
|
|
87
|
+
export interface MatchStreamStepEvent {
|
|
88
|
+
type: "step";
|
|
89
|
+
step: MatchWorkflowStep;
|
|
90
|
+
message: string;
|
|
91
|
+
}
|
|
92
|
+
/** The image type the classify step settled on. */
|
|
93
|
+
export interface MatchStreamClassifiedEvent {
|
|
94
|
+
type: "classified";
|
|
95
|
+
imageType: MatchImageType;
|
|
96
|
+
}
|
|
97
|
+
/** Every surface the decompose step found, before any of them has matched. */
|
|
98
|
+
export interface MatchStreamSurfacesEvent {
|
|
99
|
+
type: "surfaces";
|
|
100
|
+
surfaces: MatchDetectedSurface[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The run's tally at the moment a `surface` event was emitted, so a consumer
|
|
104
|
+
* reads the server's arithmetic rather than reimplementing it.
|
|
105
|
+
*
|
|
106
|
+
* A FAILED surface is the case this exists to settle: failing is a way of
|
|
107
|
+
* RESOLVING. An errored surface counts in `surfacesResolved` (it will never be
|
|
108
|
+
* reported again), counts again in `surfacesFailed`, and adds nothing to
|
|
109
|
+
* `matchesSoFar`. `surfacesResolved === surfacesDetected` is therefore the
|
|
110
|
+
* correct "the run is done" test, and never stalls at 3/4 on a failure.
|
|
111
|
+
*/
|
|
112
|
+
export interface MatchRunTotals {
|
|
113
|
+
/** Surfaces the decompose step found. The denominator; fixed for the run. */
|
|
114
|
+
surfacesDetected: number;
|
|
115
|
+
/** Surfaces that reached a terminal state — `complete` OR `error`. */
|
|
116
|
+
surfacesResolved: number;
|
|
117
|
+
/** Of those resolved, how many ended in `error`. Always ≤ `surfacesResolved`. */
|
|
118
|
+
surfacesFailed: number;
|
|
119
|
+
/** Result items accumulated across every surface resolved so far. */
|
|
120
|
+
matchesSoFar: number;
|
|
121
|
+
}
|
|
122
|
+
/** One surface's state changed — the frame that makes Match progressive. */
|
|
123
|
+
export interface MatchStreamSurfaceEvent {
|
|
124
|
+
type: "surface";
|
|
125
|
+
surface: MatchSurfaceResult;
|
|
126
|
+
/** The run's tally as of this frame. Always present on a `surface` event. */
|
|
127
|
+
totals: MatchRunTotals;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* CONNECTION LIVENESS ONLY — never progress. Proxies and load balancers kill
|
|
131
|
+
* idle connections, so the server sends this when the stream has been quiet, and
|
|
132
|
+
* a client can tell a slow run from a dropped one. It carries no payload, and no
|
|
133
|
+
* consumer should treat it as a state change: do not advance a step, reset a
|
|
134
|
+
* no-progress watchdog, or count it towards anything.
|
|
135
|
+
*/
|
|
136
|
+
export interface MatchStreamHeartbeatEvent {
|
|
137
|
+
type: "heartbeat";
|
|
138
|
+
}
|
|
139
|
+
/** Terminal: the whole run finished and carries its assembled result. */
|
|
140
|
+
export interface MatchStreamCompleteEvent {
|
|
141
|
+
type: "complete";
|
|
142
|
+
result: MatchWorkflowResult;
|
|
143
|
+
}
|
|
144
|
+
/** Terminal: the run failed as a whole (a single surface failing is not this). */
|
|
145
|
+
export interface MatchStreamErrorEvent {
|
|
146
|
+
type: "error";
|
|
147
|
+
message: string;
|
|
148
|
+
}
|
|
149
|
+
/** One frame of the Match progress stream, discriminated by `type`. */
|
|
150
|
+
export type MatchStreamEvent = MatchStreamStepEvent | MatchStreamClassifiedEvent | MatchStreamSurfacesEvent | MatchStreamSurfaceEvent | MatchStreamHeartbeatEvent | MatchStreamCompleteEvent | MatchStreamErrorEvent;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** How the workflow classified the uploaded image. */
|
|
2
|
+
export const MATCH_IMAGE_TYPES = ["swatch", "installation", "flatplan"];
|
|
3
|
+
/** What a detected surface is FOR — the element the material would serve. */
|
|
4
|
+
export const MATCH_MATERIAL_FUNCTIONS = [
|
|
5
|
+
"flooring",
|
|
6
|
+
"wall_surface",
|
|
7
|
+
"ceiling",
|
|
8
|
+
"partition",
|
|
9
|
+
"upholstery",
|
|
10
|
+
"millwork",
|
|
11
|
+
"countertop",
|
|
12
|
+
"accent",
|
|
13
|
+
"sample",
|
|
14
|
+
];
|
|
15
|
+
/** The workflow's coarse progress phase, narrated by a `step` event. */
|
|
16
|
+
export const MATCH_WORKFLOW_STEPS = ["classifying", "decomposing", "matching", "complete"];
|
|
17
|
+
/** Where one surface stands in the run. */
|
|
18
|
+
export const MATCH_SURFACE_STATUSES = ["pending", "matching", "complete", "error"];
|
|
19
|
+
/** Every frame type the stream can carry — the union's discriminant values. */
|
|
20
|
+
export const MATCH_STREAM_EVENT_TYPES = [
|
|
21
|
+
"step",
|
|
22
|
+
"classified",
|
|
23
|
+
"surfaces",
|
|
24
|
+
"surface",
|
|
25
|
+
"heartbeat",
|
|
26
|
+
"complete",
|
|
27
|
+
"error",
|
|
28
|
+
];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { HttpTransport } from "./http-transport.js";
|
|
2
|
+
import { type MatchStreamEvent } from "./match-stream-types.js";
|
|
3
|
+
import type { MatchInput } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Decode one NDJSON line into a validated {@link MatchStreamEvent}, raising a
|
|
6
|
+
* {@link MaterialGraphError} on anything the contract does not allow. Exported
|
|
7
|
+
* for `contract-parity.test.ts`, which re-checks it against the real Zod schema;
|
|
8
|
+
* it is not part of the published surface.
|
|
9
|
+
*/
|
|
10
|
+
export declare function decodeMatchStreamEvent(line: string): MatchStreamEvent;
|
|
11
|
+
/**
|
|
12
|
+
* Stream Match progress as an async iterator. Iterates until the run reaches a
|
|
13
|
+
* `complete` or an `error` event, and raises if the connection ends before one
|
|
14
|
+
* arrives.
|
|
15
|
+
*
|
|
16
|
+
* `signal` genuinely cancels: it is linked to the AbortController that owns the
|
|
17
|
+
* in-flight request, so aborting (or simply `break`ing out of the loop) tears
|
|
18
|
+
* down the HTTP connection rather than merely stopping the yields. A
|
|
19
|
+
* caller-requested abort ends the iteration CLEANLY — the caller already knows
|
|
20
|
+
* it cancelled, so the truncated-stream error is not raised for it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function matchStream(transport: HttpTransport, input: MatchInput, signal?: AbortSignal): AsyncGenerator<MatchStreamEvent, void, unknown>;
|