@instruments/materialgraph 0.7.0 → 0.8.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/association-types.d.ts +92 -0
- package/association-types.js +1 -0
- package/associations-client.d.ts +51 -0
- package/associations-client.js +40 -0
- package/by-colour-types.d.ts +119 -0
- package/by-colour-types.js +1 -0
- package/by-spec-types.d.ts +45 -0
- package/catalog-discovery-types.d.ts +30 -14
- package/client-config.d.ts +17 -0
- package/client-config.js +9 -0
- package/client.d.ts +38 -23
- package/client.js +50 -33
- package/composition-types.d.ts +31 -1
- package/index.d.ts +2 -0
- package/material-types.d.ts +8 -0
- package/operations.d.ts +7 -1
- package/operations.js +16 -0
- package/package.json +1 -1
- package/resolve-client.d.ts +71 -0
- package/resolve-client.js +51 -0
- package/resolve-types.d.ts +102 -0
- package/resolve-types.js +17 -0
- package/run-types.d.ts +25 -0
- package/runs.d.ts +15 -1
- package/runs.js +24 -0
- package/schema-resource-types.d.ts +17 -14
- package/schema-resource-types.js +17 -1
- package/search-result-validation.js +3 -0
- package/types.d.ts +42 -54
|
@@ -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,119 @@
|
|
|
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
|
+
/** The `search/colours` response. */
|
|
71
|
+
export interface ByColourResponse {
|
|
72
|
+
query: {
|
|
73
|
+
colours: string[];
|
|
74
|
+
categories?: string[];
|
|
75
|
+
/** Echoed `family` filter, normalised. Present only when one was applied. */
|
|
76
|
+
family?: string;
|
|
77
|
+
oklab?: OklabTriple;
|
|
78
|
+
};
|
|
79
|
+
/** `matches.length` — THIS PAGE. Not a corpus total; this surface states none. */
|
|
80
|
+
count: number;
|
|
81
|
+
matches: ByColourMatch[];
|
|
82
|
+
/**
|
|
83
|
+
* Present only when `family` was asked for. A gated page can be SHORTER than
|
|
84
|
+
* `limit` — the gate runs over a retrieved pool, not the corpus — and this is
|
|
85
|
+
* what says so in counts rather than adjectives.
|
|
86
|
+
*/
|
|
87
|
+
familyGate?: ColourFamilyGate;
|
|
88
|
+
}
|
|
89
|
+
/** Params for `search/colours`. */
|
|
90
|
+
export interface ByColourParams {
|
|
91
|
+
/** A single hex (with or without `#`). */
|
|
92
|
+
hex?: string;
|
|
93
|
+
/** A palette — repeated as one `colour` query entry each. */
|
|
94
|
+
colour?: string | string[];
|
|
95
|
+
/** OKLab triple (all-or-none). */
|
|
96
|
+
l?: number;
|
|
97
|
+
a?: number;
|
|
98
|
+
b?: number;
|
|
99
|
+
/** HARD category filter — display names or taxonomy ids (repeatable). */
|
|
100
|
+
category?: string | string[];
|
|
101
|
+
/**
|
|
102
|
+
* HARD colour-family filter — ONE colorscope taxonomy family id, from the
|
|
103
|
+
* closed 15-value vocabulary colorscope itself publishes (`white`, `cream`,
|
|
104
|
+
* `gray`, `black`, `beige`, `taupe`, `brown`, `red`, `orange`, `yellow`,
|
|
105
|
+
* `green`, `teal`, `blue`, `purple`, `pink`). Case-insensitive; anything else
|
|
106
|
+
* is a 400 listing the vocabulary.
|
|
107
|
+
*
|
|
108
|
+
* This is NOT the `colour_family` facet vocabulary from `colourFacets()`, and
|
|
109
|
+
* NOT a match's stored dominant-family term — those are colorscope's NAMING
|
|
110
|
+
* families, a different (13-term) list. The family here is assigned from the
|
|
111
|
+
* match's dominant hex by colorscope's taxonomy ownership rules.
|
|
112
|
+
*
|
|
113
|
+
* A filtered page may come back shorter than `limit`: the gate runs over a
|
|
114
|
+
* retrieved pool, and `familyGate` on the response reports that pool and the
|
|
115
|
+
* drop.
|
|
116
|
+
*/
|
|
117
|
+
family?: string;
|
|
118
|
+
limit?: number;
|
|
119
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/by-spec-types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ValueResolution } from "./catalog-discovery-types.js";
|
|
1
2
|
import type { VariantId } from "./identifiers.js";
|
|
2
3
|
/**
|
|
3
4
|
* One criterion a match satisfied, with the trust behind it.
|
|
@@ -107,6 +108,32 @@ export interface BySpecMatch {
|
|
|
107
108
|
minConfidence: number | null;
|
|
108
109
|
verdict: BySpecVerdict;
|
|
109
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* One criterion the server could not resolve to canonical vocabulary, with the
|
|
113
|
+
* resolver's ranked suggestions for it.
|
|
114
|
+
*
|
|
115
|
+
* `attributeId` and `value` echo the parsed criterion, both of them, so a failed
|
|
116
|
+
* query can be restated without losing either half. (The HTTP route trims the
|
|
117
|
+
* attribute and value around the first `:` before validation.) Which HALF failed
|
|
118
|
+
* is read off `resolution.query`, the token actually resolved:
|
|
119
|
+
*
|
|
120
|
+
* - `resolution.query === attributeId` — the attribute id is unknown, and
|
|
121
|
+
* `resolution.candidates` are ATTRIBUTE ids.
|
|
122
|
+
* - `resolution.query === value` — the attribute is known but its value is no
|
|
123
|
+
* canonical term, and `resolution.candidates` are TERM ids, each carrying the
|
|
124
|
+
* `dictionaryId` it came from.
|
|
125
|
+
*
|
|
126
|
+
* EVERY miss is reported, not just the first, so one round trip is enough to fix
|
|
127
|
+
* a whole spec.
|
|
128
|
+
*/
|
|
129
|
+
export interface BySpecUnresolvedCriterion {
|
|
130
|
+
/** The parsed attribute id, after the route's surrounding-whitespace trim. */
|
|
131
|
+
attributeId: string;
|
|
132
|
+
/** The parsed value — never overwritten by the attribute id when that id is unknown. */
|
|
133
|
+
value: string;
|
|
134
|
+
/** Ranked candidates for whichever half failed. See {@link ValueResolution}. */
|
|
135
|
+
resolution: ValueResolution;
|
|
136
|
+
}
|
|
110
137
|
/**
|
|
111
138
|
* The find-by-spec envelope — A PAGE, AND NOTHING BUT A PAGE.
|
|
112
139
|
*
|
|
@@ -116,6 +143,9 @@ export interface BySpecMatch {
|
|
|
116
143
|
* states NO corpus total, so "N materials meet this specification" is not a
|
|
117
144
|
* claim you can make from one response. Raise `limit`, or say "at least N".
|
|
118
145
|
* `brandsSpanned` is likewise the brand count OF THIS SAMPLE.
|
|
146
|
+
*
|
|
147
|
+
* And check `unresolvedCriteria` BEFORE reading any of that: when it is present
|
|
148
|
+
* the numbers beside it describe a query that never ran.
|
|
119
149
|
*/
|
|
120
150
|
export interface BySpecResponse {
|
|
121
151
|
/** The criteria as parsed — check them before trusting a surprising result. */
|
|
@@ -129,4 +159,19 @@ export interface BySpecResponse {
|
|
|
129
159
|
brandsSpanned: number;
|
|
130
160
|
/** The sample, photo-first, capped by `limit`. */
|
|
131
161
|
matches: BySpecMatch[];
|
|
162
|
+
/**
|
|
163
|
+
* Present ONLY when a criterion failed exact canonical-vocabulary membership —
|
|
164
|
+
* and its presence means THE QUERY WAS NEVER RUN. A pre-flight gate short-circuits
|
|
165
|
+
* before the search, so the rest of the envelope is a placeholder rather than
|
|
166
|
+
* a finding: `matches` is `[]` and `totalMatches` is `0` because nothing was
|
|
167
|
+
* searched, not because nothing qualifies. Reporting that zero as "no material
|
|
168
|
+
* meets this specification" states a fact about the corpus on the strength of
|
|
169
|
+
* a question nobody asked it.
|
|
170
|
+
*
|
|
171
|
+
* Treat these criteria as UNAPPLIED and surface them: retry with a
|
|
172
|
+
* `resolution.candidates` id (subject to `recommendedAction` — `ambiguous`
|
|
173
|
+
* means ask, not pick), or say the value could not be resolved. Absent means
|
|
174
|
+
* every criterion resolved and the query did run.
|
|
175
|
+
*/
|
|
176
|
+
unresolvedCriteria?: BySpecUnresolvedCriterion[];
|
|
132
177
|
}
|
|
@@ -24,6 +24,34 @@ export interface ResolveValueCandidate {
|
|
|
24
24
|
/** Present only for a colour_family candidate resolved via the colorscope fallback. */
|
|
25
25
|
via?: OpenEnum<"colorscope">;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* The resolver's verdict on ONE free-text token — the single shape every
|
|
29
|
+
* resolving surface speaks, so a caller learns to read it once. Returned whole
|
|
30
|
+
* by {@link MaterialGraphClient.resolveAttributeValue}, and carried per bad
|
|
31
|
+
* criterion by `bySpec`'s `unresolvedCriteria`.
|
|
32
|
+
*
|
|
33
|
+
* READ `recommendedAction`, NOT JUST `best`. `ambiguous` with a populated
|
|
34
|
+
* `candidates` list means the phrase genuinely matched several terms — taking
|
|
35
|
+
* `best` silently there is how a query ends up answering a question nobody
|
|
36
|
+
* asked. `unresolved` means no candidate cleared the resolver's confidence
|
|
37
|
+
* threshold (and may still carry weak candidates); it says nothing about how
|
|
38
|
+
* many materials would match a corrected criterion.
|
|
39
|
+
*
|
|
40
|
+
* The resolver ranks, classifies and reports; it never substitutes the nearest
|
|
41
|
+
* token for the one you asked for. `best` is populated whenever ANY candidate
|
|
42
|
+
* exists — including under an `ambiguous` or `unresolved` status — so it is a
|
|
43
|
+
* leader, not a licence.
|
|
44
|
+
*
|
|
45
|
+
* `query` is the trimmed token that was actually resolved, which is how you tell
|
|
46
|
+
* WHICH half of a failed `attribute:value` pair was the problem.
|
|
47
|
+
*/
|
|
48
|
+
export interface ValueResolution {
|
|
49
|
+
status: OpenEnum<"resolved" | "ambiguous" | "unresolved">;
|
|
50
|
+
query: string;
|
|
51
|
+
best: ResolveValueCandidate | null;
|
|
52
|
+
candidates: ResolveValueCandidate[];
|
|
53
|
+
recommendedAction: OpenEnum<"use_best" | "choose_from_candidates" | "ask_user" | "none_found">;
|
|
54
|
+
}
|
|
27
55
|
/** Response from resolving a free-text value against one attribute's dictionary. */
|
|
28
56
|
export interface ResolveAttributeValueResponse {
|
|
29
57
|
attribute: {
|
|
@@ -31,20 +59,8 @@ export interface ResolveAttributeValueResponse {
|
|
|
31
59
|
name: string;
|
|
32
60
|
dictionaryId: string;
|
|
33
61
|
};
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
* `candidates` list means the phrase genuinely matched several terms — taking
|
|
37
|
-
* `best` silently there is how a query ends up answering a question nobody
|
|
38
|
-
* asked. `unresolved` means the vocabulary holds nothing like it, which is not
|
|
39
|
-
* the same as the corpus holding no such materials.
|
|
40
|
-
*/
|
|
41
|
-
resolution: {
|
|
42
|
-
status: OpenEnum<"resolved" | "ambiguous" | "unresolved">;
|
|
43
|
-
query: string;
|
|
44
|
-
best: ResolveValueCandidate | null;
|
|
45
|
-
candidates: ResolveValueCandidate[];
|
|
46
|
-
recommendedAction: OpenEnum<"use_best" | "choose_from_candidates" | "ask_user" | "none_found">;
|
|
47
|
-
};
|
|
62
|
+
/** The verdict on `q`. Read `recommendedAction` — see {@link ValueResolution}. */
|
|
63
|
+
resolution: ValueResolution;
|
|
48
64
|
}
|
|
49
65
|
/** Options for searching registry attributes and value-dictionary terms together. */
|
|
50
66
|
export interface VocabularySearchOptions {
|
|
@@ -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
|
+
}
|
package/client-config.js
ADDED
|
@@ -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,14 @@ 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 {
|
|
18
|
-
|
|
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, MatchInput, MatchResponse, PairingsOptions, PairingsResponse, ResolveAttributeOptions, ResolveAttributeResponse, ResolveAttributeValueOptions, ResolveAttributeValueResponse, ResolveColourResponse, SearchGetParams, SearchInput, SearchResponse, SiblingsResponse, SpecFacetsDistributionResponse, SpecFacetsListResponse, SuggestColourResponse, SuggestOptions, SuggestResponse, TaxonomyResponse, VocabularySearchOptions, WhoamiResponse } from "./types.js";
|
|
28
22
|
/**
|
|
29
23
|
* The MaterialGraph client — every lens on the graph, on one object.
|
|
30
24
|
*
|
|
@@ -38,18 +32,25 @@ export interface MaterialGraphClientOptions {
|
|
|
38
32
|
* | "which materials, from anyone, meet this hard spec?" | {@link MaterialGraphClient.bySpec} |
|
|
39
33
|
* | "what's near this colour / this palette / this gradient?" | {@link MaterialGraphClient.byColour}, `colours.palette()`, `colours.gradient()` |
|
|
40
34
|
* | "what goes with THIS material?" | {@link MaterialGraphClient.pairings}, {@link MaterialGraphClient.siblings}, {@link MaterialGraphClient.coordinate} |
|
|
35
|
+
* | "what does the manufacturer say is AVAILABLE on this?" | {@link MaterialGraphClient.associatedFinishes}, {@link MaterialGraphClient.associatedProducts} |
|
|
41
36
|
* | "how much is this specified?" | {@link MaterialGraphClient.demand}, {@link MaterialGraphClient.demandBatch} |
|
|
42
37
|
* | "is this allowed in an A-2 corridor?" | `codes.*` |
|
|
43
38
|
* | "what can I even ask for?" | {@link MaterialGraphClient.specificationFacets}, {@link MaterialGraphClient.attributeValues}, {@link MaterialGraphClient.taxonomy}, {@link MaterialGraphClient.colourFacets}, {@link MaterialGraphClient.glossary} |
|
|
39
|
+
* | "I have words — what do they mean in YOUR vocabulary?" | {@link MaterialGraphClient.resolve} |
|
|
44
40
|
* | "do the whole job for me, async" | {@link MaterialGraphClient.createRun} + `mg:compose` |
|
|
45
41
|
*
|
|
46
|
-
*
|
|
42
|
+
* Three structural rules that fall out of that table:
|
|
47
43
|
* - **Colour is a separate lens.** {@link MaterialGraphClient.search} matches
|
|
48
44
|
* WORDS. A colour word in `text` finds materials whose text mentions it, not
|
|
49
45
|
* materials that look it. Route visual intent to the colour lenses.
|
|
50
46
|
* - **Relationships hang off a material, not off a query.** You cannot ask "what
|
|
51
47
|
* pairs with beige oak"; you resolve a material first, then ask what pairs
|
|
52
48
|
* with THAT `variantId`.
|
|
49
|
+
* - **Declared is not inferred.** `associatedFinishes()` /
|
|
50
|
+
* `associatedProducts()` return compatibility the MANUFACTURER stated;
|
|
51
|
+
* `pairings()` returns companionship the graph INFERRED. The response shapes
|
|
52
|
+
* share no field, on purpose — render them as separate sections and never
|
|
53
|
+
* concatenate them.
|
|
53
54
|
*
|
|
54
55
|
* And three reading rules that apply to every lens: ids are opaque (see
|
|
55
56
|
* `./identifiers.ts`), absent is not false, and a page is not a corpus.
|
|
@@ -170,6 +171,8 @@ export declare class MaterialGraphClient {
|
|
|
170
171
|
* {@link MaterialGraphClient.browse} to walk by lightness/vividness, and
|
|
171
172
|
* {@link MaterialGraphClient.search} when colour is one signal among many.
|
|
172
173
|
* Report matches as visual likeness, never a stated colour specification.
|
|
174
|
+
* `family` gates the answer to ONE colorscope colour family when nearness
|
|
175
|
+
* alone crosses a hue boundary; a gated page can be shorter than `limit`.
|
|
173
176
|
*/
|
|
174
177
|
byColour(params: ByColourParams, signal?: AbortSignal): Promise<ByColourResponse>;
|
|
175
178
|
/**
|
|
@@ -233,6 +236,10 @@ export declare class MaterialGraphClient {
|
|
|
233
236
|
* you how much of the rail is real order evidence.
|
|
234
237
|
*/
|
|
235
238
|
pairings(variantId: VariantId, options?: PairingsOptions, signal?: AbortSignal): Promise<PairingsResponse>;
|
|
239
|
+
/** "What finishes does the manufacturer declare for THIS product?" — {@link declaredFinishes}. */
|
|
240
|
+
associatedFinishes(variantId: VariantId, options?: AssociatedOptions, signal?: AbortSignal): Promise<import("./association-types.js").DeclaredAssociationsResponse>;
|
|
241
|
+
/** "What products does the manufacturer declare THIS finish for?" — {@link declaredProducts}. */
|
|
242
|
+
associatedProducts(variantId: VariantId, options?: AssociatedOptions, signal?: AbortSignal): Promise<import("./association-types.js").DeclaredAssociationsResponse>;
|
|
236
243
|
/**
|
|
237
244
|
* THE QUESTION: "hydrate these ids I already hold" (POST
|
|
238
245
|
* `/api/v1/materials/batch`, ≤ 100 ids) — never a discovery call. Throws
|
|
@@ -316,15 +323,10 @@ export declare class MaterialGraphClient {
|
|
|
316
323
|
* not invent it.
|
|
317
324
|
*/
|
|
318
325
|
resolveAttributeValue(attributeId: string, query: string, options?: ResolveAttributeValueOptions, signal?: AbortSignal): Promise<ResolveAttributeValueResponse>;
|
|
319
|
-
/**
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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>;
|
|
326
|
+
/** "Is this phrase an attribute or a value?" — see {@link searchVocabularyRequest}. */
|
|
327
|
+
searchVocabulary(query: string, options?: VocabularySearchOptions, signal?: AbortSignal): Promise<import("./catalog-discovery-types.js").VocabularySearchResponse>;
|
|
328
|
+
/** "I have words — what do they mean in YOUR vocabulary?" — see {@link resolvePhrasesRequest}. */
|
|
329
|
+
resolve(phrases: string[], signal?: AbortSignal): Promise<import("./resolve-types.js").ResolveResponse>;
|
|
328
330
|
/**
|
|
329
331
|
* THE QUESTION: "which materials, from ANYONE, are ASTM E84 Class A AND
|
|
330
332
|
* GREENGUARD-certified?" — hard specifications, no ranking, every brand at once
|
|
@@ -333,7 +335,10 @@ export declare class MaterialGraphClient {
|
|
|
333
335
|
* Instead use `search({ filters })` when the spec merely GATES a ranked
|
|
334
336
|
* question, `codes.advisory()` when the real question is what a space demands,
|
|
335
337
|
* and {@link MaterialGraphClient.specificationFacets} first if you do not know
|
|
336
|
-
* the canonical tokens
|
|
338
|
+
* the canonical tokens. Guessing one no longer returns a silently empty
|
|
339
|
+
* answer: an unresolvable criterion means the query NEVER RAN, and the
|
|
340
|
+
* response says so — read {@link BySpecResponse.unresolvedCriteria} before
|
|
341
|
+
* treating an empty `matches` as "nothing qualifies".
|
|
337
342
|
*
|
|
338
343
|
* A PAGE, NOT A CORPUS, unusually so: `totalMatches` is the size of the
|
|
339
344
|
* returned sample under `limit`, not a corpus count. See {@link BySpecResponse}.
|
|
@@ -528,6 +533,16 @@ export declare class MaterialGraphClient {
|
|
|
528
533
|
* asking you something (see {@link MaterialGraphClient.resumeRun}).
|
|
529
534
|
*/
|
|
530
535
|
run(runId: RunId, signal?: AbortSignal): Promise<PublicRunResponse>;
|
|
536
|
+
/**
|
|
537
|
+
* THE QUESTION: "which Runs are mine?" — your own Runs, newest first, paged
|
|
538
|
+
* (GET `/api/v1/runs`). This is how a client that lost a Run id finds its work
|
|
539
|
+
* again: `{ status: "suspended" }` returns the Runs still waiting on an
|
|
540
|
+
* answer, whose ids you can then feed to {@link MaterialGraphClient.resumeRun}.
|
|
541
|
+
*
|
|
542
|
+
* Only your own Runs are ever returned. Page by following `nextCursor` until
|
|
543
|
+
* it is ABSENT — a full page is not the signal to stop.
|
|
544
|
+
*/
|
|
545
|
+
listRuns(options?: ListRunsOptions): Promise<PublicRunListResponse>;
|
|
531
546
|
/**
|
|
532
547
|
* Export a completed compose Run's scheme as a finish schedule — one row per
|
|
533
548
|
* slot, every attribute value carrying its source and verification state
|