@instruments/materialgraph 0.26.0 → 0.27.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.
Files changed (113) hide show
  1. package/association-types.d.ts +18 -7
  2. package/attribution-types.d.ts +2 -2
  3. package/brand-types.d.ts +8 -0
  4. package/brands-client.d.ts +5 -36
  5. package/brands-client.js +7 -33
  6. package/by-colour-types.d.ts +40 -13
  7. package/by-spec-types.d.ts +0 -67
  8. package/by-spec-types.js +7 -5
  9. package/catalog-discovery-types.d.ts +1 -16
  10. package/catalogue-client.d.ts +12 -31
  11. package/catalogue-client.js +10 -46
  12. package/catalogue-types.d.ts +40 -0
  13. package/categories-client.d.ts +63 -0
  14. package/categories-client.js +48 -0
  15. package/category-types.d.ts +114 -0
  16. package/client-config.d.ts +2 -4
  17. package/client-config.js +2 -4
  18. package/client-vocabulary.d.ts +2 -4
  19. package/client-vocabulary.js +11 -9
  20. package/client.d.ts +103 -203
  21. package/client.js +126 -251
  22. package/codes-types.d.ts +21 -0
  23. package/colours-client.d.ts +4 -9
  24. package/colours-client.js +0 -8
  25. package/errors.d.ts +1 -1
  26. package/errors.js +1 -0
  27. package/identify-types.d.ts +49 -39
  28. package/identify-types.js +5 -3
  29. package/index.d.ts +11 -21
  30. package/index.js +4 -4
  31. package/list-variant-facets-types.d.ts +60 -0
  32. package/list-variants-types.d.ts +142 -0
  33. package/list-variants-types.js +12 -0
  34. package/match-stream-types.d.ts +13 -0
  35. package/match-stream-types.js +2 -0
  36. package/match-stream.js +12 -8
  37. package/match-types.d.ts +24 -0
  38. package/material-asset-types.d.ts +69 -49
  39. package/material-block-types.d.ts +2 -0
  40. package/material-composition-client.d.ts +21 -0
  41. package/material-composition-client.js +13 -0
  42. package/material-composition-types.d.ts +403 -0
  43. package/material-query-plan-types.d.ts +57 -141
  44. package/material-resources-client.d.ts +9 -7
  45. package/material-resources-client.js +19 -4
  46. package/material-types.d.ts +6 -14
  47. package/operations.d.ts +1 -1
  48. package/operations.js +20 -117
  49. package/package.json +5 -1
  50. package/palette-search-types.d.ts +59 -24
  51. package/query-understanding-types.d.ts +27 -5
  52. package/resolve-client.d.ts +6 -10
  53. package/resolve-client.js +5 -7
  54. package/response-schemas.d.ts +20 -0
  55. package/response-schemas.generated.d.ts +5 -0
  56. package/response-schemas.generated.js +160 -0
  57. package/response-schemas.js +113 -0
  58. package/run-types.d.ts +12 -93
  59. package/runs.d.ts +1 -37
  60. package/runs.js +2 -79
  61. package/schema-resource-types.d.ts +14 -1
  62. package/search-card-types.d.ts +22 -0
  63. package/search-index-plan-types.d.ts +70 -0
  64. package/search-refinement-types.d.ts +8 -38
  65. package/search-refinement-types.js +1 -0
  66. package/search-request-types.d.ts +117 -200
  67. package/search-response-receipt-types.d.ts +12 -268
  68. package/search-result-validation.d.ts +0 -16
  69. package/search-result-validation.js +22 -180
  70. package/spec-facet-types.d.ts +3 -8
  71. package/spec-types.d.ts +17 -10
  72. package/spec-types.js +8 -7
  73. package/specifications.d.ts +13 -55
  74. package/specifications.js +29 -73
  75. package/suggest-types.d.ts +4 -10
  76. package/suggest-types.js +1 -1
  77. package/types.d.ts +135 -338
  78. package/webmcp.d.ts +1 -1
  79. package/webmcp.generated.js +10 -94
  80. package/brand-most-sampled-types.d.ts +0 -33
  81. package/browse-types.d.ts +0 -236
  82. package/client-browse.d.ts +0 -78
  83. package/client-browse.js +0 -71
  84. package/client-exploration.d.ts +0 -9
  85. package/client-exploration.js +0 -5
  86. package/compare-types.d.ts +0 -89
  87. package/composition-client.d.ts +0 -33
  88. package/composition-client.js +0 -28
  89. package/composition-types.d.ts +0 -455
  90. package/composition-types.js +0 -10
  91. package/correction-types.d.ts +0 -88
  92. package/exploration-types.d.ts +0 -118
  93. package/exploration-types.js +0 -1
  94. package/fan-deck-types.d.ts +0 -42
  95. package/fan-deck-types.js +0 -9
  96. package/match-rejection-types.d.ts +0 -43
  97. package/match-rejection-types.js +0 -7
  98. package/palette-completion-types.d.ts +0 -115
  99. package/palette-completion-types.js +0 -1
  100. package/render-assets-types.d.ts +0 -65
  101. package/render-assets-types.js +0 -1
  102. package/search-collapse-types.d.ts +0 -69
  103. package/search-collapse-types.js +0 -1
  104. package/search-mood-types.d.ts +0 -6
  105. package/search-mood-types.js +0 -1
  106. package/search-window-types.d.ts +0 -25
  107. package/search-window-types.js +0 -1
  108. package/slot-pool-types.d.ts +0 -372
  109. package/slot-pool-types.js +0 -1
  110. /package/{brand-most-sampled-types.js → category-types.js} +0 -0
  111. /package/{browse-types.js → list-variant-facets-types.js} +0 -0
  112. /package/{compare-types.js → material-composition-types.js} +0 -0
  113. /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
- };
@@ -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
- };
@@ -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;
@@ -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
- });
@@ -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 {};
@@ -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>;
@@ -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
- });