@instruments/materialgraph 0.9.0 → 0.10.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/http-transport.js CHANGED
@@ -19,7 +19,10 @@ function parseRateLimit(headers) {
19
19
  const remaining = parseIntOrNull(headers.get("x-ratelimit-remaining"));
20
20
  const reset = parseIntOrNull(headers.get("x-ratelimit-reset"));
21
21
  const retryAfter = parseIntOrNull(headers.get("retry-after"));
22
- if (limit === null && remaining === null && reset === null && retryAfter === null) {
22
+ if (limit === null &&
23
+ remaining === null &&
24
+ reset === null &&
25
+ retryAfter === null) {
23
26
  return null;
24
27
  }
25
28
  return { limit, remaining, reset, retryAfter };
@@ -39,7 +42,11 @@ export class HttpTransport {
39
42
  }
40
43
  /** The bearer + JSON `Accept` headers, plus any per-request additions. */
41
44
  authHeaders(extra) {
42
- return { Authorization: `Bearer ${this.apiKey}`, Accept: "application/json", ...extra };
45
+ return {
46
+ Authorization: `Bearer ${this.apiKey}`,
47
+ Accept: "application/json",
48
+ ...extra,
49
+ };
43
50
  }
44
51
  buildUrl(path, query) {
45
52
  const url = new URL(`${this.baseUrl}${path}`);
@@ -83,7 +90,11 @@ export class HttpTransport {
83
90
  });
84
91
  }
85
92
  catch (cause) {
86
- throw MaterialGraphError.fromTransport({ ...metadata, cause, callerSignal: input.signal });
93
+ throw MaterialGraphError.fromTransport({
94
+ ...metadata,
95
+ cause,
96
+ callerSignal: input.signal,
97
+ });
87
98
  }
88
99
  const rateLimit = parseRateLimit(response.headers);
89
100
  this.#lastRateLimit = rateLimit;
@@ -92,7 +103,11 @@ export class HttpTransport {
92
103
  text = await response.text();
93
104
  }
94
105
  catch (cause) {
95
- throw MaterialGraphError.fromTransport({ ...metadata, cause, callerSignal: input.signal });
106
+ throw MaterialGraphError.fromTransport({
107
+ ...metadata,
108
+ cause,
109
+ callerSignal: input.signal,
110
+ });
96
111
  }
97
112
  let parsed;
98
113
  if (text.length > 0) {
@@ -147,7 +162,11 @@ export class HttpTransport {
147
162
  });
148
163
  }
149
164
  catch (cause) {
150
- throw MaterialGraphError.fromTransport({ ...metadata, cause, callerSignal: options.signal });
165
+ throw MaterialGraphError.fromTransport({
166
+ ...metadata,
167
+ cause,
168
+ callerSignal: options.signal,
169
+ });
151
170
  }
152
171
  const rateLimit = parseRateLimit(response.headers);
153
172
  this.#lastRateLimit = rateLimit;
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { MaterialGraphClient, DEFAULT_BASE_URL, MAX_BATCH_IDS } from "./client.js";
1
+ export { MaterialGraphClient, DEFAULT_BASE_URL, MAX_BATCH_IDS, } from "./client.js";
2
2
  export type { MaterialGraphClientOptions, BrowseNamespace } from "./client.js";
3
3
  export { MATERIALGRAPH_API_VERSION, SDK_OPERATIONS } from "./operations.js";
4
4
  export type { SdkOperation, SdkOperationId } from "./operations.js";
@@ -27,10 +27,11 @@ export type * from "./colour-aggregate-types.js";
27
27
  export type * from "./palette-search-types.js";
28
28
  export type * from "./association-types.js";
29
29
  export type * from "./brand-types.js";
30
+ export type * from "./fan-deck-types.js";
30
31
  export type * from "./material-asset-types.js";
31
32
  export type * from "./material-block-types.js";
32
33
  export type * from "./schema-resource-types.js";
33
34
  export type * from "./search-refinement-types.js";
34
35
  export { CERTIFICATION_PROGRAMME_ATTRIBUTE_ID } from "./spec-types.js";
35
36
  export type * from "./spec-types.js";
36
- export { certificationProgramme, certificationProgrammeCriterion, hasSpec, specCriterion, specTerm, } from "./specifications.js";
37
+ export { certificationProgramme, certificationProgrammeCriterion, hasSpec, specCriterion, specRangeCriterion, specTerm, } from "./specifications.js";
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { MaterialGraphClient, DEFAULT_BASE_URL, MAX_BATCH_IDS } from "./client.js";
1
+ export { MaterialGraphClient, DEFAULT_BASE_URL, MAX_BATCH_IDS, } from "./client.js";
2
2
  export { MATERIALGRAPH_API_VERSION, SDK_OPERATIONS } from "./operations.js";
3
3
  export { TERMINAL_RUN_EVENT_TYPES } from "./run-types.js";
4
4
  export { VARY_AXES } from "./composition-types.js";
@@ -9,4 +9,4 @@ export { MATCH_IMAGE_TYPES, MATCH_OUTCOMES, MATCH_MATERIAL_FUNCTIONS, MATCH_STRE
9
9
  export { CodesClient } from "./codes-client.js";
10
10
  export { ColoursClient } from "./colours-client.js";
11
11
  export { CERTIFICATION_PROGRAMME_ATTRIBUTE_ID } from "./spec-types.js";
12
- export { certificationProgramme, certificationProgrammeCriterion, hasSpec, specCriterion, specTerm, } from "./specifications.js";
12
+ export { certificationProgramme, certificationProgrammeCriterion, hasSpec, specCriterion, specRangeCriterion, specTerm, } from "./specifications.js";
@@ -1,3 +1,4 @@
1
+ import type { MatchCategoryInference, MatchColourReading } from "./match-types.js";
1
2
  /**
2
3
  * The Material Match PROGRESS-STREAM contracts — the wire vocabulary of
3
4
  * {@link MaterialGraphClient.matchStream} (`POST /api/v1/search/match` under
@@ -16,7 +17,6 @@
16
17
  * UI legend from the same source the validator uses.
17
18
  */
18
19
  import type { SearchResultItem } from "./types.js";
19
- import type { MatchCategoryInference, MatchColourReading } from "./match-types.js";
20
20
  /** How the workflow classified the uploaded image. */
21
21
  export declare const MATCH_IMAGE_TYPES: readonly ["swatch", "installation", "flatplan"];
22
22
  export type MatchImageType = (typeof MATCH_IMAGE_TYPES)[number];
@@ -26,8 +26,16 @@ export type MatchMaterialFunction = (typeof MATCH_MATERIAL_FUNCTIONS)[number];
26
26
  /** The workflow's coarse progress phase, narrated by a `step` event. */
27
27
  export declare const MATCH_WORKFLOW_STEPS: readonly ["classifying", "decomposing", "matching", "complete"];
28
28
  export type MatchWorkflowStep = (typeof MATCH_WORKFLOW_STEPS)[number];
29
- /** Where one surface stands in the run. */
30
- export declare const MATCH_SURFACE_STATUSES: readonly ["pending", "matching", "complete", "error"];
29
+ /**
30
+ * Where one surface stands in the run.
31
+ *
32
+ * `colour_fallback` (appended, additive): the surface completed, but through
33
+ * the colour lane — its visible sample was too small to read texture, so the
34
+ * results match the region's palette, scoped to the detected category. Such a
35
+ * surface carries a `note` saying so and no `senseCheck` verdicts (there is no
36
+ * texture to verify).
37
+ */
38
+ export declare const MATCH_SURFACE_STATUSES: readonly ["pending", "matching", "complete", "error", "colour_fallback"];
31
39
  export type MatchSurfaceStatus = (typeof MATCH_SURFACE_STATUSES)[number];
32
40
  /** Every frame type the stream can carry — the union's discriminant values. */
33
41
  export declare const MATCH_STREAM_EVENT_TYPES: readonly ["step", "classified", "surfaces", "surface", "heartbeat", "complete", "error"];
@@ -112,9 +120,10 @@ export interface MatchSurfaceResult {
112
120
  senseCheck?: MatchSenseCheckCell[];
113
121
  /**
114
122
  * TRUE on the interim frame that delivers finished results while the verifier
115
- * is still reading them. The verifier annotates; it never filters and never
116
- * reorders, so this says "badges are coming", never "these results may
117
- * change". Render the rows immediately.
123
+ * is still reading them. Render the rows immediately. The follow-up frame
124
+ * carries the SAME rows with verdicts attached, settled once into verdict
125
+ * bands (confirmed → abstained → rejected, stable within each band) — the
126
+ * page's one settling reorder. Nothing is ever removed.
118
127
  */
119
128
  senseCheckPending?: boolean;
120
129
  /**
@@ -128,6 +137,12 @@ export interface MatchSurfaceResult {
128
137
  hex: string;
129
138
  proportion: number;
130
139
  }>;
140
+ /**
141
+ * One sentence of provenance a consumer may render verbatim, when the server
142
+ * sent one — e.g. the `colour_fallback` explanation carrying the refused
143
+ * sample's pixel size. Absent on ordinary surfaces.
144
+ */
145
+ note?: string;
131
146
  /** Present only on `status: "error"` — why this one surface failed. */
132
147
  error?: string;
133
148
  }
@@ -161,6 +176,21 @@ export interface MatchWorkflowResult {
161
176
  * "nothing above the floor". Do not fold `surfaces[]` to tell those apart.
162
177
  */
163
178
  totals: MatchRunTotals;
179
+ /**
180
+ * Whole-image palette matches, sent ONLY when an installation photo yielded
181
+ * no usable surfaces at all. These match the image's overall palette — they
182
+ * are not material identifications, and the note says so. A consumer that
183
+ * ignores this field sees exactly the empty run it saw before the field
184
+ * existed.
185
+ */
186
+ paletteMatches?: {
187
+ results: SearchResultItem[];
188
+ palette: Array<{
189
+ hex: string;
190
+ proportion: number;
191
+ }>;
192
+ note: string;
193
+ };
164
194
  /**
165
195
  * What a SWATCH run compared against — a swatch detects no surfaces, so it has
166
196
  * nowhere else to put this. A scene run reports per surface instead.
@@ -1,5 +1,9 @@
1
1
  /** How the workflow classified the uploaded image. */
2
- export const MATCH_IMAGE_TYPES = ["swatch", "installation", "flatplan"];
2
+ export const MATCH_IMAGE_TYPES = [
3
+ "swatch",
4
+ "installation",
5
+ "flatplan",
6
+ ];
3
7
  /** What a detected surface is FOR — the element the material would serve. */
4
8
  export const MATCH_MATERIAL_FUNCTIONS = [
5
9
  "flooring",
@@ -13,9 +17,28 @@ export const MATCH_MATERIAL_FUNCTIONS = [
13
17
  "sample",
14
18
  ];
15
19
  /** 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"];
20
+ export const MATCH_WORKFLOW_STEPS = [
21
+ "classifying",
22
+ "decomposing",
23
+ "matching",
24
+ "complete",
25
+ ];
26
+ /**
27
+ * Where one surface stands in the run.
28
+ *
29
+ * `colour_fallback` (appended, additive): the surface completed, but through
30
+ * the colour lane — its visible sample was too small to read texture, so the
31
+ * results match the region's palette, scoped to the detected category. Such a
32
+ * surface carries a `note` saying so and no `senseCheck` verdicts (there is no
33
+ * texture to verify).
34
+ */
35
+ export const MATCH_SURFACE_STATUSES = [
36
+ "pending",
37
+ "matching",
38
+ "complete",
39
+ "error",
40
+ "colour_fallback",
41
+ ];
19
42
  /** Every frame type the stream can carry — the union's discriminant values. */
20
43
  export const MATCH_STREAM_EVENT_TYPES = [
21
44
  "step",
@@ -26,7 +49,11 @@ export const MATCH_STREAM_EVENT_TYPES = [
26
49
  "complete",
27
50
  "error",
28
51
  ];
29
- export const MATCH_SENSE_CHECK_VERDICTS = ["confirmed", "rejected", "abstained"];
52
+ export const MATCH_SENSE_CHECK_VERDICTS = [
53
+ "confirmed",
54
+ "rejected",
55
+ "abstained",
56
+ ];
30
57
  /** The terminal Match workflow result — every surface in its final state. */
31
58
  /**
32
59
  * How a Match run ended. `no_surfaces` means the detector worked and found no
package/match-stream.js CHANGED
@@ -55,16 +55,21 @@
55
55
  * invariants.
56
56
  */
57
57
  import { MaterialGraphError } from "./errors.js";
58
- import { findSdkOperation } from "./operations.js";
59
58
  import { MATCH_IMAGE_TYPES, MATCH_MATERIAL_FUNCTIONS, MATCH_STREAM_EVENT_TYPES, MATCH_SURFACE_STATUSES, MATCH_WORKFLOW_STEPS, } from "./match-stream-types.js";
60
59
  import { readNdjsonLines } from "./ndjson.js";
60
+ import { findSdkOperation, } from "./operations.js";
61
61
  import { checkSearchResultItems } from "./search-result-validation.js";
62
62
  import { requireArray, requireBoundedNumber, requireDefaultedArray, requireEnum, requireIntegerAtLeast, requireNumber, requireOptionalString, requireRecord, requireString, } from "./validation.js";
63
63
  /** The NDJSON media type the Match route negotiates the scene workflow on. */
64
64
  const MATCH_STREAM_ACCEPT = "application/x-ndjson";
65
65
  /** How much of an offending frame a diagnostic quotes (crops can be megabytes). */
66
66
  const MAX_FRAME_PREVIEW = 200;
67
- const BOUNDING_BOX_CORNERS = Object.freeze(["x0", "y0", "x1", "y1"]);
67
+ const BOUNDING_BOX_CORNERS = Object.freeze([
68
+ "x0",
69
+ "y0",
70
+ "x1",
71
+ "y1",
72
+ ]);
68
73
  function operation(operationId) {
69
74
  const found = findSdkOperation(operationId);
70
75
  if (!found)
@@ -76,7 +81,9 @@ function operation(operationId) {
76
81
  * offending path and quoting a bounded slice of the frame.
77
82
  */
78
83
  function frameFailure(line) {
79
- const preview = line.length > MAX_FRAME_PREVIEW ? `${line.slice(0, MAX_FRAME_PREVIEW)}…` : line;
84
+ const preview = line.length > MAX_FRAME_PREVIEW
85
+ ? `${line.slice(0, MAX_FRAME_PREVIEW)}…`
86
+ : line;
80
87
  return (reason) => MaterialGraphError.unexpected(`MaterialGraph match stream sent a malformed NDJSON frame: ${reason}. Frame: ${preview}`);
81
88
  }
82
89
  /** Check one `MatchCategoryInference` item. */
@@ -190,7 +197,10 @@ function readFrame(value, fail) {
190
197
  // Payload-free by contract — there is nothing else to check.
191
198
  return record;
192
199
  case "complete":
193
- return { ...record, result: readWorkflowResult(record.result, "`result`", fail) };
200
+ return {
201
+ ...record,
202
+ result: readWorkflowResult(record.result, "`result`", fail),
203
+ };
194
204
  case "error":
195
205
  requireString(record.message, "`message`", fail);
196
206
  return record;
@@ -1,3 +1,4 @@
1
+ import type { ProductId, VariantId } from "./identifiers.js";
1
2
  /**
2
3
  * Hand-mirrored wire types for the material projections — the grid-card tile,
3
4
  * the detail record, and the availability/successor block they carry.
@@ -22,7 +23,6 @@
22
23
  * that says which.
23
24
  */
24
25
  import type { ImageSource, MaterialColour, OpenEnum, PaletteSwatch } from "./types.js";
25
- import type { ProductId, VariantId } from "./identifiers.js";
26
26
  /**
27
27
  * Whether a material can still be bought.
28
28
  *
package/operations.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export declare const MATERIALGRAPH_API_VERSION = "0.7.0";
2
- declare const OPERATION_DEFINITIONS: readonly [readonly ["search", "POST", "/api/v1/search", "search_materials_post"], readonly ["searchGet", "GET", "/api/v1/search", "search_materials_get"], readonly ["match", "POST", "/api/v1/search/match", "match_material"], readonly ["byColour", "GET", "/api/v1/search/colours", "search_by_colour"], readonly ["material", "GET", "/api/v1/materials/{variantId}", "get_material"], readonly ["siblings", "GET", "/api/v1/materials/{variantId}/siblings", "get_material_siblings"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["materialAssets", "GET", "/api/v1/materials/{variantId}/assets", "list_material_assets"], readonly ["schema", "GET", "/api/v1/schema/blocks", "list_schema_blocks"], readonly ["schema", "GET", "/api/v1/schema/attributes", "list_schema_attributes"], readonly ["schema", "GET", "/api/v1/schema/verticals", "list_schema_verticals"], readonly ["schema", "GET", "/api/v1/schema/product-types", "list_schema_product_types"], readonly ["schema", "GET", "/api/v1/schema/units", "list_schema_units"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["schema", "GET", "/api/v1/schema/source-requirements", "list_schema_source_requirements"], readonly ["schema", "GET", "/api/v1/schema/product-type-templates", "list_schema_product_type_templates"], readonly ["schema", "GET", "/api/v1/schema/mappings", "list_schema_mappings"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["associatedFinishes", "GET", "/api/v1/associated/finishes", "list_associated_finishes"], readonly ["associatedProducts", "GET", "/api/v1/associated/products", "list_associated_products"], readonly ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"], readonly ["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"], readonly ["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"], readonly ["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"], readonly ["colours.profiles", "GET", "/api/v1/colours/profiles", "get_category_colour_profiles"], readonly ["colours.stats", "GET", "/api/v1/colours/stats", "get_colour_stats"], readonly ["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"], readonly ["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"], readonly ["suggest", "GET", "/api/v1/search/suggest", "search_suggest"], readonly ["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"], readonly ["resolveAttributeValue", "GET", "/api/v1/catalog/attributes/{attributeId}/values/resolve", "catalog_resolve_value"], readonly ["searchVocabulary", "GET", "/api/v1/catalog/vocabulary/search", "catalog_vocabulary_search"], readonly ["resolve", "POST", "/api/v1/resolve", "resolve_phrases"], readonly ["bySpec", "GET", "/api/v1/search/specifications", "catalog_by_spec"], readonly ["taxonomy", "GET", "/api/v1/taxonomy/categories", "get_category_taxonomy"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"], readonly ["demand", "GET", "/api/v1/demand", "get_material_demand"], readonly ["colours.palette", "POST", "/api/v1/search/palettes", "search_by_palette"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["colours.gradient", "POST", "/api/v1/search/gradients", "search_by_gradient"], readonly ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"], readonly ["attributeValues", "GET", "/api/v1/catalog/attributes/{attributeId}/values", "list_attribute_values"], readonly ["applicableSpecs", "GET", "/api/v1/taxonomy/categories/{key}/specs", "get_applicable_specs"], readonly ["glossary", "GET", "/api/v1/glossary/{term}", "explain_material_term"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"], readonly ["sourceMaterialBatch", "GET", "/api/v1/source-material/{batchId}", "get_source_material_batch"], readonly ["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"], readonly ["createRun", "POST", "/api/v1/runs", "create_public_run"], readonly ["listRuns", "GET", "/api/v1/runs", "list_public_runs"], readonly ["run", "GET", "/api/v1/runs/{runId}", "get_public_run"], readonly ["runSchedule", "GET", "/api/v1/runs/{runId}/schedule", "get_run_finish_schedule"], readonly ["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"], readonly ["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"], readonly ["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"], readonly ["varyScheme", "POST", "/api/v1/schemes/vary", "vary_schemes"], readonly ["createProject", "POST", "/api/v1/projects", "create_box_project"], readonly ["listProjects", "GET", "/api/v1/projects", "list_box_projects"], readonly ["getProject", "GET", "/api/v1/projects/{projectId}", "get_box_project"], readonly ["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"], readonly ["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"], readonly ["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"], readonly ["codes.context", "GET", "/api/v1/codes/context", "list_code_context"], readonly ["codes.requirements", "GET", "/api/v1/codes/requirements", "list_code_requirements"]];
2
+ declare const OPERATION_DEFINITIONS: readonly [readonly ["search", "POST", "/api/v1/search", "search_materials_post"], readonly ["searchGet", "GET", "/api/v1/search", "search_materials_get"], readonly ["match", "POST", "/api/v1/search/match", "match_material"], readonly ["byColour", "GET", "/api/v1/search/colours", "search_by_colour"], readonly ["material", "GET", "/api/v1/materials/{variantId}", "get_material"], readonly ["siblings", "GET", "/api/v1/materials/{variantId}/siblings", "get_material_siblings"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["materialAssets", "GET", "/api/v1/materials/{variantId}/assets", "list_material_assets"], readonly ["schema", "GET", "/api/v1/schema/blocks", "list_schema_blocks"], readonly ["schema", "GET", "/api/v1/schema/attributes", "list_schema_attributes"], readonly ["schema", "GET", "/api/v1/schema/verticals", "list_schema_verticals"], readonly ["schema", "GET", "/api/v1/schema/product-types", "list_schema_product_types"], readonly ["schema", "GET", "/api/v1/schema/units", "list_schema_units"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["schema", "GET", "/api/v1/schema/source-requirements", "list_schema_source_requirements"], readonly ["schema", "GET", "/api/v1/schema/product-type-templates", "list_schema_product_type_templates"], readonly ["schema", "GET", "/api/v1/schema/mappings", "list_schema_mappings"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["associatedFinishes", "GET", "/api/v1/associated/finishes", "list_associated_finishes"], readonly ["associatedProducts", "GET", "/api/v1/associated/products", "list_associated_products"], readonly ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"], readonly ["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"], readonly ["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"], readonly ["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"], readonly ["colours.profiles", "GET", "/api/v1/colours/profiles", "get_category_colour_profiles"], readonly ["colours.stats", "GET", "/api/v1/colours/stats", "get_colour_stats"], readonly ["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"], readonly ["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"], readonly ["suggest", "GET", "/api/v1/search/suggest", "search_suggest"], readonly ["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"], readonly ["resolveAttributeValue", "GET", "/api/v1/catalog/attributes/{attributeId}/values/resolve", "catalog_resolve_value"], readonly ["searchVocabulary", "GET", "/api/v1/catalog/vocabulary/search", "catalog_vocabulary_search"], readonly ["resolve", "POST", "/api/v1/resolve", "resolve_phrases"], readonly ["bySpec", "GET", "/api/v1/search/specifications", "catalog_by_spec"], readonly ["taxonomy", "GET", "/api/v1/taxonomy/categories", "get_category_taxonomy"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"], readonly ["demand", "GET", "/api/v1/demand", "get_material_demand"], readonly ["colours.palette", "POST", "/api/v1/search/palettes", "search_by_palette"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["colours.gradient", "POST", "/api/v1/search/gradients", "search_by_gradient"], readonly ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"], readonly ["attributeValues", "GET", "/api/v1/catalog/attributes/{attributeId}/values", "list_attribute_values"], readonly ["applicableSpecs", "GET", "/api/v1/taxonomy/categories/{key}/specs", "get_applicable_specs"], readonly ["glossary", "GET", "/api/v1/glossary/{term}", "explain_material_term"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"], readonly ["brandColourDeck", "GET", "/api/v1/brands/{brandSlug}/colours", "brand_colour_deck"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"], readonly ["sourceMaterialBatch", "GET", "/api/v1/source-material/{batchId}", "get_source_material_batch"], readonly ["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"], readonly ["createRun", "POST", "/api/v1/runs", "create_public_run"], readonly ["listRuns", "GET", "/api/v1/runs", "list_public_runs"], readonly ["run", "GET", "/api/v1/runs/{runId}", "get_public_run"], readonly ["runSchedule", "GET", "/api/v1/runs/{runId}/schedule", "get_run_finish_schedule"], readonly ["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"], readonly ["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"], readonly ["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"], readonly ["varyScheme", "POST", "/api/v1/schemes/vary", "vary_schemes"], readonly ["createProject", "POST", "/api/v1/projects", "create_box_project"], readonly ["listProjects", "GET", "/api/v1/projects", "list_box_projects"], readonly ["getProject", "GET", "/api/v1/projects/{projectId}", "get_box_project"], readonly ["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"], readonly ["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"], readonly ["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"], readonly ["codes.context", "GET", "/api/v1/codes/context", "list_code_context"], readonly ["codes.requirements", "GET", "/api/v1/codes/requirements", "list_code_requirements"]];
3
3
  export type SdkOperationId = (typeof OPERATION_DEFINITIONS)[number][3];
4
4
  export type SdkOperation = Readonly<{
5
5
  clientMethod: (typeof OPERATION_DEFINITIONS)[number][0];
package/operations.js CHANGED
@@ -6,38 +6,118 @@ const OPERATION_DEFINITIONS = [
6
6
  ["match", "POST", "/api/v1/search/match", "match_material"],
7
7
  ["byColour", "GET", "/api/v1/search/colours", "search_by_colour"],
8
8
  ["material", "GET", "/api/v1/materials/{variantId}", "get_material"],
9
- ["siblings", "GET", "/api/v1/materials/{variantId}/siblings", "get_material_siblings"],
10
- ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"],
11
- ["materialAssets", "GET", "/api/v1/materials/{variantId}/assets", "list_material_assets"],
9
+ [
10
+ "siblings",
11
+ "GET",
12
+ "/api/v1/materials/{variantId}/siblings",
13
+ "get_material_siblings",
14
+ ],
15
+ [
16
+ "materialBlocks",
17
+ "GET",
18
+ "/api/v1/materials/{variantId}/blocks",
19
+ "list_material_blocks",
20
+ ],
21
+ [
22
+ "materialAssets",
23
+ "GET",
24
+ "/api/v1/materials/{variantId}/assets",
25
+ "list_material_assets",
26
+ ],
12
27
  ["schema", "GET", "/api/v1/schema/blocks", "list_schema_blocks"],
13
28
  ["schema", "GET", "/api/v1/schema/attributes", "list_schema_attributes"],
14
29
  ["schema", "GET", "/api/v1/schema/verticals", "list_schema_verticals"],
15
- ["schema", "GET", "/api/v1/schema/product-types", "list_schema_product_types"],
30
+ [
31
+ "schema",
32
+ "GET",
33
+ "/api/v1/schema/product-types",
34
+ "list_schema_product_types",
35
+ ],
16
36
  ["schema", "GET", "/api/v1/schema/units", "list_schema_units"],
17
- ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"],
18
- ["schema", "GET", "/api/v1/schema/source-requirements", "list_schema_source_requirements"],
19
- ["schema", "GET", "/api/v1/schema/product-type-templates", "list_schema_product_type_templates"],
37
+ [
38
+ "schema",
39
+ "GET",
40
+ "/api/v1/schema/value-dictionaries",
41
+ "list_schema_value_dictionaries",
42
+ ],
43
+ [
44
+ "schema",
45
+ "GET",
46
+ "/api/v1/schema/source-requirements",
47
+ "list_schema_source_requirements",
48
+ ],
49
+ [
50
+ "schema",
51
+ "GET",
52
+ "/api/v1/schema/product-type-templates",
53
+ "list_schema_product_type_templates",
54
+ ],
20
55
  ["schema", "GET", "/api/v1/schema/mappings", "list_schema_mappings"],
21
- ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"],
22
- ["associatedFinishes", "GET", "/api/v1/associated/finishes", "list_associated_finishes"],
23
- ["associatedProducts", "GET", "/api/v1/associated/products", "list_associated_products"],
56
+ [
57
+ "pairings",
58
+ "GET",
59
+ "/api/v1/materials/{variantId}/pairings",
60
+ "get_material_pairings",
61
+ ],
62
+ [
63
+ "associatedFinishes",
64
+ "GET",
65
+ "/api/v1/associated/finishes",
66
+ "list_associated_finishes",
67
+ ],
68
+ [
69
+ "associatedProducts",
70
+ "GET",
71
+ "/api/v1/associated/products",
72
+ "list_associated_products",
73
+ ],
24
74
  ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"],
25
- ["materialsCached", "GET", "/api/v1/materials/batch", "get_materials_batch_get"],
75
+ [
76
+ "materialsCached",
77
+ "GET",
78
+ "/api/v1/materials/batch",
79
+ "get_materials_batch_get",
80
+ ],
26
81
  ["browse", "GET", "/api/v1/search/browse", "search_browse_by_colour"],
27
82
  ["colourFacets", "GET", "/api/v1/colours/facets", "get_colour_facets"],
28
- ["colours.profiles", "GET", "/api/v1/colours/profiles", "get_category_colour_profiles"],
83
+ [
84
+ "colours.profiles",
85
+ "GET",
86
+ "/api/v1/colours/profiles",
87
+ "get_category_colour_profiles",
88
+ ],
29
89
  ["colours.stats", "GET", "/api/v1/colours/stats", "get_colour_stats"],
30
- ["resolveColour", "GET", "/api/v1/search/colours/resolve", "resolve_colour_name"],
31
- ["suggestColour", "GET", "/api/v1/search/colours/suggest", "suggest_colour_names"],
90
+ [
91
+ "resolveColour",
92
+ "GET",
93
+ "/api/v1/search/colours/resolve",
94
+ "resolve_colour_name",
95
+ ],
96
+ [
97
+ "suggestColour",
98
+ "GET",
99
+ "/api/v1/search/colours/suggest",
100
+ "suggest_colour_names",
101
+ ],
32
102
  ["suggest", "GET", "/api/v1/search/suggest", "search_suggest"],
33
- ["resolveAttribute", "GET", "/api/v1/catalog/attributes/resolve", "catalog_resolve_attribute"],
103
+ [
104
+ "resolveAttribute",
105
+ "GET",
106
+ "/api/v1/catalog/attributes/resolve",
107
+ "catalog_resolve_attribute",
108
+ ],
34
109
  [
35
110
  "resolveAttributeValue",
36
111
  "GET",
37
112
  "/api/v1/catalog/attributes/{attributeId}/values/resolve",
38
113
  "catalog_resolve_value",
39
114
  ],
40
- ["searchVocabulary", "GET", "/api/v1/catalog/vocabulary/search", "catalog_vocabulary_search"],
115
+ [
116
+ "searchVocabulary",
117
+ "GET",
118
+ "/api/v1/catalog/vocabulary/search",
119
+ "catalog_vocabulary_search",
120
+ ],
41
121
  ["resolve", "POST", "/api/v1/resolve", "resolve_phrases"],
42
122
  ["bySpec", "GET", "/api/v1/search/specifications", "catalog_by_spec"],
43
123
  ["taxonomy", "GET", "/api/v1/taxonomy/categories", "get_category_taxonomy"],
@@ -45,38 +125,94 @@ const OPERATION_DEFINITIONS = [
45
125
  ["coordinate", "POST", "/api/v1/pairings/coordinate", "coordinate_materials"],
46
126
  ["demand", "GET", "/api/v1/demand", "get_material_demand"],
47
127
  ["colours.palette", "POST", "/api/v1/search/palettes", "search_by_palette"],
48
- ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"],
49
- ["colours.gradient", "POST", "/api/v1/search/gradients", "search_by_gradient"],
50
- ["specificationFacets", "GET", "/api/v1/catalog/specifications/facets", "list_spec_facets"],
128
+ [
129
+ "extractPalette",
130
+ "POST",
131
+ "/api/v1/search/palettes/extract",
132
+ "extract_image_palette",
133
+ ],
134
+ [
135
+ "colours.gradient",
136
+ "POST",
137
+ "/api/v1/search/gradients",
138
+ "search_by_gradient",
139
+ ],
140
+ [
141
+ "specificationFacets",
142
+ "GET",
143
+ "/api/v1/catalog/specifications/facets",
144
+ "list_spec_facets",
145
+ ],
51
146
  [
52
147
  "attributeValues",
53
148
  "GET",
54
149
  "/api/v1/catalog/attributes/{attributeId}/values",
55
150
  "list_attribute_values",
56
151
  ],
57
- ["applicableSpecs", "GET", "/api/v1/taxonomy/categories/{key}/specs", "get_applicable_specs"],
152
+ [
153
+ "applicableSpecs",
154
+ "GET",
155
+ "/api/v1/taxonomy/categories/{key}/specs",
156
+ "get_applicable_specs",
157
+ ],
58
158
  ["glossary", "GET", "/api/v1/glossary/{term}", "explain_material_term"],
59
159
  ["brands", "GET", "/api/v1/brands", "list_brands"],
60
160
  ["brand", "GET", "/api/v1/brands/{brandSlug}", "get_brand_profile"],
61
- ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"],
62
- ["sourceMaterialBatch", "GET", "/api/v1/source-material/{batchId}", "get_source_material_batch"],
161
+ [
162
+ "brandColourDeck",
163
+ "GET",
164
+ "/api/v1/brands/{brandSlug}/colours",
165
+ "brand_colour_deck",
166
+ ],
167
+ [
168
+ "uploadSourceMaterial",
169
+ "POST",
170
+ "/api/v1/source-material",
171
+ "upload_source_material",
172
+ ],
173
+ [
174
+ "sourceMaterialBatch",
175
+ "GET",
176
+ "/api/v1/source-material/{batchId}",
177
+ "get_source_material_batch",
178
+ ],
63
179
  ["capabilities", "GET", "/api/v1/capabilities", "list_public_capabilities"],
64
180
  ["createRun", "POST", "/api/v1/runs", "create_public_run"],
65
181
  ["listRuns", "GET", "/api/v1/runs", "list_public_runs"],
66
182
  ["run", "GET", "/api/v1/runs/{runId}", "get_public_run"],
67
- ["runSchedule", "GET", "/api/v1/runs/{runId}/schedule", "get_run_finish_schedule"],
68
- ["streamRunEvents", "GET", "/api/v1/runs/{runId}/events", "list_public_run_events"],
183
+ [
184
+ "runSchedule",
185
+ "GET",
186
+ "/api/v1/runs/{runId}/schedule",
187
+ "get_run_finish_schedule",
188
+ ],
189
+ [
190
+ "streamRunEvents",
191
+ "GET",
192
+ "/api/v1/runs/{runId}/events",
193
+ "list_public_run_events",
194
+ ],
69
195
  ["resumeRun", "POST", "/api/v1/runs/{runId}/resume", "resume_public_run"],
70
196
  ["cancelRun", "POST", "/api/v1/runs/{runId}/cancel", "cancel_public_run"],
71
197
  ["varyScheme", "POST", "/api/v1/schemes/vary", "vary_schemes"],
72
198
  ["createProject", "POST", "/api/v1/projects", "create_box_project"],
73
199
  ["listProjects", "GET", "/api/v1/projects", "list_box_projects"],
74
200
  ["getProject", "GET", "/api/v1/projects/{projectId}", "get_box_project"],
75
- ["appendProjectEvent", "POST", "/api/v1/projects/{projectId}/events", "append_box_project_event"],
201
+ [
202
+ "appendProjectEvent",
203
+ "POST",
204
+ "/api/v1/projects/{projectId}/events",
205
+ "append_box_project_event",
206
+ ],
76
207
  ["demandBatch", "POST", "/api/v1/demand/batch", "get_demand_batch"],
77
208
  ["codeAdvisory", "POST", "/api/v1/codes/advisory", "resolveCodeAdvisory"],
78
209
  ["codes.context", "GET", "/api/v1/codes/context", "list_code_context"],
79
- ["codes.requirements", "GET", "/api/v1/codes/requirements", "list_code_requirements"],
210
+ [
211
+ "codes.requirements",
212
+ "GET",
213
+ "/api/v1/codes/requirements",
214
+ "list_code_requirements",
215
+ ],
80
216
  ];
81
217
  export const SDK_OPERATIONS = Object.freeze(OPERATION_DEFINITIONS.map(([clientMethod, method, path, operationId]) => Object.freeze({ clientMethod, method, path, operationId })));
82
218
  /** Look up a registered operation by its OpenAPI `operationId`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instruments/materialgraph",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -24,13 +24,19 @@
24
24
  * - You want MATERIALS → `mg.search()`. This lens never returns one.
25
25
  */
26
26
  import type { HttpTransport } from "./http-transport.js";
27
- import type { ResolveResponse } from "./resolve-types.js";
27
+ import type { ResolvePhrasesScope, ResolveResponse } from "./resolve-types.js";
28
28
  import type { VocabularySearchOptions, VocabularySearchResponse } from "./types.js";
29
29
  /**
30
30
  * THE QUESTION: "I have words — what do they mean in YOUR vocabulary?" Send up
31
31
  * to 50 phrases as your users typed them; each comes back with every canonical
32
32
  * token MaterialGraph knows it as — brand, exact paint, colour family, category,
33
- * attribute, value — best first, in request order. Backs `mg.resolve()`.
33
+ * attribute, value, and the two code axes (jurisdiction, occupancy group) — best
34
+ * first, in request order. Backs `mg.resolve()`.
35
+ *
36
+ * The code axes read out of a sentence: "a maximum security prison in Kansas"
37
+ * returns `us-ks` and `I-3`, the pair `mg.codeAdvisory()` is keyed on. They are
38
+ * half a context, not a whole one — that call also wants a space type and a
39
+ * sprinklered flag, and validates the combination before it answers.
34
40
  *
35
41
  * A phrase can legitimately mean several things at once ("sage" is two vendor
36
42
  * paints AND a colour family), so each entry carries a LIST of readings rather
@@ -54,10 +60,23 @@ import type { VocabularySearchOptions, VocabularySearchResponse } from "./types.
54
60
  * complete a name as someone types, and `mg.glossary()` to EXPLAIN a token you
55
61
  * have already resolved.
56
62
  *
63
+ * - **Every reading says what stands behind it.** `coverage.variants` is how
64
+ * many variants carry it corpus-wide, and `0` is a measured answer rather
65
+ * than missing data — a word this vocabulary knows and the catalogue holds
66
+ * nothing under. That is the case you would otherwise discover only by making
67
+ * a second call, on a hot path, where a second call gets skipped. An ABSENT
68
+ * `coverage` means it could not be determined; never read it as none.
69
+ *
70
+ * Pass `category` when you are grounding these words for a SLOT, and read
71
+ * `coverage.inCategory`: a corpus-wide count is not a promise the reading is
72
+ * reachable inside a given category, and that conflation is what turns a
73
+ * top-ranked suggestion into an unsatisfiable requirement.
74
+ *
57
75
  * @param phrases 1-50 phrases, each 1-200 characters. Sent as-is; each is
58
76
  * resolved independently, so one meaningless phrase never spoils the batch.
77
+ * @param options `category` to scope the coverage answer, and an `AbortSignal`.
59
78
  */
60
- export declare function resolvePhrasesRequest(transport: HttpTransport, phrases: string[], signal?: AbortSignal): Promise<ResolveResponse>;
79
+ export declare function resolvePhrasesRequest(transport: HttpTransport, phrases: string[], scope?: ResolvePhrasesScope): Promise<ResolveResponse>;
61
80
  /**
62
81
  * THE QUESTION: "is 'FloorScore' an attribute or a value, and what is it called
63
82
  * here?" The catch-all for ONE phrase when you do not know which half of an