@instruments/materialgraph 3.4.0 → 3.6.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/client.d.ts +16 -0
- package/client.js +18 -0
- package/http-transport.d.ts +9 -3
- package/http-transport.js +59 -33
- package/index.d.ts +2 -1
- package/list-variants-types.d.ts +5 -1
- package/match-photo-types.d.ts +88 -0
- package/match-photo-types.js +1 -0
- package/match-photo.d.ts +9 -0
- package/match-photo.js +90 -0
- package/operations.generated.d.ts +1 -1
- package/operations.generated.js +1 -0
- package/package.json +1 -1
- package/response-schemas.generated.js +23 -23
- package/search-variants-types.d.ts +4 -0
- package/webmcp.generated.js +5 -5
package/client.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ import type { ListEnumValuesOptions, ListEnumValuesResult } from "./list-enum-va
|
|
|
16
16
|
import type { ListSampledTogetherPairsInput, ListSampledTogetherPairsOutput } from "./list-sampled-together-pairs-types.js";
|
|
17
17
|
import type { ListVariantFacetsInput, ListVariantFacetsOutput } from "./list-variant-facets-types.js";
|
|
18
18
|
import type { ListVariantsInput, ListVariantsOutput } from "./list-variants-types.js";
|
|
19
|
+
import type { MatchPhotoEvent, MatchPhotoInput } from "./match-photo-types.js";
|
|
19
20
|
import type { ListVariantAssetsOptions, MaterialDocumentPageOptions, VariantAssetPage } from "./material-asset-types.js";
|
|
20
21
|
import type { ResolveVariantInput, ResolveVariantOutput } from "./resolve-variant-types.js";
|
|
21
22
|
import type { SchemaResource, SchemaResourceResponse } from "./schema-resource-types.js";
|
|
@@ -261,6 +262,21 @@ export declare class MaterialGraphClient {
|
|
|
261
262
|
* its vector kept, so a repeat image-only query on it spends nothing.
|
|
262
263
|
*/
|
|
263
264
|
findVariantsByVector(input: FindVariantsByVectorInput, signal?: AbortSignal): Promise<FindVariantsByVectorOutput>;
|
|
265
|
+
/**
|
|
266
|
+
* THE QUESTION: "what are the surfaces in this room, and what matches each?"
|
|
267
|
+
* (POST `/api/v1/match`). Reads the photo's surfaces and streams, for each,
|
|
268
|
+
* its `finish` (label, product-type reading, palette and boxes), its crop
|
|
269
|
+
* `colors`, a provisional color list and the final color-and-look list of up
|
|
270
|
+
* to 24 variants, then `done`. Send the photo's bytes or one image reference
|
|
271
|
+
* as {@link MaterialGraphClient.findVariantsByVector} takes. Spends the key's
|
|
272
|
+
* `embed` limit: one for the photo and one per surface. About 10 to 15 s.
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* for await (const event of mg.matchPhoto({ image: file })) {
|
|
276
|
+
* if (event.type === "matches" && event.final) show(event.finishId, event.matches);
|
|
277
|
+
* }
|
|
278
|
+
*/
|
|
279
|
+
matchPhoto(input: MatchPhotoInput, signal?: AbortSignal): AsyncGenerator<MatchPhotoEvent>;
|
|
264
280
|
/** Resolve a color name to its hex, and the named paint's catalog variant. */
|
|
265
281
|
resolveColor(name: string, signal?: AbortSignal, options?: ResolveColorOptions): Promise<ResolveColorResponse>;
|
|
266
282
|
/**
|
package/client.js
CHANGED
|
@@ -6,6 +6,7 @@ import { listAttributes as listAttributesCall, listBaselines as listBaselinesCal
|
|
|
6
6
|
import { ColorsClient } from "./colors-client.js";
|
|
7
7
|
import { MaterialGraphError } from "./errors.js";
|
|
8
8
|
import { HttpTransport } from "./http-transport.js";
|
|
9
|
+
import { matchPhoto } from "./match-photo.js";
|
|
9
10
|
import { listVariantAssets as listVariantAssetsCall, materialBlocks as materialBlocksCall, materialDocuments as materialDocumentsCall, } from "./material-resources-client.js";
|
|
10
11
|
// Aliased to `operation` — the local name every call site here already used.
|
|
11
12
|
import { resolveOperation as operation } from "./operations.js";
|
|
@@ -406,6 +407,23 @@ export class MaterialGraphClient {
|
|
|
406
407
|
async findVariantsByVector(input, signal) {
|
|
407
408
|
return await this.#post("find_variants_by_vector", input, signal);
|
|
408
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* THE QUESTION: "what are the surfaces in this room, and what matches each?"
|
|
412
|
+
* (POST `/api/v1/match`). Reads the photo's surfaces and streams, for each,
|
|
413
|
+
* its `finish` (label, product-type reading, palette and boxes), its crop
|
|
414
|
+
* `colors`, a provisional color list and the final color-and-look list of up
|
|
415
|
+
* to 24 variants, then `done`. Send the photo's bytes or one image reference
|
|
416
|
+
* as {@link MaterialGraphClient.findVariantsByVector} takes. Spends the key's
|
|
417
|
+
* `embed` limit: one for the photo and one per surface. About 10 to 15 s.
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* for await (const event of mg.matchPhoto({ image: file })) {
|
|
421
|
+
* if (event.type === "matches" && event.final) show(event.finishId, event.matches);
|
|
422
|
+
* }
|
|
423
|
+
*/
|
|
424
|
+
matchPhoto(input, signal) {
|
|
425
|
+
return matchPhoto(this.#transport, input, signal);
|
|
426
|
+
}
|
|
409
427
|
/** Resolve a color name to its hex, and the named paint's catalog variant. */
|
|
410
428
|
async resolveColor(name, signal, options) {
|
|
411
429
|
return await resolveColorCall(this.#transport, name, signal, options);
|
package/http-transport.d.ts
CHANGED
|
@@ -22,8 +22,8 @@ interface SendInput {
|
|
|
22
22
|
method: string;
|
|
23
23
|
url: string;
|
|
24
24
|
headers: Record<string, string>;
|
|
25
|
-
/** The
|
|
26
|
-
body?: string | FormData;
|
|
25
|
+
/** The bodies the SDK sends: a JSON string, a multipart `FormData`, or an image `Blob`. */
|
|
26
|
+
body?: string | FormData | Blob;
|
|
27
27
|
signal?: AbortSignal;
|
|
28
28
|
}
|
|
29
29
|
export interface HttpTransportOptions {
|
|
@@ -50,9 +50,15 @@ export declare class HttpTransport {
|
|
|
50
50
|
* Send one request, capture rate limits, read the body, and map a non-2xx or
|
|
51
51
|
* transport failure to a {@link MaterialGraphError}. The parsed JSON body (or
|
|
52
52
|
* raw text when unparseable) is returned on success. Not for streaming reads —
|
|
53
|
-
* it consumes the whole body via `response.text()
|
|
53
|
+
* it consumes the whole body via `response.text()`; see {@link stream}.
|
|
54
54
|
*/
|
|
55
55
|
send(input: SendInput): Promise<SentResponse>;
|
|
56
|
+
/**
|
|
57
|
+
* Send one request whose 2xx body is read as it arrives. A non-2xx or
|
|
58
|
+
* transport failure is the same {@link MaterialGraphError} {@link send}
|
|
59
|
+
* throws; a 2xx response is returned with its body unread.
|
|
60
|
+
*/
|
|
61
|
+
stream(input: SendInput): Promise<Response>;
|
|
56
62
|
/** Execute a registered JSON operation and return its typed body. */
|
|
57
63
|
requestJson<K extends JsonOperationId>(sdkOperation: SdkOperation<K>, options?: RequestOptions): Promise<OperationResponses[K]>;
|
|
58
64
|
}
|
package/http-transport.js
CHANGED
|
@@ -9,6 +9,18 @@ import { MaterialGraphError } from "./errors.js";
|
|
|
9
9
|
import { parseJson } from "./json.js";
|
|
10
10
|
import { operationPath } from "./operations.js";
|
|
11
11
|
import { parseOperationResponse } from "./parse-operation-response.js";
|
|
12
|
+
/** The body as JSON, its raw text when unparseable, or undefined when empty. */
|
|
13
|
+
const parseBody = (text) => {
|
|
14
|
+
if (text.length === 0) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return parseJson(text);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return text;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
12
24
|
const parseNumberOrNull = (value) => {
|
|
13
25
|
if (value === null || value.trim() === "") {
|
|
14
26
|
return null;
|
|
@@ -83,13 +95,8 @@ export class HttpTransport {
|
|
|
83
95
|
}
|
|
84
96
|
return url.toString();
|
|
85
97
|
}
|
|
86
|
-
/**
|
|
87
|
-
|
|
88
|
-
* transport failure to a {@link MaterialGraphError}. The parsed JSON body (or
|
|
89
|
-
* raw text when unparseable) is returned on success. Not for streaming reads —
|
|
90
|
-
* it consumes the whole body via `response.text()`.
|
|
91
|
-
*/
|
|
92
|
-
async send(input) {
|
|
98
|
+
/** Fetch, capture rate limits, and map a transport failure; the body is unread. */
|
|
99
|
+
async #open(input) {
|
|
93
100
|
const requestUrl = new URL(input.url);
|
|
94
101
|
const metadata = {
|
|
95
102
|
method: input.method,
|
|
@@ -116,39 +123,58 @@ export class HttpTransport {
|
|
|
116
123
|
}
|
|
117
124
|
const rateLimit = parseRateLimit(response.headers);
|
|
118
125
|
this.#lastRateLimit = rateLimit;
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
text = await response.text();
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
throw MaterialGraphError.fromTransport({
|
|
125
|
-
...metadata,
|
|
126
|
-
callerSignal: signal,
|
|
127
|
-
cause: error,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
let parsed;
|
|
131
|
-
if (text.length > 0) {
|
|
126
|
+
const readText = async () => {
|
|
132
127
|
try {
|
|
133
|
-
|
|
128
|
+
return await response.text();
|
|
134
129
|
}
|
|
135
|
-
catch {
|
|
136
|
-
|
|
130
|
+
catch (error) {
|
|
131
|
+
throw MaterialGraphError.fromTransport({
|
|
132
|
+
...metadata,
|
|
133
|
+
callerSignal: signal,
|
|
134
|
+
cause: error,
|
|
135
|
+
});
|
|
137
136
|
}
|
|
138
|
-
}
|
|
137
|
+
};
|
|
138
|
+
/** The {@link MaterialGraphError} for a non-2xx response. */
|
|
139
|
+
const failure = (text, parsed) => MaterialGraphError.fromResponse({
|
|
140
|
+
...metadata,
|
|
141
|
+
apiKey: this.apiKey,
|
|
142
|
+
body: parsed,
|
|
143
|
+
queryValues: [...requestUrl.searchParams.values()],
|
|
144
|
+
rateLimit,
|
|
145
|
+
responseText: text,
|
|
146
|
+
status: response.status,
|
|
147
|
+
});
|
|
148
|
+
return { failure, readText, response };
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Send one request, capture rate limits, read the body, and map a non-2xx or
|
|
152
|
+
* transport failure to a {@link MaterialGraphError}. The parsed JSON body (or
|
|
153
|
+
* raw text when unparseable) is returned on success. Not for streaming reads —
|
|
154
|
+
* it consumes the whole body via `response.text()`; see {@link stream}.
|
|
155
|
+
*/
|
|
156
|
+
async send(input) {
|
|
157
|
+
const { failure, readText, response } = await this.#open(input);
|
|
158
|
+
const text = await readText();
|
|
159
|
+
const parsed = parseBody(text);
|
|
139
160
|
if (!response.ok) {
|
|
140
|
-
throw
|
|
141
|
-
...metadata,
|
|
142
|
-
apiKey: this.apiKey,
|
|
143
|
-
body: parsed,
|
|
144
|
-
queryValues: [...requestUrl.searchParams.values()],
|
|
145
|
-
rateLimit,
|
|
146
|
-
responseText: text,
|
|
147
|
-
status: response.status,
|
|
148
|
-
});
|
|
161
|
+
throw failure(text, parsed);
|
|
149
162
|
}
|
|
150
163
|
return { parsed, response, text };
|
|
151
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Send one request whose 2xx body is read as it arrives. A non-2xx or
|
|
167
|
+
* transport failure is the same {@link MaterialGraphError} {@link send}
|
|
168
|
+
* throws; a 2xx response is returned with its body unread.
|
|
169
|
+
*/
|
|
170
|
+
async stream(input) {
|
|
171
|
+
const { failure, readText, response } = await this.#open(input);
|
|
172
|
+
if (response.ok) {
|
|
173
|
+
return response;
|
|
174
|
+
}
|
|
175
|
+
const text = await readText();
|
|
176
|
+
throw failure(text, parseBody(text));
|
|
177
|
+
}
|
|
152
178
|
/** Execute a registered JSON operation and return its typed body. */
|
|
153
179
|
async requestJson(sdkOperation, options = {}) {
|
|
154
180
|
const url = this.buildUrl(operationPath(sdkOperation, options.pathParams), options.query);
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { DiscoveryReference, DiscoveryTerms, InterpretDiscoveryTermsInput, ColorlessVariantsResult, VariantSibling, } from "./discovery-context-types.js";
|
|
2
2
|
export { MaterialGraphClient, DEFAULT_BASE_URL } from "./client.js";
|
|
3
3
|
export type { MaterialGraphClientOptions } from "./client.js";
|
|
4
|
-
export type { FilterOp, FilterTermCondition, FilterNumericCondition, FilterPresenceCondition, FilterAnyFieldCondition, FilterCondition, FilterGroups, OptionalFilterGroups, OrderByEntry, ListVariantsInclude, ListVariantsCollapse, ColorRegion, ColorRangeCondition, ListVariantsInput, ListVariantsPreference, ListVariantsSpecifications, ListVariantsNotApplicable, SampleSizeObservation, VariantObservations, SampleSubject, ListVariantsItem, ListVariantsOutput, VariantCard, } from "./list-variants-types.js";
|
|
4
|
+
export type { FilterOp, FilterTermCondition, FilterNumericCondition, FilterPresenceCondition, FilterAnyFieldCondition, FilterCondition, FilterGroups, OptionalFilterGroups, OrderByEntry, ListVariantsInclude, ListVariantsCollapse, ColorRegion, ColorRangeCondition, ListVariantsInput, ListVariantsPreference, ListVariantsSpecifications, ListVariantsNotApplicable, SampleSizeObservation, VariantObservations, SampleSubject, ListVariantsItem, Market, ListVariantsOutput, VariantCard, } from "./list-variants-types.js";
|
|
5
5
|
export type { ListVariantFacetsInput, VariantFacetValue, CategoricalVariantFacet, BooleanVariantFacet, NumericVariantFacet, VariantFacet, ListVariantFacetsOutput, } from "./list-variant-facets-types.js";
|
|
6
6
|
export type { SearchVariantsSelection, SearchVariantsInput, SearchVariantsUnderstood, SearchVariantsReading, SearchVariantsSuggestion, SearchVariantsChoice, SearchVariantsOutput, } from "./search-variants-types.js";
|
|
7
7
|
export type { Baseline, BaselineResult, BaselineSource, BaselineStrength, BaselineWayToMeet, CheckBaselinesInput, CheckBaselinesResult, ListBaselinesOptions, ListBaselinesResult, } from "./baseline-types.js";
|
|
@@ -13,6 +13,7 @@ export type { GetVariantDemandInput, GetVariantDemandOutput, DemandTrend, Varian
|
|
|
13
13
|
export type { ListSampledTogetherPairsInput, ListSampledTogetherPairsOutput, SampledTogetherPair, } from "./list-sampled-together-pairs-types.js";
|
|
14
14
|
export type { FindVariantsByColorMode, ColorConditionInput, FindVariantsByColorInput, ColorConditionReceipt, PaintCatalogVariant, ResolvedColor, ColorAlternative, UnresolvedColor, ColorVariantMatch, ColorPageInfo, FindVariantsByColorContainsResult, FindVariantsByColorPaletteResult, ColorRankedSeed, ColorRankMatch, ResolvedColorReference, CompareColorSimilarityReference, FindVariantsByColorRerankResult, FindVariantsByColorOutput, } from "./find-variants-by-color-types.js";
|
|
15
15
|
export { FIND_VARIANTS_BY_VECTOR_MODES } from "./find-variants-by-vector-types.js";
|
|
16
|
+
export type { MatchPhotoEvent, MatchPhotoInput, MatchPhotoMatch, MatchPhotoRegion, MatchPhotoSurface, } from "./match-photo-types.js";
|
|
16
17
|
export type { FindVariantsByVectorMode, FindVariantsByVectorQuery, FindVariantsByVectorInput, FindVariantsByVectorItem, FindVariantsByVectorReference, FindVariantsByVectorScan, FindVariantsByVectorTimings, FindVariantsByVectorOutput, } from "./find-variants-by-vector-types.js";
|
|
17
18
|
export { MATERIALGRAPH_API_VERSION, SDK_OPERATIONS } from "./operations.js";
|
|
18
19
|
export type { SdkOperation, SdkOperationId } from "./operations.js";
|
package/list-variants-types.d.ts
CHANGED
|
@@ -93,7 +93,7 @@ export interface OptionalFilterGroups {
|
|
|
93
93
|
}
|
|
94
94
|
export interface OrderByEntry {
|
|
95
95
|
direction: "asc" | "desc";
|
|
96
|
-
/** A numeric attribute id. */
|
|
96
|
+
/** A single-valued numeric attribute id (`getAttribute` says orderable), or `color_lightness` or `color_chroma`: the lightness and chroma of the variant's dominant measured color. */
|
|
97
97
|
field: string;
|
|
98
98
|
}
|
|
99
99
|
export type ListVariantsInclude = "image" | "url";
|
|
@@ -196,6 +196,8 @@ export interface ListVariantsNotApplicable {
|
|
|
196
196
|
}
|
|
197
197
|
/** What a sample represents, independently of its specification type. */
|
|
198
198
|
export type SampleSubject = "material" | "paint_color_sample" | "unknown" | "conflict";
|
|
199
|
+
/** A Material Bank market: North America or Europe. */
|
|
200
|
+
export type Market = "na" | "eu";
|
|
199
201
|
/**
|
|
200
202
|
* One list item: the compact identity needed to pick a variant, with image
|
|
201
203
|
* and brand page only when requested (`include`) and selected attributes
|
|
@@ -234,6 +236,8 @@ export interface ListVariantsItem {
|
|
|
234
236
|
};
|
|
235
237
|
/** The variant's Material Bank SKU. Absent means none is recorded. */
|
|
236
238
|
materialBankId?: string;
|
|
239
|
+
/** The Material Bank markets it is orderable in: `na`, `eu`, both or neither. The `market` filter field takes them. */
|
|
240
|
+
markets: Market[];
|
|
237
241
|
name: string;
|
|
238
242
|
/** The trimmed source variant label; null when unknown or for a paint color sample, whose own name identifies its color. Separate from surface finish, color family and derived color names. */
|
|
239
243
|
variantLabel: string | null;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module match-photo-types
|
|
3
|
+
* @description `match_photo` (POST `/api/v1/match`): the stream of a room
|
|
4
|
+
* photo's surfaces and their matches, one {@link MatchPhotoEvent} a line.
|
|
5
|
+
*/
|
|
6
|
+
import type { ImageReferenceInput } from "./image-reference-types.js";
|
|
7
|
+
/** The photo: its bytes, or one image reference as `findVariantsByVector` takes. */
|
|
8
|
+
export type MatchPhotoInput = {
|
|
9
|
+
/** The photo's bytes, at most 8 MB (4.5 MB is the platform's body limit). */
|
|
10
|
+
readonly image: Blob | Uint8Array | ArrayBuffer;
|
|
11
|
+
/** The image's media type, such as `image/jpeg`; a `Blob`'s own type when omitted. */
|
|
12
|
+
readonly contentType?: string;
|
|
13
|
+
} | {
|
|
14
|
+
readonly reference: ImageReferenceInput;
|
|
15
|
+
};
|
|
16
|
+
/** A box in the photo, as fractions of its width and height (0 to 1). */
|
|
17
|
+
export interface MatchPhotoRegion {
|
|
18
|
+
readonly x: number;
|
|
19
|
+
readonly y: number;
|
|
20
|
+
readonly width: number;
|
|
21
|
+
readonly height: number;
|
|
22
|
+
}
|
|
23
|
+
/** One surface seen in the photo: its material and color reading, and where it sits. */
|
|
24
|
+
export interface MatchPhotoSurface {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
/** The surface, in the model's words: "Back wall". */
|
|
27
|
+
readonly label: string;
|
|
28
|
+
readonly description: string;
|
|
29
|
+
/** Product types, most likely first. */
|
|
30
|
+
readonly categories: readonly {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly name: string;
|
|
33
|
+
}[];
|
|
34
|
+
readonly palette: readonly {
|
|
35
|
+
readonly hex: string;
|
|
36
|
+
readonly weight: number;
|
|
37
|
+
}[];
|
|
38
|
+
/** Where it sits; the first is the largest. */
|
|
39
|
+
readonly regions: readonly MatchPhotoRegion[];
|
|
40
|
+
}
|
|
41
|
+
/** One variant offered for a surface. */
|
|
42
|
+
export interface MatchPhotoMatch {
|
|
43
|
+
readonly variantId: string;
|
|
44
|
+
readonly productName: string;
|
|
45
|
+
readonly variantName: string | null;
|
|
46
|
+
readonly brandName: string;
|
|
47
|
+
readonly imageUrl: string | null;
|
|
48
|
+
/** The combined match, 0 to 100: color and look, or color alone when `basis` is `"color"`. */
|
|
49
|
+
readonly percent: number;
|
|
50
|
+
/** The variant's own category when it is a look-alike from another; null when it is the surface's own. */
|
|
51
|
+
readonly lookalikeType: string | null;
|
|
52
|
+
/** Other colorways of the same product that also matched, not listed. */
|
|
53
|
+
readonly otherColorways: number;
|
|
54
|
+
}
|
|
55
|
+
export type MatchPhotoEvent = {
|
|
56
|
+
readonly type: "query-interpretation";
|
|
57
|
+
} | {
|
|
58
|
+
readonly type: "finish";
|
|
59
|
+
readonly finish: MatchPhotoSurface;
|
|
60
|
+
} | {
|
|
61
|
+
readonly type: "colors";
|
|
62
|
+
readonly finishId: string;
|
|
63
|
+
readonly cropColors: readonly {
|
|
64
|
+
readonly hex: string;
|
|
65
|
+
readonly share: number;
|
|
66
|
+
}[];
|
|
67
|
+
}
|
|
68
|
+
/** A later `matches` replaces the list; `final: true` completes it. */
|
|
69
|
+
| {
|
|
70
|
+
readonly type: "matches";
|
|
71
|
+
readonly finishId: string;
|
|
72
|
+
readonly basis: "color" | "color-and-look";
|
|
73
|
+
readonly final: boolean;
|
|
74
|
+
readonly matches: readonly MatchPhotoMatch[];
|
|
75
|
+
}
|
|
76
|
+
/** This surface could not be matched; the others carry on. */
|
|
77
|
+
| {
|
|
78
|
+
readonly type: "failed";
|
|
79
|
+
readonly finishId: string;
|
|
80
|
+
readonly message: string;
|
|
81
|
+
}
|
|
82
|
+
/** The run failed: nothing more arrives. */
|
|
83
|
+
| {
|
|
84
|
+
readonly type: "error";
|
|
85
|
+
readonly message: string;
|
|
86
|
+
} | {
|
|
87
|
+
readonly type: "done";
|
|
88
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/match-photo.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { HttpTransport } from "./http-transport.js";
|
|
2
|
+
import type { MatchPhotoEvent, MatchPhotoInput } from "./match-photo-types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The room photo's events, in the order the server sends them, ending with
|
|
5
|
+
* `done` or `error`. Breaking out of the loop cancels the request.
|
|
6
|
+
*
|
|
7
|
+
* @yields {MatchPhotoEvent} One event a line.
|
|
8
|
+
*/
|
|
9
|
+
export declare const matchPhoto: (transport: HttpTransport, input: MatchPhotoInput, signal?: AbortSignal) => AsyncGenerator<MatchPhotoEvent>;
|
package/match-photo.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `match_photo` over the shared {@link HttpTransport}: the photo goes up as
|
|
3
|
+
* raw bytes or a JSON reference, and the NDJSON stream comes back one event at
|
|
4
|
+
* a time. `MaterialGraphClient.matchPhoto` is a thin wrapper over it.
|
|
5
|
+
*/
|
|
6
|
+
import { MaterialGraphError } from "./errors.js";
|
|
7
|
+
import { parseJson } from "./json.js";
|
|
8
|
+
import { operationPath, resolveOperation } from "./operations.js";
|
|
9
|
+
const bodyOf = (input) => {
|
|
10
|
+
if ("reference" in input) {
|
|
11
|
+
return {
|
|
12
|
+
body: JSON.stringify({ image: input.reference }),
|
|
13
|
+
contentType: "application/json",
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const { image } = input;
|
|
17
|
+
const blob = image instanceof Blob
|
|
18
|
+
? image
|
|
19
|
+
: new Blob([
|
|
20
|
+
image instanceof ArrayBuffer ? image : new Uint8Array(image),
|
|
21
|
+
]);
|
|
22
|
+
const contentType = input.contentType ?? blob.type;
|
|
23
|
+
if (!contentType.startsWith("image/")) {
|
|
24
|
+
throw MaterialGraphError.unexpected("matchPhoto needs the image's media type, such as image/jpeg: pass contentType.");
|
|
25
|
+
}
|
|
26
|
+
return { body: blob, contentType };
|
|
27
|
+
};
|
|
28
|
+
const EVENT_TYPES = new Set([
|
|
29
|
+
"colors",
|
|
30
|
+
"done",
|
|
31
|
+
"error",
|
|
32
|
+
"failed",
|
|
33
|
+
"finish",
|
|
34
|
+
"matches",
|
|
35
|
+
"query-interpretation",
|
|
36
|
+
]);
|
|
37
|
+
/** A line the server wrote from its MatchEvent contract, told apart by `type`. */
|
|
38
|
+
const isMatchPhotoEvent = (value) => typeof value === "object" &&
|
|
39
|
+
value !== null &&
|
|
40
|
+
"type" in value &&
|
|
41
|
+
typeof value.type === "string" &&
|
|
42
|
+
EVENT_TYPES.has(value.type);
|
|
43
|
+
/**
|
|
44
|
+
* Each complete line of a byte stream, as it arrives.
|
|
45
|
+
*
|
|
46
|
+
* @yields {string} One line, without its newline.
|
|
47
|
+
*/
|
|
48
|
+
const linesOf = async function* linesOf(body) {
|
|
49
|
+
const decoder = new TextDecoder();
|
|
50
|
+
let buffered = "";
|
|
51
|
+
for await (const chunk of body) {
|
|
52
|
+
buffered += decoder.decode(chunk, { stream: true });
|
|
53
|
+
const lines = buffered.split("\n");
|
|
54
|
+
buffered = lines.pop() ?? "";
|
|
55
|
+
yield* lines.filter((line) => line !== "");
|
|
56
|
+
}
|
|
57
|
+
buffered += decoder.decode();
|
|
58
|
+
if (buffered !== "") {
|
|
59
|
+
yield buffered;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* The room photo's events, in the order the server sends them, ending with
|
|
64
|
+
* `done` or `error`. Breaking out of the loop cancels the request.
|
|
65
|
+
*
|
|
66
|
+
* @yields {MatchPhotoEvent} One event a line.
|
|
67
|
+
*/
|
|
68
|
+
export const matchPhoto = async function* matchPhoto(transport, input, signal) {
|
|
69
|
+
const { body, contentType } = bodyOf(input);
|
|
70
|
+
const response = await transport.stream({
|
|
71
|
+
body,
|
|
72
|
+
headers: transport.authHeaders({
|
|
73
|
+
Accept: "application/x-ndjson",
|
|
74
|
+
"Content-Type": contentType,
|
|
75
|
+
}),
|
|
76
|
+
method: "POST",
|
|
77
|
+
signal,
|
|
78
|
+
url: transport.buildUrl(operationPath(resolveOperation("match_photo"))),
|
|
79
|
+
});
|
|
80
|
+
if (response.body === null) {
|
|
81
|
+
throw MaterialGraphError.unexpected("match_photo answered with no body.");
|
|
82
|
+
}
|
|
83
|
+
for await (const line of linesOf(response.body)) {
|
|
84
|
+
const event = parseJson(line);
|
|
85
|
+
if (!isMatchPhotoEvent(event)) {
|
|
86
|
+
throw MaterialGraphError.unexpected(`match_photo sent a line that is not an event: ${line.slice(0, 200)}`);
|
|
87
|
+
}
|
|
88
|
+
yield event;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const MATERIALGRAPH_API_VERSION = "0.8.0";
|
|
2
|
-
declare const OPERATION_DEFINITIONS: readonly [readonly ["discoveryReference", "GET", "/api/v1/catalog/discovery-reference", "get_discovery_reference"], readonly ["interpretDiscoveryTerms", "POST", "/api/v1/catalog/interpret-terms", "interpret_discovery_terms"], readonly ["colorlessVariants", "POST", "/api/v1/variants/colorless", "find_colorless_variants"], readonly ["variantSiblings", "GET", "/api/v1/variants/{variantId}/siblings", "list_variant_siblings"], readonly ["colors.nearestPaints", "GET", "/api/v1/colors/nearest-paints", "find_nearest_paints"], readonly ["getCatalogGeneration", "GET", "/api/v1/catalog/generation", "get_catalog_generation"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["listVariantAssets", "GET", "/api/v1/variants/{variantId}/assets", "list_variant_assets"], readonly ["materialDocuments", "GET", "/api/v1/materials/{variantId}/documents", "list_material_documents"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["associatedFinishes", "GET", "/api/v1/associated/finishes", "list_associated_finishes"], readonly ["associatedProducts", "GET", "/api/v1/associated/products", "list_associated_products"], readonly ["applicability", "GET", "/api/v1/applicability", "get_applicability"], readonly ["listVariants", "GET", "/api/v1/variants", "list_variants"], readonly ["listVariantFacets", "POST", "/api/v1/variants/facets", "list_variant_facets"], readonly ["searchVariants", "POST", "/api/v1/variants/search", "search_variants"], readonly ["findVariantsByColor", "GET", "/api/v1/variants/by-color", "find_variants_by_color"], readonly ["findVariantsByVector", "POST", "/api/v1/variants/by-vector", "find_variants_by_vector"], readonly ["findSampledTogether", "GET", "/api/v1/variants/sampled-together", "find_sampled_together"], readonly ["listSampledTogetherPairs", "GET", "/api/v1/variants/sampled-together/pairs", "list_sampled_together_pairs"], readonly ["getVariantDemand", "POST", "/api/v1/demand", "get_variant_demand"], readonly ["listRisingVariants", "GET", "/api/v1/demand/rising", "list_rising_variants"], readonly ["resolveVariant", "POST", "/api/v1/variants/resolve", "resolve_variant"], readonly ["getVariant", "GET", "/api/v1/variants/{variantId}", "get_variant"], readonly ["resolveColor", "GET", "/api/v1/search/colors/resolve", "resolve_color_name"], readonly ["listAttributes", "GET", "/api/v1/catalog/attributes", "catalog_list_attributes"], readonly ["listEnumValues", "GET", "/api/v1/catalog/enum-values", "catalog_list_enum_values"], readonly ["listBaselines", "GET", "/api/v1/baselines", "list_baselines"], readonly ["checkBaselines", "POST", "/api/v1/baselines/check", "check_baselines"], readonly ["categories.list", "GET", "/api/v1/categories", "list_categories"], readonly ["categories.get", "GET", "/api/v1/categories/{id}", "get_category"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["colors.profiles", "GET", "/api/v1/colors/profiles", "get_category_color_profiles"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"]];
|
|
2
|
+
declare const OPERATION_DEFINITIONS: readonly [readonly ["discoveryReference", "GET", "/api/v1/catalog/discovery-reference", "get_discovery_reference"], readonly ["interpretDiscoveryTerms", "POST", "/api/v1/catalog/interpret-terms", "interpret_discovery_terms"], readonly ["colorlessVariants", "POST", "/api/v1/variants/colorless", "find_colorless_variants"], readonly ["variantSiblings", "GET", "/api/v1/variants/{variantId}/siblings", "list_variant_siblings"], readonly ["colors.nearestPaints", "GET", "/api/v1/colors/nearest-paints", "find_nearest_paints"], readonly ["getCatalogGeneration", "GET", "/api/v1/catalog/generation", "get_catalog_generation"], readonly ["materialBlocks", "GET", "/api/v1/materials/{variantId}/blocks", "list_material_blocks"], readonly ["listVariantAssets", "GET", "/api/v1/variants/{variantId}/assets", "list_variant_assets"], readonly ["materialDocuments", "GET", "/api/v1/materials/{variantId}/documents", "list_material_documents"], readonly ["schema", "GET", "/api/v1/schema/value-dictionaries", "list_schema_value_dictionaries"], readonly ["pairings", "GET", "/api/v1/materials/{variantId}/pairings", "get_material_pairings"], readonly ["associatedFinishes", "GET", "/api/v1/associated/finishes", "list_associated_finishes"], readonly ["associatedProducts", "GET", "/api/v1/associated/products", "list_associated_products"], readonly ["applicability", "GET", "/api/v1/applicability", "get_applicability"], readonly ["listVariants", "GET", "/api/v1/variants", "list_variants"], readonly ["listVariantFacets", "POST", "/api/v1/variants/facets", "list_variant_facets"], readonly ["searchVariants", "POST", "/api/v1/variants/search", "search_variants"], readonly ["findVariantsByColor", "GET", "/api/v1/variants/by-color", "find_variants_by_color"], readonly ["findVariantsByVector", "POST", "/api/v1/variants/by-vector", "find_variants_by_vector"], readonly ["matchPhoto", "POST", "/api/v1/match", "match_photo"], readonly ["findSampledTogether", "GET", "/api/v1/variants/sampled-together", "find_sampled_together"], readonly ["listSampledTogetherPairs", "GET", "/api/v1/variants/sampled-together/pairs", "list_sampled_together_pairs"], readonly ["getVariantDemand", "POST", "/api/v1/demand", "get_variant_demand"], readonly ["listRisingVariants", "GET", "/api/v1/demand/rising", "list_rising_variants"], readonly ["resolveVariant", "POST", "/api/v1/variants/resolve", "resolve_variant"], readonly ["getVariant", "GET", "/api/v1/variants/{variantId}", "get_variant"], readonly ["resolveColor", "GET", "/api/v1/search/colors/resolve", "resolve_color_name"], readonly ["listAttributes", "GET", "/api/v1/catalog/attributes", "catalog_list_attributes"], readonly ["listEnumValues", "GET", "/api/v1/catalog/enum-values", "catalog_list_enum_values"], readonly ["listBaselines", "GET", "/api/v1/baselines", "list_baselines"], readonly ["checkBaselines", "POST", "/api/v1/baselines/check", "check_baselines"], readonly ["categories.list", "GET", "/api/v1/categories", "list_categories"], readonly ["categories.get", "GET", "/api/v1/categories/{id}", "get_category"], readonly ["whoami", "GET", "/api/v1/whoami", "whoami"], readonly ["colors.profiles", "GET", "/api/v1/colors/profiles", "get_category_color_profiles"], readonly ["extractPalette", "POST", "/api/v1/search/palettes/extract", "extract_image_palette"], readonly ["brands", "GET", "/api/v1/brands", "list_brands"], readonly ["uploadSourceMaterial", "POST", "/api/v1/source-material", "upload_source_material"]];
|
|
3
3
|
export type SdkOperationId = (typeof OPERATION_DEFINITIONS)[number][3];
|
|
4
4
|
export type SdkOperation<K extends SdkOperationId = SdkOperationId> = Readonly<{
|
|
5
5
|
clientMethod: (typeof OPERATION_DEFINITIONS)[number][0];
|
package/operations.generated.js
CHANGED