@instruments/materialgraph 0.10.0 → 0.11.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/browse-types.d.ts +147 -0
- package/browse-types.js +1 -0
- package/by-colour-types.d.ts +56 -0
- package/client-browse.d.ts +13 -3
- package/client-browse.js +12 -2
- package/client-exploration.d.ts +9 -0
- package/client-exploration.js +7 -0
- package/client-source-material-comments.d.ts +51 -0
- package/client-source-material-comments.js +40 -0
- package/client.d.ts +28 -22
- package/client.js +33 -24
- package/colour-aggregate-types.d.ts +58 -0
- package/colours-client.d.ts +8 -2
- package/colours-client.js +15 -2
- package/composition-client.d.ts +33 -0
- package/composition-client.js +27 -0
- package/composition-types.d.ts +46 -0
- package/coordinate-client.d.ts +23 -0
- package/coordinate-client.js +25 -0
- package/exploration-types.d.ts +110 -0
- package/exploration-types.js +1 -0
- package/fan-deck-types.d.ts +4 -1
- package/index.d.ts +4 -1
- package/operations.d.ts +1 -1
- package/operations.js +25 -0
- package/package.json +1 -1
- package/palette-search-types.d.ts +31 -0
- package/run-types.d.ts +132 -0
- package/runs.d.ts +35 -1
- package/runs.js +61 -1
- package/schedule-types.d.ts +128 -0
- package/schema-resource-types.d.ts +1 -1
- package/search-refinement-types.d.ts +28 -1
- package/search-request-types.d.ts +172 -0
- package/search-request-types.js +1 -0
- package/slot-pool-types.d.ts +370 -0
- package/slot-pool-types.js +1 -0
- package/source-material-validation.d.ts +5 -1
- package/source-material-validation.js +111 -4
- package/types.d.ts +149 -199
- package/validation.d.ts +12 -0
- package/validation.js +23 -0
package/run-types.d.ts
CHANGED
|
@@ -105,6 +105,113 @@ export interface SourceImageAnalysis {
|
|
|
105
105
|
/** ISO-8601 timestamp of the most recent analysis. */
|
|
106
106
|
analysedAt: string;
|
|
107
107
|
}
|
|
108
|
+
/** Who a comment is from, as you SUBMIT it. Not the API principal posting it. */
|
|
109
|
+
export interface SourceMaterialCommentAuthorInput {
|
|
110
|
+
/** Trimmed, 1–160 characters. Longer or blank is refused by the server. */
|
|
111
|
+
name: string;
|
|
112
|
+
/**
|
|
113
|
+
* Their practice or team; `null` for a note carrying no organisation.
|
|
114
|
+
* Trimmed, 1–160 characters when present. Nullable, not optional.
|
|
115
|
+
*/
|
|
116
|
+
organisation: string | null;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Who a comment is from, as you READ IT BACK.
|
|
120
|
+
*
|
|
121
|
+
* `attribution` is the honesty field: nothing verified this name. Render it as
|
|
122
|
+
* "recorded as from X", never as a statement X signed — the identity that IS
|
|
123
|
+
* verified is {@link SourceMaterialCommentReply.recordedBy}.
|
|
124
|
+
*/
|
|
125
|
+
export interface SourceMaterialCommentAuthor extends SourceMaterialCommentAuthorInput {
|
|
126
|
+
/**
|
|
127
|
+
* How the name got here. `reported` means a caller typed it. {@link OpenEnum}
|
|
128
|
+
* keeps a newer server value type-checking; treat anything unfamiliar as
|
|
129
|
+
* unverified.
|
|
130
|
+
*/
|
|
131
|
+
attribution: OpenEnum<"reported">;
|
|
132
|
+
}
|
|
133
|
+
/** The authenticated principal that recorded a comment. */
|
|
134
|
+
export interface SourceMaterialCommentRecorder {
|
|
135
|
+
/**
|
|
136
|
+
* The principal that wrote the row. Comments can only be posted against a
|
|
137
|
+
* batch the poster owns, so on every comment you can read this is your own.
|
|
138
|
+
*/
|
|
139
|
+
principalId: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* One reply on a source-material file. A reply carries no replies of its own:
|
|
143
|
+
* threads are one level deep because the database makes a reply-to-reply
|
|
144
|
+
* unstorable, and this type says so rather than implying a tree.
|
|
145
|
+
*/
|
|
146
|
+
export interface SourceMaterialCommentReply {
|
|
147
|
+
/** The stored comment id (`smc_…`). Opaque. */
|
|
148
|
+
commentId: string;
|
|
149
|
+
author: SourceMaterialCommentAuthor;
|
|
150
|
+
/** Who recorded it — the audit answer behind the reported name. */
|
|
151
|
+
recordedBy: SourceMaterialCommentRecorder;
|
|
152
|
+
body: string;
|
|
153
|
+
/** ISO-8601 timestamp of the posting. */
|
|
154
|
+
createdAt: string;
|
|
155
|
+
}
|
|
156
|
+
/** One root remark with the replies that answer it. */
|
|
157
|
+
export interface SourceMaterialComment extends SourceMaterialCommentReply {
|
|
158
|
+
/** ALWAYS an array. `[]` means nobody answered — never a missing field. */
|
|
159
|
+
replies: SourceMaterialCommentReply[];
|
|
160
|
+
}
|
|
161
|
+
/** The `200` body of `GET …/sources/{sourceId}/comments`. */
|
|
162
|
+
export interface SourceMaterialCommentThread {
|
|
163
|
+
/** The file the thread hangs off (`smf_…`). Opaque. */
|
|
164
|
+
sourceId: string;
|
|
165
|
+
/**
|
|
166
|
+
* The thread, oldest root first. ALWAYS an array; `[]` means the file is
|
|
167
|
+
* yours and nobody has commented on it.
|
|
168
|
+
*/
|
|
169
|
+
comments: SourceMaterialComment[];
|
|
170
|
+
/**
|
|
171
|
+
* `true` when the file holds more comment rows than one read inlines (500),
|
|
172
|
+
* so `comments` is the oldest of them rather than all of them.
|
|
173
|
+
*/
|
|
174
|
+
truncated: boolean;
|
|
175
|
+
}
|
|
176
|
+
/** The body of `POST …/sources/{sourceId}/comments`. */
|
|
177
|
+
export interface PostSourceMaterialCommentInput {
|
|
178
|
+
author: SourceMaterialCommentAuthorInput;
|
|
179
|
+
/** Trimmed, 1–4000 characters. */
|
|
180
|
+
body: string;
|
|
181
|
+
/**
|
|
182
|
+
* The root comment being answered, or absent for a new remark. Must be a ROOT
|
|
183
|
+
* comment on the same file: replying to a reply is a 400.
|
|
184
|
+
*/
|
|
185
|
+
parentCommentId?: string | null;
|
|
186
|
+
/**
|
|
187
|
+
* Your own token for this one intended remark, 1–200 characters. Send it and
|
|
188
|
+
* a retry after a network timeout returns the comment you already posted
|
|
189
|
+
* rather than a permanent duplicate. Scoped to the file.
|
|
190
|
+
*/
|
|
191
|
+
idempotencyKey?: string | null;
|
|
192
|
+
}
|
|
193
|
+
/** A comment the server just stored, as a root. */
|
|
194
|
+
export interface StoredSourceMaterialRootComment extends SourceMaterialComment {
|
|
195
|
+
kind: "root";
|
|
196
|
+
}
|
|
197
|
+
/** A comment the server just stored, as a reply to a root. */
|
|
198
|
+
export interface StoredSourceMaterialReplyComment extends SourceMaterialCommentReply {
|
|
199
|
+
kind: "reply";
|
|
200
|
+
/** The root this answers. Present because a reply always has one. */
|
|
201
|
+
parentCommentId: string;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* What a POST returns, discriminated on `kind`. Switch on it: a `root` carries
|
|
205
|
+
* an empty `replies`, a `reply` carries `parentCommentId` and no `replies` at
|
|
206
|
+
* all, because it can never have any.
|
|
207
|
+
*/
|
|
208
|
+
export type StoredSourceMaterialComment = StoredSourceMaterialRootComment | StoredSourceMaterialReplyComment;
|
|
209
|
+
/** The `201` body of `POST …/sources/{sourceId}/comments`. */
|
|
210
|
+
export interface PostSourceMaterialCommentResult {
|
|
211
|
+
/** The file the comment hangs off (`smf_…`). Opaque. */
|
|
212
|
+
sourceId: string;
|
|
213
|
+
comment: StoredSourceMaterialComment;
|
|
214
|
+
}
|
|
108
215
|
/** One file as `GET /api/v1/source-material/{batchId}` returns it. */
|
|
109
216
|
export interface SourceMaterialBatchFile extends SourceMaterialReceiptFile {
|
|
110
217
|
/** Fetchable URL for the stored bytes. */
|
|
@@ -115,6 +222,18 @@ export interface SourceMaterialBatchFile extends SourceMaterialReceiptFile {
|
|
|
115
222
|
* only the trigger changes and this contract does not.
|
|
116
223
|
*/
|
|
117
224
|
analyses: SourceImageAnalysis[];
|
|
225
|
+
/**
|
|
226
|
+
* The argument about this file, oldest root first. ALWAYS an array; `[]` means
|
|
227
|
+
* nobody has commented, which is a state to draw rather than an absence to
|
|
228
|
+
* guess at.
|
|
229
|
+
*/
|
|
230
|
+
comments: SourceMaterialComment[];
|
|
231
|
+
/**
|
|
232
|
+
* `true` when this file holds more comment rows than one read inlines (500),
|
|
233
|
+
* so `comments` is the oldest of them. `false` on a file whose whole thread
|
|
234
|
+
* you have, including one nobody has commented on.
|
|
235
|
+
*/
|
|
236
|
+
commentsTruncated: boolean;
|
|
118
237
|
}
|
|
119
238
|
/**
|
|
120
239
|
* The `200` body of `GET /api/v1/source-material/{batchId}` — the batch, every
|
|
@@ -139,6 +258,12 @@ export interface CapabilityInvokeLink {
|
|
|
139
258
|
href: string;
|
|
140
259
|
method: OpenEnum<"GET" | "POST">;
|
|
141
260
|
}
|
|
261
|
+
/** One human-input checkpoint exposed by a workflow capability. */
|
|
262
|
+
export interface PublicCapabilitySuspension {
|
|
263
|
+
stepId: string;
|
|
264
|
+
suspend: Record<string, unknown>;
|
|
265
|
+
resume: Record<string, unknown>;
|
|
266
|
+
}
|
|
142
267
|
/** One invocable capability from the governed public manifest. */
|
|
143
268
|
export interface PublicCapabilityMetadata {
|
|
144
269
|
publicId: string;
|
|
@@ -150,6 +275,7 @@ export interface PublicCapabilityMetadata {
|
|
|
150
275
|
schemas: {
|
|
151
276
|
input: Record<string, unknown>;
|
|
152
277
|
output: Record<string, unknown>;
|
|
278
|
+
suspensions?: PublicCapabilitySuspension[];
|
|
153
279
|
};
|
|
154
280
|
sideEffectClass: CapabilitySideEffectClass;
|
|
155
281
|
scopes: CapabilityScope[];
|
|
@@ -193,10 +319,16 @@ export interface PublicRun {
|
|
|
193
319
|
usageJson: Record<string, unknown> | null;
|
|
194
320
|
artifactRefs: PublicRunArtifactRef[] | null;
|
|
195
321
|
failureJson: PublicRunFailure | null;
|
|
322
|
+
/** The current typed question payload while this Run is suspended. */
|
|
323
|
+
suspensionPayload: Record<string, unknown> | null;
|
|
196
324
|
/**
|
|
197
325
|
* The step awaiting input while `status` is `suspended`. Pass it as
|
|
198
326
|
* {@link ResumeRunOptions.stepId} so a stale client cannot resume a step the
|
|
199
327
|
* Run has already moved past.
|
|
328
|
+
*
|
|
329
|
+
* Read `suspensionPayload` for the current typed question. The same payload is
|
|
330
|
+
* also retained on the `suspended` ledger event; composition-specific events
|
|
331
|
+
* remain available as historical progress records.
|
|
200
332
|
*/
|
|
201
333
|
suspensionStepId: string | null;
|
|
202
334
|
eventCursor: number;
|
package/runs.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpTransport } from "./http-transport.js";
|
|
2
|
-
import { type CapabilityManifestResponse, type CreatePublicRunRequest, type CreatePublicRunResponse, type DrainRunEventsOptions, type ListRunsOptions, type PublicRunListResponse, type PublicRunResponse, type ResumeRunOptions, type RunEvent, type SourceMaterialBatchDetail, type SourceMaterialBatchReceipt, type StreamRunEventsOptions, type UploadSourceMaterialInput } from "./run-types.js";
|
|
2
|
+
import { type CapabilityManifestResponse, type CreatePublicRunRequest, type CreatePublicRunResponse, type DrainRunEventsOptions, type ListRunsOptions, type PostSourceMaterialCommentInput, type PostSourceMaterialCommentResult, type PublicRunListResponse, type PublicRunResponse, type ResumeRunOptions, type RunEvent, type SourceMaterialBatchDetail, type SourceMaterialBatchReceipt, type SourceMaterialCommentThread, type StreamRunEventsOptions, type UploadSourceMaterialInput } from "./run-types.js";
|
|
3
3
|
import type { FinishScheduleResponse, GetRunScheduleOptions } from "./schedule-types.js";
|
|
4
4
|
/**
|
|
5
5
|
* Upload one atomic source-material batch (`POST /api/v1/source-material`).
|
|
@@ -21,6 +21,40 @@ export declare function uploadSourceMaterial(transport: HttpTransport, input: Up
|
|
|
21
21
|
* the payload rather than as an undefined three frames away in caller code.
|
|
22
22
|
*/
|
|
23
23
|
export declare function getSourceMaterialBatch(transport: HttpTransport, batchId: string, signal?: AbortSignal): Promise<SourceMaterialBatchDetail>;
|
|
24
|
+
/**
|
|
25
|
+
* Read one file's comment thread
|
|
26
|
+
* (`GET /api/v1/source-material/{batchId}/sources/{sourceId}/comments`).
|
|
27
|
+
*
|
|
28
|
+
* The remarks people left on one uploaded file, oldest root first, each root
|
|
29
|
+
* carrying the replies that answer it. `comments` is always an array — `[]`
|
|
30
|
+
* means the file is yours and nobody has said anything about it.
|
|
31
|
+
*
|
|
32
|
+
* Author names are REPORTED, never verified: each carries
|
|
33
|
+
* `author.attribution: "reported"` plus the principal that recorded it in
|
|
34
|
+
* `recordedBy`. Render "recorded as from X", not "X said".
|
|
35
|
+
*
|
|
36
|
+
* `truncated` is `true` when the file holds more rows than one read inlines.
|
|
37
|
+
*
|
|
38
|
+
* A file that is not in that batch, or a batch you do not own, raises the same
|
|
39
|
+
* 404 as a file that never existed.
|
|
40
|
+
*/
|
|
41
|
+
export declare function listSourceMaterialComments(transport: HttpTransport, batchId: string, sourceId: string, signal?: AbortSignal): Promise<SourceMaterialCommentThread>;
|
|
42
|
+
/**
|
|
43
|
+
* Add one remark to a file's thread
|
|
44
|
+
* (`POST /api/v1/source-material/{batchId}/sources/{sourceId}/comments`).
|
|
45
|
+
*
|
|
46
|
+
* `author` is who the comment is FROM — usually the client or a colleague, not
|
|
47
|
+
* the key posting it — so it is stored and read back as reported attribution.
|
|
48
|
+
* Pass `parentCommentId` to answer a ROOT comment on the same file; anything
|
|
49
|
+
* deeper is a 400, because threads are one level deep.
|
|
50
|
+
*
|
|
51
|
+
* Pass `idempotencyKey` and a retry after a network timeout returns the comment
|
|
52
|
+
* you already posted rather than a permanent duplicate.
|
|
53
|
+
*
|
|
54
|
+
* The result is discriminated on `kind`: switch on it rather than reaching for
|
|
55
|
+
* `replies`, which a `reply` does not have.
|
|
56
|
+
*/
|
|
57
|
+
export declare function postSourceMaterialComment(transport: HttpTransport, batchId: string, sourceId: string, input: PostSourceMaterialCommentInput, signal?: AbortSignal): Promise<PostSourceMaterialCommentResult>;
|
|
24
58
|
/** List invocable agents, workflows, and tools (`GET /api/v1/capabilities`). */
|
|
25
59
|
export declare function capabilities(transport: HttpTransport, signal?: AbortSignal): Promise<CapabilityManifestResponse>;
|
|
26
60
|
/** Start an asynchronous capability Run (`POST /api/v1/runs`). */
|
package/runs.js
CHANGED
|
@@ -9,7 +9,7 @@ import { MaterialGraphError } from "./errors.js";
|
|
|
9
9
|
import { HttpTransport } from "./http-transport.js";
|
|
10
10
|
import { findSdkOperation, operationPath, } from "./operations.js";
|
|
11
11
|
import { TERMINAL_RUN_EVENT_TYPES, } from "./run-types.js";
|
|
12
|
-
import { checkSourceMaterialBatchDetail } from "./source-material-validation.js";
|
|
12
|
+
import { checkPostSourceMaterialCommentResult, checkSourceMaterialBatchDetail, checkSourceMaterialCommentThread, } from "./source-material-validation.js";
|
|
13
13
|
import { readSseFrames } from "./sse.js";
|
|
14
14
|
function runOperation(operationId) {
|
|
15
15
|
const match = findSdkOperation(operationId);
|
|
@@ -86,6 +86,66 @@ export async function getSourceMaterialBatch(transport, batchId, signal) {
|
|
|
86
86
|
});
|
|
87
87
|
return checkSourceMaterialBatchDetail(body, batchId);
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Read one file's comment thread
|
|
91
|
+
* (`GET /api/v1/source-material/{batchId}/sources/{sourceId}/comments`).
|
|
92
|
+
*
|
|
93
|
+
* The remarks people left on one uploaded file, oldest root first, each root
|
|
94
|
+
* carrying the replies that answer it. `comments` is always an array — `[]`
|
|
95
|
+
* means the file is yours and nobody has said anything about it.
|
|
96
|
+
*
|
|
97
|
+
* Author names are REPORTED, never verified: each carries
|
|
98
|
+
* `author.attribution: "reported"` plus the principal that recorded it in
|
|
99
|
+
* `recordedBy`. Render "recorded as from X", not "X said".
|
|
100
|
+
*
|
|
101
|
+
* `truncated` is `true` when the file holds more rows than one read inlines.
|
|
102
|
+
*
|
|
103
|
+
* A file that is not in that batch, or a batch you do not own, raises the same
|
|
104
|
+
* 404 as a file that never existed.
|
|
105
|
+
*/
|
|
106
|
+
export async function listSourceMaterialComments(transport, batchId, sourceId, signal) {
|
|
107
|
+
const body = await transport.requestJson(runOperation("list_source_material_comments"), {
|
|
108
|
+
pathParams: { batchId, sourceId },
|
|
109
|
+
signal,
|
|
110
|
+
});
|
|
111
|
+
return checkSourceMaterialCommentThread(body, sourceId);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Add one remark to a file's thread
|
|
115
|
+
* (`POST /api/v1/source-material/{batchId}/sources/{sourceId}/comments`).
|
|
116
|
+
*
|
|
117
|
+
* `author` is who the comment is FROM — usually the client or a colleague, not
|
|
118
|
+
* the key posting it — so it is stored and read back as reported attribution.
|
|
119
|
+
* Pass `parentCommentId` to answer a ROOT comment on the same file; anything
|
|
120
|
+
* deeper is a 400, because threads are one level deep.
|
|
121
|
+
*
|
|
122
|
+
* Pass `idempotencyKey` and a retry after a network timeout returns the comment
|
|
123
|
+
* you already posted rather than a permanent duplicate.
|
|
124
|
+
*
|
|
125
|
+
* The result is discriminated on `kind`: switch on it rather than reaching for
|
|
126
|
+
* `replies`, which a `reply` does not have.
|
|
127
|
+
*/
|
|
128
|
+
export async function postSourceMaterialComment(transport, batchId, sourceId, input, signal) {
|
|
129
|
+
const body = await transport.requestJson(runOperation("post_source_material_comment"), {
|
|
130
|
+
// Built key by key rather than spread: `parentCommentId` and
|
|
131
|
+
// `idempotencyKey` are forwarded only when PRESENT, so a caller that
|
|
132
|
+
// omits them sends a body byte-identical to one that never knew about
|
|
133
|
+
// them.
|
|
134
|
+
body: {
|
|
135
|
+
author: input.author,
|
|
136
|
+
body: input.body,
|
|
137
|
+
...(input.parentCommentId === undefined
|
|
138
|
+
? {}
|
|
139
|
+
: { parentCommentId: input.parentCommentId }),
|
|
140
|
+
...(input.idempotencyKey === undefined
|
|
141
|
+
? {}
|
|
142
|
+
: { idempotencyKey: input.idempotencyKey }),
|
|
143
|
+
},
|
|
144
|
+
pathParams: { batchId, sourceId },
|
|
145
|
+
signal,
|
|
146
|
+
});
|
|
147
|
+
return checkPostSourceMaterialCommentResult(body, sourceId);
|
|
148
|
+
}
|
|
89
149
|
/** List invocable agents, workflows, and tools (`GET /api/v1/capabilities`). */
|
|
90
150
|
export function capabilities(transport, signal) {
|
|
91
151
|
return transport.requestJson(runOperation("list_public_capabilities"), {
|
package/schedule-types.d.ts
CHANGED
|
@@ -12,6 +12,14 @@
|
|
|
12
12
|
* ABSENT IS NOT FALSE, enforced structurally: because the column set is fixed,
|
|
13
13
|
* "we do not know" has to be SAID rather than omitted. Read
|
|
14
14
|
* {@link FinishScheduleCell.state} before printing `value`.
|
|
15
|
+
*
|
|
16
|
+
* MG-1351 added the schedule's organising principle: every row carries an
|
|
17
|
+
* {@link FinishScheduleRow.code} L-code under a numbered section, rows arrive in
|
|
18
|
+
* READING order, slots with nothing specified are served as
|
|
19
|
+
* {@link FinishSchedule.openSlots}, and a row may carry or-equal
|
|
20
|
+
* {@link FinishScheduleAlternate}s. Read `alternates` with
|
|
21
|
+
* {@link FinishSchedule.alternatesResolved}: an empty list means either "we did
|
|
22
|
+
* not ask" or "there is none", and those are different claims.
|
|
15
23
|
*/
|
|
16
24
|
import type { RunId, SchemeId, SlotId, VariantId } from "./identifiers.js";
|
|
17
25
|
/** One schedule column: the attribute and its header text. */
|
|
@@ -51,8 +59,106 @@ export interface FinishScheduleCell {
|
|
|
51
59
|
* `hard_no` means a required cell failed on a real value.
|
|
52
60
|
*/
|
|
53
61
|
export type FinishScheduleGrade = "meets_all" | "close" | "hard_no";
|
|
62
|
+
/** One numbered section of the schedule, and the heading it prints. */
|
|
63
|
+
export interface FinishScheduleSection {
|
|
64
|
+
/** The hundreds block, e.g. `100`. Also the base of every line number in it. */
|
|
65
|
+
code: number;
|
|
66
|
+
name: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* What MaterialGraph claims about an alternate — and never more than it can
|
|
70
|
+
* support. None of the three means "interchangeable": accepting an alternate is
|
|
71
|
+
* a specification decision, and this API does not make it.
|
|
72
|
+
*
|
|
73
|
+
* `or_equal` matched the specified material's equivalence signature with every
|
|
74
|
+
* supporting value above its confidence floor. `or_equal_unverified` matched the
|
|
75
|
+
* same signature on values that did not clear it. `successor` is the brand's own
|
|
76
|
+
* declared replacement, from a reviewed relationship rather than a spec match —
|
|
77
|
+
* its verdict cells are `not_evaluated` by construction.
|
|
78
|
+
*/
|
|
79
|
+
export type FinishScheduleAlternateStance = "or_equal" | "or_equal_unverified" | "successor";
|
|
80
|
+
/** One criterion of the equivalence signature an alternate was matched on. */
|
|
81
|
+
export interface FinishScheduleAlternateCriterion {
|
|
82
|
+
role: "primary_material" | "performance";
|
|
83
|
+
attributeId: string;
|
|
84
|
+
term: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Why an alternate is on the row. Every field is evidence, not adjective — a
|
|
88
|
+
* substitute presented without its basis looks like a decision when it is a
|
|
89
|
+
* lookup, so this is never absent from an alternate.
|
|
90
|
+
*/
|
|
91
|
+
export interface FinishScheduleAlternateBasis {
|
|
92
|
+
/** The SPECIFIED variant this alternate was graded against. Opaque. */
|
|
93
|
+
specifiedVariantId: VariantId;
|
|
94
|
+
/** The product-type form the signature was projected in; null for a successor. */
|
|
95
|
+
form: string | null;
|
|
96
|
+
/** The criteria that matched. Empty for a successor: nothing was matched. */
|
|
97
|
+
signature: FinishScheduleAlternateCriterion[];
|
|
98
|
+
/** The seed-relative report card, projected exactly as row cells are. */
|
|
99
|
+
verdict: {
|
|
100
|
+
cells: FinishScheduleCell[];
|
|
101
|
+
grade: FinishScheduleGrade;
|
|
102
|
+
minConfidence: number | null;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** One or-equal line sitting beneath the specified line it answers. */
|
|
106
|
+
export interface FinishScheduleAlternate {
|
|
107
|
+
stance: FinishScheduleAlternateStance;
|
|
108
|
+
/** Opaque — round-trip verbatim into the material lenses. */
|
|
109
|
+
variantId: VariantId;
|
|
110
|
+
brand: string;
|
|
111
|
+
product: string;
|
|
112
|
+
variant: string;
|
|
113
|
+
/**
|
|
114
|
+
* The colourway, when the catalogue names one. Carried separately because a
|
|
115
|
+
* substitute's `variant` is frequently the product name repeated, and a line
|
|
116
|
+
* that cannot name the colour has not named a material.
|
|
117
|
+
*/
|
|
118
|
+
colour: string | null;
|
|
119
|
+
imageUrl: string | null;
|
|
120
|
+
hex: string | null;
|
|
121
|
+
recordUrl: string;
|
|
122
|
+
/**
|
|
123
|
+
* Further colourways of this same product that also cleared the signature and
|
|
124
|
+
* were collapsed into this line. A COUNTED collapse: `0` means this product
|
|
125
|
+
* offered exactly one match, never that the count is unknown.
|
|
126
|
+
*/
|
|
127
|
+
otherColourways: number;
|
|
128
|
+
basis: FinishScheduleAlternateBasis;
|
|
129
|
+
}
|
|
130
|
+
/** A slot holding a code with nothing specified against it yet. */
|
|
131
|
+
export interface FinishScheduleOpenSlot {
|
|
132
|
+
code: string;
|
|
133
|
+
sectionCode: number;
|
|
134
|
+
sectionName: string;
|
|
135
|
+
line: number;
|
|
136
|
+
slotId: SlotId;
|
|
137
|
+
slotName: string;
|
|
138
|
+
space: string | null;
|
|
139
|
+
/**
|
|
140
|
+
* Why this line is blank, in words — or `null` when nothing measured a reason.
|
|
141
|
+
*
|
|
142
|
+
* `null` is NOT "nothing was found". It means no diagnosis was recorded for
|
|
143
|
+
* this slot, most often because it was never searched, and it must not be
|
|
144
|
+
* rendered as a search that came back empty.
|
|
145
|
+
*/
|
|
146
|
+
reason: string | null;
|
|
147
|
+
}
|
|
54
148
|
/** One row: one slot, one specified material, fully graded. */
|
|
55
149
|
export interface FinishScheduleRow {
|
|
150
|
+
/**
|
|
151
|
+
* The printed line reference, e.g. `L-101` — how a drawing, an RFI or a
|
|
152
|
+
* substitution request names this line. Allocated from the Run's PROGRAMME
|
|
153
|
+
* rather than its picks, so recomposing the scheme, varying it, or accepting a
|
|
154
|
+
* substitution renumbers nothing.
|
|
155
|
+
*/
|
|
156
|
+
code: string;
|
|
157
|
+
/** The hundreds block this line sits in, e.g. `100`. */
|
|
158
|
+
sectionCode: number;
|
|
159
|
+
sectionName: string;
|
|
160
|
+
/** The 1-based line within its (space, section) group. */
|
|
161
|
+
line: number;
|
|
56
162
|
slotId: SlotId;
|
|
57
163
|
slotName: string;
|
|
58
164
|
/** The space NAME, or null for a spaceless slot. */
|
|
@@ -73,6 +179,11 @@ export interface FinishScheduleRow {
|
|
|
73
179
|
attributes: FinishScheduleCell[];
|
|
74
180
|
fitNotes: string[];
|
|
75
181
|
grade: FinishScheduleGrade;
|
|
182
|
+
/**
|
|
183
|
+
* Or-equal alternates for this line. An empty array is AMBIGUOUS on its own —
|
|
184
|
+
* read it with {@link FinishSchedule.alternatesResolved}.
|
|
185
|
+
*/
|
|
186
|
+
alternates: FinishScheduleAlternate[];
|
|
76
187
|
}
|
|
77
188
|
/** A whole composed scheme as an issuable finish schedule. */
|
|
78
189
|
export interface FinishSchedule {
|
|
@@ -85,7 +196,24 @@ export interface FinishSchedule {
|
|
|
85
196
|
generatedAt: string;
|
|
86
197
|
/** The stable column order every row's `attributes` array follows. */
|
|
87
198
|
columns: FinishScheduleColumn[];
|
|
199
|
+
/**
|
|
200
|
+
* The section table to group by, in schedule order. A section may carry no
|
|
201
|
+
* rows: three are reserved so their conventional numbers are held rather than
|
|
202
|
+
* reused, and any section can simply be unspecified in this scheme.
|
|
203
|
+
*/
|
|
204
|
+
sections: FinishScheduleSection[];
|
|
205
|
+
/** In READING order — space, then section, then line. Not pick order. */
|
|
88
206
|
rows: FinishScheduleRow[];
|
|
207
|
+
/**
|
|
208
|
+
* Slots holding a code with nothing specified against them. Served explicitly
|
|
209
|
+
* because a schedule that omitted them would read as complete.
|
|
210
|
+
*/
|
|
211
|
+
openSlots: FinishScheduleOpenSlot[];
|
|
212
|
+
/**
|
|
213
|
+
* Whether the or-equal lookup RAN. When false, no row's empty `alternates`
|
|
214
|
+
* may be read as "no alternate exists".
|
|
215
|
+
*/
|
|
216
|
+
alternatesResolved: boolean;
|
|
89
217
|
}
|
|
90
218
|
/** `GET /api/v1/runs/{runId}/schedule`. */
|
|
91
219
|
export interface FinishScheduleResponse {
|
|
@@ -130,7 +130,7 @@ export interface SchemaSourceRequirementItem {
|
|
|
130
130
|
minConfidence: number;
|
|
131
131
|
reviewRequired: boolean;
|
|
132
132
|
freshnessDays?: number;
|
|
133
|
-
sourceBasis?: OpenEnum<"declared" | "derived" | "
|
|
133
|
+
sourceBasis?: OpenEnum<"declared" | "derived" | "declared">;
|
|
134
134
|
semanticId: string;
|
|
135
135
|
lifecycle: SchemaLifecycle;
|
|
136
136
|
}
|
|
@@ -35,7 +35,7 @@ export interface SearchFilterTrust {
|
|
|
35
35
|
* - `derived` — MaterialGraph inferred it. Ours, not the manufacturer's — never
|
|
36
36
|
* present a `derived` value as something the brand claims.
|
|
37
37
|
*/
|
|
38
|
-
sourceBasis?: "catalog_feed" | "declared" | "derived" | "
|
|
38
|
+
sourceBasis?: "catalog_feed" | "declared" | "derived" | "declared";
|
|
39
39
|
/** Fold the attribute's own registry confidence requirement in, server-side. */
|
|
40
40
|
meetsRequirement?: true;
|
|
41
41
|
}
|
|
@@ -67,3 +67,30 @@ export interface SearchSuggestion {
|
|
|
67
67
|
/** Complete replacement request, not a partial patch. */
|
|
68
68
|
request: SearchInput;
|
|
69
69
|
}
|
|
70
|
+
/** One of the two readings a {@link SearchDisambiguation} offers. */
|
|
71
|
+
export interface SearchInterpretation {
|
|
72
|
+
id: string;
|
|
73
|
+
label: string;
|
|
74
|
+
kind: string;
|
|
75
|
+
/** True for the reading that ACTUALLY RAN and produced this page. */
|
|
76
|
+
isDefault: boolean;
|
|
77
|
+
/** The colour this reading resolved to, when the ambiguity was a colour one. */
|
|
78
|
+
hex?: string;
|
|
79
|
+
/** Complete replacement request, not a partial patch. */
|
|
80
|
+
request: SearchInput;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The server saying a query had TWO defensible readings, and which one it took.
|
|
84
|
+
*
|
|
85
|
+
* `interpretations` always holds exactly two, the one that ran flagged
|
|
86
|
+
* `isDefault`. Each carries a complete executable request, so offering the
|
|
87
|
+
* other reading is a re-issue rather than a patch. Ignoring this block does not
|
|
88
|
+
* make the ambiguity go away — it makes it invisible to the person who asked.
|
|
89
|
+
*/
|
|
90
|
+
export interface SearchDisambiguation {
|
|
91
|
+
/** Which ambiguity this is: "compliance-phrase", "colour-material", "paint-colour". */
|
|
92
|
+
kind: string;
|
|
93
|
+
/** The ambiguous term or phrase. */
|
|
94
|
+
term: string;
|
|
95
|
+
interpretations: SearchInterpretation[];
|
|
96
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module search-request-types
|
|
3
|
+
* The two request shapes for the `/search` surface — the POST JSON body and the
|
|
4
|
+
* GET query-string form. Split out of `types.ts` so neither file outgrows the
|
|
5
|
+
* repo's source-size gate; every name here is re-exported from `types.ts`, so
|
|
6
|
+
* no import path changes.
|
|
7
|
+
*/
|
|
8
|
+
import type { Cursor, VariantId } from "./identifiers.js";
|
|
9
|
+
import type { SearchFilter } from "./search-refinement-types.js";
|
|
10
|
+
import type { ColourInput, OpenEnum, SearchImageInput } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* The POST `/search` JSON body — the `searchMaterials` contract. Every field is
|
|
13
|
+
* optional; supply at least one signal (`text`, `colours`, `mood`, `similarTo`,
|
|
14
|
+
* or `image`). Passed through to the server verbatim.
|
|
15
|
+
*/
|
|
16
|
+
export interface SearchInput {
|
|
17
|
+
text?: string;
|
|
18
|
+
textModes?: Array<OpenEnum<"visual" | "lexical">>;
|
|
19
|
+
colours?: ColourInput[];
|
|
20
|
+
colourLogic?: OpenEnum<"all" | "any">;
|
|
21
|
+
colourTolerance?: OpenEnum<"tight" | "normal" | "loose">;
|
|
22
|
+
colourAppearance?: {
|
|
23
|
+
lightness?: {
|
|
24
|
+
min: number;
|
|
25
|
+
max: number;
|
|
26
|
+
};
|
|
27
|
+
warmth?: {
|
|
28
|
+
min: number;
|
|
29
|
+
max: number;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
mood?: string;
|
|
33
|
+
sort?: OpenEnum<"relevance" | "light" | "dark" | "vivid" | "muted" | "colourful">;
|
|
34
|
+
/**
|
|
35
|
+
* Seed a visual search from an existing variant's image (`variantId`), from a
|
|
36
|
+
* Material Bank SKU (`materialBankId`), or from a pre-computed image vector
|
|
37
|
+
* (`vector`).
|
|
38
|
+
*
|
|
39
|
+
* `materialBankId` exists so a caller already holding a SKU does not have to
|
|
40
|
+
* ask us to translate it before it can ask us anything useful — one call, not
|
|
41
|
+
* two. It resolves to the variant server-side and then behaves identically to
|
|
42
|
+
* the `{ variantId }` form, seed verdicts included. A SKU that names no
|
|
43
|
+
* variant is a 400 carrying the offending value: never an empty result set,
|
|
44
|
+
* never a silent fall back to unseeded search.
|
|
45
|
+
*
|
|
46
|
+
* The `{ vector }` form carries no seed variant, so it is the only one
|
|
47
|
+
* `verdicts` cannot diff a candidate against.
|
|
48
|
+
*/
|
|
49
|
+
similarTo?: {
|
|
50
|
+
variantId: VariantId;
|
|
51
|
+
} | {
|
|
52
|
+
materialBankId: string;
|
|
53
|
+
} | {
|
|
54
|
+
vector: string;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Canonical variants that must not appear in the returned page. The server
|
|
58
|
+
* applies these exclusions before the page limit, so consumers can request
|
|
59
|
+
* unseen results without shortening an already-bounded page client-side.
|
|
60
|
+
* Maximum 256; ids are opaque.
|
|
61
|
+
*/
|
|
62
|
+
excludeVariantIds?: VariantId[];
|
|
63
|
+
image?: SearchImageInput;
|
|
64
|
+
category?: {
|
|
65
|
+
boost?: string;
|
|
66
|
+
filter?: string[];
|
|
67
|
+
};
|
|
68
|
+
weights?: {
|
|
69
|
+
image?: number;
|
|
70
|
+
category?: number;
|
|
71
|
+
colour?: number;
|
|
72
|
+
lexical?: number;
|
|
73
|
+
/** Text rerank multiplier applied when a seeded search also carries `text` (default 0.4). */
|
|
74
|
+
text?: number;
|
|
75
|
+
};
|
|
76
|
+
filters?: SearchFilter[];
|
|
77
|
+
/**
|
|
78
|
+
* SOFT spec criteria (rank-never-exclude). Same shape as `filters`; a preference
|
|
79
|
+
* never removes a result — it adds a `preferred` cell to each hit's `verdict` and
|
|
80
|
+
* demotes a failed/missing preferred hit WITHIN its page only.
|
|
81
|
+
*/
|
|
82
|
+
preferences?: SearchFilter[];
|
|
83
|
+
/** Page size — the size of ONE page, never a bound on the match set. */
|
|
84
|
+
limit?: number;
|
|
85
|
+
/** The previous response's `cursor`, passed back verbatim. Never synthesise one. */
|
|
86
|
+
cursor?: Cursor;
|
|
87
|
+
explain?: boolean;
|
|
88
|
+
collapse?: OpenEnum<"none" | "product">;
|
|
89
|
+
/**
|
|
90
|
+
* Opt-in feed total: EXACT up to 1,000, `lowerBound` beyond / for ANN pools.
|
|
91
|
+
* Without it the response states NO total — and a page size is not one.
|
|
92
|
+
*/
|
|
93
|
+
count?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Opt-in per-query facet counts (MG-1010): up to 10 facet ids — `"brand"`,
|
|
96
|
+
* `"category"`, or spec attribute ids — and the response carries a `facets`
|
|
97
|
+
* block counting each facet's values WITHIN this query's match set. An
|
|
98
|
+
* unknown id is a 400 naming it; a known but uncountable id lands in
|
|
99
|
+
* `facets.absent`. Absent = no `facets` in the response, no counting cost.
|
|
100
|
+
*/
|
|
101
|
+
facetCounts?: string[];
|
|
102
|
+
/** Only return materials with at least one catalog image. Default false (full catalog). */
|
|
103
|
+
hasImages?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Opt-in seed-relative verdict cells (MG-629). Only meaningful with
|
|
106
|
+
* `similarTo: { variantId }`: each hit's `verdict` gains `preferred` cells
|
|
107
|
+
* diffing its comparable attributes (numeric + a `colour` axis) against the
|
|
108
|
+
* seed's, with directional deviations. Inert on every other seed form.
|
|
109
|
+
*/
|
|
110
|
+
verdicts?: boolean;
|
|
111
|
+
/** Result projection — `"full"` (default) or the lean `"card"` grid form. */
|
|
112
|
+
view?: OpenEnum<"full" | "card">;
|
|
113
|
+
/**
|
|
114
|
+
* Budget profile for the search primitive.
|
|
115
|
+
*
|
|
116
|
+
* `"search"` (default) is the full engine. `"typeahead"` is the as-you-type
|
|
117
|
+
* profile: lexical-only with prefix match on the final token, forced
|
|
118
|
+
* `view: "card"`, `limit` capped at 24, and no mood/colour detection tiers,
|
|
119
|
+
* semantic fallback, category boost, colour rerank, refinement suggestions,
|
|
120
|
+
* counts, or meanings. `textModes` is ignored under typeahead.
|
|
121
|
+
*/
|
|
122
|
+
intent?: OpenEnum<"search" | "typeahead">;
|
|
123
|
+
/**
|
|
124
|
+
* Which corpus to answer from.
|
|
125
|
+
*
|
|
126
|
+
* **ABSENT means the WHOLE GRAPH** — every material MaterialGraph knows,
|
|
127
|
+
* orderable or not. Absence is never a narrower default, so a caller that
|
|
128
|
+
* says nothing gets materials it may not be able to buy.
|
|
129
|
+
* `"materialBankOrderable"` restricts retrieval to materials carrying a
|
|
130
|
+
* Material Bank identity. `"designShopOrderable"` restricts it to what a
|
|
131
|
+
* consumer can buy on DesignShop today — about 16,200 materials, with the
|
|
132
|
+
* products DesignShop hides from browse excluded.
|
|
133
|
+
*
|
|
134
|
+
* A scope answers what is BUYABLE. It never answers what is SUITABLE: scope
|
|
135
|
+
* on channel, then refine on sector. A trade-only material can be right for a
|
|
136
|
+
* room and still unbuyable, and most of what a consumer can buy is
|
|
137
|
+
* commercial-suited product.
|
|
138
|
+
*
|
|
139
|
+
* The scope is applied INSIDE the query: `limit` fills with eligible rows,
|
|
140
|
+
* `counts` describe the SCOPED corpus, and the response echoes the scope.
|
|
141
|
+
* That is what post-filtering a page cannot give you — a client-side filter
|
|
142
|
+
* shortens the page exactly when the query drifts toward the corpus beyond
|
|
143
|
+
* the Material Bank feed, which is unpredictable from outside.
|
|
144
|
+
*/
|
|
145
|
+
corpusScope?: OpenEnum<"all" | "materialBankOrderable" | "designShopOrderable">;
|
|
146
|
+
/**
|
|
147
|
+
* OPS/AUDIT escape hatch — do NOT use for normal search. Default behaviour
|
|
148
|
+
* (absent or false) excludes sampling-artifact products: multi-swatch sample
|
|
149
|
+
* cards, which are orderable samples and never specifiable materials.
|
|
150
|
+
*/
|
|
151
|
+
includeSamplingArtifacts?: boolean;
|
|
152
|
+
}
|
|
153
|
+
/** Query-string params for the GET `/search` form. */
|
|
154
|
+
export interface SearchGetParams {
|
|
155
|
+
q?: string;
|
|
156
|
+
colours?: string;
|
|
157
|
+
logic?: OpenEnum<"all" | "any">;
|
|
158
|
+
mood?: string;
|
|
159
|
+
sort?: OpenEnum<"relevance" | "light" | "dark" | "vivid" | "muted" | "colourful">;
|
|
160
|
+
tol?: OpenEnum<"tight" | "normal" | "loose">;
|
|
161
|
+
/** Seed the search from this variant's image. Opaque — pass it through verbatim. */
|
|
162
|
+
variantId?: VariantId;
|
|
163
|
+
limit?: number;
|
|
164
|
+
/** "0" or "1" — include per-signal `signals` on each result. */
|
|
165
|
+
explain?: OpenEnum<"0" | "1">;
|
|
166
|
+
/** "true" — opt in to a `counts.variants` feed total. */
|
|
167
|
+
count?: OpenEnum<"true">;
|
|
168
|
+
/** "true" — only return materials with at least one catalog image. */
|
|
169
|
+
hasImages?: OpenEnum<"true">;
|
|
170
|
+
/** Result projection — `"full"` (default) or the lean `"card"` grid form. */
|
|
171
|
+
view?: OpenEnum<"full" | "card">;
|
|
172
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|