@instruments/materialgraph 0.7.1 → 0.9.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.
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Wire types for the DECLARED FF&E association reads
3
+ * (`GET /api/v1/associated/finishes`, `GET /api/v1/associated/products`).
4
+ *
5
+ * THE AUTHORITY ON "THE MANUFACTURER SAID SO". Every other relationship lens in
6
+ * this SDK is inference: `pairings` reads order and board activity, `search
7
+ * ({ similarTo })` reads appearance. These two reads are the only ones carrying
8
+ * a compatibility the manufacturer themselves enumerated — this fabric is
9
+ * genuinely available on this chair — and the shapes deliberately share no field
10
+ * with {@link PairingItem}, so the two can never be concatenated into one list.
11
+ * Present them as separate sections, and never describe a pairing as available.
12
+ */
13
+ import type { Cursor, VariantId } from "./identifiers.js";
14
+ /**
15
+ * Why an association is believed.
16
+ *
17
+ * Every field is ALWAYS present. `confidence` is a FIXED convention value (0.9)
18
+ * that the ingest writes on every manufacturer-declared edge — it is not a
19
+ * measurement, it does not vary between edges, and it must never be shown as a
20
+ * score or used to rank. Declared associations carry no ranking at all: that is
21
+ * the point of them.
22
+ */
23
+ export interface DeclaredProvenance {
24
+ /** The stored convention value (0.9 today). Never a per-edge measurement. */
25
+ confidence: number;
26
+ /** The manufacturer enumerated this association in their own catalog data. */
27
+ basis: "manufacturer_declared";
28
+ /** The provenance recorded on the edge — the manufacturer catalog feed. */
29
+ source: "warehouse";
30
+ }
31
+ /** The material the question was asked about, resolved to its identity. */
32
+ export interface DeclaredAssociationSubject {
33
+ variantId: VariantId;
34
+ brand: string;
35
+ product: string;
36
+ /** The variant's own name; null when it is unnamed. */
37
+ variantName: string | null;
38
+ }
39
+ /** One declared association, plus why it is believed. */
40
+ export interface DeclaredAssociation {
41
+ variantId: VariantId;
42
+ brand: string;
43
+ product: string;
44
+ variantName: string | null;
45
+ /** Canonical thumbnail; null when the variant has no eligible image. */
46
+ imageUrl: string | null;
47
+ declared: DeclaredProvenance;
48
+ }
49
+ /**
50
+ * One page of declared associations.
51
+ *
52
+ * READ `totalAssociations`, NOT `associations.length`, when rendering a count
53
+ * like "Compatible Finishes (128)": the array is one page (up to 48), the total
54
+ * is the whole set for this subject, direction and corpus. A page is not a
55
+ * corpus — walk `nextCursor` before making any claim about the full set.
56
+ */
57
+ export interface DeclaredAssociationsResponse {
58
+ subject: DeclaredAssociationSubject;
59
+ /**
60
+ * Constant discriminator. Its presence is how a caller tells a declared fact
61
+ * from the inferred `/pairings` rail — check it before merging anything.
62
+ */
63
+ kind: "declared_association";
64
+ associations: DeclaredAssociation[];
65
+ /** The TRUE count for this subject, direction and corpus. Not the page size. */
66
+ totalAssociations: number;
67
+ /** How many associations this page carries. */
68
+ returnedCount: number;
69
+ /** Present only when another page exists; pass back verbatim as `cursor`. */
70
+ nextCursor?: Cursor;
71
+ }
72
+ /** Options for a declared-association read. */
73
+ export interface AssociatedOptions {
74
+ /**
75
+ * Page size, 1–48 inclusive (default 24). The server rejects (400), not
76
+ * clamps, values outside this range.
77
+ */
78
+ limit?: number;
79
+ /**
80
+ * Orderability scope. `material_bank` returns only associations pointing at a
81
+ * variant carrying a Material Bank SKU — the ones a caller can actually
82
+ * order. Today every visible variant carries one, so this currently removes
83
+ * nothing; it tracks orderability rather than a snapshot of it.
84
+ */
85
+ corpus?: "all" | "material_bank";
86
+ /**
87
+ * The previous response's `nextCursor`, passed back verbatim. A cursor is
88
+ * bound to its subject, direction and corpus — replaying it against a
89
+ * different one is a 400, never a silently different page.
90
+ */
91
+ cursor?: Cursor;
92
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The DECLARED-association lens: `GET /api/v1/associated/finishes` and
3
+ * `GET /api/v1/associated/products`, as thin wrappers over the shared
4
+ * {@link HttpTransport}. `MaterialGraphClient.associatedFinishes` /
5
+ * `.associatedProducts` delegate here; the contract lives in this file.
6
+ *
7
+ * DECLARED IS NOT INFERRED — the rule this whole lens exists to make
8
+ * unmissable. These two reads return compatibility the MANUFACTURER stated
9
+ * ("this fabric is available on this chair"); `pairings()` returns
10
+ * companionship the graph INFERRED from orders, boards and colour. The two
11
+ * response shapes share no field, on purpose: render them as separate
12
+ * sections, never concatenate them, and never describe a pairing as
13
+ * "available on".
14
+ */
15
+ import type { AssociatedOptions, DeclaredAssociationsResponse } from "./association-types.js";
16
+ import { HttpTransport } from "./http-transport.js";
17
+ import type { VariantId } from "./identifiers.js";
18
+ /**
19
+ * THE QUESTION: "what finishes can I get on THIS chair?" — the fabrics,
20
+ * leathers and surfaces the MANUFACTURER declares are available on a product
21
+ * variant (GET `/api/v1/associated/finishes`).
22
+ *
23
+ * Instead use `pairings()` for what merely GOES WITH something (inference from
24
+ * orders, boards and colour) and `siblings()` for the same product in another
25
+ * colourway. These are the only relationships the manufacturer themselves
26
+ * stated: `kind` is always `"declared_association"`, and the shape shares no
27
+ * field with a pairing — see the module docblock for why that matters.
28
+ *
29
+ * Every association carries `declared` in full: `basis`, `source`, and a
30
+ * `confidence` that is a FIXED 0.9 convention, not a measurement — never rank
31
+ * by it or render it as a score. Render counts from `totalAssociations` (the
32
+ * true total), never `associations.length` (one page, up to 48).
33
+ *
34
+ * @param variantId The PRODUCT variant. Opaque — pass back exactly what you were given.
35
+ */
36
+ export declare function declaredFinishes(transport: HttpTransport, variantId: VariantId, options?: AssociatedOptions, signal?: AbortSignal): Promise<DeclaredAssociationsResponse>;
37
+ /**
38
+ * THE QUESTION: "what furniture takes THIS fabric?" — the products the
39
+ * MANUFACTURER declares a finish variant is available on (GET
40
+ * `/api/v1/associated/products`).
41
+ *
42
+ * The exact reverse of {@link declaredFinishes} over one stored relationship,
43
+ * so the two directions can never disagree. Everything in that function's
44
+ * contract applies here: declared, never inferred; `declared` is always
45
+ * present and its `confidence` is a fixed 0.9 convention; read
46
+ * `totalAssociations` for a count and walk `nextCursor` before claiming
47
+ * anything about the whole set.
48
+ *
49
+ * @param variantId The FINISH variant. Opaque — pass back exactly what you were given.
50
+ */
51
+ export declare function declaredProducts(transport: HttpTransport, variantId: VariantId, options?: AssociatedOptions, signal?: AbortSignal): Promise<DeclaredAssociationsResponse>;
@@ -0,0 +1,40 @@
1
+ import { HttpTransport } from "./http-transport.js";
2
+ import { resolveOperation } from "./operations.js";
3
+ /**
4
+ * THE QUESTION: "what finishes can I get on THIS chair?" — the fabrics,
5
+ * leathers and surfaces the MANUFACTURER declares are available on a product
6
+ * variant (GET `/api/v1/associated/finishes`).
7
+ *
8
+ * Instead use `pairings()` for what merely GOES WITH something (inference from
9
+ * orders, boards and colour) and `siblings()` for the same product in another
10
+ * colourway. These are the only relationships the manufacturer themselves
11
+ * stated: `kind` is always `"declared_association"`, and the shape shares no
12
+ * field with a pairing — see the module docblock for why that matters.
13
+ *
14
+ * Every association carries `declared` in full: `basis`, `source`, and a
15
+ * `confidence` that is a FIXED 0.9 convention, not a measurement — never rank
16
+ * by it or render it as a score. Render counts from `totalAssociations` (the
17
+ * true total), never `associations.length` (one page, up to 48).
18
+ *
19
+ * @param variantId The PRODUCT variant. Opaque — pass back exactly what you were given.
20
+ */
21
+ export function declaredFinishes(transport, variantId, options, signal) {
22
+ return transport.requestJson(resolveOperation("list_associated_finishes"), { query: { variantId, ...options }, signal });
23
+ }
24
+ /**
25
+ * THE QUESTION: "what furniture takes THIS fabric?" — the products the
26
+ * MANUFACTURER declares a finish variant is available on (GET
27
+ * `/api/v1/associated/products`).
28
+ *
29
+ * The exact reverse of {@link declaredFinishes} over one stored relationship,
30
+ * so the two directions can never disagree. Everything in that function's
31
+ * contract applies here: declared, never inferred; `declared` is always
32
+ * present and its `confidence` is a fixed 0.9 convention; read
33
+ * `totalAssociations` for a count and walk `nextCursor` before claiming
34
+ * anything about the whole set.
35
+ *
36
+ * @param variantId The FINISH variant. Opaque — pass back exactly what you were given.
37
+ */
38
+ export function declaredProducts(transport, variantId, options, signal) {
39
+ return transport.requestJson(resolveOperation("list_associated_products"), { query: { variantId, ...options }, signal });
40
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Wire types for the single-colour lane — GET `/api/v1/search/colours`.
3
+ *
4
+ * Kept out of `types.ts` for the same reason `palette-search-types.ts` and
5
+ * `colour-aggregate-types.ts` are: the shared mirror is at its source-size
6
+ * budget. Same tolerant-parsing contract as the rest of the mirror — response
7
+ * enums are widened so a new server value still type-checks.
8
+ */
9
+ import type { VariantId } from "./identifiers.js";
10
+ import type { OklabTriple, PaletteSwatch } from "./types.js";
11
+ /** Coarse group + fine type of a colour match's primary product type. */
12
+ export interface MatchCategory {
13
+ group: string | null;
14
+ type: string | null;
15
+ }
16
+ /**
17
+ * One colour-palette match.
18
+ *
19
+ * `hex`, `distance` and `closeness` are OKLab geometry over colour EXTRACTED
20
+ * FROM PRODUCT IMAGERY, not declared by the manufacturer. Report it as visual
21
+ * likeness ("reads close to this colour"), never as a stated colour
22
+ * specification.
23
+ */
24
+ export interface ByColourMatch {
25
+ /** Opaque — round-trip verbatim. */
26
+ variantId: VariantId;
27
+ brand: string;
28
+ product: string;
29
+ color: string | null;
30
+ variantName: string | null;
31
+ imageUrl: string | null;
32
+ hex: string | null;
33
+ distance: number;
34
+ closeness: number;
35
+ category: MatchCategory | null;
36
+ oklab: OklabTriple | null;
37
+ /**
38
+ * The match's own image palette — the FULL stored palette, uncapped and in
39
+ * stored order, each swatch with `oklab` (see {@link ByColourSwatch}), so a
40
+ * card can render, sort or group colours without a second call. A one-swatch
41
+ * palette means the extractor found one colour (solid-colour materials are
42
+ * genuinely monochrome), not that the list was trimmed. ABSENT when no palette
43
+ * is on record: absent means unknown, NOT that the material has no colours.
44
+ * Like `hex`, these swatches come from product imagery, not from the
45
+ * manufacturer.
46
+ */
47
+ palette?: ByColourSwatch[];
48
+ }
49
+ /** A {@link PaletteSwatch} on the colour lane, where `oklab` is always derived. */
50
+ export interface ByColourSwatch extends PaletteSwatch {
51
+ /** `hex` in OKLab — guaranteed present on this surface. */
52
+ oklab: OklabTriple;
53
+ }
54
+ /**
55
+ * Receipt for the `family` filter — ABSENT when none was asked for, so absence
56
+ * means "no gate ran", never "a gate that dropped nothing".
57
+ */
58
+ export interface ColourFamilyGate {
59
+ /** The colorscope taxonomy family id the gate kept (normalised). */
60
+ family: string;
61
+ /** Candidates the gate examined — the retrieved pool. NOT a corpus total. */
62
+ considered: number;
63
+ /**
64
+ * Candidates the gate rejected — another family, or no readable hex (unknown
65
+ * is not membership). `considered − matches.length` is not the same number
66
+ * once the page is cut to `limit`; this is the gate's own cost.
67
+ */
68
+ dropped: number;
69
+ }
70
+ /**
71
+ * Receipt for the colour-identity collapse that turns a variant-grain ranking
72
+ * into a page of TILES — one per (brand, product, dominant hex), one per stored
73
+ * photograph. The live endpoint always sends it; it is optional here only so
74
+ * responses captured before the collapse existed still type-check.
75
+ */
76
+ export interface ColourCollapse {
77
+ /** Ranked rows the collapse examined — this page's pool, never a corpus total. */
78
+ considered: number;
79
+ /** Rows dropped for repeating a better-ranked row's brand, product and hex. */
80
+ droppedByIdentity: number;
81
+ /** Rows dropped for resolving to a better-ranked row's stored photograph. */
82
+ droppedBySharedImage: number;
83
+ }
84
+ /** The `search/colours` response. */
85
+ export interface ByColourResponse {
86
+ query: {
87
+ colours: string[];
88
+ categories?: string[];
89
+ /** Echoed `family` filter, normalised. Present only when one was applied. */
90
+ family?: string;
91
+ oklab?: OklabTriple;
92
+ };
93
+ /** `matches.length` — THIS PAGE. Not a corpus total; this surface states none. */
94
+ count: number;
95
+ matches: ByColourMatch[];
96
+ /**
97
+ * Present only when `family` was asked for. A gated page can be SHORTER than
98
+ * `limit` — the gate runs over a retrieved pool, not the corpus — and this is
99
+ * what says so in counts rather than adjectives.
100
+ */
101
+ familyGate?: ColourFamilyGate;
102
+ /**
103
+ * What the colour-identity collapse cost this page. Matches are TILES: a page
104
+ * can be SHORTER than `limit` when the pool collapsed harder than the
105
+ * overfetch allowed, and this says so in counts rather than adjectives.
106
+ */
107
+ collapse?: ColourCollapse;
108
+ }
109
+ /** Params for `search/colours`. */
110
+ export interface ByColourParams {
111
+ /** A single hex (with or without `#`). */
112
+ hex?: string;
113
+ /** A palette — repeated as one `colour` query entry each. */
114
+ colour?: string | string[];
115
+ /** OKLab triple (all-or-none). */
116
+ l?: number;
117
+ a?: number;
118
+ b?: number;
119
+ /** HARD category filter — display names or taxonomy ids (repeatable). */
120
+ category?: string | string[];
121
+ /**
122
+ * HARD colour-family filter — ONE colorscope taxonomy family id, from the
123
+ * closed 15-value vocabulary colorscope itself publishes (`white`, `cream`,
124
+ * `gray`, `black`, `beige`, `taupe`, `brown`, `red`, `orange`, `yellow`,
125
+ * `green`, `teal`, `blue`, `purple`, `pink`). Case-insensitive; anything else
126
+ * is a 400 listing the vocabulary.
127
+ *
128
+ * This is NOT the `colour_family` facet vocabulary from `colourFacets()`, and
129
+ * NOT a match's stored dominant-family term — those are colorscope's NAMING
130
+ * families, a different (13-term) list. The family here is assigned from the
131
+ * match's dominant hex by colorscope's taxonomy ownership rules.
132
+ *
133
+ * A filtered page may come back shorter than `limit`: the gate runs over a
134
+ * retrieved pool, and `familyGate` on the response reports that pool and the
135
+ * drop.
136
+ */
137
+ family?: string;
138
+ limit?: number;
139
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Construction-time configuration for {@link MaterialGraphClient} and the two
3
+ * published constants callers read off it. Split out of `client.ts` so the
4
+ * client file carries lenses rather than setup.
5
+ */
6
+ /** The hosted API origin the client talks to unless `baseUrl` overrides it. */
7
+ export declare const DEFAULT_BASE_URL = "https://beta.materialgraph.com";
8
+ /** The server's per-call ceiling on batch material lookups, enforced client-side. */
9
+ export declare const MAX_BATCH_IDS = 100;
10
+ export interface MaterialGraphClientOptions {
11
+ /** A `mg_read_*` API key, presented as `Authorization: Bearer <key>`. */
12
+ apiKey: string;
13
+ /** Override the API origin. Defaults to {@link DEFAULT_BASE_URL}. */
14
+ baseUrl?: string;
15
+ /** Inject a custom `fetch` (tests, proxies). Defaults to the global `fetch`. */
16
+ fetch?: typeof fetch;
17
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Construction-time configuration for {@link MaterialGraphClient} and the two
3
+ * published constants callers read off it. Split out of `client.ts` so the
4
+ * client file carries lenses rather than setup.
5
+ */
6
+ /** The hosted API origin the client talks to unless `baseUrl` overrides it. */
7
+ export const DEFAULT_BASE_URL = "https://beta.materialgraph.com";
8
+ /** The server's per-call ceiling on batch material lookups, enforced client-side. */
9
+ export const MAX_BATCH_IDS = 100;
package/client.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { type RateLimit } from "./errors.js";
2
+ import type { MaterialGraphClientOptions } from "./client-config.js";
3
+ export * from "./client-config.js";
2
4
  import { type BrowseNamespace } from "./client-browse.js";
3
5
  export type { BrowseNamespace } from "./client-browse.js";
4
- import type { CapabilityManifestResponse, CreatePublicRunRequest, CreatePublicRunResponse, DrainRunEventsOptions, PublicRunResponse, ResumeRunOptions, RunEvent, SourceMaterialBatchDetail, SourceMaterialBatchReceipt, StreamRunEventsOptions, UploadSourceMaterialInput } from "./run-types.js";
6
+ import type { CapabilityManifestResponse, CreatePublicRunRequest, CreatePublicRunResponse, DrainRunEventsOptions, ListRunsOptions, PublicRunListResponse, PublicRunResponse, ResumeRunOptions, RunEvent, SourceMaterialBatchDetail, SourceMaterialBatchReceipt, StreamRunEventsOptions, UploadSourceMaterialInput } from "./run-types.js";
5
7
  import type { FinishScheduleResponse, GetRunScheduleOptions } from "./schedule-types.js";
6
8
  import type { MatchStreamEvent } from "./match-stream-types.js";
7
9
  import type { AppendProjectEventRequest, CreateProjectRequest, ProjectEventReceipt, ProjectListResponse, ProjectState } from "./project-types.js";
@@ -9,22 +11,15 @@ import type { VarySchemesRequest, VarySchemesResponse } from "./composition-type
9
11
  import { CodesClient } from "./codes-client.js";
10
12
  import { ColoursClient } from "./colours-client.js";
11
13
  import type { CodeAdvisoryInput, CodeAdvisoryResponse } from "./codes-types.js";
14
+ import type { AssociatedOptions } from "./association-types.js";
12
15
  import type { BrandCatalogListPage, BrandsOptions } from "./brand-types.js";
13
16
  import type { MaterialAssetProjection } from "./material-asset-types.js";
14
17
  import type { MaterialBlockProjection } from "./material-block-types.js";
15
18
  import type { SchemaResource, SchemaResourceResponse } from "./schema-resource-types.js";
16
19
  import type { BatchId, ProjectId, RunId, VariantId } from "./identifiers.js";
17
- import type { ApplicableSpecsResponse, BrandOptions, BrandProfileResponse, ByColourParams, ByColourResponse, BySpecOptions, BySpecResponse, ColourFacetsResponse, CoordinateInput, CoordinateResponse, DemandBatchInput, DemandBatchResponse, DemandParams, DemandResponse, ExtractPaletteInput, ExtractPaletteResponse, GlossaryTermResponse, MaterialResponse, MaterialsBatchDetailResponse, MaterialsBatchGetOptions, MaterialsBatchOptions, MaterialsBatchTileResponse, MatchInput, MatchResponse, PairingsOptions, PairingsResponse, ResolveAttributeOptions, ResolveAttributeResponse, ResolveAttributeValueOptions, ResolveAttributeValueResponse, ResolveColourResponse, SearchGetParams, SearchInput, SearchResponse, SiblingsResponse, SpecFacetsDistributionResponse, SpecFacetsListResponse, SuggestColourResponse, SuggestOptions, SuggestResponse, TaxonomyResponse, VocabularySearchOptions, VocabularySearchResponse, WhoamiResponse } from "./types.js";
18
- export declare const DEFAULT_BASE_URL = "https://beta.materialgraph.com";
19
- export declare const MAX_BATCH_IDS = 100;
20
- export interface MaterialGraphClientOptions {
21
- /** A `mg_read_*` API key, presented as `Authorization: Bearer <key>`. */
22
- apiKey: string;
23
- /** Override the API origin. Defaults to {@link DEFAULT_BASE_URL}. */
24
- baseUrl?: string;
25
- /** Inject a custom `fetch` (tests, proxies). Defaults to the global `fetch`. */
26
- fetch?: typeof fetch;
27
- }
20
+ import type { ByColourParams, ByColourResponse } from "./by-colour-types.js";
21
+ import type { ApplicableSpecsResponse, BrandOptions, BrandProfileResponse, BySpecOptions, BySpecResponse, ColourFacetsResponse, CoordinateInput, CoordinateResponse, DemandBatchInput, DemandBatchResponse, DemandParams, DemandResponse, ExtractPaletteInput, ExtractPaletteResponse, GlossaryTermResponse, MaterialResponse, MaterialsBatchDetailResponse, MaterialsBatchGetOptions, MaterialsBatchOptions, MaterialsBatchTileResponse, PairingsOptions, PairingsResponse, ResolveAttributeOptions, ResolveAttributeResponse, ResolveAttributeValueOptions, ResolveAttributeValueResponse, ResolveColourResponse, SearchGetParams, SearchInput, SearchResponse, SiblingsResponse, SpecFacetsDistributionResponse, SpecFacetsListResponse, SuggestColourResponse, SuggestOptions, SuggestResponse, TaxonomyResponse, VocabularySearchOptions, WhoamiResponse } from "./types.js";
22
+ import type { MatchInput, MatchResponse } from "./match-types.js";
28
23
  /**
29
24
  * The MaterialGraph client — every lens on the graph, on one object.
30
25
  *
@@ -38,18 +33,25 @@ export interface MaterialGraphClientOptions {
38
33
  * | "which materials, from anyone, meet this hard spec?" | {@link MaterialGraphClient.bySpec} |
39
34
  * | "what's near this colour / this palette / this gradient?" | {@link MaterialGraphClient.byColour}, `colours.palette()`, `colours.gradient()` |
40
35
  * | "what goes with THIS material?" | {@link MaterialGraphClient.pairings}, {@link MaterialGraphClient.siblings}, {@link MaterialGraphClient.coordinate} |
36
+ * | "what does the manufacturer say is AVAILABLE on this?" | {@link MaterialGraphClient.associatedFinishes}, {@link MaterialGraphClient.associatedProducts} |
41
37
  * | "how much is this specified?" | {@link MaterialGraphClient.demand}, {@link MaterialGraphClient.demandBatch} |
42
38
  * | "is this allowed in an A-2 corridor?" | `codes.*` |
43
39
  * | "what can I even ask for?" | {@link MaterialGraphClient.specificationFacets}, {@link MaterialGraphClient.attributeValues}, {@link MaterialGraphClient.taxonomy}, {@link MaterialGraphClient.colourFacets}, {@link MaterialGraphClient.glossary} |
40
+ * | "I have words — what do they mean in YOUR vocabulary?" | {@link MaterialGraphClient.resolve} |
44
41
  * | "do the whole job for me, async" | {@link MaterialGraphClient.createRun} + `mg:compose` |
45
42
  *
46
- * Two structural rules that fall out of that table:
43
+ * Three structural rules that fall out of that table:
47
44
  * - **Colour is a separate lens.** {@link MaterialGraphClient.search} matches
48
45
  * WORDS. A colour word in `text` finds materials whose text mentions it, not
49
46
  * materials that look it. Route visual intent to the colour lenses.
50
47
  * - **Relationships hang off a material, not off a query.** You cannot ask "what
51
48
  * pairs with beige oak"; you resolve a material first, then ask what pairs
52
49
  * with THAT `variantId`.
50
+ * - **Declared is not inferred.** `associatedFinishes()` /
51
+ * `associatedProducts()` return compatibility the MANUFACTURER stated;
52
+ * `pairings()` returns companionship the graph INFERRED. The response shapes
53
+ * share no field, on purpose — render them as separate sections and never
54
+ * concatenate them.
53
55
  *
54
56
  * And three reading rules that apply to every lens: ids are opaque (see
55
57
  * `./identifiers.ts`), absent is not false, and a page is not a corpus.
@@ -170,6 +172,8 @@ export declare class MaterialGraphClient {
170
172
  * {@link MaterialGraphClient.browse} to walk by lightness/vividness, and
171
173
  * {@link MaterialGraphClient.search} when colour is one signal among many.
172
174
  * Report matches as visual likeness, never a stated colour specification.
175
+ * `family` gates the answer to ONE colorscope colour family when nearness
176
+ * alone crosses a hue boundary; a gated page can be shorter than `limit`.
173
177
  */
174
178
  byColour(params: ByColourParams, signal?: AbortSignal): Promise<ByColourResponse>;
175
179
  /**
@@ -233,6 +237,10 @@ export declare class MaterialGraphClient {
233
237
  * you how much of the rail is real order evidence.
234
238
  */
235
239
  pairings(variantId: VariantId, options?: PairingsOptions, signal?: AbortSignal): Promise<PairingsResponse>;
240
+ /** "What finishes does the manufacturer declare for THIS product?" — {@link declaredFinishes}. */
241
+ associatedFinishes(variantId: VariantId, options?: AssociatedOptions, signal?: AbortSignal): Promise<import("./association-types.js").DeclaredAssociationsResponse>;
242
+ /** "What products does the manufacturer declare THIS finish for?" — {@link declaredProducts}. */
243
+ associatedProducts(variantId: VariantId, options?: AssociatedOptions, signal?: AbortSignal): Promise<import("./association-types.js").DeclaredAssociationsResponse>;
236
244
  /**
237
245
  * THE QUESTION: "hydrate these ids I already hold" (POST
238
246
  * `/api/v1/materials/batch`, ≤ 100 ids) — never a discovery call. Throws
@@ -316,15 +324,10 @@ export declare class MaterialGraphClient {
316
324
  * not invent it.
317
325
  */
318
326
  resolveAttributeValue(attributeId: string, query: string, options?: ResolveAttributeValueOptions, signal?: AbortSignal): Promise<ResolveAttributeValueResponse>;
319
- /**
320
- * THE QUESTION: "is 'FloorScore' an attribute or a value, and what is it called
321
- * here?" The catch-all when you do not know which half of an `attribute:value`
322
- * pair a phrase belongs to. Instead use
323
- * {@link MaterialGraphClient.resolveAttribute} /
324
- * {@link MaterialGraphClient.resolveAttributeValue} once you do, and
325
- * {@link MaterialGraphClient.glossary} to EXPLAIN a term rather than find it.
326
- */
327
- searchVocabulary(query: string, options?: VocabularySearchOptions, signal?: AbortSignal): Promise<VocabularySearchResponse>;
327
+ /** "Is this phrase an attribute or a value?" — see {@link searchVocabularyRequest}. */
328
+ searchVocabulary(query: string, options?: VocabularySearchOptions, signal?: AbortSignal): Promise<import("./catalog-discovery-types.js").VocabularySearchResponse>;
329
+ /** "I have words — what do they mean in YOUR vocabulary?" — see {@link resolvePhrasesRequest}. */
330
+ resolve(phrases: string[], signal?: AbortSignal): Promise<import("./resolve-types.js").ResolveResponse>;
328
331
  /**
329
332
  * THE QUESTION: "which materials, from ANYONE, are ASTM E84 Class A AND
330
333
  * GREENGUARD-certified?" — hard specifications, no ranking, every brand at once
@@ -531,6 +534,16 @@ export declare class MaterialGraphClient {
531
534
  * asking you something (see {@link MaterialGraphClient.resumeRun}).
532
535
  */
533
536
  run(runId: RunId, signal?: AbortSignal): Promise<PublicRunResponse>;
537
+ /**
538
+ * THE QUESTION: "which Runs are mine?" — your own Runs, newest first, paged
539
+ * (GET `/api/v1/runs`). This is how a client that lost a Run id finds its work
540
+ * again: `{ status: "suspended" }` returns the Runs still waiting on an
541
+ * answer, whose ids you can then feed to {@link MaterialGraphClient.resumeRun}.
542
+ *
543
+ * Only your own Runs are ever returned. Page by following `nextCursor` until
544
+ * it is ABSENT — a full page is not the signal to stop.
545
+ */
546
+ listRuns(options?: ListRunsOptions): Promise<PublicRunListResponse>;
534
547
  /**
535
548
  * Export a completed compose Run's scheme as a finish schedule — one row per
536
549
  * slot, every attribute value carrying its source and verification state
package/client.js CHANGED
@@ -1,33 +1,20 @@
1
1
  import { MaterialGraphError } from "./errors.js";
2
2
  import { HttpTransport } from "./http-transport.js";
3
- import { findSdkOperation } from "./operations.js";
4
- import { cancelRun, capabilities as capabilitiesRun, createRun, drainRunEvents, getRun, getRunSchedule, getSourceMaterialBatch as getSourceMaterialBatchCall, resumeRun, streamRunEvents, uploadSourceMaterial, } from "./runs.js";
3
+ // Aliased to `operation` — the local name every call site here already used.
4
+ import { resolveOperation as operation } from "./operations.js";
5
+ import { cancelRun, capabilities as capabilitiesRun, createRun, drainRunEvents, getRun, getRunSchedule, getSourceMaterialBatch as getSourceMaterialBatchCall, listRuns, resumeRun, streamRunEvents, uploadSourceMaterial, } from "./runs.js";
6
+ import { DEFAULT_BASE_URL, MAX_BATCH_IDS } from "./client-config.js";
7
+ // Re-exported whole so `index.ts` and every existing importer keep their path.
8
+ export * from "./client-config.js";
5
9
  import { browseQuery, createBrowseNamespace } from "./client-browse.js";
6
10
  import { suggestQuery } from "./suggest-types.js";
7
11
  import { matchStream as matchStreamCall } from "./match-stream.js";
8
12
  import { appendProjectEvent as appendProjectEventCall, createProject as createProjectCall, getProject as getProjectCall, listProjects as listProjectsCall, } from "./projects.js";
9
13
  import { CodesClient } from "./codes-client.js";
10
14
  import { ColoursClient } from "./colours-client.js";
11
- export const DEFAULT_BASE_URL = "https://beta.materialgraph.com";
12
- export const MAX_BATCH_IDS = 100;
13
- const SCHEMA_RESOURCE_OPERATION_IDS = {
14
- blocks: "list_schema_blocks",
15
- attributes: "list_schema_attributes",
16
- verticals: "list_schema_verticals",
17
- productTypes: "list_schema_product_types",
18
- units: "list_schema_units",
19
- valueDictionaries: "list_schema_value_dictionaries",
20
- sourceRequirements: "list_schema_source_requirements",
21
- productTypeTemplates: "list_schema_product_type_templates",
22
- mappings: "list_schema_mappings",
23
- };
24
- function operation(operationId) {
25
- const match = findSdkOperation(operationId);
26
- if (!match) {
27
- throw MaterialGraphError.unexpected(`Unknown SDK operation: ${operationId}`);
28
- }
29
- return match;
30
- }
15
+ import { resolvePhrasesRequest, searchVocabularyRequest } from "./resolve-client.js";
16
+ import { declaredFinishes, declaredProducts } from "./associations-client.js";
17
+ import { SCHEMA_RESOURCE_OPERATION_IDS } from "./schema-resource-types.js";
31
18
  /**
32
19
  * The MaterialGraph client — every lens on the graph, on one object.
33
20
  *
@@ -41,18 +28,25 @@ function operation(operationId) {
41
28
  * | "which materials, from anyone, meet this hard spec?" | {@link MaterialGraphClient.bySpec} |
42
29
  * | "what's near this colour / this palette / this gradient?" | {@link MaterialGraphClient.byColour}, `colours.palette()`, `colours.gradient()` |
43
30
  * | "what goes with THIS material?" | {@link MaterialGraphClient.pairings}, {@link MaterialGraphClient.siblings}, {@link MaterialGraphClient.coordinate} |
31
+ * | "what does the manufacturer say is AVAILABLE on this?" | {@link MaterialGraphClient.associatedFinishes}, {@link MaterialGraphClient.associatedProducts} |
44
32
  * | "how much is this specified?" | {@link MaterialGraphClient.demand}, {@link MaterialGraphClient.demandBatch} |
45
33
  * | "is this allowed in an A-2 corridor?" | `codes.*` |
46
34
  * | "what can I even ask for?" | {@link MaterialGraphClient.specificationFacets}, {@link MaterialGraphClient.attributeValues}, {@link MaterialGraphClient.taxonomy}, {@link MaterialGraphClient.colourFacets}, {@link MaterialGraphClient.glossary} |
35
+ * | "I have words — what do they mean in YOUR vocabulary?" | {@link MaterialGraphClient.resolve} |
47
36
  * | "do the whole job for me, async" | {@link MaterialGraphClient.createRun} + `mg:compose` |
48
37
  *
49
- * Two structural rules that fall out of that table:
38
+ * Three structural rules that fall out of that table:
50
39
  * - **Colour is a separate lens.** {@link MaterialGraphClient.search} matches
51
40
  * WORDS. A colour word in `text` finds materials whose text mentions it, not
52
41
  * materials that look it. Route visual intent to the colour lenses.
53
42
  * - **Relationships hang off a material, not off a query.** You cannot ask "what
54
43
  * pairs with beige oak"; you resolve a material first, then ask what pairs
55
44
  * with THAT `variantId`.
45
+ * - **Declared is not inferred.** `associatedFinishes()` /
46
+ * `associatedProducts()` return compatibility the MANUFACTURER stated;
47
+ * `pairings()` returns companionship the graph INFERRED. The response shapes
48
+ * share no field, on purpose — render them as separate sections and never
49
+ * concatenate them.
56
50
  *
57
51
  * And three reading rules that apply to every lens: ids are opaque (see
58
52
  * `./identifiers.ts`), absent is not false, and a page is not a corpus.
@@ -208,6 +202,8 @@ export class MaterialGraphClient {
208
202
  * {@link MaterialGraphClient.browse} to walk by lightness/vividness, and
209
203
  * {@link MaterialGraphClient.search} when colour is one signal among many.
210
204
  * Report matches as visual likeness, never a stated colour specification.
205
+ * `family` gates the answer to ONE colorscope colour family when nearness
206
+ * alone crosses a hue boundary; a gated page can be shorter than `limit`.
211
207
  */
212
208
  byColour(params, signal) {
213
209
  return this.#get("search_by_colour", {
@@ -217,6 +213,7 @@ export class MaterialGraphClient {
217
213
  a: params.a,
218
214
  b: params.b,
219
215
  category: params.category,
216
+ family: params.family,
220
217
  limit: params.limit,
221
218
  }, signal);
222
219
  }
@@ -298,6 +295,14 @@ export class MaterialGraphClient {
298
295
  variantId,
299
296
  });
300
297
  }
298
+ /** "What finishes does the manufacturer declare for THIS product?" — {@link declaredFinishes}. */
299
+ associatedFinishes(variantId, options, signal) {
300
+ return declaredFinishes(this.#transport, variantId, options, signal);
301
+ }
302
+ /** "What products does the manufacturer declare THIS finish for?" — {@link declaredProducts}. */
303
+ associatedProducts(variantId, options, signal) {
304
+ return declaredProducts(this.#transport, variantId, options, signal);
305
+ }
301
306
  materials(ids, options, signal) {
302
307
  if (ids.length > MAX_BATCH_IDS) {
303
308
  throw MaterialGraphError.unexpected(`MaterialGraphClient.materials(): at most ${MAX_BATCH_IDS} ids per call, received ${ids.length}. Split into multiple batches.`);
@@ -379,16 +384,13 @@ export class MaterialGraphClient {
379
384
  resolveAttributeValue(attributeId, query, options, signal) {
380
385
  return this.#get("catalog_resolve_value", { q: query, limit: options?.limit }, signal, { attributeId });
381
386
  }
382
- /**
383
- * THE QUESTION: "is 'FloorScore' an attribute or a value, and what is it called
384
- * here?" The catch-all when you do not know which half of an `attribute:value`
385
- * pair a phrase belongs to. Instead use
386
- * {@link MaterialGraphClient.resolveAttribute} /
387
- * {@link MaterialGraphClient.resolveAttributeValue} once you do, and
388
- * {@link MaterialGraphClient.glossary} to EXPLAIN a term rather than find it.
389
- */
387
+ /** "Is this phrase an attribute or a value?" — see {@link searchVocabularyRequest}. */
390
388
  searchVocabulary(query, options, signal) {
391
- return this.#get("catalog_vocabulary_search", { q: query, limit: options?.limit, kind: options?.kind }, signal);
389
+ return searchVocabularyRequest(this.#transport, query, options, signal);
390
+ }
391
+ /** "I have words — what do they mean in YOUR vocabulary?" — see {@link resolvePhrasesRequest}. */
392
+ resolve(phrases, signal) {
393
+ return resolvePhrasesRequest(this.#transport, phrases, signal);
392
394
  }
393
395
  /**
394
396
  * THE QUESTION: "which materials, from ANYONE, are ASTM E84 Class A AND
@@ -645,6 +647,18 @@ export class MaterialGraphClient {
645
647
  run(runId, signal) {
646
648
  return getRun(this.#transport, runId, signal);
647
649
  }
650
+ /**
651
+ * THE QUESTION: "which Runs are mine?" — your own Runs, newest first, paged
652
+ * (GET `/api/v1/runs`). This is how a client that lost a Run id finds its work
653
+ * again: `{ status: "suspended" }` returns the Runs still waiting on an
654
+ * answer, whose ids you can then feed to {@link MaterialGraphClient.resumeRun}.
655
+ *
656
+ * Only your own Runs are ever returned. Page by following `nextCursor` until
657
+ * it is ABSENT — a full page is not the signal to stop.
658
+ */
659
+ listRuns(options) {
660
+ return listRuns(this.#transport, options);
661
+ }
648
662
  /**
649
663
  * Export a completed compose Run's scheme as a finish schedule — one row per
650
664
  * slot, every attribute value carrying its source and verification state