@instruments/materialgraph 3.0.2 → 3.2.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/brand-types.d.ts +6 -6
- package/by-spec-types.d.ts +6 -6
- package/catalog-discovery-types.d.ts +4 -4
- package/categories-client.d.ts +3 -3
- package/category-types.d.ts +18 -18
- package/client-config.d.ts +2 -2
- package/client-response-schema.d.ts +2 -2
- package/client-response-schema.js +4 -4
- package/client-vocabulary.js +1 -1
- package/client.d.ts +1 -1
- package/client.js +14 -10
- package/color-profiles-types.d.ts +8 -8
- package/color-wire-types.d.ts +6 -6
- package/colors-client.d.ts +3 -3
- package/discovery-context-types.d.ts +8 -8
- package/errors.d.ts +12 -13
- package/errors.js +84 -74
- package/find-sampled-together-types.d.ts +6 -6
- package/find-variants-by-color-types.d.ts +30 -30
- package/find-variants-by-vector-types.d.ts +13 -13
- package/get-variant-types.d.ts +11 -7
- package/http-transport.d.ts +14 -10
- package/http-transport.js +14 -12
- package/image-reference-types.d.ts +2 -2
- package/index.d.ts +2 -2
- package/json.d.ts +18 -0
- package/json.js +36 -0
- package/list-enum-values-types.d.ts +8 -8
- package/list-sampled-together-pairs-types.d.ts +6 -6
- package/list-variant-facets-types.d.ts +8 -8
- package/list-variants-types.d.ts +59 -29
- package/material-asset-types.d.ts +37 -24
- package/material-block-types.d.ts +6 -6
- package/material-types.d.ts +6 -6
- package/nearest-paints-types.d.ts +8 -8
- package/operation-responses.d.ts +2 -2
- package/operations.d.ts +1 -1
- package/operations.js +1 -1
- package/package.json +1 -1
- package/parse-operation-response.d.ts +4 -3
- package/parse-operation-response.js +112 -135
- package/resolve-color-types.d.ts +6 -6
- package/resolve-variant-types.d.ts +10 -10
- package/response-schema-catalog.d.ts +8 -0
- package/response-schema-catalog.js +82 -0
- package/response-schemas.d.ts +32 -3
- package/response-schemas.generated.js +26 -25
- package/response-schemas.js +5 -77
- package/schema-resource-types.d.ts +12 -12
- package/search-catalog-subset-types.d.ts +2 -2
- package/search-variants-types.d.ts +12 -12
- package/source-material-types.d.ts +6 -6
- package/source-material.js +2 -32
- package/types.d.ts +38 -38
- package/webmcp.d.ts +10 -4
- package/webmcp.generated.js +5 -4
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
* product names visible on the page, sent only to tell several URL candidates
|
|
14
14
|
* apart - they never widen a search or invent an identity on their own.
|
|
15
15
|
*/
|
|
16
|
-
export
|
|
16
|
+
export interface ResolveVariantInput {
|
|
17
17
|
url?: string;
|
|
18
18
|
materialBankId?: string;
|
|
19
19
|
code?: string;
|
|
20
20
|
brandId?: string;
|
|
21
21
|
labels?: string[];
|
|
22
|
-
}
|
|
22
|
+
}
|
|
23
23
|
/** One identified, placed or bounded-candidate subject. */
|
|
24
|
-
export
|
|
24
|
+
export interface ResolveVariantSubject {
|
|
25
25
|
kind: "variant" | "brand";
|
|
26
26
|
id: string;
|
|
27
27
|
name: string;
|
|
@@ -30,23 +30,23 @@ export type ResolveVariantSubject = {
|
|
|
30
30
|
id: string;
|
|
31
31
|
name: string;
|
|
32
32
|
};
|
|
33
|
-
}
|
|
33
|
+
}
|
|
34
34
|
/** What stood behind an outcome. Exact and scoreless: never a similarity guess. */
|
|
35
|
-
export
|
|
35
|
+
export interface ResolveVariantEvidence {
|
|
36
36
|
kind: "brand-url" | "brand-homepage" | "brand-code" | "material-bank-id" | "visible-label";
|
|
37
37
|
url?: string;
|
|
38
38
|
detail: string;
|
|
39
|
-
}
|
|
39
|
+
}
|
|
40
40
|
/** The submitted URL, normalized, and the steps that normalization took. Absent for a code-only call. */
|
|
41
|
-
export
|
|
41
|
+
export interface ResolveVariantReceipt {
|
|
42
42
|
submittedUrl: string;
|
|
43
43
|
normalizedUrl: string;
|
|
44
44
|
normalizationSteps: string[];
|
|
45
|
-
}
|
|
46
|
-
|
|
45
|
+
}
|
|
46
|
+
interface ResolveVariantOutputBase {
|
|
47
47
|
evidence: ResolveVariantEvidence[];
|
|
48
48
|
receipt?: ResolveVariantReceipt;
|
|
49
|
-
}
|
|
49
|
+
}
|
|
50
50
|
/**
|
|
51
51
|
* Why nothing was resolved. Absent and empty differ: this names what was
|
|
52
52
|
* looked for and not found, never a similarity guess.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JsonObject } from "./json.js";
|
|
2
|
+
export declare const JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
|
|
3
|
+
/**
|
|
4
|
+
* The success-response schema for one operation, self-contained and carrying
|
|
5
|
+
* only the definitions it reaches. Each call returns a fresh copy, so a caller
|
|
6
|
+
* that annotates or mutates it cannot change what the next caller gets.
|
|
7
|
+
*/
|
|
8
|
+
export declare const buildResponseSchema: (operationId: string) => JsonObject;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generated response-schema catalog, parsed once on first use. It is its
|
|
3
|
+
* own module because the catalog is large: importing the client does not load
|
|
4
|
+
* it.
|
|
5
|
+
*/
|
|
6
|
+
import { MaterialGraphError } from "./errors.js";
|
|
7
|
+
import { isJsonObject, isJsonString, parseJson } from "./json.js";
|
|
8
|
+
import { RESPONSE_SCHEMA_CATALOG_JSON } from "./response-schemas.generated.js";
|
|
9
|
+
export const JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
|
|
10
|
+
const DEFINITION_PREFIX = "#/$defs/";
|
|
11
|
+
const isJsonSchemaMap = (value) => isJsonObject(value) && Object.values(value).every(isJsonObject);
|
|
12
|
+
const isCatalog = (value) => isJsonObject(value) &&
|
|
13
|
+
isJsonSchemaMap(value.definitions) &&
|
|
14
|
+
isJsonSchemaMap(value.operations);
|
|
15
|
+
let parsedCatalog;
|
|
16
|
+
const loadCatalog = () => {
|
|
17
|
+
if (parsedCatalog === undefined) {
|
|
18
|
+
const parsed = parseJson(RESPONSE_SCHEMA_CATALOG_JSON);
|
|
19
|
+
if (!isCatalog(parsed)) {
|
|
20
|
+
throw MaterialGraphError.unexpected("The response schema catalog is malformed");
|
|
21
|
+
}
|
|
22
|
+
parsedCatalog = parsed;
|
|
23
|
+
}
|
|
24
|
+
return parsedCatalog;
|
|
25
|
+
};
|
|
26
|
+
/** Add every definition `value` reaches, transitively, to `into`. */
|
|
27
|
+
const collectReferences = (catalog, value, into) => {
|
|
28
|
+
if (Array.isArray(value)) {
|
|
29
|
+
for (const item of value) {
|
|
30
|
+
collectReferences(catalog, item, into);
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (!isJsonObject(value)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
for (const [key, child] of Object.entries(value)) {
|
|
38
|
+
if (key === "$ref" &&
|
|
39
|
+
isJsonString(child) &&
|
|
40
|
+
child.startsWith(DEFINITION_PREFIX)) {
|
|
41
|
+
const name = child
|
|
42
|
+
.slice(DEFINITION_PREFIX.length)
|
|
43
|
+
.replaceAll("~1", "/")
|
|
44
|
+
.replaceAll("~0", "~");
|
|
45
|
+
if (!into.has(name)) {
|
|
46
|
+
into.add(name);
|
|
47
|
+
collectReferences(catalog, catalog.definitions[name], into);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
collectReferences(catalog, child, into);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The success-response schema for one operation, self-contained and carrying
|
|
57
|
+
* only the definitions it reaches. Each call returns a fresh copy, so a caller
|
|
58
|
+
* that annotates or mutates it cannot change what the next caller gets.
|
|
59
|
+
*/
|
|
60
|
+
export const buildResponseSchema = (operationId) => {
|
|
61
|
+
const catalog = loadCatalog();
|
|
62
|
+
const root = Object.hasOwn(catalog.operations, operationId)
|
|
63
|
+
? catalog.operations[operationId]
|
|
64
|
+
: undefined;
|
|
65
|
+
if (root === undefined || !isJsonObject(root)) {
|
|
66
|
+
throw MaterialGraphError.unexpected(`No response schema for operation: ${operationId}`);
|
|
67
|
+
}
|
|
68
|
+
const names = new Set();
|
|
69
|
+
collectReferences(catalog, root, names);
|
|
70
|
+
const definitions = {};
|
|
71
|
+
for (const name of [...names].toSorted()) {
|
|
72
|
+
const definition = catalog.definitions[name];
|
|
73
|
+
if (definition !== undefined) {
|
|
74
|
+
definitions[name] = definition;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const schema = { $schema: JSON_SCHEMA_DIALECT, ...root };
|
|
78
|
+
if (names.size > 0) {
|
|
79
|
+
schema.$defs = definitions;
|
|
80
|
+
}
|
|
81
|
+
return structuredClone(schema);
|
|
82
|
+
};
|
package/response-schemas.d.ts
CHANGED
|
@@ -1,17 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime response contracts: the JSON Schema of each operation's success body.
|
|
3
|
+
*
|
|
4
|
+
* The schemas are the producer's own. They are projected from the served
|
|
5
|
+
* OpenAPI document, whose response bodies are generated from the Zod contracts
|
|
6
|
+
* the routes validate against, so a consumer never has to re-describe a large,
|
|
7
|
+
* partly open payload and watch it drift. Hand one to anything that takes a
|
|
8
|
+
* JSON Schema, such as a Mastra tool's `outputSchema`:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { responseSchema } from "@instruments/materialgraph/response-schemas";
|
|
12
|
+
*
|
|
13
|
+
* createTool({ outputSchema: responseSchema("search_variants"), … });
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Keyed by OpenAPI `operationId`, the same id `SDK_OPERATIONS` pairs with each
|
|
17
|
+
* client method. Schemas are JSON Schema 2020-12 and describe the parsed body
|
|
18
|
+
* the client method returns.
|
|
19
|
+
*
|
|
20
|
+
* Objects accept properties the schema does not declare, so a field the API
|
|
21
|
+
* adds later never makes a pinned consumer reject a live response; enums and
|
|
22
|
+
* constants stay closed. That is why these are more lenient than the WebMCP
|
|
23
|
+
* `outputSchema`s, which describe what the API sends rather than what a
|
|
24
|
+
* consumer should tolerate.
|
|
25
|
+
*
|
|
26
|
+
* This is its own entry point because the catalog is large; importing the
|
|
27
|
+
* client does not load it, and it is parsed on the first `responseSchema()`
|
|
28
|
+
* call rather than on import.
|
|
29
|
+
*/
|
|
30
|
+
import { JSON_SCHEMA_DIALECT } from "./response-schema-catalog.js";
|
|
1
31
|
import type { RESPONSE_SCHEMA_OPERATION_IDS } from "./response-schemas.generated.js";
|
|
2
32
|
export { RESPONSE_SCHEMA_API_VERSION, RESPONSE_SCHEMA_OPERATION_IDS, } from "./response-schemas.generated.js";
|
|
3
33
|
/** Every operation {@link responseSchema} answers for. */
|
|
4
34
|
export type ResponseSchemaOperationId = (typeof RESPONSE_SCHEMA_OPERATION_IDS)[number];
|
|
5
35
|
/** A JSON Schema document or subschema, as plain JSON. */
|
|
6
|
-
export
|
|
36
|
+
export interface JsonSchema {
|
|
7
37
|
[keyword: string]: unknown;
|
|
8
|
-
}
|
|
38
|
+
}
|
|
9
39
|
/** A self-contained schema: every `$ref` resolves inside its own `$defs`. */
|
|
10
40
|
export type ResponseJsonSchema = JsonSchema & {
|
|
11
41
|
$schema: typeof JSON_SCHEMA_DIALECT;
|
|
12
42
|
$defs?: Record<string, JsonSchema>;
|
|
13
43
|
};
|
|
14
|
-
declare const JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
|
|
15
44
|
/**
|
|
16
45
|
* The success-response schema for one operation, self-contained and carrying
|
|
17
46
|
* only the definitions it reaches. Each call returns a fresh copy, so a caller
|