@instruments/materialgraph 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/association-types.d.ts +20 -20
- package/associations-client.d.ts +4 -4
- package/associations-client.js +3 -10
- package/attribution-types.d.ts +3 -3
- package/brand-most-sampled-types.d.ts +4 -4
- package/brand-types.d.ts +6 -6
- package/brands-client.d.ts +4 -4
- package/brands-client.js +9 -13
- package/browse-types.d.ts +8 -8
- package/by-colour-types.d.ts +18 -18
- package/by-spec-types.d.ts +14 -14
- package/catalog-discovery-types.d.ts +14 -14
- package/catalogue-client.d.ts +4 -4
- package/catalogue-client.js +38 -37
- package/catalogue-types.d.ts +4 -4
- package/citation-types.d.ts +4 -4
- package/client-browse.d.ts +4 -4
- package/client-browse.js +24 -25
- package/client-config.d.ts +2 -2
- package/client-exploration.d.ts +3 -3
- package/client-exploration.js +4 -6
- package/client-source-material-comments.d.ts +4 -4
- package/client-source-material-comments.js +4 -38
- package/client-vocabulary.d.ts +8 -8
- package/client-vocabulary.js +20 -30
- package/client.d.ts +11 -45
- package/client.js +143 -188
- package/codes-client.d.ts +2 -2
- package/codes-client.js +8 -8
- package/codes-types.d.ts +34 -34
- package/colour-aggregate-types.d.ts +24 -24
- package/colours-client.d.ts +2 -2
- package/colours-client.js +15 -15
- package/compare-types.d.ts +10 -10
- package/composition-client.d.ts +3 -3
- package/composition-client.js +8 -7
- package/composition-types.d.ts +12 -12
- package/coordinate-client.d.ts +2 -2
- package/coordinate-client.js +1 -13
- package/corpus-scope-types.d.ts +3 -3
- package/corpus-scope-types.js +1 -25
- package/correction-types.d.ts +10 -10
- package/demand-batch-types.d.ts +18 -18
- package/demand-types.d.ts +24 -24
- package/errors.d.ts +12 -12
- package/errors.js +53 -52
- package/exploration-types.d.ts +22 -22
- package/fan-deck-types.d.ts +6 -6
- package/http-transport.d.ts +13 -21
- package/http-transport.js +31 -28
- package/identify-types.d.ts +10 -10
- package/index.d.ts +0 -6
- package/index.js +0 -2
- package/match-rejection-types.d.ts +6 -6
- package/match-stream-types.d.ts +32 -32
- package/match-stream.d.ts +2 -2
- package/match-stream.js +55 -41
- package/match-types.d.ts +8 -8
- package/material-asset-types.d.ts +26 -26
- package/material-block-types.d.ts +6 -6
- package/material-query-plan-types.d.ts +26 -26
- package/material-resources-client.d.ts +5 -5
- package/material-resources-client.js +10 -20
- package/material-types.d.ts +12 -12
- package/ndjson.js +16 -8
- package/operations.d.ts +6 -6
- package/operations.js +9 -39
- package/package.json +1 -1
- package/palette-completion-types.d.ts +22 -22
- package/palette-search-types.d.ts +28 -28
- package/query-understanding-types.d.ts +38 -35
- package/render-assets-types.d.ts +8 -8
- package/resolve-client.d.ts +2 -2
- package/resolve-client.js +4 -6
- package/resolve-colour-types.d.ts +8 -8
- package/resolve-types.d.ts +10 -10
- package/run-types.d.ts +70 -70
- package/runs.d.ts +13 -26
- package/runs.js +77 -89
- package/samplize-catalogue-types.d.ts +18 -18
- package/samplize-colorvec.d.ts +20 -20
- package/samplize-colorvec.js +66 -42
- package/schema-resource-types.d.ts +38 -38
- package/schema-resource-types.js +5 -5
- package/search-card-types.d.ts +4 -4
- package/search-collapse-types.d.ts +4 -4
- package/search-mood-types.d.ts +2 -2
- package/search-refinement-types.d.ts +8 -8
- package/search-request-types.d.ts +5 -5
- package/search-response-receipt-types.d.ts +58 -42
- package/search-response-types.d.ts +2 -2
- package/search-result-validation.d.ts +3 -3
- package/search-result-validation.js +53 -36
- package/search-window-types.d.ts +2 -2
- package/slot-pool-types.d.ts +20 -20
- package/source-material-validation.d.ts +3 -3
- package/source-material-validation.js +35 -39
- package/spec-facet-types.d.ts +10 -10
- package/spec-types.d.ts +6 -6
- package/specifications.d.ts +9 -25
- package/specifications.js +15 -21
- package/sse.d.ts +2 -2
- package/sse.js +32 -16
- package/substitute-types.d.ts +22 -22
- package/suggest-types.d.ts +7 -7
- package/suggest-types.js +3 -3
- package/taxonomy-coverage-types.d.ts +8 -8
- package/types.d.ts +119 -119
- package/validation.d.ts +26 -26
- package/validation.js +97 -92
- package/webmcp.d.ts +2 -2
- package/webmcp.generated.js +94 -93
- package/project-types.d.ts +0 -79
- package/project-types.js +0 -9
- package/projects.d.ts +0 -13
- package/projects.js +0 -46
- package/run-events.d.ts +0 -106
- package/run-events.js +0 -32
- package/schedule-reference-types.d.ts +0 -46
- package/schedule-reference-types.js +0 -1
- package/schedule-references-client.d.ts +0 -14
- package/schedule-references-client.js +0 -26
- package/schedule-types.d.ts +0 -261
- package/schedule-types.js +0 -1
package/coordinate-client.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* limit, in the same per-lens shape as `brands-client` / `codes-client` /
|
|
8
8
|
* `colours-client`. Behaviour is unchanged.
|
|
9
9
|
*/
|
|
10
|
-
import { HttpTransport } from "./http-transport.js";
|
|
10
|
+
import type { HttpTransport } from "./http-transport.js";
|
|
11
11
|
import type { CoordinateInput, CoordinateResponse } from "./types.js";
|
|
12
12
|
/**
|
|
13
13
|
* THE QUESTION: "here is my whole scheme so far — what completes it?" The
|
|
@@ -20,4 +20,4 @@ import type { CoordinateInput, CoordinateResponse } from "./types.js";
|
|
|
20
20
|
* scheme is COLOURS rather than materials. Check `unresolved` before presenting:
|
|
21
21
|
* a reference that resolved to nothing is named there, not dropped.
|
|
22
22
|
*/
|
|
23
|
-
export declare
|
|
23
|
+
export declare const coordinate: (transport: HttpTransport, input: CoordinateInput, signal?: AbortSignal) => Promise<CoordinateResponse>;
|
package/coordinate-client.js
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The COORDINATION lens: given several materials already chosen, what completes
|
|
3
|
-
* them. A thin wrapper over the shared {@link HttpTransport};
|
|
4
|
-
* `MaterialGraphClient.coordinate` delegates here.
|
|
5
|
-
*
|
|
6
|
-
* Extracted from `client.ts`, which sits above the repository's source-size
|
|
7
|
-
* limit, in the same per-lens shape as `brands-client` / `codes-client` /
|
|
8
|
-
* `colours-client`. Behaviour is unchanged.
|
|
9
|
-
*/
|
|
10
|
-
import { HttpTransport } from "./http-transport.js";
|
|
11
1
|
import { resolveOperation } from "./operations.js";
|
|
12
2
|
/**
|
|
13
3
|
* THE QUESTION: "here is my whole scheme so far — what completes it?" The
|
|
@@ -20,6 +10,4 @@ import { resolveOperation } from "./operations.js";
|
|
|
20
10
|
* scheme is COLOURS rather than materials. Check `unresolved` before presenting:
|
|
21
11
|
* a reference that resolved to nothing is named there, not dropped.
|
|
22
12
|
*/
|
|
23
|
-
export
|
|
24
|
-
return transport.requestJson(resolveOperation("coordinate_materials"), { body: input, signal });
|
|
25
|
-
}
|
|
13
|
+
export const coordinate = async (transport, input, signal) => await transport.requestJson(resolveOperation("coordinate_materials"), { body: input, signal });
|
package/corpus-scope-types.d.ts
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* and this build does not, so a new scope is not a compile error in a client
|
|
23
23
|
* that simply has not been republished yet.
|
|
24
24
|
*/
|
|
25
|
-
import {
|
|
25
|
+
import type { OpenEnum } from "./types.js";
|
|
26
26
|
/**
|
|
27
27
|
* Every corpus a caller may ask for.
|
|
28
28
|
*
|
|
@@ -50,8 +50,8 @@ export type CorpusScope = OpenEnum<"samplizeCatalogue" | "all" | "designShopOrde
|
|
|
50
50
|
* the counts: a short page under a scope is a small SCOPED corpus, never a
|
|
51
51
|
* truncated one.
|
|
52
52
|
*/
|
|
53
|
-
export
|
|
53
|
+
export type CorpusScopeReceipt = {
|
|
54
54
|
scope: CorpusScope;
|
|
55
55
|
/** What the scope admits, in one sentence, for a human reader. */
|
|
56
56
|
description: string;
|
|
57
|
-
}
|
|
57
|
+
};
|
package/corpus-scope-types.js
CHANGED
|
@@ -1,25 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* The corpus scope vocabulary, declared ONCE for the whole client.
|
|
3
|
-
*
|
|
4
|
-
* ── Why this file exists ──
|
|
5
|
-
* Until MG-1168 the union was spelled inline at six places across the SDK, and
|
|
6
|
-
* five of them had gone stale: `designShopOrderable` shipped in MG-1144 and
|
|
7
|
-
* reached only the search request type, so a TypeScript consumer could send the
|
|
8
|
-
* scope to `/search` and not to `/browse` — against a server that had accepted
|
|
9
|
-
* it on both since the day it landed. The narrowed RECEIPT types were worse
|
|
10
|
-
* than the request ones, because a caller could not even name the scope it got
|
|
11
|
-
* back.
|
|
12
|
-
*
|
|
13
|
-
* That is the third hop of the reachability checklist failing quietly: server
|
|
14
|
-
* correct, published client unable to reach the surface. One alias means the
|
|
15
|
-
* next member is one edit here rather than six edits nobody is reminded to
|
|
16
|
-
* make. `corpus-scope-parity.test.ts` in `apps/web/config` fails if this file
|
|
17
|
-
* falls behind `CORPUS_SCOPES`.
|
|
18
|
-
*
|
|
19
|
-
* ── Why `OpenEnum` ──
|
|
20
|
-
* The server may add a scope before a consumer upgrades. `OpenEnum` keeps the
|
|
21
|
-
* known members as autocomplete while still accepting a string the server knows
|
|
22
|
-
* and this build does not, so a new scope is not a compile error in a client
|
|
23
|
-
* that simply has not been republished yet.
|
|
24
|
-
*/
|
|
25
|
-
import {} from "./types.js";
|
|
1
|
+
export {};
|
package/correction-types.d.ts
CHANGED
|
@@ -27,20 +27,20 @@ export type CorrectionTarget = {
|
|
|
27
27
|
url: string;
|
|
28
28
|
};
|
|
29
29
|
/** Caller-supplied evidence is not an independently verified graph reading. */
|
|
30
|
-
export
|
|
30
|
+
export type CorrectionEvidence = {
|
|
31
31
|
state: "known" | "unknown" | "conflicting" | "not_applicable";
|
|
32
32
|
value?: CorrectionJson;
|
|
33
33
|
sourceUrl?: string;
|
|
34
34
|
note?: string;
|
|
35
|
-
}
|
|
36
|
-
export
|
|
35
|
+
};
|
|
36
|
+
export type CreateCorrectionInput = {
|
|
37
37
|
target: CorrectionTarget;
|
|
38
38
|
pageEvidence: CorrectionEvidence;
|
|
39
39
|
graphEvidence: CorrectionEvidence;
|
|
40
40
|
proposedChange: CorrectionJson;
|
|
41
41
|
rationale: string;
|
|
42
|
-
}
|
|
43
|
-
export
|
|
42
|
+
};
|
|
43
|
+
export type Correction = {
|
|
44
44
|
id: string;
|
|
45
45
|
submission: CreateCorrectionInput;
|
|
46
46
|
status: "submitted" | "in_review" | "resolved" | "withdrawn";
|
|
@@ -48,8 +48,8 @@ export interface Correction {
|
|
|
48
48
|
createdAt: string;
|
|
49
49
|
updatedAt: string;
|
|
50
50
|
acceptedTruthChanged: false;
|
|
51
|
-
}
|
|
52
|
-
export
|
|
51
|
+
};
|
|
52
|
+
export type CorrectionResponse = {
|
|
53
53
|
data: Correction;
|
|
54
54
|
meta: {
|
|
55
55
|
operationId: "corrections.create";
|
|
@@ -65,8 +65,8 @@ export interface CorrectionResponse {
|
|
|
65
65
|
self: string;
|
|
66
66
|
};
|
|
67
67
|
warnings: never[];
|
|
68
|
-
}
|
|
69
|
-
export
|
|
68
|
+
};
|
|
69
|
+
export type CorrectionsListResponse = {
|
|
70
70
|
data: Correction[];
|
|
71
71
|
meta: {
|
|
72
72
|
operationId: "corrections.list";
|
|
@@ -85,4 +85,4 @@ export interface CorrectionsListResponse {
|
|
|
85
85
|
next: string | null;
|
|
86
86
|
};
|
|
87
87
|
warnings: never[];
|
|
88
|
-
}
|
|
88
|
+
};
|
package/demand-batch-types.d.ts
CHANGED
|
@@ -18,35 +18,35 @@ import type { DemandMomentum, DemandQuarter, DemandSignal, OpenEnum } from "./ty
|
|
|
18
18
|
* Body for `POST /api/v1/demand/batch` — enrich 1–48 already-selected candidate
|
|
19
19
|
* ids with their real specification demand, in input order and cardinality.
|
|
20
20
|
*/
|
|
21
|
-
export
|
|
21
|
+
export type DemandBatchInput = {
|
|
22
22
|
/** 1–48 canonical variant ids. Order AND cardinality are preserved — duplicates are echoed, never collapsed. */
|
|
23
23
|
variantIds: VariantId[];
|
|
24
24
|
/** The behavioural signal to read demand from. Defaults to `sampled` (order co-occurrence) server-side. */
|
|
25
25
|
signal?: DemandSignal;
|
|
26
26
|
/** Optional declared context (e.g. `{ projectType: "hospitality" }`) — echoed for provenance, never used to rank or filter. */
|
|
27
27
|
context?: Record<string, string>;
|
|
28
|
-
}
|
|
28
|
+
};
|
|
29
29
|
/** Headline population behind the metric — this variant's own counts. */
|
|
30
|
-
export
|
|
30
|
+
export type DemandBatchPopulation = {
|
|
31
31
|
specCount: number;
|
|
32
32
|
projectCount: number;
|
|
33
33
|
customerCount: number;
|
|
34
|
-
}
|
|
34
|
+
};
|
|
35
35
|
/** The consideration→specification funnel (project-grain; `exclusionRate` is annotate-only). */
|
|
36
|
-
export
|
|
36
|
+
export type DemandBatchFunnel = {
|
|
37
37
|
consideredProjects: number;
|
|
38
38
|
specifiedProjects: number;
|
|
39
39
|
excludedCount: number;
|
|
40
40
|
exclusionRate: number;
|
|
41
|
-
}
|
|
41
|
+
};
|
|
42
42
|
/** Per-result guardrail status — present on `internal` (`mg:admin`) results only. */
|
|
43
|
-
export
|
|
43
|
+
export type DemandBatchEvidence = {
|
|
44
44
|
belowPrivacyFloor: boolean;
|
|
45
45
|
belowQualityFloor: boolean;
|
|
46
46
|
suppressedForPublic: boolean;
|
|
47
|
-
}
|
|
47
|
+
};
|
|
48
48
|
/** A variant that carries a usable demand signal. */
|
|
49
|
-
export
|
|
49
|
+
export type DemandBatchAvailableResult = {
|
|
50
50
|
variantId: VariantId;
|
|
51
51
|
state: "available";
|
|
52
52
|
population: DemandBatchPopulation;
|
|
@@ -57,28 +57,28 @@ export interface DemandBatchAvailableResult {
|
|
|
57
57
|
funnel: DemandBatchFunnel | null;
|
|
58
58
|
/** Exact guardrail evidence for `internal` callers; null for public. */
|
|
59
59
|
evidence: DemandBatchEvidence | null;
|
|
60
|
-
}
|
|
60
|
+
};
|
|
61
61
|
/**
|
|
62
62
|
* A variant with a stats row that is below the public quality floor. We HAVE a
|
|
63
63
|
* reading and are declining to publish it — not a claim that nobody specifies it.
|
|
64
64
|
*/
|
|
65
|
-
export
|
|
65
|
+
export type DemandBatchInsufficientResult = {
|
|
66
66
|
variantId: VariantId;
|
|
67
67
|
state: "insufficient_data";
|
|
68
|
-
}
|
|
68
|
+
};
|
|
69
69
|
/**
|
|
70
70
|
* A variant with no stats — indistinguishable from a privacy-suppressed one for
|
|
71
71
|
* public callers, and therefore never reportable as "nobody specifies this".
|
|
72
72
|
*/
|
|
73
|
-
export
|
|
73
|
+
export type DemandBatchNoSignalResult = {
|
|
74
74
|
variantId: VariantId;
|
|
75
75
|
state: "no_signal";
|
|
76
|
-
}
|
|
76
|
+
};
|
|
77
77
|
/** A requested id that resolves to no real variant. */
|
|
78
|
-
export
|
|
78
|
+
export type DemandBatchUnknownResult = {
|
|
79
79
|
variantId: VariantId;
|
|
80
80
|
state: "unknown_variant";
|
|
81
|
-
}
|
|
81
|
+
};
|
|
82
82
|
/**
|
|
83
83
|
* One discriminated result per input id, in input order and cardinality.
|
|
84
84
|
*
|
|
@@ -88,7 +88,7 @@ export interface DemandBatchUnknownResult {
|
|
|
88
88
|
*/
|
|
89
89
|
export type DemandBatchResult = DemandBatchAvailableResult | DemandBatchInsufficientResult | DemandBatchNoSignalResult | DemandBatchUnknownResult;
|
|
90
90
|
/** The `POST /api/v1/demand/batch` envelope. */
|
|
91
|
-
export
|
|
91
|
+
export type DemandBatchResponse = {
|
|
92
92
|
/** The completed ingestion-session id these counts were mined in (the dataset generation). */
|
|
93
93
|
generationId: string;
|
|
94
94
|
metric: "specification_demand";
|
|
@@ -115,4 +115,4 @@ export interface DemandBatchResponse {
|
|
|
115
115
|
/** The echoed declared context, or null when none was supplied. */
|
|
116
116
|
context: Record<string, string> | null;
|
|
117
117
|
results: DemandBatchResult[];
|
|
118
|
-
}
|
|
118
|
+
};
|
package/demand-types.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import type { ProductId, VariantId } from "./identifiers.js";
|
|
16
16
|
import type { CompanionDemand, DemandMomentum, DemandQuarter, OpenEnum } from "./types.js";
|
|
17
17
|
/** A resolved seed material (identity block shared by the demand reads). */
|
|
18
|
-
export
|
|
18
|
+
export type DemandSeed = {
|
|
19
19
|
/** Opaque — round-trip verbatim. */
|
|
20
20
|
variantId: VariantId;
|
|
21
21
|
brand: string;
|
|
@@ -25,16 +25,16 @@ export interface DemandSeed {
|
|
|
25
25
|
imageUrl: string | null;
|
|
26
26
|
brandSlug: string;
|
|
27
27
|
productId: ProductId;
|
|
28
|
-
}
|
|
28
|
+
};
|
|
29
29
|
/** Headline demand stats for a seed: counts, quarterly series, momentum. */
|
|
30
|
-
export
|
|
30
|
+
export type MaterialDemandStats = {
|
|
31
31
|
specCount: number;
|
|
32
32
|
projectCount: number;
|
|
33
33
|
customerCount: number;
|
|
34
34
|
/** Sparse — a quarter with no activity is ABSENT, not a zero row. */
|
|
35
35
|
byQuarter: DemandQuarter[];
|
|
36
36
|
momentum: DemandMomentum;
|
|
37
|
-
}
|
|
37
|
+
};
|
|
38
38
|
/**
|
|
39
39
|
* `getMaterialDemand` result, passed through verbatim by the `/demand` route.
|
|
40
40
|
*
|
|
@@ -42,14 +42,14 @@ export interface MaterialDemandStats {
|
|
|
42
42
|
* the publication floor. It is never a reading of zero, and never licenses
|
|
43
43
|
* "nobody specifies this".
|
|
44
44
|
*/
|
|
45
|
-
export
|
|
45
|
+
export type MaterialDemandResult = {
|
|
46
46
|
found: boolean;
|
|
47
47
|
query: string;
|
|
48
48
|
seed: DemandSeed | null;
|
|
49
49
|
/** The only window v1 serves. Activity outside it is unread, not absent. */
|
|
50
50
|
window: "trailing_36mo";
|
|
51
51
|
demand: MaterialDemandStats | null;
|
|
52
|
-
}
|
|
52
|
+
};
|
|
53
53
|
/**
|
|
54
54
|
* `getCoSpecifiedMaterials` result, passed through verbatim. The `companions`
|
|
55
55
|
* list (materials specified alongside the seed) is included but left loosely
|
|
@@ -59,7 +59,7 @@ export interface MaterialDemandResult {
|
|
|
59
59
|
* phrase matched several materials and `seedAlternatives` holds them — pick one
|
|
60
60
|
* and re-ask by `variantId` rather than treating the first as the answer.
|
|
61
61
|
*/
|
|
62
|
-
export
|
|
62
|
+
export type CoSpecifiedResult = {
|
|
63
63
|
found: boolean;
|
|
64
64
|
resolution: OpenEnum<"resolved" | "ambiguous" | "not_found">;
|
|
65
65
|
query: string;
|
|
@@ -67,25 +67,25 @@ export interface CoSpecifiedResult {
|
|
|
67
67
|
seedCategory: string | null;
|
|
68
68
|
seedAlternatives: DemandSeed[];
|
|
69
69
|
companions?: unknown[];
|
|
70
|
-
}
|
|
70
|
+
};
|
|
71
71
|
/** The `/api/v1/demand` envelope — both domain reads side by side, verbatim. */
|
|
72
|
-
export
|
|
72
|
+
export type DemandResponse = {
|
|
73
73
|
query: string;
|
|
74
74
|
demand: MaterialDemandResult;
|
|
75
75
|
cospec: CoSpecifiedResult;
|
|
76
|
-
}
|
|
76
|
+
};
|
|
77
77
|
/** Seed a demand read by EXACTLY ONE of `variantId` or `material`. */
|
|
78
|
-
export
|
|
78
|
+
export type DemandParams = {
|
|
79
79
|
/** An exact variant id. Opaque — pass through verbatim. */
|
|
80
80
|
variantId?: VariantId;
|
|
81
81
|
/** Free text ("Brand Product Colour"), resolved server-side. May be ambiguous. */
|
|
82
82
|
material?: string;
|
|
83
|
-
}
|
|
84
|
-
export
|
|
83
|
+
};
|
|
84
|
+
export type CoordinateSeed = {
|
|
85
85
|
/** The input reference this seed resolved from. */
|
|
86
86
|
resolvedFrom: string;
|
|
87
|
-
}
|
|
88
|
-
export
|
|
87
|
+
} & DemandSeed;
|
|
88
|
+
export type CoordinateCompanion = {
|
|
89
89
|
/** Opaque — round-trip verbatim. */
|
|
90
90
|
variantId: VariantId;
|
|
91
91
|
brand: string;
|
|
@@ -118,37 +118,37 @@ export interface CoordinateCompanion {
|
|
|
118
118
|
totalLift: number;
|
|
119
119
|
liftLabel: string;
|
|
120
120
|
seedMatchCount: number;
|
|
121
|
-
seedContributions:
|
|
121
|
+
seedContributions: {
|
|
122
122
|
seedVariantId: VariantId;
|
|
123
123
|
lift: number;
|
|
124
|
-
}
|
|
124
|
+
}[];
|
|
125
125
|
demand: CompanionDemand | null;
|
|
126
|
-
swatches:
|
|
126
|
+
swatches: {
|
|
127
127
|
hex: string;
|
|
128
128
|
proportion: number;
|
|
129
|
-
}
|
|
129
|
+
}[];
|
|
130
130
|
/** Annotate-only appearance similarity — never reorders companions in v1. */
|
|
131
131
|
colourFit: number | null;
|
|
132
|
-
}
|
|
132
|
+
};
|
|
133
133
|
/**
|
|
134
134
|
* The `POST /api/v1/pairings/coordinate` envelope. `unresolved` lists the input
|
|
135
135
|
* references that matched nothing — read it before presenting `companions` as
|
|
136
136
|
* "what goes with your palette", because a companion set derived from two of
|
|
137
137
|
* four seeds is a different claim.
|
|
138
138
|
*/
|
|
139
|
-
export
|
|
139
|
+
export type CoordinateResponse = {
|
|
140
140
|
seeds: CoordinateSeed[];
|
|
141
141
|
/** Input references that resolved to no material. Never silently dropped. */
|
|
142
142
|
unresolved: string[];
|
|
143
143
|
window: "trailing_36mo";
|
|
144
144
|
companions: CoordinateCompanion[];
|
|
145
|
-
}
|
|
145
|
+
};
|
|
146
146
|
/** Body for `POST /api/v1/pairings/coordinate`. */
|
|
147
|
-
export
|
|
147
|
+
export type CoordinateInput = {
|
|
148
148
|
/** Free-text material references, resolved server-side; misses land in `unresolved`. */
|
|
149
149
|
materials?: string[];
|
|
150
150
|
/** Exact variant ids. Opaque — pass through verbatim. */
|
|
151
151
|
variantIds?: VariantId[];
|
|
152
152
|
projectType?: string;
|
|
153
153
|
limit?: number;
|
|
154
|
-
}
|
|
154
|
+
};
|
package/errors.d.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
export type MaterialGraphErrorKind = "problem" | "simple" | "network" | "timeout" | "aborted" | "rate_limited" | "unexpected";
|
|
3
3
|
export type SanitizedErrorValue = null | boolean | number | string | SanitizedErrorValue[] | SanitizedIssue;
|
|
4
4
|
/** A bounded, recursively sanitized validation-issue record. */
|
|
5
|
-
export
|
|
5
|
+
export type SanitizedIssue = {
|
|
6
6
|
[key: string]: SanitizedErrorValue;
|
|
7
|
-
}
|
|
7
|
+
};
|
|
8
8
|
/** Normalized RFC 7807 body. Unknown response fields are intentionally discarded. */
|
|
9
9
|
export interface ProblemDetail {
|
|
10
10
|
type: string;
|
|
@@ -25,22 +25,22 @@ export interface ProblemDetail {
|
|
|
25
25
|
readonly [extension: string]: unknown;
|
|
26
26
|
}
|
|
27
27
|
/** Normalized residual `{ error }` body. Unknown response fields are discarded. */
|
|
28
|
-
export
|
|
28
|
+
export type SimpleErrorBody = {
|
|
29
29
|
error: string;
|
|
30
30
|
issues?: SanitizedIssue[];
|
|
31
|
-
}
|
|
31
|
+
};
|
|
32
32
|
/** Parsed `X-RateLimit-*` / `Retry-After` headers. `null` means absent. */
|
|
33
|
-
export
|
|
33
|
+
export type RateLimit = {
|
|
34
34
|
limit: number | null;
|
|
35
35
|
remaining: number | null;
|
|
36
36
|
reset: number | null;
|
|
37
37
|
retryAfter: number | null;
|
|
38
|
-
}
|
|
39
|
-
export
|
|
38
|
+
};
|
|
39
|
+
export type MaterialGraphRequestMetadata = {
|
|
40
40
|
method: string;
|
|
41
41
|
origin: string;
|
|
42
42
|
path: string;
|
|
43
|
-
}
|
|
43
|
+
};
|
|
44
44
|
export interface MaterialGraphErrorInit {
|
|
45
45
|
kind: MaterialGraphErrorKind;
|
|
46
46
|
status?: number;
|
|
@@ -55,18 +55,18 @@ export interface MaterialGraphErrorInit {
|
|
|
55
55
|
/** Accepted for compatibility but deliberately never retained on the public error. */
|
|
56
56
|
cause?: unknown;
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
type ResponseErrorInput = {
|
|
59
59
|
status: number;
|
|
60
60
|
body: unknown;
|
|
61
61
|
responseText: string;
|
|
62
62
|
rateLimit: RateLimit | null;
|
|
63
63
|
apiKey: string;
|
|
64
64
|
queryValues: readonly string[];
|
|
65
|
-
}
|
|
66
|
-
|
|
65
|
+
} & MaterialGraphRequestMetadata;
|
|
66
|
+
type TransportErrorInput = {
|
|
67
67
|
cause: unknown;
|
|
68
68
|
callerSignal?: AbortSignal;
|
|
69
|
-
}
|
|
69
|
+
} & MaterialGraphRequestMetadata;
|
|
70
70
|
/**
|
|
71
71
|
* MaterialGraph's documented problem EXTENSION members — the only non-standard
|
|
72
72
|
* keys this client retains.
|
package/errors.js
CHANGED
|
@@ -43,38 +43,36 @@ const PROBLEM_EXTENSION_KEYS = new Set(MATERIALGRAPH_PROBLEM_EXTENSIONS);
|
|
|
43
43
|
const MAX_UNEXPECTED_TEXT = 768;
|
|
44
44
|
const MAX_EXTENSION_DEPTH = 8;
|
|
45
45
|
const MAX_EXTENSION_ITEMS = 100;
|
|
46
|
-
const MAX_EXTENSION_STRING =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
];
|
|
67
|
-
}
|
|
68
|
-
function requestMetadata(input) {
|
|
69
|
-
return { method: input.method, origin: input.origin, path: input.path };
|
|
70
|
-
}
|
|
46
|
+
const MAX_EXTENSION_STRING = 1024;
|
|
47
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
48
|
+
const redact = (text, secrets) => secrets.reduce((sanitized, secret) => {
|
|
49
|
+
if (secret.length === 0) {
|
|
50
|
+
return sanitized;
|
|
51
|
+
}
|
|
52
|
+
return sanitized.split(secret).join("[REDACTED]");
|
|
53
|
+
}, text);
|
|
54
|
+
const safeString = (text, secrets) => redact(text, secrets).slice(0, MAX_EXTENSION_STRING);
|
|
55
|
+
const secretVariants = (apiKey, queryValues) => [
|
|
56
|
+
apiKey,
|
|
57
|
+
...queryValues,
|
|
58
|
+
...queryValues.map(encodeURIComponent),
|
|
59
|
+
...queryValues.map((value) => new URLSearchParams([["value", value]]).toString().slice("value=".length)),
|
|
60
|
+
];
|
|
61
|
+
const requestMetadata = (input) => ({
|
|
62
|
+
method: input.method,
|
|
63
|
+
origin: input.origin,
|
|
64
|
+
path: input.path,
|
|
65
|
+
});
|
|
71
66
|
function sanitizeValue(value, secrets, depth = 0) {
|
|
72
|
-
if (depth >= MAX_EXTENSION_DEPTH)
|
|
67
|
+
if (depth >= MAX_EXTENSION_DEPTH) {
|
|
73
68
|
return undefined;
|
|
74
|
-
|
|
69
|
+
}
|
|
70
|
+
if (value === null || typeof value === "boolean") {
|
|
75
71
|
return value;
|
|
76
|
-
|
|
72
|
+
}
|
|
73
|
+
if (typeof value === "number") {
|
|
77
74
|
return Number.isFinite(value) ? value : undefined;
|
|
75
|
+
}
|
|
78
76
|
if (typeof value === "string") {
|
|
79
77
|
return safeString(value, secrets);
|
|
80
78
|
}
|
|
@@ -84,8 +82,9 @@ function sanitizeValue(value, secrets, depth = 0) {
|
|
|
84
82
|
.map((item) => sanitizeValue(item, secrets, depth + 1))
|
|
85
83
|
.filter((item) => item !== undefined);
|
|
86
84
|
}
|
|
87
|
-
if (!isRecord(value))
|
|
85
|
+
if (!isRecord(value)) {
|
|
88
86
|
return undefined;
|
|
87
|
+
}
|
|
89
88
|
return sanitizeRecord(value, secrets, depth);
|
|
90
89
|
}
|
|
91
90
|
function sanitizeRecord(value, secrets, depth) {
|
|
@@ -99,15 +98,16 @@ function sanitizeRecord(value, secrets, depth) {
|
|
|
99
98
|
});
|
|
100
99
|
return Object.fromEntries(entries);
|
|
101
100
|
}
|
|
102
|
-
|
|
103
|
-
if (!Array.isArray(value))
|
|
101
|
+
const sanitizeIssues = (value, secrets) => {
|
|
102
|
+
if (!Array.isArray(value)) {
|
|
104
103
|
return undefined;
|
|
104
|
+
}
|
|
105
105
|
return value
|
|
106
106
|
.slice(0, MAX_EXTENSION_ITEMS)
|
|
107
107
|
.filter((issue) => isRecord(issue) && !Array.isArray(issue))
|
|
108
108
|
.map((issue) => sanitizeRecord(issue, secrets, 0));
|
|
109
|
-
}
|
|
110
|
-
|
|
109
|
+
};
|
|
110
|
+
const sanitizeSuggestions = (value, secrets) => {
|
|
111
111
|
if (!Array.isArray(value) ||
|
|
112
112
|
value.some((suggestion) => typeof suggestion !== "string")) {
|
|
113
113
|
return undefined;
|
|
@@ -115,8 +115,8 @@ function sanitizeSuggestions(value, secrets) {
|
|
|
115
115
|
return value
|
|
116
116
|
.slice(0, MAX_EXTENSION_ITEMS)
|
|
117
117
|
.map((suggestion) => safeString(suggestion, secrets));
|
|
118
|
-
}
|
|
119
|
-
|
|
118
|
+
};
|
|
119
|
+
const normalizeProblem = (body, secrets) => {
|
|
120
120
|
if (!isRecord(body) ||
|
|
121
121
|
typeof body.type !== "string" ||
|
|
122
122
|
typeof body.title !== "string" ||
|
|
@@ -150,24 +150,25 @@ function normalizeProblem(body, secrets) {
|
|
|
150
150
|
: {}),
|
|
151
151
|
...(issues === undefined ? {} : { issues }),
|
|
152
152
|
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (!isRecord(body) || typeof body.error !== "string")
|
|
153
|
+
};
|
|
154
|
+
const normalizeSimple = (body, secrets) => {
|
|
155
|
+
if (!isRecord(body) || typeof body.error !== "string") {
|
|
156
156
|
return undefined;
|
|
157
|
+
}
|
|
157
158
|
const issues = sanitizeIssues(body.issues, secrets);
|
|
158
159
|
return {
|
|
159
160
|
error: safeString(body.error, secrets),
|
|
160
161
|
...(issues === undefined ? {} : { issues }),
|
|
161
162
|
};
|
|
162
|
-
}
|
|
163
|
-
|
|
163
|
+
};
|
|
164
|
+
const unexpectedMessage = (status, responseText, secrets) => {
|
|
164
165
|
const sanitized = redact(responseText, secrets)
|
|
165
166
|
.trim()
|
|
166
167
|
.slice(0, MAX_UNEXPECTED_TEXT);
|
|
167
168
|
return sanitized.length > 0
|
|
168
169
|
? `MaterialGraph request failed with HTTP ${status}: ${sanitized}`
|
|
169
170
|
: `MaterialGraph request failed with HTTP ${status}`;
|
|
170
|
-
}
|
|
171
|
+
};
|
|
171
172
|
/** The one error class thrown by all SDK transport and HTTP failures. */
|
|
172
173
|
export class MaterialGraphError extends Error {
|
|
173
174
|
kind;
|
|
@@ -237,42 +238,42 @@ export class MaterialGraphError extends Error {
|
|
|
237
238
|
simple?.error ??
|
|
238
239
|
"MaterialGraph rate limit exceeded (429)", {
|
|
239
240
|
...metadata,
|
|
240
|
-
status: input.status,
|
|
241
|
-
rateLimit: input.rateLimit,
|
|
242
241
|
kind: "rate_limited",
|
|
243
|
-
retriable: false,
|
|
244
|
-
traceId: problem?.trace_id,
|
|
245
242
|
problem,
|
|
243
|
+
rateLimit: input.rateLimit,
|
|
244
|
+
retriable: false,
|
|
246
245
|
simple,
|
|
246
|
+
status: input.status,
|
|
247
|
+
traceId: problem?.trace_id,
|
|
247
248
|
});
|
|
248
249
|
}
|
|
249
250
|
if (problem !== undefined) {
|
|
250
251
|
return new MaterialGraphError(problem.detail || problem.title, {
|
|
251
252
|
...metadata,
|
|
252
|
-
status: input.status,
|
|
253
|
-
rateLimit: input.rateLimit,
|
|
254
253
|
kind: "problem",
|
|
254
|
+
problem,
|
|
255
|
+
rateLimit: input.rateLimit,
|
|
255
256
|
retriable: retriable || problem.is_retriable,
|
|
257
|
+
status: input.status,
|
|
256
258
|
traceId: problem.trace_id,
|
|
257
|
-
problem,
|
|
258
259
|
});
|
|
259
260
|
}
|
|
260
261
|
if (simple !== undefined) {
|
|
261
262
|
return new MaterialGraphError(simple.error, {
|
|
262
263
|
...metadata,
|
|
263
|
-
status: input.status,
|
|
264
|
-
rateLimit: input.rateLimit,
|
|
265
264
|
kind: "simple",
|
|
265
|
+
rateLimit: input.rateLimit,
|
|
266
266
|
retriable,
|
|
267
267
|
simple,
|
|
268
|
+
status: input.status,
|
|
268
269
|
});
|
|
269
270
|
}
|
|
270
271
|
return new MaterialGraphError(unexpectedMessage(input.status, input.responseText, secrets), {
|
|
271
272
|
...metadata,
|
|
272
273
|
kind: "unexpected",
|
|
273
|
-
status: input.status,
|
|
274
|
-
retriable,
|
|
275
274
|
rateLimit: input.rateLimit,
|
|
275
|
+
retriable,
|
|
276
|
+
status: input.status,
|
|
276
277
|
});
|
|
277
278
|
}
|
|
278
279
|
}
|