@instruments/materialgraph 0.26.0 → 0.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/association-types.d.ts +18 -7
- package/attribution-types.d.ts +2 -2
- package/brand-types.d.ts +8 -0
- package/brands-client.d.ts +5 -36
- package/brands-client.js +7 -33
- package/by-colour-types.d.ts +40 -13
- package/by-spec-types.d.ts +0 -67
- package/by-spec-types.js +7 -5
- package/catalog-discovery-types.d.ts +1 -16
- package/catalogue-client.d.ts +12 -31
- package/catalogue-client.js +10 -46
- package/catalogue-types.d.ts +40 -0
- package/categories-client.d.ts +63 -0
- package/categories-client.js +48 -0
- package/category-types.d.ts +114 -0
- package/client-config.d.ts +2 -4
- package/client-config.js +2 -4
- package/client-vocabulary.d.ts +2 -4
- package/client-vocabulary.js +11 -9
- package/client.d.ts +103 -203
- package/client.js +128 -251
- package/codes-types.d.ts +21 -0
- package/colours-client.d.ts +4 -9
- package/colours-client.js +0 -8
- package/errors.d.ts +1 -1
- package/errors.js +1 -0
- package/identify-types.d.ts +49 -39
- package/identify-types.js +5 -3
- package/index.d.ts +11 -21
- package/index.js +4 -4
- package/list-variant-facets-types.d.ts +60 -0
- package/list-variants-types.d.ts +149 -0
- package/list-variants-types.js +12 -0
- package/match-stream-types.d.ts +13 -0
- package/match-stream-types.js +2 -0
- package/match-stream.js +12 -8
- package/match-types.d.ts +24 -0
- package/material-asset-types.d.ts +69 -49
- package/material-block-types.d.ts +2 -0
- package/material-composition-client.d.ts +21 -0
- package/material-composition-client.js +13 -0
- package/material-composition-types.d.ts +403 -0
- package/material-query-plan-types.d.ts +57 -141
- package/material-resources-client.d.ts +9 -7
- package/material-resources-client.js +19 -4
- package/material-types.d.ts +6 -14
- package/operations.d.ts +1 -1
- package/operations.js +20 -117
- package/package.json +5 -1
- package/palette-search-types.d.ts +59 -24
- package/query-understanding-types.d.ts +27 -5
- package/resolve-client.d.ts +6 -10
- package/resolve-client.js +5 -7
- package/response-schemas.d.ts +20 -0
- package/response-schemas.generated.d.ts +5 -0
- package/response-schemas.generated.js +160 -0
- package/response-schemas.js +113 -0
- package/run-types.d.ts +12 -93
- package/runs.d.ts +1 -37
- package/runs.js +2 -79
- package/schema-resource-types.d.ts +14 -1
- package/search-card-types.d.ts +22 -0
- package/search-index-plan-types.d.ts +70 -0
- package/search-refinement-types.d.ts +8 -38
- package/search-refinement-types.js +1 -0
- package/search-request-types.d.ts +117 -200
- package/search-response-receipt-types.d.ts +12 -268
- package/search-result-validation.d.ts +0 -16
- package/search-result-validation.js +22 -180
- package/spec-facet-types.d.ts +3 -8
- package/spec-types.d.ts +17 -10
- package/spec-types.js +8 -7
- package/specifications.d.ts +13 -55
- package/specifications.js +29 -73
- package/suggest-types.d.ts +4 -10
- package/suggest-types.js +1 -1
- package/types.d.ts +135 -338
- package/webmcp.d.ts +1 -1
- package/webmcp.generated.js +10 -94
- package/brand-most-sampled-types.d.ts +0 -33
- package/browse-types.d.ts +0 -236
- package/client-browse.d.ts +0 -78
- package/client-browse.js +0 -71
- package/client-exploration.d.ts +0 -9
- package/client-exploration.js +0 -5
- package/compare-types.d.ts +0 -89
- package/composition-client.d.ts +0 -33
- package/composition-client.js +0 -28
- package/composition-types.d.ts +0 -455
- package/composition-types.js +0 -10
- package/correction-types.d.ts +0 -88
- package/exploration-types.d.ts +0 -118
- package/exploration-types.js +0 -1
- package/fan-deck-types.d.ts +0 -42
- package/fan-deck-types.js +0 -9
- package/match-rejection-types.d.ts +0 -43
- package/match-rejection-types.js +0 -7
- package/palette-completion-types.d.ts +0 -115
- package/palette-completion-types.js +0 -1
- package/render-assets-types.d.ts +0 -65
- package/render-assets-types.js +0 -1
- package/search-collapse-types.d.ts +0 -69
- package/search-collapse-types.js +0 -1
- package/search-mood-types.d.ts +0 -6
- package/search-mood-types.js +0 -1
- package/search-window-types.d.ts +0 -25
- package/search-window-types.js +0 -1
- package/slot-pool-types.d.ts +0 -372
- package/slot-pool-types.js +0 -1
- /package/{brand-most-sampled-types.js → category-types.js} +0 -0
- /package/{browse-types.js → list-variant-facets-types.js} +0 -0
- /package/{compare-types.js → material-composition-types.js} +0 -0
- /package/{correction-types.js → search-index-plan-types.js} +0 -0
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { CandidateReadCard } from "./exploration-types.js";
|
|
2
|
-
export type BrandMostSampledResponse = {
|
|
3
|
-
data: {
|
|
4
|
-
brand: {
|
|
5
|
-
id: string;
|
|
6
|
-
name: string;
|
|
7
|
-
};
|
|
8
|
-
status: "ready" | "unavailable";
|
|
9
|
-
metric: "sample_order_lines";
|
|
10
|
-
provenance: {
|
|
11
|
-
source: "material_bank_order_history";
|
|
12
|
-
signal: "sampled";
|
|
13
|
-
sessionId: string;
|
|
14
|
-
window: "trailing_36mo";
|
|
15
|
-
windowStart: string;
|
|
16
|
-
windowEnd: string;
|
|
17
|
-
} | null;
|
|
18
|
-
products: {
|
|
19
|
-
rank: number;
|
|
20
|
-
sampleOrderLines: number;
|
|
21
|
-
representativeVariantSampleOrderLines: number;
|
|
22
|
-
card: CandidateReadCard & {
|
|
23
|
-
category: string | null;
|
|
24
|
-
};
|
|
25
|
-
}[];
|
|
26
|
-
};
|
|
27
|
-
meta: {
|
|
28
|
-
operationId: "brands.most_sampled";
|
|
29
|
-
requestId: string;
|
|
30
|
-
};
|
|
31
|
-
links: Record<string, never>;
|
|
32
|
-
warnings: never[];
|
|
33
|
-
};
|
package/browse-types.d.ts
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import type { ByColourSwatch } from "./by-colour-types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Wire types for the browse lane — GET `/api/v1/search/browse`.
|
|
4
|
-
*
|
|
5
|
-
* The SDK's one genuinely cursor-paginated surface, and the only lane with no
|
|
6
|
-
* query, no relevance and no ranking: the corpus in a stated order. That is what
|
|
7
|
-
* a category listing or a brand fan deck actually needs, and it is why `count`
|
|
8
|
-
* matters here more than anywhere else — a listing that cannot state its own
|
|
9
|
-
* size has to either walk every page or report a page size as a total.
|
|
10
|
-
*
|
|
11
|
-
* Lifted out of `types.ts` for the repository's source-file size budget when the
|
|
12
|
-
* lane gained its non-colour sorts and its feed count (MG-1483 / MG-1542).
|
|
13
|
-
* Nothing about the client's surface changed: `index.ts` re-exports these
|
|
14
|
-
* exactly as before.
|
|
15
|
-
*/
|
|
16
|
-
import type { CorpusScope, CorpusScopeReceipt } from "./corpus-scope-types.js";
|
|
17
|
-
import type { Cursor, ProductId, VariantId } from "./identifiers.js";
|
|
18
|
-
import type { CountedTotal, OpenEnum, SearchFacets } from "./types.js";
|
|
19
|
-
/** One item from the pure colour-scalar browse. */
|
|
20
|
-
export type BrowseResultItem = {
|
|
21
|
-
/** Opaque — round-trip verbatim. */
|
|
22
|
-
variantId: VariantId;
|
|
23
|
-
brand: string;
|
|
24
|
-
product: string;
|
|
25
|
-
brandSlug: string | null;
|
|
26
|
-
productId: ProductId | null;
|
|
27
|
-
color: string | null;
|
|
28
|
-
variantName: string | null;
|
|
29
|
-
/** MaterialGraph record creation time, not a manufacturer release date. */
|
|
30
|
-
recordCreatedAt: string | null;
|
|
31
|
-
imageUrl: string | null;
|
|
32
|
-
scalars: {
|
|
33
|
-
l: number | null;
|
|
34
|
-
chroma: number | null;
|
|
35
|
-
vibrancy: number | null;
|
|
36
|
-
};
|
|
37
|
-
retired?: boolean;
|
|
38
|
-
/**
|
|
39
|
-
* The row's own image palette — the FULL stored palette of the SAME image
|
|
40
|
-
* `imageUrl` serves, uncapped and in stored order, each swatch with `oklab`
|
|
41
|
-
* (see {@link ByColourSwatch}), so a card can render, sort or group colours
|
|
42
|
-
* without a second call. A one-swatch palette means the extractor found one
|
|
43
|
-
* colour (solid-colour materials are genuinely monochrome), not that the list
|
|
44
|
-
* was trimmed. ABSENT when no palette is on record for that image: absent
|
|
45
|
-
* means unknown, NOT that the material has no colours. These swatches come
|
|
46
|
-
* from product imagery, not from the manufacturer.
|
|
47
|
-
*/
|
|
48
|
-
palette?: ByColourSwatch[];
|
|
49
|
-
/**
|
|
50
|
-
* Resolved values for the attribute ids sent in `returnAttributes`, keyed by
|
|
51
|
-
* attribute id. ABSENT entirely when `returnAttributes` was not sent; a key
|
|
52
|
-
* whose value is `null` means this material carries no accepted value for that
|
|
53
|
-
* attribute. Three states, all different.
|
|
54
|
-
*/
|
|
55
|
-
attributes?: Record<string, string | string[] | null>;
|
|
56
|
-
};
|
|
57
|
-
/**
|
|
58
|
-
* The browse orders. `name` (A→Z) is the default, `name_desc` the same index
|
|
59
|
-
* read Z→A, `recent` newest first; the rest are the colour ramps. `/search`
|
|
60
|
-
* takes all eight too — one word, one order, on both lanes.
|
|
61
|
-
*/
|
|
62
|
-
export type BrowseSort = OpenEnum<"name" | "name_desc" | "recent" | "light" | "dark" | "vivid" | "muted" | "colourful">;
|
|
63
|
-
/**
|
|
64
|
-
* The browse feed's total — how many materials the browse DESCRIBES, which is
|
|
65
|
-
* never `results.length`.
|
|
66
|
-
*
|
|
67
|
-
* Uncapped, unlike {@link SearchCounts}: `basis:"exact"` is the true figure
|
|
68
|
-
* whatever its size, and `basis:"lowerBound"` means the count query timed out
|
|
69
|
-
* and the value is a floor. Counted over the SAME filters the page was drawn
|
|
70
|
-
* from, so a `types=`, `brands=` or `corpusScope=` narrowed browse reports the
|
|
71
|
-
* narrowed total — you will never get a whole-graph number beside a filtered
|
|
72
|
-
* page.
|
|
73
|
-
*/
|
|
74
|
-
export type BrowseCounts = {
|
|
75
|
-
variants: CountedTotal;
|
|
76
|
-
};
|
|
77
|
-
/**
|
|
78
|
-
* A single page of the browse — the SDK's one genuinely cursor-paginated
|
|
79
|
-
* surface. `results.length` is this page. Ask for `count: true` and `counts`
|
|
80
|
-
* states the total; without it the browse states no total at all, and a corpus
|
|
81
|
-
* claim can only come from walking every page (`client.browse.pages(...)` does
|
|
82
|
-
* the loop).
|
|
83
|
-
*/
|
|
84
|
-
export type BrowseResponse = {
|
|
85
|
-
/** THIS PAGE. */
|
|
86
|
-
results: BrowseResultItem[];
|
|
87
|
-
/** Opaque keyset for the next page. Absent = the walk is finished. */
|
|
88
|
-
cursor?: Cursor;
|
|
89
|
-
/**
|
|
90
|
-
* The order this page was built in — echoed, because `sort` is optional and
|
|
91
|
-
* defaulted, so a caller that omitted it need not know the server's default
|
|
92
|
-
* to label its own listing.
|
|
93
|
-
*/
|
|
94
|
-
sort: BrowseSort;
|
|
95
|
-
/**
|
|
96
|
-
* The attribute ladder this page was ordered by — present ONLY when `orderBy`
|
|
97
|
-
* was sent, in which case it SUPERSEDED `sort` (still echoed above, and still
|
|
98
|
-
* describing nothing this page was ordered by).
|
|
99
|
-
*/
|
|
100
|
-
orderBy?: {
|
|
101
|
-
attributeId: string;
|
|
102
|
-
direction: OpenEnum<"asc" | "desc">;
|
|
103
|
-
};
|
|
104
|
-
/**
|
|
105
|
-
* Reader prose about how the request was interpreted — today, only that
|
|
106
|
-
* `orderBy` superseded a STATED `sort`. Absent means nothing needed saying.
|
|
107
|
-
*/
|
|
108
|
-
note?: string;
|
|
109
|
-
/**
|
|
110
|
-
* The total, present ONLY when `count: true` was passed. Absent means it was
|
|
111
|
-
* not asked for — never that the set is empty.
|
|
112
|
-
*/
|
|
113
|
-
counts?: BrowseCounts;
|
|
114
|
-
/**
|
|
115
|
-
* Per-facet value counts, present ONLY when `facetCounts` was asked for.
|
|
116
|
-
*
|
|
117
|
-
* Counted over this browse's OWN match set — the same predicate the page and
|
|
118
|
-
* `counts` ran under — so every value is `basis:"exact"` and `poolSize` is
|
|
119
|
-
* ABSENT. That differs from {@link SearchResponse}'s facets, which are
|
|
120
|
-
* normally counted over a ranked candidate pool and are therefore floors:
|
|
121
|
-
* browse retrieves no pool, so there is nothing for a figure to be a floor
|
|
122
|
-
* of. A facet the server could not count in time appears in `absent` with
|
|
123
|
-
* `reason:"timeout"` and the results are unaffected.
|
|
124
|
-
*/
|
|
125
|
-
facets?: SearchFacets;
|
|
126
|
-
/**
|
|
127
|
-
* The corpus this page was drawn from — present ONLY when the request
|
|
128
|
-
* narrowed it. ABSENT means the whole graph: every material MaterialGraph
|
|
129
|
-
* knows, orderable or not.
|
|
130
|
-
*/
|
|
131
|
-
corpusScope?: CorpusScopeReceipt;
|
|
132
|
-
};
|
|
133
|
-
/** Params for the pure colour `search/browse` surface. */
|
|
134
|
-
export type BrowseParams = {
|
|
135
|
-
/**
|
|
136
|
-
* OPTIONAL. Defaults to `name` (A→Z); `recent` is newest first.
|
|
137
|
-
*
|
|
138
|
-
* The five colour ramps (`light`/`dark`/`vivid`/`muted`/`colourful`) order by
|
|
139
|
-
* an EXTRACTED colour scalar and therefore see only colour-extracted
|
|
140
|
-
* materials — a genuinely smaller set than `name` and `recent` walk. That is
|
|
141
|
-
* not a bug to route around; ask for `count` and each sort reports the size of
|
|
142
|
-
* the set it actually describes.
|
|
143
|
-
*/
|
|
144
|
-
sort?: BrowseSort;
|
|
145
|
-
/**
|
|
146
|
-
* The previous page's `cursor`, verbatim. Opaque keyset state — not an offset.
|
|
147
|
-
* Changing `sort`, `limit`, `types`, `brands` or `corpusScope` invalidates it.
|
|
148
|
-
*/
|
|
149
|
-
cursor?: Cursor;
|
|
150
|
-
/** Page size. */
|
|
151
|
-
limit?: number;
|
|
152
|
-
/**
|
|
153
|
-
* Return `counts.variants` — the total size of the set this browse walks,
|
|
154
|
-
* counted over the same filters the page came from.
|
|
155
|
-
*
|
|
156
|
-
* Opt-in, because it is a second query. A category listing that cannot state
|
|
157
|
-
* how many things it is listing has to either walk every page or report the
|
|
158
|
-
* page size as a total; this is the third option.
|
|
159
|
-
*/
|
|
160
|
-
count?: boolean;
|
|
161
|
-
/**
|
|
162
|
-
* Comma-separated taxonomy keys to restrict the browse to, at EITHER tier of
|
|
163
|
-
* `/api/v1/taxonomy/categories`. A GROUP key (`tile_surfaces`) expands to
|
|
164
|
-
* every product type under it, a TYPE key (`ceramic_porcelain_tile`) stands
|
|
165
|
-
* for itself, and a mixed list is their deduplicated union. A key in neither
|
|
166
|
-
* tier is a 400 listing the valid keys — never an empty page.
|
|
167
|
-
*/
|
|
168
|
-
types?: string;
|
|
169
|
-
/**
|
|
170
|
-
* Comma-separated canonical brand ids, exact slugs (`farrow-ball`), or exact
|
|
171
|
-
* display names (`Farrow & Ball`). Canonical ids and slugs are case-sensitive
|
|
172
|
-
* and opaque; display names are case-insensitive. One brand plus a colour `sort` is that brand's
|
|
173
|
-
* fan deck. An unknown brand is a typed 400 naming it, and an exact display
|
|
174
|
-
* name shared by multiple brands is a typed 400 with the candidates; neither
|
|
175
|
-
* becomes an empty page.
|
|
176
|
-
*/
|
|
177
|
-
brands?: string;
|
|
178
|
-
/**
|
|
179
|
-
* Positive manufacturer declarations. accepts_finishes includes variants of
|
|
180
|
-
* product-level declarations; it does not assert variant-specific applicability.
|
|
181
|
-
* Absence means no role filter.
|
|
182
|
-
*/
|
|
183
|
-
role?: "finish" | "accepts_finishes";
|
|
184
|
-
/**
|
|
185
|
-
* Typed attribute filters, in the SAME grammar `search` takes — a term, a
|
|
186
|
-
* term SET (OR within the entry), a presence check or a numeric range, each
|
|
187
|
-
* optionally carrying a `trust` bar. Entries AND together.
|
|
188
|
-
*
|
|
189
|
-
* Each entry is sent as ITS OWN `filters=` parameter, JSON-encoded, which is
|
|
190
|
-
* what the route reads. Pass the objects; the client does the encoding.
|
|
191
|
-
*
|
|
192
|
-
* Applied INSIDE the keyset scan and counted under the same predicate, so a
|
|
193
|
-
* filtered page still fills to `limit`, `cursor` still means another MATCHING
|
|
194
|
-
* row exists, and `counts.variants` describes the FILTERED set. An attribute
|
|
195
|
-
* id outside the registry is a 400 carrying ranked candidates, never an empty
|
|
196
|
-
* page.
|
|
197
|
-
*/
|
|
198
|
-
filters?: readonly Record<string, unknown>[];
|
|
199
|
-
/**
|
|
200
|
-
* Facet ids to count over this browse's match set — `brand`, `category`, or a
|
|
201
|
-
* spec attribute id. Sent as one comma-separated `facetCounts=` parameter.
|
|
202
|
-
*
|
|
203
|
-
* The response's `facets` block comes back `basis:"exact"` with no
|
|
204
|
-
* `poolSize`, because browse counts the whole predicate rather than a ranked
|
|
205
|
-
* pool. Omit it for no `facets` block and no counting cost.
|
|
206
|
-
*/
|
|
207
|
-
facetCounts?: readonly string[];
|
|
208
|
-
/**
|
|
209
|
-
* Which corpus to walk. ABSENT means the whole graph — every material
|
|
210
|
-
* MaterialGraph knows, orderable or not. `materialBankOrderable` restricts to
|
|
211
|
-
* materials carrying a Material Bank identity, applied inside the scan, so a
|
|
212
|
-
* scoped page is still a full page and `cursor` still means another eligible
|
|
213
|
-
* row exists.
|
|
214
|
-
*/
|
|
215
|
-
corpusScope?: CorpusScope;
|
|
216
|
-
/**
|
|
217
|
-
* Sort along ONE attribute's declared value ladder instead of a `sort` word —
|
|
218
|
-
* the same field, ladder and refusals `search` takes. Sent as one JSON-encoded
|
|
219
|
-
* `orderBy=` parameter; pass the object, the client encodes it.
|
|
220
|
-
*
|
|
221
|
-
* SUPERSEDES `sort` when both are given, and the response `note` says so.
|
|
222
|
-
* Ordering is DECLARED, never inferred from a value's spelling: an attribute
|
|
223
|
-
* with no stated ladder is a 400 carrying `orderable_attributes`. Materials
|
|
224
|
-
* off the ladder, or carrying no value, sort LAST in both directions.
|
|
225
|
-
*/
|
|
226
|
-
orderBy?: {
|
|
227
|
-
attributeId: string;
|
|
228
|
-
direction?: OpenEnum<"asc" | "desc">;
|
|
229
|
-
};
|
|
230
|
-
/**
|
|
231
|
-
* Attribute ids whose resolved value rides each result under `attributes`, so
|
|
232
|
-
* a filtered listing can display what it filtered on. Sent as one
|
|
233
|
-
* comma-separated `returnAttributes=` parameter.
|
|
234
|
-
*/
|
|
235
|
-
returnAttributes?: readonly string[];
|
|
236
|
-
};
|
package/client-browse.d.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `browse` namespace's implementation — the paged colour-ordered walk of the
|
|
3
|
-
* corpus behind `client.browse(...)` and `client.browse.pages(...)`.
|
|
4
|
-
*
|
|
5
|
-
* Lifted out of `client.ts` for the repository's source-file size budget. These
|
|
6
|
-
* were already private implementations behind a public namespace, so nothing
|
|
7
|
-
* about the client's surface changes: `client.browse` is still a callable with a
|
|
8
|
-
* `.pages()` generator, and the class still owns the transport. What moved is
|
|
9
|
-
* the pagination LOOP, which is the only real logic here and the only part worth
|
|
10
|
-
* testing on its own.
|
|
11
|
-
*/
|
|
12
|
-
import type { BrowseParams, BrowseResponse } from "./browse-types.js";
|
|
13
|
-
/**
|
|
14
|
-
* `client.browse(...)` — callable for ONE PAGE, `.pages()` for the whole walk.
|
|
15
|
-
*
|
|
16
|
-
* The distinction is the whole point: the single-call form hands you a page and
|
|
17
|
-
* states no total, so a corpus claim can only come from `.pages()`. Never report
|
|
18
|
-
* one page's length as "how many materials there are".
|
|
19
|
-
*/
|
|
20
|
-
export type BrowseNamespace = {
|
|
21
|
-
(params: BrowseParams, signal?: AbortSignal): Promise<BrowseResponse>;
|
|
22
|
-
pages(params: BrowseParams, signal?: AbortSignal): AsyncGenerator<BrowseResponse, void, unknown>;
|
|
23
|
-
};
|
|
24
|
-
/** Fetch one page. The client supplies this, so the transport stays private. */
|
|
25
|
-
export type BrowsePageFetcher = (params: BrowseParams, signal?: AbortSignal) => Promise<BrowseResponse>;
|
|
26
|
-
/**
|
|
27
|
-
* The query string for one browse page — EVERY param the route reads, named here
|
|
28
|
-
* so the client method and the pagination loop cannot disagree about them.
|
|
29
|
-
*
|
|
30
|
-
* "Every" is load-bearing. This is an allow-list wrapper, and an allow-list
|
|
31
|
-
* wrapper that rebuilds a query from named fields is precisely the shape that
|
|
32
|
-
* silently drops a param the server already honours: `brands` shipped on the
|
|
33
|
-
* route and never reached it from here, so the fan deck was unreachable through
|
|
34
|
-
* the published client while the server had supported it all along. When the
|
|
35
|
-
* route gains a param, it gains a line HERE too, or it is not shipped.
|
|
36
|
-
*
|
|
37
|
-
* It happened again, and twice: `filters` shipped with MG-1576 and `facetCounts`
|
|
38
|
-
* with MG-1590, and neither had a line here. Attribute filtering — the thing
|
|
39
|
-
* MG-1588 spent a materialized view making fast — was therefore unreachable
|
|
40
|
-
* through the published client for as long as it existed on the route. Both are
|
|
41
|
-
* enrolled below, along with MG-1593's `orderBy` and `returnAttributes`.
|
|
42
|
-
*
|
|
43
|
-
* `filters` is a REPEATED parameter and `facetCounts` a comma list, because that
|
|
44
|
-
* is what the route reads: the transport appends an array once per element, so
|
|
45
|
-
* the array shape and the comma-joined string are each doing exactly what the
|
|
46
|
-
* server expects rather than being two spellings of one idea.
|
|
47
|
-
*/
|
|
48
|
-
export declare const browseQuery: (params: BrowseParams) => {
|
|
49
|
-
sort: BrowseParams["sort"];
|
|
50
|
-
cursor: BrowseParams["cursor"];
|
|
51
|
-
limit: BrowseParams["limit"];
|
|
52
|
-
count: BrowseParams["count"];
|
|
53
|
-
types: BrowseParams["types"];
|
|
54
|
-
brands: BrowseParams["brands"];
|
|
55
|
-
role: BrowseParams["role"];
|
|
56
|
-
filters: string[] | undefined;
|
|
57
|
-
facetCounts: string | undefined;
|
|
58
|
-
corpusScope: BrowseParams["corpusScope"];
|
|
59
|
-
orderBy: string | undefined;
|
|
60
|
-
returnAttributes: string | undefined;
|
|
61
|
-
};
|
|
62
|
-
/**
|
|
63
|
-
* Walk every page from `params.cursor` onward.
|
|
64
|
-
*
|
|
65
|
-
* Stops on the first page that reports no next cursor OR comes back empty. Both
|
|
66
|
-
* conditions matter: a server that keeps handing out a cursor for an exhausted
|
|
67
|
-
* scan would otherwise spin here forever, and an infinite generator is a hang,
|
|
68
|
-
* not an error anyone can see.
|
|
69
|
-
*/
|
|
70
|
-
export declare function browsePages(fetchPage: BrowsePageFetcher, params: BrowseParams, signal?: AbortSignal): AsyncGenerator<BrowseResponse, void, unknown>;
|
|
71
|
-
/**
|
|
72
|
-
* Build the callable-with-`.pages()` object the client exposes as `browse`.
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* const page = await client.browse({ sort: "lightest" });
|
|
76
|
-
* for await (const page of client.browse.pages({ sort: "lightest" })) { … }
|
|
77
|
-
*/
|
|
78
|
-
export declare const createBrowseNamespace: (fetchPage: BrowsePageFetcher) => BrowseNamespace;
|
package/client-browse.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The query string for one browse page — EVERY param the route reads, named here
|
|
3
|
-
* so the client method and the pagination loop cannot disagree about them.
|
|
4
|
-
*
|
|
5
|
-
* "Every" is load-bearing. This is an allow-list wrapper, and an allow-list
|
|
6
|
-
* wrapper that rebuilds a query from named fields is precisely the shape that
|
|
7
|
-
* silently drops a param the server already honours: `brands` shipped on the
|
|
8
|
-
* route and never reached it from here, so the fan deck was unreachable through
|
|
9
|
-
* the published client while the server had supported it all along. When the
|
|
10
|
-
* route gains a param, it gains a line HERE too, or it is not shipped.
|
|
11
|
-
*
|
|
12
|
-
* It happened again, and twice: `filters` shipped with MG-1576 and `facetCounts`
|
|
13
|
-
* with MG-1590, and neither had a line here. Attribute filtering — the thing
|
|
14
|
-
* MG-1588 spent a materialized view making fast — was therefore unreachable
|
|
15
|
-
* through the published client for as long as it existed on the route. Both are
|
|
16
|
-
* enrolled below, along with MG-1593's `orderBy` and `returnAttributes`.
|
|
17
|
-
*
|
|
18
|
-
* `filters` is a REPEATED parameter and `facetCounts` a comma list, because that
|
|
19
|
-
* is what the route reads: the transport appends an array once per element, so
|
|
20
|
-
* the array shape and the comma-joined string are each doing exactly what the
|
|
21
|
-
* server expects rather than being two spellings of one idea.
|
|
22
|
-
*/
|
|
23
|
-
export const browseQuery = (params) => ({
|
|
24
|
-
brands: params.brands,
|
|
25
|
-
corpusScope: params.corpusScope,
|
|
26
|
-
count: params.count,
|
|
27
|
-
cursor: params.cursor,
|
|
28
|
-
facetCounts: params.facetCounts?.join(","),
|
|
29
|
-
filters: params.filters?.map((entry) => JSON.stringify(entry)),
|
|
30
|
-
limit: params.limit,
|
|
31
|
-
// `orderBy` is JSON-encoded and `returnAttributes` comma-joined because that
|
|
32
|
-
// is what the route reads — the same two transports `filters` and
|
|
33
|
-
// `facetCounts` already use. Enrolled HERE at the same time they shipped on
|
|
34
|
-
// the route, which is the thing the three misses above did not do.
|
|
35
|
-
orderBy: params.orderBy ? JSON.stringify(params.orderBy) : undefined,
|
|
36
|
-
returnAttributes: params.returnAttributes?.join(","),
|
|
37
|
-
role: params.role,
|
|
38
|
-
sort: params.sort,
|
|
39
|
-
types: params.types,
|
|
40
|
-
});
|
|
41
|
-
/**
|
|
42
|
-
* Walk every page from `params.cursor` onward.
|
|
43
|
-
*
|
|
44
|
-
* Stops on the first page that reports no next cursor OR comes back empty. Both
|
|
45
|
-
* conditions matter: a server that keeps handing out a cursor for an exhausted
|
|
46
|
-
* scan would otherwise spin here forever, and an infinite generator is a hang,
|
|
47
|
-
* not an error anyone can see.
|
|
48
|
-
*/
|
|
49
|
-
export async function* browsePages(fetchPage, params, signal) {
|
|
50
|
-
let { cursor } = params;
|
|
51
|
-
for (;;) {
|
|
52
|
-
const page = await fetchPage({ ...params, cursor }, signal);
|
|
53
|
-
yield page;
|
|
54
|
-
if (page.cursor === undefined || page.results.length === 0) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
cursor = page.cursor;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Build the callable-with-`.pages()` object the client exposes as `browse`.
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* const page = await client.browse({ sort: "lightest" });
|
|
65
|
-
* for await (const page of client.browse.pages({ sort: "lightest" })) { … }
|
|
66
|
-
*/
|
|
67
|
-
export const createBrowseNamespace = (fetchPage) => {
|
|
68
|
-
const browse = async (params, signal) => await fetchPage(params, signal);
|
|
69
|
-
browse.pages = (params, signal) => browsePages(fetchPage, params, signal);
|
|
70
|
-
return browse;
|
|
71
|
-
};
|
package/client-exploration.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { CandidateReadInput, CandidateReadResponse, GraphOperatorEvidence } from "./exploration-types.js";
|
|
2
|
-
import type { HttpTransport } from "./http-transport.js";
|
|
3
|
-
import type { VariantId } from "./identifiers.js";
|
|
4
|
-
/** Discovery and traversal share one namespace so availability precedes use. */
|
|
5
|
-
export type ExplorationNamespace = {
|
|
6
|
-
operators(variantId: VariantId, signal?: AbortSignal): Promise<GraphOperatorEvidence>;
|
|
7
|
-
candidates(variantId: VariantId, input: CandidateReadInput, signal?: AbortSignal): Promise<CandidateReadResponse>;
|
|
8
|
-
};
|
|
9
|
-
export declare const createExplorationNamespace: (transport: HttpTransport) => ExplorationNamespace;
|
package/client-exploration.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { resolveOperation } from "./operations.js";
|
|
2
|
-
export const createExplorationNamespace = (transport) => ({
|
|
3
|
-
candidates: async (variantId, input, signal) => await transport.requestJson(resolveOperation("read_material_candidates"), { body: input, pathParams: { variantId }, signal }),
|
|
4
|
-
operators: async (variantId, signal) => await transport.requestJson(resolveOperation("list_material_operators"), { pathParams: { variantId }, signal }),
|
|
5
|
-
});
|
package/compare-types.d.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
export type CompareInput = {
|
|
2
|
-
variantIds: string[];
|
|
3
|
-
selection?: {
|
|
4
|
-
kind: "blocks";
|
|
5
|
-
blockIds: string[];
|
|
6
|
-
} | {
|
|
7
|
-
kind: "attributes";
|
|
8
|
-
attributeIds: string[];
|
|
9
|
-
};
|
|
10
|
-
};
|
|
11
|
-
type Availability = "available" | "discontinued" | "superseded" | "unknown";
|
|
12
|
-
type ClaimScope = "variant" | "product" | "brand";
|
|
13
|
-
export type CompareTarget = {
|
|
14
|
-
variantId: string;
|
|
15
|
-
version: string;
|
|
16
|
-
freshness: {
|
|
17
|
-
oldestAt: string | null;
|
|
18
|
-
newestAt: string | null;
|
|
19
|
-
};
|
|
20
|
-
identity: {
|
|
21
|
-
variantId: string;
|
|
22
|
-
variantName: string | null;
|
|
23
|
-
brand: string;
|
|
24
|
-
brandSlug: string;
|
|
25
|
-
product: string;
|
|
26
|
-
productId: string;
|
|
27
|
-
colourName: string | null;
|
|
28
|
-
category: string | null;
|
|
29
|
-
categoryPath: string[];
|
|
30
|
-
imageUrl: string | null;
|
|
31
|
-
installationImageUrl: string | null;
|
|
32
|
-
dominantHex: string | null;
|
|
33
|
-
siblingCount: number;
|
|
34
|
-
};
|
|
35
|
-
availability: {
|
|
36
|
-
state: Availability;
|
|
37
|
-
label: string | null;
|
|
38
|
-
supersededBy: {
|
|
39
|
-
variantId: string;
|
|
40
|
-
brandSlug: string;
|
|
41
|
-
productId: string;
|
|
42
|
-
brand: string;
|
|
43
|
-
product: string;
|
|
44
|
-
variant: string;
|
|
45
|
-
color: string | null;
|
|
46
|
-
availability: Availability;
|
|
47
|
-
} | null;
|
|
48
|
-
};
|
|
49
|
-
sampleability: boolean | null;
|
|
50
|
-
};
|
|
51
|
-
export type CompareCell = {
|
|
52
|
-
variantId: string;
|
|
53
|
-
state: "known" | "unknown" | "conflicting" | "inapplicable";
|
|
54
|
-
recordState: "stated" | "conflicting" | "stale" | "unknown" | "missing" | "not_applicable" | "not_evaluated";
|
|
55
|
-
value: unknown;
|
|
56
|
-
unit: string | null;
|
|
57
|
-
verification: "verified" | "unverified" | "unbacked";
|
|
58
|
-
confidence: number | null;
|
|
59
|
-
sourceBasis: string | null;
|
|
60
|
-
sourceRef: string | null;
|
|
61
|
-
freshnessAt: string | null;
|
|
62
|
-
claimScope: ClaimScope | null;
|
|
63
|
-
competingClaims: {
|
|
64
|
-
scope: ClaimScope;
|
|
65
|
-
value: unknown;
|
|
66
|
-
sourceRef: string | null;
|
|
67
|
-
}[];
|
|
68
|
-
};
|
|
69
|
-
export type CompareResult = {
|
|
70
|
-
targets: CompareTarget[];
|
|
71
|
-
rows: {
|
|
72
|
-
blockId: string;
|
|
73
|
-
blockName: string;
|
|
74
|
-
attributeId: string;
|
|
75
|
-
label: string;
|
|
76
|
-
outcome: "same" | "different" | "incomplete" | "indeterminate" | "not_applicable";
|
|
77
|
-
cells: CompareCell[];
|
|
78
|
-
}[];
|
|
79
|
-
};
|
|
80
|
-
export type CompareResponse = {
|
|
81
|
-
data: CompareResult;
|
|
82
|
-
meta: {
|
|
83
|
-
operationId: "retrieval.compare";
|
|
84
|
-
requestId: string;
|
|
85
|
-
};
|
|
86
|
-
links: Record<string, never>;
|
|
87
|
-
warnings: never[];
|
|
88
|
-
};
|
|
89
|
-
export {};
|
package/composition-client.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The COMPOSITION lens: the two synchronous scheme surfaces, as thin wrappers
|
|
3
|
-
* over the shared {@link HttpTransport}. `MaterialGraphClient.varyScheme` and
|
|
4
|
-
* `.slotPools` delegate here; the contract lives in this file.
|
|
5
|
-
*
|
|
6
|
-
* They are two altitudes of the same capability. `varyScheme` re-derives a
|
|
7
|
-
* scheme MaterialGraph itself composed; `slotPools` hands the ranked candidates
|
|
8
|
-
* to a composer that is not MaterialGraph, and keeps none of the ownership.
|
|
9
|
-
*/
|
|
10
|
-
import type { VarySchemesRequest, VarySchemesResponse } from "./composition-types.js";
|
|
11
|
-
import type { HttpTransport } from "./http-transport.js";
|
|
12
|
-
import type { SlotPoolsRequest, SlotPoolsResponse } from "./slot-pool-types.js";
|
|
13
|
-
/**
|
|
14
|
-
* THE QUESTION: "show me the same brief, decided differently." Re-derives an
|
|
15
|
-
* option B from a succeeded compose Run along ONE constraint axis. Deterministic
|
|
16
|
-
* and synchronous — the response IS the hydrated scheme (no Run envelope),
|
|
17
|
-
* stamped with axis + source provenance whose `notes` are the receipts.
|
|
18
|
-
* Requires the `mg:read` scope.
|
|
19
|
-
*/
|
|
20
|
-
export declare const varyScheme: (transport: HttpTransport, body: VarySchemesRequest, signal?: AbortSignal) => Promise<VarySchemesResponse>;
|
|
21
|
-
/**
|
|
22
|
-
* THE QUESTION: "for each of these slots, what should I be choosing between?"
|
|
23
|
-
* Up to 24 self-contained tasks in, one ranked and gated candidate pool each
|
|
24
|
-
* out, correlated by YOUR `taskKey`. Synchronous, uncached and project-free —
|
|
25
|
-
* no Run, no programme, no state kept on the MaterialGraph side.
|
|
26
|
-
*
|
|
27
|
-
* READ THE POOLS, NOT JUST THE CANDIDATES. A task whose search failed comes back
|
|
28
|
-
* with a `search-failed` shortfall while the rest answer normally; a candidate
|
|
29
|
-
* with `eligible: false` is LABELLED rather than dropped (read `flags`); and a
|
|
30
|
-
* criterion that graded `missing` or `not_evaluated` was never a failure.
|
|
31
|
-
* Requires the `mg:read` scope.
|
|
32
|
-
*/
|
|
33
|
-
export declare const slotPools: (transport: HttpTransport, body: SlotPoolsRequest, signal?: AbortSignal) => Promise<SlotPoolsResponse>;
|
package/composition-client.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { resolveOperation } from "./operations.js";
|
|
2
|
-
/**
|
|
3
|
-
* THE QUESTION: "show me the same brief, decided differently." Re-derives an
|
|
4
|
-
* option B from a succeeded compose Run along ONE constraint axis. Deterministic
|
|
5
|
-
* and synchronous — the response IS the hydrated scheme (no Run envelope),
|
|
6
|
-
* stamped with axis + source provenance whose `notes` are the receipts.
|
|
7
|
-
* Requires the `mg:read` scope.
|
|
8
|
-
*/
|
|
9
|
-
export const varyScheme = async (transport, body, signal) => await transport.requestJson(resolveOperation("vary_schemes"), {
|
|
10
|
-
body,
|
|
11
|
-
signal,
|
|
12
|
-
});
|
|
13
|
-
/**
|
|
14
|
-
* THE QUESTION: "for each of these slots, what should I be choosing between?"
|
|
15
|
-
* Up to 24 self-contained tasks in, one ranked and gated candidate pool each
|
|
16
|
-
* out, correlated by YOUR `taskKey`. Synchronous, uncached and project-free —
|
|
17
|
-
* no Run, no programme, no state kept on the MaterialGraph side.
|
|
18
|
-
*
|
|
19
|
-
* READ THE POOLS, NOT JUST THE CANDIDATES. A task whose search failed comes back
|
|
20
|
-
* with a `search-failed` shortfall while the rest answer normally; a candidate
|
|
21
|
-
* with `eligible: false` is LABELLED rather than dropped (read `flags`); and a
|
|
22
|
-
* criterion that graded `missing` or `not_evaluated` was never a failure.
|
|
23
|
-
* Requires the `mg:read` scope.
|
|
24
|
-
*/
|
|
25
|
-
export const slotPools = async (transport, body, signal) => await transport.requestJson(resolveOperation("slot_pools"), {
|
|
26
|
-
body,
|
|
27
|
-
signal,
|
|
28
|
-
});
|