@instruments/materialgraph 0.16.0 → 0.18.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.
@@ -39,10 +39,79 @@ export interface MaterialAssetProjectionDownload {
39
39
  fileExtension: string | null;
40
40
  filename: string | null;
41
41
  }
42
+ export type MaterialDocumentStatus = OpenEnum<"downloaded" | "linked_not_downloaded" | "unavailable_source" | "dead_link" | "failed_ingest">;
43
+ export type MaterialDocumentCoverageState = OpenEnum<"available" | "none_declared" | "not_ingested" | "unavailable_source" | "dead_link" | "failed_ingest">;
44
+ export interface MaterialDocumentScope {
45
+ kind: "product" | "variant";
46
+ id: string;
47
+ }
48
+ export interface MaterialDocumentSource {
49
+ /** Opaque, public identifier for one source collection; never a PIM/DAM path. */
50
+ id: string;
51
+ authority: string;
52
+ revision: string;
53
+ }
54
+ export interface MaterialDocumentProjectionItem {
55
+ id: string;
56
+ scope: MaterialDocumentScope & {
57
+ inherited: boolean;
58
+ };
59
+ declaredScopes: MaterialDocumentScope[];
60
+ variantAttribution: string[];
61
+ /** True only for a Product declaration or this exact requested Variant. */
62
+ appliesToRequestedVariant: boolean;
63
+ /** Every exact source-grain declaration collapsed into this URL identity. */
64
+ declarations: Array<{
65
+ scope: MaterialDocumentScope;
66
+ source: MaterialDocumentSource;
67
+ status: MaterialDocumentStatus;
68
+ readable: boolean;
69
+ }>;
70
+ documentType: string | null;
71
+ originalName: string;
72
+ status: MaterialDocumentStatus;
73
+ /** Null for unavailable, dead, or failed source documents. */
74
+ url: string | null;
75
+ contentType: string | null;
76
+ fileExtension: string | null;
77
+ source: MaterialDocumentSource;
78
+ }
79
+ export interface MaterialDocumentCoverageSource {
80
+ scope: MaterialDocumentScope;
81
+ state: MaterialDocumentCoverageState;
82
+ exact: boolean;
83
+ declaredCount: number | null;
84
+ readableCount: number;
85
+ source: MaterialDocumentSource;
86
+ }
87
+ export interface MaterialDocumentCoverage {
88
+ productUnion: MaterialDocumentCoverageSummary;
89
+ requestedVariant: MaterialDocumentCoverageSummary;
90
+ sources: MaterialDocumentCoverageSource[];
91
+ }
92
+ export interface MaterialDocumentCoverageSummary {
93
+ state: MaterialDocumentCoverageState;
94
+ exact: boolean;
95
+ declaredCount: number | null;
96
+ readableCount: number;
97
+ }
98
+ export interface MaterialDocumentPageOptions {
99
+ cursor?: string;
100
+ limit?: number;
101
+ }
102
+ export interface MaterialDocumentPage {
103
+ variantId: VariantId;
104
+ documents: MaterialDocumentProjectionItem[];
105
+ coverage: MaterialDocumentCoverage;
106
+ totalDocuments: number;
107
+ nextCursor: string | null;
108
+ }
42
109
  /** Tallies over what this projection returned — never a claim about the brand's own library. */
43
110
  export interface MaterialAssetProjectionSummary {
44
111
  imageCount: number;
45
112
  downloadCount: number;
113
+ documentCount: number;
114
+ readableDocumentCount: number;
46
115
  downloadedImageCount: number;
47
116
  }
48
117
  export interface MaterialAssetProjection {
@@ -50,5 +119,7 @@ export interface MaterialAssetProjection {
50
119
  variantId: VariantId;
51
120
  images: MaterialAssetProjectionImage[];
52
121
  downloads: MaterialAssetProjectionDownload[];
122
+ documents: MaterialDocumentProjectionItem[];
123
+ documentCoverage: MaterialDocumentCoverage;
53
124
  summary: MaterialAssetProjectionSummary;
54
125
  }
@@ -0,0 +1,15 @@
1
+ /** Material blocks, safe assets, and technical documents over one shared transport. */
2
+ import { HttpTransport } from "./http-transport.js";
3
+ import type { VariantId } from "./identifiers.js";
4
+ import type { MaterialAssetProjection, MaterialDocumentPage, MaterialDocumentPageOptions } from "./material-asset-types.js";
5
+ import type { MaterialBlockProjection } from "./material-block-types.js";
6
+ /** Read known and explicitly unobserved canonical material fields. */
7
+ export declare function materialBlocks(transport: HttpTransport, variantId: VariantId, signal?: AbortSignal): Promise<MaterialBlockProjection>;
8
+ /** Read only redistribution-safe images and downloads for a material. */
9
+ export declare function materialAssets(transport: HttpTransport, variantId: VariantId, signal?: AbortSignal): Promise<MaterialAssetProjection>;
10
+ /**
11
+ * Read the owning Product's URL-deduplicated document page. Variant declarations
12
+ * retain exact attribution; consumers inspect `appliesToRequestedVariant`
13
+ * before presenting a sibling Variant declaration as applicable.
14
+ */
15
+ export declare function materialDocuments(transport: HttpTransport, variantId: VariantId, options?: MaterialDocumentPageOptions, signal?: AbortSignal): Promise<MaterialDocumentPage>;
@@ -0,0 +1,25 @@
1
+ /** Material blocks, safe assets, and technical documents over one shared transport. */
2
+ import { HttpTransport } from "./http-transport.js";
3
+ import { resolveOperation } from "./operations.js";
4
+ /** Read known and explicitly unobserved canonical material fields. */
5
+ export function materialBlocks(transport, variantId, signal) {
6
+ return transport.requestJson(resolveOperation("list_material_blocks"), { signal, pathParams: { variantId } });
7
+ }
8
+ /** Read only redistribution-safe images and downloads for a material. */
9
+ export function materialAssets(transport, variantId, signal) {
10
+ return transport.requestJson(resolveOperation("list_material_assets"), { signal, pathParams: { variantId } });
11
+ }
12
+ /**
13
+ * Read the owning Product's URL-deduplicated document page. Variant declarations
14
+ * retain exact attribution; consumers inspect `appliesToRequestedVariant`
15
+ * before presenting a sibling Variant declaration as applicable.
16
+ */
17
+ export function materialDocuments(transport, variantId, options, signal) {
18
+ return transport.requestJson(resolveOperation("list_material_documents"), {
19
+ query: options
20
+ ? { cursor: options.cursor, limit: options.limit }
21
+ : undefined,
22
+ signal,
23
+ pathParams: { variantId },
24
+ });
25
+ }
@@ -133,6 +133,41 @@ export interface MaterialDetail extends MaterialTile {
133
133
  installationImageUrl: string | null;
134
134
  displayCategory: string | null;
135
135
  categoryPath: string[];
136
+ /** Durable state of image-colour extraction and its variant projection. */
137
+ colourEvidence: {
138
+ status: "no_eligible_asset";
139
+ } | {
140
+ status: "extractor_not_run";
141
+ origin: {
142
+ assetId: string;
143
+ method: "colorscope_primary_image";
144
+ };
145
+ } | {
146
+ status: "available";
147
+ basis: "palette" | "palette_and_dominant_oklab" | "dominant_oklab";
148
+ extractedAt: string;
149
+ extractionVersion: string | null;
150
+ origin: {
151
+ assetId: string;
152
+ method: "colorscope_primary_image";
153
+ };
154
+ } | {
155
+ status: "projection_missing";
156
+ extractedAt: string;
157
+ extractionVersion: string | null;
158
+ origin: {
159
+ assetId: string;
160
+ method: "colorscope_primary_image";
161
+ };
162
+ } | {
163
+ status: "genuinely_colourless";
164
+ extractedAt: string;
165
+ extractionVersion: string | null;
166
+ origin: {
167
+ assetId: string;
168
+ method: "colorscope_primary_image";
169
+ };
170
+ };
136
171
  /** Extracted from imagery — visual likeness, never a declared colour spec. */
137
172
  palette: PaletteSwatch[];
138
173
  /** What is ON RECORD. An absent attribute is unrecorded, never known-false. */
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 ["planQuery", "POST", "/api/v1/search/plan", "plan_material_query"], 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 ["catalogueReferences", "POST", "/api/v1/catalogues/references", "get_catalogue_references_batch"], readonly ["catalogueDocumentAccess", "GET", "/api/v1/catalogues/documents/{documentId}", "get_catalogue_document_access"], 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 ["exploration.operators", "GET", "/api/v1/materials/{variantId}/operators", "list_material_operators"], readonly ["exploration.candidates", "POST", "/api/v1/materials/{variantId}/candidates", "read_material_candidates"], 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 ["sourceMaterialComments", "GET", "/api/v1/source-material/{batchId}/sources/{sourceId}/comments", "list_source_material_comments"], readonly ["sourceMaterialComments", "POST", "/api/v1/source-material/{batchId}/sources/{sourceId}/comments", "post_source_material_comment"], 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 ["slotPools", "POST", "/api/v1/schemes/pools", "slot_pools"], 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 ["catalogs", "GET", "/api/v1/catalogs", "list_catalog_documents"], readonly ["catalogPages", "GET", "/api/v1/catalogs/{catalogId}/pages", "list_catalog_pages"], readonly ["catalogPageRepresentation", "GET", "/api/v1/catalog-pages/{pageId}/representation", "get_catalog_page_representation"], readonly ["catalogReferences", "GET", "/api/v1/catalogs/{catalogId}/references", "list_catalog_product_references"], readonly ["search", "POST", "/api/v1/search", "search_materials_post"], readonly ["planQuery", "POST", "/api/v1/search/plan", "plan_material_query"], 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 ["catalogueReferences", "POST", "/api/v1/catalogues/references", "get_catalogue_references_batch"], readonly ["catalogueCollections", "GET", "/api/v1/catalogues/collections", "list_catalogue_channel_collections"], readonly ["catalogueDocumentAccess", "GET", "/api/v1/catalogues/documents/{documentId}", "get_catalogue_document_access"], 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 ["materialDocuments", "GET", "/api/v1/materials/{variantId}/documents", "list_material_documents"], readonly ["exploration.operators", "GET", "/api/v1/materials/{variantId}/operators", "list_material_operators"], readonly ["exploration.candidates", "POST", "/api/v1/materials/{variantId}/candidates", "read_material_candidates"], 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 ["applicability", "GET", "/api/v1/applicability", "get_applicability"], 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 ["sourceMaterialComments", "GET", "/api/v1/source-material/{batchId}/sources/{sourceId}/comments", "list_source_material_comments"], readonly ["sourceMaterialComments", "POST", "/api/v1/source-material/{batchId}/sources/{sourceId}/comments", "post_source_material_comment"], 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 ["slotPools", "POST", "/api/v1/schemes/pools", "slot_pools"], 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
@@ -1,6 +1,25 @@
1
1
  import { MaterialGraphError } from "./errors.js";
2
2
  export const MATERIALGRAPH_API_VERSION = "0.7.0";
3
3
  const OPERATION_DEFINITIONS = [
4
+ ["catalogs", "GET", "/api/v1/catalogs", "list_catalog_documents"],
5
+ [
6
+ "catalogPages",
7
+ "GET",
8
+ "/api/v1/catalogs/{catalogId}/pages",
9
+ "list_catalog_pages",
10
+ ],
11
+ [
12
+ "catalogPageRepresentation",
13
+ "GET",
14
+ "/api/v1/catalog-pages/{pageId}/representation",
15
+ "get_catalog_page_representation",
16
+ ],
17
+ [
18
+ "catalogReferences",
19
+ "GET",
20
+ "/api/v1/catalogs/{catalogId}/references",
21
+ "list_catalog_product_references",
22
+ ],
4
23
  ["search", "POST", "/api/v1/search", "search_materials_post"],
5
24
  ["planQuery", "POST", "/api/v1/search/plan", "plan_material_query"],
6
25
  ["searchGet", "GET", "/api/v1/search", "search_materials_get"],
@@ -13,6 +32,12 @@ const OPERATION_DEFINITIONS = [
13
32
  "/api/v1/catalogues/references",
14
33
  "get_catalogue_references_batch",
15
34
  ],
35
+ [
36
+ "catalogueCollections",
37
+ "GET",
38
+ "/api/v1/catalogues/collections",
39
+ "list_catalogue_channel_collections",
40
+ ],
16
41
  [
17
42
  "catalogueDocumentAccess",
18
43
  "GET",
@@ -37,6 +62,12 @@ const OPERATION_DEFINITIONS = [
37
62
  "/api/v1/materials/{variantId}/assets",
38
63
  "list_material_assets",
39
64
  ],
65
+ [
66
+ "materialDocuments",
67
+ "GET",
68
+ "/api/v1/materials/{variantId}/documents",
69
+ "list_material_documents",
70
+ ],
40
71
  [
41
72
  "exploration.operators",
42
73
  "GET",
@@ -96,6 +127,7 @@ const OPERATION_DEFINITIONS = [
96
127
  "/api/v1/associated/products",
97
128
  "list_associated_products",
98
129
  ],
130
+ ["applicability", "GET", "/api/v1/applicability", "get_applicability"],
99
131
  ["materials", "POST", "/api/v1/materials/batch", "get_materials_batch_post"],
100
132
  [
101
133
  "materialsCached",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instruments/materialgraph",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,3 +1,4 @@
1
+ import type { SearchFilter } from "./search-refinement-types.js";
1
2
  import type { OpenEnum } from "./types.js";
2
3
  export type QuerySenseKind = OpenEnum<"brand" | "product" | "paint_identity" | "category" | "material" | "colour" | "attribute" | "attribute_value" | "certification">;
3
4
  export interface QuerySense {
@@ -26,6 +27,184 @@ export interface QuerySense {
26
27
  };
27
28
  reason?: string;
28
29
  }
30
+ export interface CanonicalLexicalAuthorityReceipt {
31
+ authority: OpenEnum<"taxonomy" | "colorscope">;
32
+ version: string;
33
+ revision: string;
34
+ digest?: string;
35
+ }
36
+ export interface CanonicalLexicalReading {
37
+ authority: OpenEnum<"taxonomy" | "colorscope">;
38
+ identity: string;
39
+ outcome: OpenEnum<"equivalent" | "ambiguous" | "declined">;
40
+ executionStatus: OpenEnum<"applied" | "suppressed" | "executable" | "recognized-but-unexecutable" | "declined">;
41
+ role: OpenEnum<"primary" | "secondary"> | null;
42
+ kind?: OpenEnum<"domain" | "core-colour" | "paint">;
43
+ label?: string;
44
+ libraryId?: string;
45
+ code?: string;
46
+ aliasCount?: number;
47
+ matchForm?: OpenEnum<"name" | "code" | "authoritative-term">;
48
+ collisionCount?: number;
49
+ familyId?: string;
50
+ hex?: string;
51
+ level?: OpenEnum<"family" | "tone-band" | "branch" | "concept">;
52
+ lifecycle?: {
53
+ introducedAt: string;
54
+ reviewedAt?: string;
55
+ status: string;
56
+ };
57
+ provenance?: {
58
+ basis: string;
59
+ snapshot?: string;
60
+ source: string;
61
+ };
62
+ }
63
+ export interface CanonicalLexicalMatch {
64
+ original: string;
65
+ canonical: string;
66
+ rule: OpenEnum<"exact" | "plural-to-singular" | "typo-to-canonical">;
67
+ span: {
68
+ start: number;
69
+ end: number;
70
+ };
71
+ coverage: number;
72
+ readings: CanonicalLexicalReading[];
73
+ receipt: {
74
+ original: string;
75
+ canonical: string;
76
+ rule: OpenEnum<"exact" | "plural-to-singular" | "typo-to-canonical">;
77
+ authorities: CanonicalLexicalAuthorityReceipt[];
78
+ };
79
+ }
80
+ export interface CanonicalLexicalReceipt {
81
+ original: string;
82
+ outcome: OpenEnum<"resolved" | "ambiguous" | "declined" | "partial" | "unresolved">;
83
+ matches: CanonicalLexicalMatch[];
84
+ unresolved: CanonicalLexicalMatch[];
85
+ }
86
+ export interface QueryResidualReceipt {
87
+ kind: OpenEnum<"compound" | "descriptive" | "ambiguous" | "empty">;
88
+ tokens: string[];
89
+ resolvedTokens: string[];
90
+ unresolvedTokens: string[];
91
+ resolvedRatio: number;
92
+ comparisonFrame: boolean;
93
+ }
94
+ /** Deterministic criteria compiled from the query before retrieval. */
95
+ export interface QueryCriteriaReceipt {
96
+ applied: Array<{
97
+ criterion: SearchFilter;
98
+ lane: OpenEnum<"filter" | "preference">;
99
+ lexicalSignal?: {
100
+ attributeId: string;
101
+ canonical: string;
102
+ identity: string;
103
+ original: string;
104
+ relation: OpenEnum<"equivalent" | "broader-storage-term">;
105
+ span: {
106
+ start: number;
107
+ end: number;
108
+ };
109
+ storedTerm: string;
110
+ };
111
+ source: OpenEnum<"compiled" | "taxonomy-adapter">;
112
+ span: string;
113
+ }>;
114
+ conflicts: Array<{
115
+ attributeId: string;
116
+ caller: SearchFilter;
117
+ compiled: SearchFilter;
118
+ reason: string;
119
+ }>;
120
+ exclusions: Array<{
121
+ criterion: SearchFilter;
122
+ execution: OpenEnum<"blocked-positive-only-contract">;
123
+ lane: OpenEnum<"hard" | "soft">;
124
+ missingValuePolicy: OpenEnum<"retain-unknown">;
125
+ span: string;
126
+ }>;
127
+ preferences: SearchFilter[];
128
+ residualText: string | null;
129
+ unresolved: Array<{
130
+ code: OpenEnum<"missing-searchable-attribute" | "missing-dimension-pair-contract" | "missing-explicit-comparator" | "negative-criterion-contract-blocked" | "registry-contract-drift" | "unsupported-numeric-format" | "unsupported-unit" | "unsupported-strict-comparator">;
131
+ reason: string;
132
+ span: string;
133
+ }>;
134
+ }
135
+ /** How the query named or inferred a product type before retrieval. */
136
+ export interface QueryProductTypeReceipt {
137
+ kind: OpenEnum<"leaf" | "group" | "vector" | "ambiguous" | "none">;
138
+ gate: OpenEnum<"hard" | "constrain" | "none">;
139
+ pass: OpenEnum<"taxonomy" | "synonym" | "display-name" | "group" | "head-noun" | "vector" | "none">;
140
+ reason: string;
141
+ displayName: string | null;
142
+ productTypeId: string | null;
143
+ groupKey: string | null;
144
+ productTypeIds: string[];
145
+ similarity: number | null;
146
+ margin: number | null;
147
+ residualText: string;
148
+ vectorPassRan: boolean;
149
+ registryVersion: string;
150
+ durationMs: number;
151
+ candidates: Array<{
152
+ productTypeId: string;
153
+ displayName: string;
154
+ similarity: number;
155
+ }>;
156
+ }
157
+ /** The selected retrieval arm; null when the path is browse or criteria-only. */
158
+ export interface QueryArmReceipt {
159
+ arm: OpenEnum<"dense" | "lexical" | "colour"> | null;
160
+ reason: string;
161
+ reasonDetail?: OpenEnum<"derived-colour-veto" | "typo-recovery" | "literal-compound-without-type">;
162
+ outcome: OpenEnum<"dense" | "lexical" | "colour" | "browse" | "criteria-only" | "ambiguous">;
163
+ decidedBy: OpenEnum<"deterministic" | "model">;
164
+ productTypeGate: OpenEnum<"hard" | "constrain" | "none">;
165
+ productTypeIds: string[];
166
+ residual: QueryResidualReceipt;
167
+ registryVersion: string;
168
+ }
169
+ export interface QueryAmbiguityReceipt {
170
+ outcome: OpenEnum<"applied" | "abstained" | "refused" | "timeout">;
171
+ modelId: string | null;
172
+ providerRan: boolean;
173
+ durationMs: number;
174
+ timeoutMs: number;
175
+ validation: OpenEnum<"valid" | "invalid" | "not-run">;
176
+ refusalCode: string | null;
177
+ confidence: number | null;
178
+ routing: OpenEnum<"compound" | "descriptive"> | null;
179
+ productTypeIds: string[];
180
+ changedArm: boolean;
181
+ }
182
+ export interface QueryCategoryScopeReceipt {
183
+ source: OpenEnum<"caller" | "anchor">;
184
+ mechanism: OpenEnum<"sql-predicate" | "allowlist" | "prune">;
185
+ variantCount: number | null;
186
+ ceiling: number;
187
+ specScanBounded?: boolean;
188
+ [key: string]: unknown;
189
+ }
190
+ export interface QuerySpecAllowlistReceipt {
191
+ limit: number;
192
+ returned: number;
193
+ retrieved?: number;
194
+ truncated: boolean;
195
+ emptyMeansScarcity: boolean;
196
+ outcome?: OpenEnum<"corpus-scarcity" | "retrieval-miss" | "unknown">;
197
+ }
198
+ export interface QueryUnsatisfiableNarrowing {
199
+ narrowing: OpenEnum<"product-type">;
200
+ gate: OpenEnum<"hard">;
201
+ productTypeId: string;
202
+ displayName: string;
203
+ productTypeIds: string[];
204
+ pass: string;
205
+ evidence: OpenEnum<"zero-accepted-variants">;
206
+ relaxedTo: OpenEnum<"none">;
207
+ }
29
208
  /** Inspectable interpretation of the raw query by ordinary MaterialGraph search. */
30
209
  export interface QueryUnderstanding {
31
210
  version: OpenEnum<"1">;
@@ -35,4 +214,12 @@ export interface QueryUnderstanding {
35
214
  status: OpenEnum<"resolved" | "ambiguous" | "partial" | "unresolved">;
36
215
  mode: OpenEnum<"image" | "colour" | "text" | "lexical" | "entity-browse">;
37
216
  senses: QuerySense[];
217
+ canonicalLexical?: CanonicalLexicalReceipt;
218
+ criteria?: QueryCriteriaReceipt;
219
+ productType?: QueryProductTypeReceipt;
220
+ arm?: QueryArmReceipt;
221
+ ambiguity?: QueryAmbiguityReceipt;
222
+ categoryScope?: QueryCategoryScopeReceipt;
223
+ specAllowlist?: QuerySpecAllowlistReceipt;
224
+ unsatisfiableNarrowing?: QueryUnsatisfiableNarrowing;
38
225
  }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The product-collapsed view of a search result — what `collapse: "product"`
3
+ * hands back beside each card. Kept out of `types.ts` for the same reason
4
+ * `palette-search-types.ts` and `by-colour-types.ts` are: that file is at its
5
+ * source-size ceiling, and this is a self-contained corner of the contract.
6
+ *
7
+ * READ THIS FIRST, because the default is the opposite of what "duplicates"
8
+ * intuition suggests. MaterialGraph search is VARIANT-based: without `collapse`,
9
+ * every matching variant comes back as its own result, and that is correct —
10
+ * twelve SKUs of one tile are twelve separately orderable products, not twelve
11
+ * copies of one. Product grouping is an optional VIEW over that, not a fix for
12
+ * it.
13
+ *
14
+ * When you do ask for the view, the product arrives CARRYING its variants
15
+ * (`SearchResultItem.variants`) rather than a bare count, so a colourway strip
16
+ * renders from the same response as the card. That array is CAPPED at 12, while
17
+ * `SearchResultItem.siblingCount` stays the complete tally — so a truncated list
18
+ * is decidable from the response alone: `(siblingCount ?? 0) + 1 >
19
+ * variants.length`. A full-length array is never on its own evidence that the
20
+ * product holds no further variants.
21
+ */
22
+ import type { VariantId } from "./identifiers.js";
23
+ import type { SearchResultSignals } from "./types.js";
24
+ /**
25
+ * One variant of a product-collapsed result.
26
+ *
27
+ * A slim shape, deliberately: the fields a colourway strip renders, projected
28
+ * from the row the engine already held in memory, so the array costs no extra
29
+ * query. It is NOT a nested search result — no `attributes`, `palette`,
30
+ * `verdict` or `productType` — because those come from per-result reads the
31
+ * engine issues for the card only. Fetch the variant by `variantId` when you
32
+ * need its full record.
33
+ */
34
+ export interface CollapsedVariant {
35
+ /** Opaque — round-trip verbatim. */
36
+ variantId: VariantId;
37
+ /** The variant's own label; for paint, the colourway name. Null when unnamed. */
38
+ variantName: string | null;
39
+ /** The variant's coarse colour family. */
40
+ color: string | null;
41
+ /**
42
+ * `#rrggbb` dominant colour EXTRACTED FROM the variant's image — a visual
43
+ * likeness, never a manufacturer-declared colour specification. ABSENT means
44
+ * no palette is on record: unknown, never "this variant has no colour".
45
+ */
46
+ hex?: string;
47
+ imageUrl: string | null;
48
+ /**
49
+ * The variant's Material Bank SKU, on the same contract as the result item's:
50
+ * ABSENT means no MB identity is on record, never that it cannot be ordered.
51
+ */
52
+ materialBankId?: string;
53
+ /** This variant's own mode-primary score — the value the array is ordered by. */
54
+ score: number;
55
+ }
56
+ /**
57
+ * One member of a collapsed result's `SearchResultItem.anatomy` — the
58
+ * EXPLAIN-only readout of the same grouping {@link CollapsedVariant} publishes.
59
+ * Where a collapsed variant is what a surface renders, this is what an operator
60
+ * inspects: it carries the per-arm `signals` that made the variant a candidate.
61
+ */
62
+ export interface SearchAnatomyMember {
63
+ /** Opaque — round-trip verbatim. */
64
+ variantId: VariantId;
65
+ variantName: string | null;
66
+ color: string | null;
67
+ score: number;
68
+ signals: SearchResultSignals;
69
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -76,6 +76,13 @@ export type SearchFilter = {
76
76
  attributeId: string;
77
77
  hasValue: true;
78
78
  trust?: SearchFilterTrust;
79
+ }
80
+ /** Date-sensitive presence is currently executable only for EPD expiration. */
81
+ | {
82
+ attributeId: "epd_expiration_date";
83
+ hasValue: true;
84
+ asOf: string;
85
+ trust?: SearchFilterTrust;
79
86
  } | {
80
87
  attributeId: string;
81
88
  /** Inclusive lower bound. Omit for a ceiling-only range. */
@@ -90,29 +97,31 @@ export interface SearchSuggestion {
90
97
  /** Complete replacement request, not a partial patch. */
91
98
  request: SearchInput;
92
99
  }
93
- /** One of the two readings a {@link SearchDisambiguation} offers. */
100
+ /** One reading a {@link SearchDisambiguation} offers. */
94
101
  export interface SearchInterpretation {
95
102
  id: string;
96
103
  label: string;
97
104
  kind: string;
98
- /** True for the reading that ACTUALLY RAN and produced this page. */
105
+ /** True for the reading that ran; all false means the engine chose none. */
99
106
  isDefault: boolean;
100
107
  /** The colour this reading resolved to, when the ambiguity was a colour one. */
101
108
  hex?: string;
102
109
  /** Complete replacement request, not a partial patch. */
103
110
  request: SearchInput;
104
111
  }
112
+ /** The governed ambiguity classes emitted by MaterialGraph search. */
113
+ export type SearchDisambiguationKind = "compliance-phrase" | "colour-material" | "paint-colour" | "paint-identity";
105
114
  /**
106
- * The server saying a query had TWO defensible readings, and which one it took.
115
+ * The server saying a query had multiple defensible readings.
107
116
  *
108
- * `interpretations` always holds exactly two, the one that ran flagged
109
- * `isDefault`. Each carries a complete executable request, so offering the
110
- * other reading is a re-issue rather than a patch. Ignoring this block does not
117
+ * The reading that ran is flagged `isDefault`; every reading is false when the
118
+ * engine deliberately chose none. Each carries a complete executable request,
119
+ * so choosing a reading is a re-issue rather than a patch. Ignoring this block does not
111
120
  * make the ambiguity go away — it makes it invisible to the person who asked.
112
121
  */
113
122
  export interface SearchDisambiguation {
114
- /** Which ambiguity this is: "compliance-phrase", "colour-material", "paint-colour". */
115
- kind: string;
123
+ /** Compliance, colour-material, paint-colour, or paint-identity ambiguity. */
124
+ kind: SearchDisambiguationKind;
116
125
  /** The ambiguous term or phrase. */
117
126
  term: string;
118
127
  interpretations: SearchInterpretation[];