@instruments/materialgraph 0.26.0 → 0.27.1

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.
Files changed (113) hide show
  1. package/association-types.d.ts +18 -7
  2. package/attribution-types.d.ts +2 -2
  3. package/brand-types.d.ts +8 -0
  4. package/brands-client.d.ts +5 -36
  5. package/brands-client.js +7 -33
  6. package/by-colour-types.d.ts +40 -13
  7. package/by-spec-types.d.ts +0 -67
  8. package/by-spec-types.js +7 -5
  9. package/catalog-discovery-types.d.ts +1 -16
  10. package/catalogue-client.d.ts +12 -31
  11. package/catalogue-client.js +10 -46
  12. package/catalogue-types.d.ts +40 -0
  13. package/categories-client.d.ts +63 -0
  14. package/categories-client.js +48 -0
  15. package/category-types.d.ts +114 -0
  16. package/client-config.d.ts +2 -4
  17. package/client-config.js +2 -4
  18. package/client-vocabulary.d.ts +2 -4
  19. package/client-vocabulary.js +11 -9
  20. package/client.d.ts +103 -203
  21. package/client.js +128 -251
  22. package/codes-types.d.ts +21 -0
  23. package/colours-client.d.ts +4 -9
  24. package/colours-client.js +0 -8
  25. package/errors.d.ts +1 -1
  26. package/errors.js +1 -0
  27. package/identify-types.d.ts +49 -39
  28. package/identify-types.js +5 -3
  29. package/index.d.ts +11 -21
  30. package/index.js +4 -4
  31. package/list-variant-facets-types.d.ts +60 -0
  32. package/list-variants-types.d.ts +149 -0
  33. package/list-variants-types.js +12 -0
  34. package/match-stream-types.d.ts +13 -0
  35. package/match-stream-types.js +2 -0
  36. package/match-stream.js +12 -8
  37. package/match-types.d.ts +24 -0
  38. package/material-asset-types.d.ts +69 -49
  39. package/material-block-types.d.ts +2 -0
  40. package/material-composition-client.d.ts +21 -0
  41. package/material-composition-client.js +13 -0
  42. package/material-composition-types.d.ts +403 -0
  43. package/material-query-plan-types.d.ts +57 -141
  44. package/material-resources-client.d.ts +9 -7
  45. package/material-resources-client.js +19 -4
  46. package/material-types.d.ts +6 -14
  47. package/operations.d.ts +1 -1
  48. package/operations.js +20 -117
  49. package/package.json +5 -1
  50. package/palette-search-types.d.ts +59 -24
  51. package/query-understanding-types.d.ts +27 -5
  52. package/resolve-client.d.ts +6 -10
  53. package/resolve-client.js +5 -7
  54. package/response-schemas.d.ts +20 -0
  55. package/response-schemas.generated.d.ts +5 -0
  56. package/response-schemas.generated.js +160 -0
  57. package/response-schemas.js +113 -0
  58. package/run-types.d.ts +12 -93
  59. package/runs.d.ts +1 -37
  60. package/runs.js +2 -79
  61. package/schema-resource-types.d.ts +14 -1
  62. package/search-card-types.d.ts +22 -0
  63. package/search-index-plan-types.d.ts +70 -0
  64. package/search-refinement-types.d.ts +8 -38
  65. package/search-refinement-types.js +1 -0
  66. package/search-request-types.d.ts +117 -200
  67. package/search-response-receipt-types.d.ts +12 -268
  68. package/search-result-validation.d.ts +0 -16
  69. package/search-result-validation.js +22 -180
  70. package/spec-facet-types.d.ts +3 -8
  71. package/spec-types.d.ts +17 -10
  72. package/spec-types.js +8 -7
  73. package/specifications.d.ts +13 -55
  74. package/specifications.js +29 -73
  75. package/suggest-types.d.ts +4 -10
  76. package/suggest-types.js +1 -1
  77. package/types.d.ts +135 -338
  78. package/webmcp.d.ts +1 -1
  79. package/webmcp.generated.js +10 -94
  80. package/brand-most-sampled-types.d.ts +0 -33
  81. package/browse-types.d.ts +0 -236
  82. package/client-browse.d.ts +0 -78
  83. package/client-browse.js +0 -71
  84. package/client-exploration.d.ts +0 -9
  85. package/client-exploration.js +0 -5
  86. package/compare-types.d.ts +0 -89
  87. package/composition-client.d.ts +0 -33
  88. package/composition-client.js +0 -28
  89. package/composition-types.d.ts +0 -455
  90. package/composition-types.js +0 -10
  91. package/correction-types.d.ts +0 -88
  92. package/exploration-types.d.ts +0 -118
  93. package/exploration-types.js +0 -1
  94. package/fan-deck-types.d.ts +0 -42
  95. package/fan-deck-types.js +0 -9
  96. package/match-rejection-types.d.ts +0 -43
  97. package/match-rejection-types.js +0 -7
  98. package/palette-completion-types.d.ts +0 -115
  99. package/palette-completion-types.js +0 -1
  100. package/render-assets-types.d.ts +0 -65
  101. package/render-assets-types.js +0 -1
  102. package/search-collapse-types.d.ts +0 -69
  103. package/search-collapse-types.js +0 -1
  104. package/search-mood-types.d.ts +0 -6
  105. package/search-mood-types.js +0 -1
  106. package/search-window-types.d.ts +0 -25
  107. package/search-window-types.js +0 -1
  108. package/slot-pool-types.d.ts +0 -372
  109. package/slot-pool-types.js +0 -1
  110. /package/{brand-most-sampled-types.js → category-types.js} +0 -0
  111. /package/{browse-types.js → list-variant-facets-types.js} +0 -0
  112. /package/{compare-types.js → material-composition-types.js} +0 -0
  113. /package/{correction-types.js → search-index-plan-types.js} +0 -0
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Runtime response contracts: the JSON Schema of each operation's success body.
3
+ *
4
+ * The schemas are the producer's own. They are projected from the served
5
+ * OpenAPI document, whose response bodies are generated from the Zod contracts
6
+ * the routes validate against, so a consumer never has to re-describe a large,
7
+ * partly open payload and watch it drift. Hand one to anything that takes a
8
+ * JSON Schema, such as a Mastra tool's `outputSchema`:
9
+ *
10
+ * ```ts
11
+ * import { responseSchema } from "@instruments/materialgraph/response-schemas";
12
+ *
13
+ * createTool({ outputSchema: responseSchema("search_materials_post"), … });
14
+ * ```
15
+ *
16
+ * Keyed by OpenAPI `operationId`, the same id `SDK_OPERATIONS` pairs with each
17
+ * client method. Schemas are JSON Schema 2020-12 and describe the parsed body
18
+ * the client method returns. `list_public_run_events` is absent: it streams
19
+ * Server-Sent Events rather than returning one JSON body.
20
+ *
21
+ * Objects accept properties the schema does not declare, so a field the API
22
+ * adds later never makes a pinned consumer reject a live response; enums and
23
+ * constants stay closed. That is why these are more lenient than the WebMCP
24
+ * `outputSchema`s, which describe what the API sends rather than what a
25
+ * consumer should tolerate.
26
+ *
27
+ * This is its own entry point because the catalogue is large; importing the
28
+ * client does not load it, and it is parsed on the first `responseSchema()`
29
+ * call rather than on import.
30
+ */
31
+ import { MaterialGraphError } from "./errors.js";
32
+ import { RESPONSE_SCHEMA_CATALOGUE_JSON } from "./response-schemas.generated.js";
33
+ export { RESPONSE_SCHEMA_API_VERSION, RESPONSE_SCHEMA_OPERATION_IDS, } from "./response-schemas.generated.js";
34
+ const JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
35
+ const DEFINITION_PREFIX = "#/$defs/";
36
+ const isJsonSchemaMap = (value) => typeof value === "object" &&
37
+ value !== null &&
38
+ Object.values(value).every((schema) => typeof schema === "object" && schema !== null && !Array.isArray(schema));
39
+ const isCatalogue = (value) => typeof value === "object" &&
40
+ value !== null &&
41
+ "definitions" in value &&
42
+ isJsonSchemaMap(value.definitions) &&
43
+ "operations" in value &&
44
+ isJsonSchemaMap(value.operations);
45
+ let parsedCatalogue;
46
+ const loadCatalogue = () => {
47
+ if (parsedCatalogue === undefined) {
48
+ const parsed = JSON.parse(RESPONSE_SCHEMA_CATALOGUE_JSON);
49
+ if (!isCatalogue(parsed)) {
50
+ throw MaterialGraphError.unexpected("The response schema catalogue is malformed");
51
+ }
52
+ parsedCatalogue = parsed;
53
+ }
54
+ return parsedCatalogue;
55
+ };
56
+ /** Add every definition `value` reaches, transitively, to `into`. */
57
+ const collectReferences = (catalogue, value, into) => {
58
+ if (Array.isArray(value)) {
59
+ for (const item of value) {
60
+ collectReferences(catalogue, item, into);
61
+ }
62
+ return;
63
+ }
64
+ if (typeof value !== "object" || value === null) {
65
+ return;
66
+ }
67
+ for (const [key, child] of Object.entries(value)) {
68
+ if (key === "$ref" &&
69
+ typeof child === "string" &&
70
+ child.startsWith(DEFINITION_PREFIX)) {
71
+ const name = child
72
+ .slice(DEFINITION_PREFIX.length)
73
+ .replaceAll("~1", "/")
74
+ .replaceAll("~0", "~");
75
+ if (!into.has(name)) {
76
+ into.add(name);
77
+ collectReferences(catalogue, catalogue.definitions[name], into);
78
+ }
79
+ }
80
+ else {
81
+ collectReferences(catalogue, child, into);
82
+ }
83
+ }
84
+ };
85
+ /**
86
+ * The success-response schema for one operation, self-contained and carrying
87
+ * only the definitions it reaches. Each call returns a fresh copy, so a caller
88
+ * that annotates or mutates it cannot change what the next caller gets.
89
+ */
90
+ export const responseSchema = (operationId) => {
91
+ const catalogue = loadCatalogue();
92
+ const root = Object.hasOwn(catalogue.operations, operationId)
93
+ ? catalogue.operations[operationId]
94
+ : undefined;
95
+ if (root === undefined) {
96
+ throw MaterialGraphError.unexpected(`No response schema for operation: ${operationId}`);
97
+ }
98
+ const names = new Set();
99
+ collectReferences(catalogue, root, names);
100
+ const definitions = {};
101
+ for (const name of [...names].toSorted()) {
102
+ const definition = catalogue.definitions[name];
103
+ if (definition !== undefined) {
104
+ definitions[name] = definition;
105
+ }
106
+ }
107
+ const schema = {
108
+ $schema: JSON_SCHEMA_DIALECT,
109
+ ...root,
110
+ ...(names.size > 0 ? { $defs: definitions } : {}),
111
+ };
112
+ return structuredClone(schema);
113
+ };
package/run-types.d.ts CHANGED
@@ -2,9 +2,8 @@
2
2
  * Hand-mirrored wire types for the MaterialGraph public Run + source-material
3
3
  * surface (spec 0.7.0). Like {@link ./types.ts}, these interfaces carry NO
4
4
  * runtime dependency on any `@materialgraph/*` package — they mirror the shapes
5
- * in `apps/web/public/openapi.yaml` (`list_public_capabilities`,
6
- * `create_public_run`, `get_public_run`, `list_public_run_events`,
7
- * `resume_public_run`, `cancel_public_run`, `upload_source_material`).
5
+ * in `apps/web/public/openapi.yaml` (`create_public_run`, `get_public_run`,
6
+ * `list_public_run_events`, `upload_source_material`).
8
7
  *
9
8
  * TOLERANT-PARSING CONTRACT: response enums are widened with {@link OpenEnum}
10
9
  * so a new server value still type-checks, and objects are read structurally —
@@ -246,50 +245,14 @@ export type SourceMaterialBatchDetail = {
246
245
  createdAt: string;
247
246
  sources: SourceMaterialBatchFile[];
248
247
  };
248
+ /**
249
+ * Still carried on {@link PublicRun.capabilityKind}, so a Run created before
250
+ * MG-2252 reads back as `"agent"`. The rest of the public capability manifest
251
+ * shape retired with `GET /api/v1/capabilities`.
252
+ */
249
253
  export type CapabilityKind = OpenEnum<"agent" | "tool" | "workflow">;
250
- export type CapabilityExposure = OpenEnum<"public" | "partner" | "internal">;
251
- export type CapabilityScope = OpenEnum<"mg:read" | "mg:write" | "mg:ingest" | "mg:admin">;
252
- export type CapabilitySideEffectClass = OpenEnum<"read_only" | "browser_local" | "external_read" | "writes_proposed" | "writes_persisted" | "planned_only">;
253
- export type CapabilityRetryPolicy = {
254
- maxAttempts: number;
255
- backoff: OpenEnum<"none" | "fixed" | "exponential">;
256
- };
257
- export type CapabilityInvokeLink = {
258
- href: string;
259
- method: OpenEnum<"GET" | "POST">;
260
- };
261
- /** One human-input checkpoint exposed by a workflow capability. */
262
- export type PublicCapabilitySuspension = {
263
- stepId: string;
264
- suspend: Record<string, unknown>;
265
- resume: Record<string, unknown>;
266
- };
267
- /** One invocable capability from the governed public manifest. */
268
- export type PublicCapabilityMetadata = {
269
- publicId: string;
270
- runtimeId: string;
271
- kind: CapabilityKind;
272
- exposure: CapabilityExposure;
273
- name: string;
274
- description: string;
275
- schemas: {
276
- input: Record<string, unknown>;
277
- output: Record<string, unknown>;
278
- suspensions?: PublicCapabilitySuspension[];
279
- };
280
- sideEffectClass: CapabilitySideEffectClass;
281
- scopes: CapabilityScope[];
282
- timeoutMs: number;
283
- retryPolicy: CapabilityRetryPolicy;
284
- tags: string[];
285
- links: {
286
- invoke: CapabilityInvokeLink;
287
- };
288
- };
289
- /** The `GET /api/v1/capabilities` response envelope. */
290
- export type CapabilityManifestResponse = {
291
- capabilities: PublicCapabilityMetadata[];
292
- };
254
+ /** What `POST /api/v1/runs` admits: never an agent since MG-2252. */
255
+ export type CreatableCapabilityKind = "tool" | "workflow";
293
256
  export type PublicRunStatus = OpenEnum<"queued" | "claimed" | "running" | "suspended" | "succeeded" | "failed" | "canceled">;
294
257
  export type PublicRunFailure = {
295
258
  code: string;
@@ -322,9 +285,7 @@ export type PublicRun = {
322
285
  /** The current typed question payload while this Run is suspended. */
323
286
  suspensionPayload: Record<string, unknown> | null;
324
287
  /**
325
- * The step awaiting input while `status` is `suspended`. Pass it as
326
- * {@link ResumeRunOptions.stepId} so a stale client cannot resume a step the
327
- * Run has already moved past.
288
+ * The step awaiting input while `status` is `suspended`.
328
289
  *
329
290
  * Read `suspensionPayload` for the current typed question. The same payload is
330
291
  * also retained on the `suspended` ledger event; composition-specific events
@@ -341,7 +302,7 @@ export type PublicRun = {
341
302
  };
342
303
  /** Body for `POST /api/v1/runs` ({@link MaterialGraphClient.createRun}). */
343
304
  export type CreatePublicRunRequest = {
344
- capabilityKind: CapabilityKind;
305
+ capabilityKind: CreatableCapabilityKind;
345
306
  capabilityId: string;
346
307
  /** The capability input, validated server-side against its live input schema. */
347
308
  inputJson: unknown;
@@ -361,40 +322,10 @@ export type CreatePublicRunResponse = {
361
322
  workflowRunId: string;
362
323
  run: PublicRun;
363
324
  };
364
- /** The `GET /api/v1/runs/{runId}` and resume/cancel response envelope. */
325
+ /** The `GET /api/v1/runs/{runId}` response envelope. */
365
326
  export type PublicRunResponse = {
366
327
  run: PublicRun;
367
328
  };
368
- /** Query for `GET /api/v1/runs` ({@link MaterialGraphClient.listRuns}). */
369
- export type ListRunsOptions = {
370
- /**
371
- * Return only Runs in this status. `"suspended"` is the recovery question:
372
- * which of my Runs is still waiting on an answer?
373
- */
374
- status?: PublicRunStatus;
375
- /** Page size, 1–100. Defaults to 20 server-side. */
376
- limit?: number;
377
- /**
378
- * The previous page's {@link PublicRunListResponse.nextCursor}, round-tripped
379
- * VERBATIM. It is opaque — never parse or construct one.
380
- */
381
- cursor?: string;
382
- signal?: AbortSignal;
383
- };
384
- /** The `GET /api/v1/runs` response envelope — one page, newest first. */
385
- export type PublicRunListResponse = {
386
- runs: PublicRun[];
387
- /**
388
- * Pass as {@link ListRunsOptions.cursor} for the next page. ABSENT — not
389
- * empty-string, not null — once the last page has been served.
390
- */
391
- nextCursor?: string;
392
- };
393
- /** Body for `POST /api/v1/runs/{runId}/resume`. */
394
- export type ResumePublicRunRequest = {
395
- /** The resume payload, validated against the suspended step's resume schema. */
396
- resumeData: unknown;
397
- };
398
329
  /**
399
330
  * One redacted Run event, decoded from an SSE `data:` frame. The stream yields
400
331
  * these in ascending {@link RunEvent.sequence} order. `payloadJson` is read
@@ -421,15 +352,3 @@ export type DrainRunEventsOptions = {
421
352
  /** Stop collecting after this many milliseconds even without a terminal event. */
422
353
  deadlineMs: number;
423
354
  };
424
- /** Options for {@link MaterialGraphClient.resumeRun}. */
425
- export type ResumeRunOptions = {
426
- /**
427
- * The step the caller believes is suspended. When it mismatches the run
428
- * doc's live `suspensionStepId` the API answers RFC-7807 409 (MG-608) —
429
- * which is what stops a stale client resuming a step the run has already
430
- * moved past. Omitted, the resume is unguarded, exactly as before.
431
- */
432
- stepId?: string;
433
- /** Abort the request. */
434
- signal?: AbortSignal;
435
- };
package/runs.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { HttpTransport } from "./http-transport.js";
2
- import type { CapabilityManifestResponse, CreatePublicRunRequest, CreatePublicRunResponse, DrainRunEventsOptions, ListRunsOptions, PostSourceMaterialCommentInput, PostSourceMaterialCommentResult, PublicRunListResponse, PublicRunResponse, ResumeRunOptions, RunEvent, SourceMaterialBatchDetail, SourceMaterialBatchReceipt, SourceMaterialCommentThread, StreamRunEventsOptions, UploadSourceMaterialInput } from "./run-types.js";
2
+ import type { CreatePublicRunRequest, CreatePublicRunResponse, DrainRunEventsOptions, PostSourceMaterialCommentInput, PostSourceMaterialCommentResult, PublicRunResponse, RunEvent, SourceMaterialBatchDetail, SourceMaterialBatchReceipt, SourceMaterialCommentThread, StreamRunEventsOptions, UploadSourceMaterialInput } from "./run-types.js";
3
3
  /**
4
4
  * Upload one atomic source-material batch (`POST /api/v1/source-material`).
5
5
  * Accepts the pile-of-files shape (`{ files }` → repeatable `files` field) or
@@ -54,46 +54,10 @@ export declare const listSourceMaterialComments: (transport: HttpTransport, batc
54
54
  * `replies`, which a `reply` does not have.
55
55
  */
56
56
  export declare const postSourceMaterialComment: (transport: HttpTransport, batchId: string, sourceId: string, input: PostSourceMaterialCommentInput, signal?: AbortSignal) => Promise<PostSourceMaterialCommentResult>;
57
- /** List invocable agents, workflows, and tools (`GET /api/v1/capabilities`). */
58
- export declare const capabilities: (transport: HttpTransport, signal?: AbortSignal) => Promise<CapabilityManifestResponse>;
59
57
  /** Start an asynchronous capability Run (`POST /api/v1/runs`). */
60
58
  export declare const createRun: (transport: HttpTransport, body: CreatePublicRunRequest, signal?: AbortSignal) => Promise<CreatePublicRunResponse>;
61
- /**
62
- * List your own Runs, newest first (`GET /api/v1/runs`).
63
- *
64
- * The recovery path for a client that still holds its API key but has lost a Run
65
- * id — a reinstalled app, or a crash before the id from {@link createRun} was
66
- * persisted. `{ status: "suspended" }` finds the Run still waiting on an answer.
67
- *
68
- * Only the calling principal's own Runs are ever returned; there is no
69
- * cross-principal listing.
70
- *
71
- * Page by following `nextCursor` until it is ABSENT — a full page is not the
72
- * signal to stop, and an absent cursor is the only one.
73
- */
74
- export declare const listRuns: (transport: HttpTransport, options?: ListRunsOptions) => Promise<PublicRunListResponse>;
75
59
  /** Get a Run's current state (`GET /api/v1/runs/{runId}`). */
76
60
  export declare const getRun: (transport: HttpTransport, runId: string, signal?: AbortSignal) => Promise<PublicRunResponse>;
77
- /**
78
- * Resume a suspended Run (`POST /api/v1/runs/{runId}/resume`).
79
- *
80
- * The third argument takes either an `AbortSignal` (legacy) or
81
- * {@link ResumeRunOptions}.
82
- *
83
- * `stepId` is forwarded whenever it is PRESENT — `!== undefined`, not truthy.
84
- * An empty string is a present-but-malformed value and must reach the server
85
- * so its schema rejects it; treating it as absent would silently downgrade a
86
- * guarded resume into an unguarded one, which is the exact failure this guard
87
- * exists to prevent.
88
- *
89
- * Omitting it sends a body byte-identical to what this sent before. Note the
90
- * wire cannot distinguish `{ stepId: undefined }` from no key at all —
91
- * `JSON.stringify` drops both — so the distinction here is construction
92
- * intent, kept so the serialised body is provably unchanged for old callers.
93
- */
94
- export declare const resumeRun: (transport: HttpTransport, runId: string, resumeData: unknown, signalOrOptions?: AbortSignal | ResumeRunOptions) => Promise<PublicRunResponse>;
95
- /** Request durable Run cancellation (`POST /api/v1/runs/{runId}/cancel`). */
96
- export declare const cancelRun: (transport: HttpTransport, runId: string, signal?: AbortSignal) => Promise<PublicRunResponse>;
97
61
  /**
98
62
  * Stream ordered Run events as an async iterator (`GET
99
63
  * /api/v1/runs/{runId}/events`, SSE). Iterates until the stream ends or
package/runs.js CHANGED
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * The Run + source-material namespace — the agentic half of the SDK. These
3
3
  * functions take the shared {@link HttpTransport} and speak the hosted Run
4
- * lifecycle: upload source material, list capabilities, create/read/resume/
5
- * cancel Runs, and stream (or drain) their events. Box's proxy routes are thin
6
- * wrappers over exactly these calls.
4
+ * lifecycle: upload source material, create/read Runs, and stream (or drain)
5
+ * their events. Box's proxy routes are thin wrappers over exactly these calls.
7
6
  */
8
7
  import { MaterialGraphError } from "./errors.js";
9
8
  import { findSdkOperation, operationPath } from "./operations.js";
@@ -151,92 +150,16 @@ export const postSourceMaterialComment = async (transport, batchId, sourceId, in
151
150
  });
152
151
  return checkPostSourceMaterialCommentResult(body, sourceId);
153
152
  };
154
- /** List invocable agents, workflows, and tools (`GET /api/v1/capabilities`). */
155
- export const capabilities = async (transport, signal) => await transport.requestJson(runOperation("list_public_capabilities"), {
156
- signal,
157
- });
158
153
  /** Start an asynchronous capability Run (`POST /api/v1/runs`). */
159
154
  export const createRun = async (transport, body, signal) => await transport.requestJson(runOperation("create_public_run"), {
160
155
  body,
161
156
  signal,
162
157
  });
163
- /**
164
- * List your own Runs, newest first (`GET /api/v1/runs`).
165
- *
166
- * The recovery path for a client that still holds its API key but has lost a Run
167
- * id — a reinstalled app, or a crash before the id from {@link createRun} was
168
- * persisted. `{ status: "suspended" }` finds the Run still waiting on an answer.
169
- *
170
- * Only the calling principal's own Runs are ever returned; there is no
171
- * cross-principal listing.
172
- *
173
- * Page by following `nextCursor` until it is ABSENT — a full page is not the
174
- * signal to stop, and an absent cursor is the only one.
175
- */
176
- export const listRuns = async (transport, options = {}) => {
177
- const query = {
178
- ...(options.status === undefined ? {} : { status: options.status }),
179
- ...(options.limit === undefined ? {} : { limit: options.limit }),
180
- ...(options.cursor === undefined ? {} : { cursor: options.cursor }),
181
- };
182
- return await transport.requestJson(runOperation("list_public_runs"), {
183
- ...(Object.keys(query).length > 0 ? { query } : {}),
184
- ...(options.signal ? { signal: options.signal } : {}),
185
- });
186
- };
187
158
  /** Get a Run's current state (`GET /api/v1/runs/{runId}`). */
188
159
  export const getRun = async (transport, runId, signal) => await transport.requestJson(runOperation("get_public_run"), {
189
160
  pathParams: { runId },
190
161
  signal,
191
162
  });
192
- /**
193
- * Normalise `resumeRun`'s overloaded third argument.
194
- *
195
- * A bare `AbortSignal` stays supported because it is the published shape:
196
- * reordering that parameter would break any consumer passing it positionally,
197
- * and this ships with no version bump.
198
- *
199
- * Discriminated on `instanceof AbortSignal`, NOT on the presence of `stepId`.
200
- * `{ signal }` with no `stepId` is a perfectly valid options object, and a
201
- * key-presence test would misread it as a signal and silently drop the abort.
202
- */
203
- const resumeOptions = (value) => {
204
- if (value === undefined) {
205
- return {};
206
- }
207
- return value instanceof AbortSignal ? { signal: value } : value;
208
- };
209
- /**
210
- * Resume a suspended Run (`POST /api/v1/runs/{runId}/resume`).
211
- *
212
- * The third argument takes either an `AbortSignal` (legacy) or
213
- * {@link ResumeRunOptions}.
214
- *
215
- * `stepId` is forwarded whenever it is PRESENT — `!== undefined`, not truthy.
216
- * An empty string is a present-but-malformed value and must reach the server
217
- * so its schema rejects it; treating it as absent would silently downgrade a
218
- * guarded resume into an unguarded one, which is the exact failure this guard
219
- * exists to prevent.
220
- *
221
- * Omitting it sends a body byte-identical to what this sent before. Note the
222
- * wire cannot distinguish `{ stepId: undefined }` from no key at all —
223
- * `JSON.stringify` drops both — so the distinction here is construction
224
- * intent, kept so the serialised body is provably unchanged for old callers.
225
- */
226
- export const resumeRun = async (transport, runId, resumeData, signalOrOptions) => {
227
- const { stepId, signal } = resumeOptions(signalOrOptions);
228
- return await transport.requestJson(runOperation("resume_public_run"), {
229
- body: { resumeData, ...(stepId === undefined ? {} : { stepId }) },
230
- pathParams: { runId },
231
- signal,
232
- });
233
- };
234
- /** Request durable Run cancellation (`POST /api/v1/runs/{runId}/cancel`). */
235
- export const cancelRun = async (transport, runId, signal) => await transport.requestJson(runOperation("cancel_public_run"), {
236
- body: {},
237
- pathParams: { runId },
238
- signal,
239
- });
240
163
  /** Decode one SSE `data:` frame into a {@link RunEvent}, or `null` when unusable. */
241
164
  const decodeRunEvent = (data) => {
242
165
  if (data.length === 0) {
@@ -29,8 +29,14 @@ export type SchemaExternalMapping = {
29
29
  target: OpenEnum<"schema_org" | "ifc_pset" | "bsdd" | "iso_23387" | "eclass" | "gs1_gdm" | "gdsn" | "etim" | "uniclass">;
30
30
  targetPath: string;
31
31
  mappingType: OpenEnum<"direct" | "transform" | "derived" | "classification" | "semantic">;
32
+ /**
33
+ * SKOS mapping strength, read from the MaterialGraph side. Only `exact`
34
+ * licenses substituting one concept for the other.
35
+ */
36
+ matchStrength?: SchemaMatchStrength;
32
37
  notes?: string;
33
38
  };
39
+ export type SchemaMatchStrength = OpenEnum<"exact" | "close" | "broad" | "narrow" | "related">;
34
40
  export type SchemaBlockItem = {
35
41
  id: string;
36
42
  semanticId: string;
@@ -39,7 +45,7 @@ export type SchemaBlockItem = {
39
45
  description: string;
40
46
  attributeIds: string[];
41
47
  };
42
- export type KnownSchemaQuantityKind = "length" | "area" | "volume" | "mass" | "time" | "temperature" | "currency" | "ratio" | "density" | "coverage" | "emissions" | "force" | "power" | "voltage" | "current" | "frequency" | "luminous_flux" | "color_temperature" | "count" | "unitless";
48
+ export type KnownSchemaQuantityKind = "length" | "area" | "volume" | "mass" | "time" | "temperature" | "currency" | "ratio" | "density" | "coverage" | "emissions" | "force" | "thermal_conductivity" | "power" | "voltage" | "current" | "frequency" | "luminous_flux" | "color_temperature" | "count" | "unitless";
43
49
  export type SchemaQuantityKind = OpenEnum<KnownSchemaQuantityKind>;
44
50
  export type SchemaAttributeItem = {
45
51
  id: string;
@@ -79,6 +85,8 @@ export type SchemaProductTypeItem = {
79
85
  id: string;
80
86
  name: string;
81
87
  description?: string;
88
+ /** Display names this type carried before a rename, kept so they still find it. */
89
+ formerNames?: string[];
82
90
  verticalId: string;
83
91
  parentProductTypeGroupId?: string;
84
92
  blocks: SchemaProductTypeBlock[];
@@ -97,6 +105,10 @@ export type SchemaUnitItem = {
97
105
  offset?: number;
98
106
  };
99
107
  notes?: string;
108
+ /** UN/CEFACT Rec 20 common code (schema.org `unitCode`); absent when Rec 20 has none. */
109
+ uncefactCode?: string;
110
+ /** QUDT unit IRI, where exactly one QUDT unit matches. */
111
+ qudtUnit?: string;
100
112
  semanticId: string;
101
113
  lifecycle: SchemaLifecycle;
102
114
  };
@@ -160,6 +172,7 @@ export type SchemaMappingItem = {
160
172
  canonicalPath: string;
161
173
  targetPath: string;
162
174
  mappingType: SchemaExternalMapping["mappingType"];
175
+ matchStrength?: SchemaMatchStrength;
163
176
  notes?: string;
164
177
  };
165
178
  export type SchemaResourceItemMap = {
@@ -31,6 +31,19 @@ export type SearchCardItem = {
31
31
  name: string;
32
32
  variantName: string | null;
33
33
  imageUrl: string | null;
34
+ /**
35
+ * SHA-256 of the displayed image `imageUrl` points at, as a hex digest. Key a
36
+ * cache or dedupe on it rather than the URL. ABSENT means no hash is on
37
+ * record, never that there is no image.
38
+ */
39
+ imageSha256?: string;
40
+ /**
41
+ * How many eligible variants the row's product has, the same set
42
+ * `listVariants` pages, so a follow-up list returns this many: the card's
43
+ * "12 colourways". ABSENT means the product is unknown or has no eligible
44
+ * variant.
45
+ */
46
+ variantCount?: number;
34
47
  /**
35
48
  * The manufacturer's own item code, the same key the full item carries.
36
49
  * Reach for it when two cards read alike: `name` is the PRODUCT name, and a
@@ -62,3 +75,12 @@ export type SearchCardResponse = {
62
75
  results: SearchCardItem[];
63
76
  };
64
77
  } & Omit<SearchResponse, "data">;
78
+ /**
79
+ * The request forms the server answers with {@link SearchCardResponse}:
80
+ * `view: "card"`, or `intent: "typeahead"`, which forces the card view.
81
+ */
82
+ export type CardProjectionRequest = {
83
+ view: "card";
84
+ } | {
85
+ intent: "typeahead";
86
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @module search-index-plan-types
3
+ * @description The plan the one search ran on, as `/search` reports it under
4
+ * `explain.plan` (ADR 0031). Mirrors `searchIndexPlanReceiptSchema` in
5
+ * `@materialgraph/products/search-index/plan-schema`; `key-parity.test.ts`
6
+ * holds the two to the same fields.
7
+ */
8
+ import type { CorpusScope } from "./corpus-scope-types.js";
9
+ /** One attribute the plan wants: its terms, or any value when `presence`. */
10
+ export type SearchIndexPlanWant = {
11
+ attributeId: string;
12
+ /** Share of the category's materials stating the attribute, 0 to 1. */
13
+ fill: number;
14
+ /** The words it was read from, or `"(filter)"` for a caller's filter. */
15
+ phrase: string;
16
+ /** Any stated value satisfies it; `terms` is then empty. */
17
+ presence?: true;
18
+ /** A hard want removes rows; a soft one only counts toward the order. */
19
+ strength: "hard" | "soft";
20
+ terms: string[];
21
+ };
22
+ /**
23
+ * The query's plan: the category, makers, wanted and excluded attribute terms,
24
+ * the colour, the words left for text, and the caller's corpus scope.
25
+ */
26
+ export type SearchIndexPlan = {
27
+ brands: {
28
+ ids: string[];
29
+ names: string[];
30
+ phrase: string;
31
+ } | null;
32
+ category: {
33
+ label: string;
34
+ phrase: string;
35
+ productTypeIds: string[];
36
+ } | null;
37
+ colour: {
38
+ family12: string[];
39
+ familyId: string;
40
+ hex: string;
41
+ oklab: [number, number, number];
42
+ phrase: string;
43
+ } | null;
44
+ /** Present only when the caller sent a scope narrower than the whole graph. */
45
+ corpusScope?: Exclude<CorpusScope, "all">;
46
+ excludeBrands: string[];
47
+ excludes: {
48
+ attributeId: string;
49
+ phrase: string;
50
+ term: string;
51
+ }[];
52
+ /** Words the reading did not place, left for the text match. */
53
+ leftover: string[];
54
+ normalised: string;
55
+ softAlone: boolean;
56
+ vendorColourNames: string[];
57
+ wants: SearchIndexPlanWant[];
58
+ };
59
+ /**
60
+ * Which reading a search ran on (`version`, `source`), whether it came from
61
+ * the cache, the plan itself, and its signature, so two runs of one request
62
+ * can be compared plan for plan.
63
+ */
64
+ export type SearchPlanReceipt = {
65
+ cached: boolean;
66
+ plan: SearchIndexPlan;
67
+ signature: string;
68
+ source: "dictionary" | "jev";
69
+ version: string;
70
+ };
@@ -1,5 +1,4 @@
1
- /** Types for structured refinements returned by MaterialGraph search. */
2
- import type { SearchInput } from "./types.js";
1
+ /** The spec-filter vocabulary MaterialGraph's filter and preference inputs share. */
3
2
  /**
4
3
  * A hard canonical-attribute filter accepted by the search engine — a term match
5
4
  * (`{ attributeId, term }`), a presence check (`{ attributeId, hasValue: true }`)
@@ -57,10 +56,16 @@ export type SearchFilter = {
57
56
  term: string;
58
57
  trust?: SearchFilterTrust;
59
58
  } | {
60
- /** Known effective values are required, and this term must be absent. */
61
59
  attributeId: string;
62
60
  excludesTerm: string;
63
61
  trust?: SearchFilterTrust;
62
+ /**
63
+ * What an UNKNOWN effective value does. Absent or `"drop"` (default)
64
+ * requires a known effective value — a variant that never states the
65
+ * attribute does not pass. `"pass"` admits it — a variant only fails by
66
+ * STATING the excluded term.
67
+ */
68
+ whenUnstated?: "drop" | "pass";
64
69
  }
65
70
  /**
66
71
  * TERM SET — the attribute must equal any ONE of these. OR within the entry;
@@ -96,38 +101,3 @@ export type SearchFilter = {
96
101
  max?: number;
97
102
  trust?: SearchFilterTrust;
98
103
  };
99
- /** One structured refinement returned with a search response. */
100
- export type SearchSuggestion = {
101
- label: string;
102
- /** Complete replacement request, not a partial patch. */
103
- request: SearchInput;
104
- };
105
- /** One reading a {@link SearchDisambiguation} offers. */
106
- export type SearchInterpretation = {
107
- id: string;
108
- label: string;
109
- kind: string;
110
- /** True for the reading that ran; all false means the engine chose none. */
111
- isDefault: boolean;
112
- /** The colour this reading resolved to, when the ambiguity was a colour one. */
113
- hex?: string;
114
- /** Complete replacement request, not a partial patch. */
115
- request: SearchInput;
116
- };
117
- /** The governed ambiguity classes emitted by MaterialGraph search. */
118
- export type SearchDisambiguationKind = "compliance-phrase" | "colour-material" | "paint-colour" | "paint-identity";
119
- /**
120
- * The server saying a query had multiple defensible readings.
121
- *
122
- * The reading that ran is flagged `isDefault`; every reading is false when the
123
- * engine deliberately chose none. Each carries a complete executable request,
124
- * so choosing a reading is a re-issue rather than a patch. Ignoring this block does not
125
- * make the ambiguity go away — it makes it invisible to the person who asked.
126
- */
127
- export type SearchDisambiguation = {
128
- /** Compliance, colour-material, paint-colour, or paint-identity ambiguity. */
129
- kind: SearchDisambiguationKind;
130
- /** The ambiguous term or phrase. */
131
- term: string;
132
- interpretations: SearchInterpretation[];
133
- };
@@ -1 +1,2 @@
1
+ /** The spec-filter vocabulary MaterialGraph's filter and preference inputs share. */
1
2
  export {};