@instruments/materialgraph 0.1.0 → 0.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/client.d.ts +141 -7
- package/client.js +185 -125
- package/codes-types.d.ts +93 -0
- package/codes-types.js +1 -0
- package/composition-types.d.ts +117 -0
- package/composition-types.js +10 -0
- package/http-transport.d.ts +57 -0
- package/http-transport.js +131 -0
- package/index.d.ts +12 -0
- package/index.js +6 -0
- package/operations.d.ts +6 -2
- package/operations.js +24 -2
- package/package.json +1 -1
- package/project-types.d.ts +67 -0
- package/project-types.js +16 -0
- package/projects.d.ts +13 -0
- package/projects.js +46 -0
- package/run-events.d.ts +82 -0
- package/run-events.js +29 -0
- package/run-types.d.ts +202 -0
- package/run-types.js +6 -0
- package/runs.d.ts +42 -0
- package/runs.js +214 -0
- package/search-refinement-types.d.ts +13 -1
- package/spec-types.d.ts +78 -0
- package/spec-types.js +40 -0
- package/specifications.d.ts +63 -0
- package/specifications.js +73 -0
- package/sse.d.ts +35 -0
- package/sse.js +147 -0
- package/types.d.ts +145 -4
package/client.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { type RateLimit } from "./errors.js";
|
|
2
|
+
import type { CapabilityManifestResponse, CreatePublicRunRequest, CreatePublicRunResponse, DrainRunEventsOptions, PublicRunResponse, RunEvent, SourceMaterialBatchReceipt, StreamRunEventsOptions, UploadSourceMaterialInput } from "./run-types.js";
|
|
3
|
+
import type { AppendProjectEventRequest, CreateProjectRequest, ProjectEventReceipt, ProjectListResponse, ProjectState } from "./project-types.js";
|
|
4
|
+
import type { VarySchemesRequest, VarySchemesResponse } from "./composition-types.js";
|
|
5
|
+
import type { CodeAdvisoryInput, CodeAdvisoryResponse } from "./codes-types.js";
|
|
2
6
|
import type { BrandCatalogListPage, BrandsOptions } from "./brand-types.js";
|
|
3
7
|
import type { MaterialAssetProjection } from "./material-asset-types.js";
|
|
4
8
|
import type { MaterialBlockProjection } from "./material-block-types.js";
|
|
5
9
|
import type { SchemaResource, SchemaResourceResponse } from "./schema-resource-types.js";
|
|
6
|
-
import type { ApplicableSpecsResponse, BrandOptions, BrandProfileResponse, ByColourParams, ByColourResponse, BrowseParams, BrowseResponse, BySpecOptions, BySpecResponse, ColourFacetsResponse, CoordinateInput, CoordinateResponse, DemandParams, DemandResponse, ExtractPaletteInput, ExtractPaletteResponse, GlossaryTermResponse, MaterialResponse, MaterialsBatchDetailResponse, MaterialsBatchGetOptions, MaterialsBatchOptions, MaterialsBatchTileResponse, MatchInput, MatchResponse, PairingsOptions, PairingsResponse, ResolveAttributeOptions, ResolveAttributeResponse, ResolveAttributeValueOptions, ResolveAttributeValueResponse, ResolveColourResponse, SearchGetParams, SearchInput, SearchResponse, SiblingsResponse, SpecFacetsDistributionResponse, SpecFacetsListResponse, SuggestColourResponse, TaxonomyResponse, VocabularySearchOptions, VocabularySearchResponse, WhoamiResponse } from "./types.js";
|
|
10
|
+
import type { ApplicableSpecsResponse, BrandOptions, BrandProfileResponse, ByColourParams, ByColourResponse, BrowseParams, BrowseResponse, BySpecOptions, BySpecResponse, ColourFacetsResponse, CoordinateInput, CoordinateResponse, DemandBatchInput, DemandBatchResponse, DemandParams, DemandResponse, ExtractPaletteInput, ExtractPaletteResponse, GlossaryTermResponse, MaterialResponse, MaterialsBatchDetailResponse, MaterialsBatchGetOptions, MaterialsBatchOptions, MaterialsBatchTileResponse, MatchInput, MatchResponse, PairingsOptions, PairingsResponse, ResolveAttributeOptions, ResolveAttributeResponse, ResolveAttributeValueOptions, ResolveAttributeValueResponse, ResolveColourResponse, SearchGetParams, SearchInput, SearchResponse, SiblingsResponse, SpecFacetsDistributionResponse, SpecFacetsListResponse, SuggestColourResponse, TaxonomyResponse, VocabularySearchOptions, VocabularySearchResponse, WhoamiResponse } from "./types.js";
|
|
7
11
|
export declare const DEFAULT_BASE_URL = "https://beta.materialgraph.com";
|
|
8
12
|
export declare const MAX_BATCH_IDS = 100;
|
|
9
13
|
export interface MaterialGraphClientOptions {
|
|
@@ -24,6 +28,17 @@ export declare class MaterialGraphClient {
|
|
|
24
28
|
readonly browse: BrowseNamespace;
|
|
25
29
|
constructor(options: MaterialGraphClientOptions);
|
|
26
30
|
get lastRateLimit(): RateLimit | null;
|
|
31
|
+
/**
|
|
32
|
+
* The full search primitive (POST `/api/v1/search`). Layer a hard
|
|
33
|
+
* specification gate onto a ranked (colour / visual / lexical / seed) search
|
|
34
|
+
* via `input.filters` — each entry is a typed {@link SearchFilter} criterion:
|
|
35
|
+
* a term match (`{ attributeId, term }`), a presence check
|
|
36
|
+
* (`{ attributeId, hasValue: true }`), or a certification programme
|
|
37
|
+
* (`{ attributeId: "certification_programme", term }`). This is the seam for
|
|
38
|
+
* "a GREENGUARD-certified alternative to THIS material" (seed by `similarTo`,
|
|
39
|
+
* gate by `filters`) or "the same fire class, with an EPD on record". Build the
|
|
40
|
+
* filters with the `certificationProgramme` / `specTerm` / `hasSpec` helpers.
|
|
41
|
+
*/
|
|
27
42
|
search(input: SearchInput, signal?: AbortSignal): Promise<SearchResponse>;
|
|
28
43
|
searchGet(params: SearchGetParams, signal?: AbortSignal): Promise<SearchResponse>;
|
|
29
44
|
match(input: MatchInput, signal?: AbortSignal): Promise<MatchResponse>;
|
|
@@ -83,8 +98,15 @@ export declare class MaterialGraphClient {
|
|
|
83
98
|
/** Search canonical attributes and value-dictionary terms together. */
|
|
84
99
|
searchVocabulary(query: string, options?: VocabularySearchOptions, signal?: AbortSignal): Promise<VocabularySearchResponse>;
|
|
85
100
|
/**
|
|
86
|
-
* Find materials by canonical specification
|
|
87
|
-
*
|
|
101
|
+
* Find materials by canonical specification across every brand — the
|
|
102
|
+
* value-anchored, spec-only primitive (GET `/api/v1/search/specifications`).
|
|
103
|
+
* Each `spec` is an `attribute:value` criterion; a variant must satisfy ALL of
|
|
104
|
+
* them, and each match carries per-criterion trust (`confidence` +
|
|
105
|
+
* `sourceBasis`). Answers "which materials, from anyone, are ASTM E84 Class A
|
|
106
|
+
* AND GREENGUARD-certified?" Build the strings with `specCriterion` /
|
|
107
|
+
* `certificationProgrammeCriterion` (e.g.
|
|
108
|
+
* `certificationProgrammeCriterion("GREENGUARD Gold")`); discover the queryable
|
|
109
|
+
* vocabulary with {@link specificationFacets} and {@link attributeValues}.
|
|
88
110
|
*/
|
|
89
111
|
bySpec(spec: string | string[], options?: BySpecOptions, signal?: AbortSignal): Promise<BySpecResponse>;
|
|
90
112
|
/** The product-category tree, flattened (GET `/api/v1/taxonomy/categories`). */
|
|
@@ -102,11 +124,72 @@ export declare class MaterialGraphClient {
|
|
|
102
124
|
* `/api/v1/demand`). Provide EXACTLY ONE of `variantId` or `material`.
|
|
103
125
|
*/
|
|
104
126
|
demand(params: DemandParams, signal?: AbortSignal): Promise<DemandResponse>;
|
|
127
|
+
/**
|
|
128
|
+
* Enrich a bounded set of ALREADY-CHOSEN candidate ids with their real
|
|
129
|
+
* specification demand (POST `/api/v1/demand/batch`, 1–48 ids). The bounded
|
|
130
|
+
* batch complement to {@link demand}: no fuzzy resolution, ranking, or
|
|
131
|
+
* reordering — each id yields one discriminated `state` (`available` /
|
|
132
|
+
* `insufficient_data` / `no_signal` / `unknown_variant`) in input order and
|
|
133
|
+
* cardinality. This is the Desk's "show demand momentum for every pick in this
|
|
134
|
+
* scheme" read; an `mg:admin` key additionally sees per-result `evidence`.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* // Rank a shortlist by real specification momentum before locking the scheme.
|
|
138
|
+
* const { results } = await mg.demandBatch({
|
|
139
|
+
* variantIds: scheme.picks.map((p) => p.variantId),
|
|
140
|
+
* signal: "sampled",
|
|
141
|
+
* context: { projectType: "hospitality" },
|
|
142
|
+
* });
|
|
143
|
+
* const rising = results.filter(
|
|
144
|
+
* (r) => r.state === "available" && r.momentum.state === "rising",
|
|
145
|
+
* );
|
|
146
|
+
*/
|
|
147
|
+
demandBatch(input: DemandBatchInput, signal?: AbortSignal): Promise<DemandBatchResponse>;
|
|
148
|
+
/**
|
|
149
|
+
* Badge a set of materials against the advisory building-code registry for one
|
|
150
|
+
* (jurisdiction, occupancy, space, sprinklered) context (POST
|
|
151
|
+
* `/api/v1/codes/advisory`, ≤ 48 ids). Every applicable requirement is badged
|
|
152
|
+
* `meets` / `shortfall` / `unknown` from the material's accepted fire and
|
|
153
|
+
* slip-resistance facts, in input order. ADVISORY ONLY — every verdict and the
|
|
154
|
+
* envelope carry the AHJ caveat; this is model code as adopted, never a legal
|
|
155
|
+
* determination.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* // Badge this scheme's picks for a Texas hospitality fire-code review.
|
|
159
|
+
* const advisory = await mg.codeAdvisory({
|
|
160
|
+
* variantIds: scheme.picks.map((p) => p.variantId),
|
|
161
|
+
* jurisdictionId: "us-tx",
|
|
162
|
+
* occupancyGroup: "A-2", // assembly — restaurant / bar
|
|
163
|
+
* spaceType: "corridor",
|
|
164
|
+
* sprinklered: true,
|
|
165
|
+
* });
|
|
166
|
+
* const shortfalls = advisory.results.filter((r) =>
|
|
167
|
+
* r.requirements.some((req) => req.verdict === "shortfall"),
|
|
168
|
+
* );
|
|
169
|
+
*/
|
|
170
|
+
codeAdvisory(input: CodeAdvisoryInput, signal?: AbortSignal): Promise<CodeAdvisoryResponse>;
|
|
105
171
|
/** Extract a named colour palette from an uploaded image (POST `/api/v1/search/palettes/extract`). */
|
|
106
172
|
extractPalette(input: ExtractPaletteInput, signal?: AbortSignal): Promise<ExtractPaletteResponse>;
|
|
107
|
-
/**
|
|
173
|
+
/**
|
|
174
|
+
* Discover the live specification vocabulary (GET
|
|
175
|
+
* `/api/v1/catalog/specifications/facets`) — the facetable spec attributes that
|
|
176
|
+
* have real product-graph data, each with its top canonical values and the
|
|
177
|
+
* variants/brands behind them. This is where a caller LEARNS that the data
|
|
178
|
+
* exists: LEED, GREENGUARD, ASTM E84 Class A, FloorScore, EPDs all surface here
|
|
179
|
+
* with counts. Includes the virtual `certification_programme` facet (its values
|
|
180
|
+
* are programme names). Learn an `attribute:value` here, then reuse it as a
|
|
181
|
+
* {@link bySpec} criterion or a {@link search} `filters` entry. NOTE: continuous
|
|
182
|
+
* numeric specs (e.g. `slip_resistance_dcof`, a 0–1 DCOF range) are NOT
|
|
183
|
+
* token-facetable and do not appear here — read those per-attribute.
|
|
184
|
+
*/
|
|
108
185
|
specificationFacets(signal?: AbortSignal): Promise<SpecFacetsListResponse>;
|
|
109
|
-
/**
|
|
186
|
+
/**
|
|
187
|
+
* List the live value distribution for ONE canonical attribute (GET
|
|
188
|
+
* `/api/v1/catalog/attributes/{attributeId}/values`), ordered by cross-brand
|
|
189
|
+
* reach with variant + brand counts. The drill-in companion to
|
|
190
|
+
* {@link specificationFacets} — e.g. every certification programme on record,
|
|
191
|
+
* or every fire classification and how many variants carry each.
|
|
192
|
+
*/
|
|
110
193
|
attributeValues(attributeId: string, signal?: AbortSignal): Promise<SpecFacetsDistributionResponse>;
|
|
111
194
|
/** The specs a product type COULD carry (GET `/api/v1/taxonomy/categories/{key}/specs`). */
|
|
112
195
|
applicableSpecs(key: string, signal?: AbortSignal): Promise<ApplicableSpecsResponse>;
|
|
@@ -114,6 +197,57 @@ export declare class MaterialGraphClient {
|
|
|
114
197
|
glossary(term: string, signal?: AbortSignal): Promise<GlossaryTermResponse>;
|
|
115
198
|
/** List registered brands (GET `/api/v1/brands`). */
|
|
116
199
|
brands(options?: BrandsOptions, signal?: AbortSignal): Promise<BrandCatalogListPage>;
|
|
117
|
-
/** Resolve a brand and return its profile + trade signals (GET `/api/v1/brands/{
|
|
118
|
-
brand(
|
|
200
|
+
/** Resolve a brand and return its profile + trade signals (GET `/api/v1/brands/{brandSlug}`). */
|
|
201
|
+
brand(brandSlug: string, options?: BrandOptions, signal?: AbortSignal): Promise<BrandProfileResponse>;
|
|
202
|
+
/**
|
|
203
|
+
* Upload one atomic source-material batch (POST `/api/v1/source-material`,
|
|
204
|
+
* multipart). Pass a heterogeneous pile via `{ files }` (any mix of PDF,
|
|
205
|
+
* text/markdown, and images — the server classifies each) or the legacy
|
|
206
|
+
* `{ brief, references }` shape. Requires the `mg:ingest` scope.
|
|
207
|
+
*/
|
|
208
|
+
uploadSourceMaterial(input: UploadSourceMaterialInput, signal?: AbortSignal): Promise<SourceMaterialBatchReceipt>;
|
|
209
|
+
/** List invocable agents, workflows, and tools (GET `/api/v1/capabilities`). */
|
|
210
|
+
capabilities(signal?: AbortSignal): Promise<CapabilityManifestResponse>;
|
|
211
|
+
/** Start an asynchronous capability Run (POST `/api/v1/runs`). */
|
|
212
|
+
createRun(body: CreatePublicRunRequest, signal?: AbortSignal): Promise<CreatePublicRunResponse>;
|
|
213
|
+
/** Get a Run's current state (GET `/api/v1/runs/{runId}`). */
|
|
214
|
+
run(runId: string, signal?: AbortSignal): Promise<PublicRunResponse>;
|
|
215
|
+
/** Resume a suspended Run (POST `/api/v1/runs/{runId}/resume`). */
|
|
216
|
+
resumeRun(runId: string, resumeData: unknown, signal?: AbortSignal): Promise<PublicRunResponse>;
|
|
217
|
+
/** Request durable Run cancellation (POST `/api/v1/runs/{runId}/cancel`). */
|
|
218
|
+
cancelRun(runId: string, signal?: AbortSignal): Promise<PublicRunResponse>;
|
|
219
|
+
/**
|
|
220
|
+
* Derive an "option B" scheme from a succeeded compose Run along one
|
|
221
|
+
* constraint axis (POST `/api/v1/schemes/vary`). Deterministic and
|
|
222
|
+
* synchronous — the response IS the hydrated scheme (no Run envelope), stamped
|
|
223
|
+
* with axis + source provenance whose `notes` are the variation's receipts.
|
|
224
|
+
* Requires the `mg:read` scope.
|
|
225
|
+
*/
|
|
226
|
+
varyScheme(body: VarySchemesRequest, signal?: AbortSignal): Promise<VarySchemesResponse>;
|
|
227
|
+
/**
|
|
228
|
+
* Stream ordered Run events as an async iterator (GET
|
|
229
|
+
* `/api/v1/runs/{runId}/events`, SSE). Iterates until the stream ends or the
|
|
230
|
+
* signal aborts; pass `after` to resume from the last observed sequence.
|
|
231
|
+
*/
|
|
232
|
+
streamRunEvents(runId: string, options?: StreamRunEventsOptions): AsyncGenerator<RunEvent>;
|
|
233
|
+
/**
|
|
234
|
+
* Create a persistent project owned by the caller (POST `/api/v1/projects`).
|
|
235
|
+
* Returns the freshly assembled state (an empty event log). Requires the
|
|
236
|
+
* `mg:ingest` scope.
|
|
237
|
+
*/
|
|
238
|
+
createProject(body?: CreateProjectRequest, signal?: AbortSignal): Promise<ProjectState>;
|
|
239
|
+
/** Read a project's assembled state (GET `/api/v1/projects/{projectId}`). */
|
|
240
|
+
getProject(projectId: string, signal?: AbortSignal): Promise<ProjectState>;
|
|
241
|
+
/**
|
|
242
|
+
* Append one event to a project's append-only process trail (POST
|
|
243
|
+
* `/api/v1/projects/{projectId}/events`). Requires the `mg:ingest` scope.
|
|
244
|
+
*/
|
|
245
|
+
appendProjectEvent(projectId: string, body: AppendProjectEventRequest, signal?: AbortSignal): Promise<ProjectEventReceipt>;
|
|
246
|
+
/** List the caller's own projects, newest first (GET `/api/v1/projects`). */
|
|
247
|
+
listProjects(signal?: AbortSignal): Promise<ProjectListResponse>;
|
|
248
|
+
/**
|
|
249
|
+
* Collect Run events until a terminal event, the deadline, or the stream ends
|
|
250
|
+
* — the bounded events-log pattern (a single pass over {@link streamRunEvents}).
|
|
251
|
+
*/
|
|
252
|
+
drainRunEvents(runId: string, options: DrainRunEventsOptions): Promise<RunEvent[]>;
|
|
119
253
|
}
|
package/client.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { MaterialGraphError } from "./errors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { HttpTransport } from "./http-transport.js";
|
|
3
|
+
import { findSdkOperation } from "./operations.js";
|
|
4
|
+
import { cancelRun, capabilities as capabilitiesRun, createRun, drainRunEvents, getRun, resumeRun, streamRunEvents, uploadSourceMaterial, } from "./runs.js";
|
|
5
|
+
import { appendProjectEvent as appendProjectEventCall, createProject as createProjectCall, getProject as getProjectCall, listProjects as listProjectsCall, } from "./projects.js";
|
|
3
6
|
export const DEFAULT_BASE_URL = "https://beta.materialgraph.com";
|
|
4
7
|
export const MAX_BATCH_IDS = 100;
|
|
5
8
|
const SCHEMA_RESOURCE_OPERATION_IDS = {
|
|
@@ -14,145 +17,50 @@ const SCHEMA_RESOURCE_OPERATION_IDS = {
|
|
|
14
17
|
mappings: "list_schema_mappings",
|
|
15
18
|
};
|
|
16
19
|
function operation(operationId) {
|
|
17
|
-
const match =
|
|
20
|
+
const match = findSdkOperation(operationId);
|
|
18
21
|
if (!match) {
|
|
19
22
|
throw MaterialGraphError.unexpected(`Unknown SDK operation: ${operationId}`);
|
|
20
23
|
}
|
|
21
24
|
return match;
|
|
22
25
|
}
|
|
23
|
-
function operationPath(sdkOperation, pathParams = {}) {
|
|
24
|
-
return Object.entries(pathParams).reduce((path, [name, value]) => path.replace(`{${name}}`, encodeURIComponent(value)), sdkOperation.path);
|
|
25
|
-
}
|
|
26
|
-
function parseIntOrNull(value) {
|
|
27
|
-
if (value === null)
|
|
28
|
-
return null;
|
|
29
|
-
const n = Number.parseInt(value, 10);
|
|
30
|
-
return Number.isNaN(n) ? null : n;
|
|
31
|
-
}
|
|
32
|
-
function parseRateLimit(headers) {
|
|
33
|
-
const limit = parseIntOrNull(headers.get("x-ratelimit-limit"));
|
|
34
|
-
const remaining = parseIntOrNull(headers.get("x-ratelimit-remaining"));
|
|
35
|
-
const reset = parseIntOrNull(headers.get("x-ratelimit-reset"));
|
|
36
|
-
const retryAfter = parseIntOrNull(headers.get("retry-after"));
|
|
37
|
-
if (limit === null && remaining === null && reset === null && retryAfter === null) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
return { limit, remaining, reset, retryAfter };
|
|
41
|
-
}
|
|
42
26
|
export class MaterialGraphClient {
|
|
43
27
|
baseUrl;
|
|
44
28
|
browse;
|
|
45
|
-
#
|
|
46
|
-
#fetch;
|
|
47
|
-
#lastRateLimit = null;
|
|
29
|
+
#transport;
|
|
48
30
|
constructor(options) {
|
|
49
31
|
if (!options.apiKey) {
|
|
50
32
|
throw MaterialGraphError.unexpected("MaterialGraphClient: `apiKey` is required.");
|
|
51
33
|
}
|
|
52
|
-
this.#
|
|
53
|
-
|
|
54
|
-
|
|
34
|
+
this.#transport = new HttpTransport({
|
|
35
|
+
apiKey: options.apiKey,
|
|
36
|
+
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
37
|
+
fetch: options.fetch ?? globalThis.fetch,
|
|
38
|
+
});
|
|
39
|
+
this.baseUrl = this.#transport.baseUrl;
|
|
55
40
|
const browse = (params, signal) => this.#browsePage(params, signal);
|
|
56
41
|
browse.pages = (params, signal) => this.#browsePages(params, signal);
|
|
57
42
|
this.browse = browse;
|
|
58
43
|
}
|
|
59
44
|
get lastRateLimit() {
|
|
60
|
-
return this.#lastRateLimit;
|
|
61
|
-
}
|
|
62
|
-
#buildUrl(path, query) {
|
|
63
|
-
const url = new URL(`${this.baseUrl}${path}`);
|
|
64
|
-
if (query) {
|
|
65
|
-
for (const [key, value] of Object.entries(query)) {
|
|
66
|
-
if (value === undefined || value === null)
|
|
67
|
-
continue;
|
|
68
|
-
if (Array.isArray(value)) {
|
|
69
|
-
for (const item of value) {
|
|
70
|
-
if (item !== undefined && item !== null) {
|
|
71
|
-
url.searchParams.append(key, String(item));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
url.searchParams.append(key, String(value));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return url.toString();
|
|
81
|
-
}
|
|
82
|
-
async #request(sdkOperation, options = {}) {
|
|
83
|
-
const path = operationPath(sdkOperation, options.pathParams);
|
|
84
|
-
const url = this.#buildUrl(path, options.query);
|
|
85
|
-
const requestUrl = new URL(url);
|
|
86
|
-
const requestMetadata = {
|
|
87
|
-
method: sdkOperation.method,
|
|
88
|
-
origin: requestUrl.origin,
|
|
89
|
-
path: requestUrl.pathname,
|
|
90
|
-
};
|
|
91
|
-
const headers = {
|
|
92
|
-
Authorization: `Bearer ${this.#apiKey}`,
|
|
93
|
-
Accept: "application/json",
|
|
94
|
-
};
|
|
95
|
-
const hasBody = options.body !== undefined;
|
|
96
|
-
if (hasBody)
|
|
97
|
-
headers["Content-Type"] = "application/json";
|
|
98
|
-
let response;
|
|
99
|
-
try {
|
|
100
|
-
response = await this.#fetch(url, {
|
|
101
|
-
method: sdkOperation.method,
|
|
102
|
-
headers,
|
|
103
|
-
body: hasBody ? JSON.stringify(options.body) : undefined,
|
|
104
|
-
signal: options.signal,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
catch (cause) {
|
|
108
|
-
throw MaterialGraphError.fromTransport({
|
|
109
|
-
...requestMetadata,
|
|
110
|
-
cause,
|
|
111
|
-
callerSignal: options.signal,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
const rateLimit = parseRateLimit(response.headers);
|
|
115
|
-
this.#lastRateLimit = rateLimit;
|
|
116
|
-
let text;
|
|
117
|
-
try {
|
|
118
|
-
text = await response.text();
|
|
119
|
-
}
|
|
120
|
-
catch (cause) {
|
|
121
|
-
throw MaterialGraphError.fromTransport({
|
|
122
|
-
...requestMetadata,
|
|
123
|
-
cause,
|
|
124
|
-
callerSignal: options.signal,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
let parsed;
|
|
128
|
-
if (text.length > 0) {
|
|
129
|
-
try {
|
|
130
|
-
parsed = JSON.parse(text);
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
parsed = text;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (!response.ok) {
|
|
137
|
-
const querySecrets = [...requestUrl.searchParams.values()];
|
|
138
|
-
throw MaterialGraphError.fromResponse({
|
|
139
|
-
...requestMetadata,
|
|
140
|
-
status: response.status,
|
|
141
|
-
body: parsed,
|
|
142
|
-
responseText: text,
|
|
143
|
-
rateLimit,
|
|
144
|
-
apiKey: this.#apiKey,
|
|
145
|
-
queryValues: querySecrets,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
return parsed;
|
|
45
|
+
return this.#transport.lastRateLimit;
|
|
149
46
|
}
|
|
150
47
|
#get(operationId, query, signal, pathParams) {
|
|
151
|
-
return this.#
|
|
48
|
+
return this.#transport.requestJson(operation(operationId), { query, signal, pathParams });
|
|
152
49
|
}
|
|
153
50
|
#post(operationId, body, signal) {
|
|
154
|
-
return this.#
|
|
51
|
+
return this.#transport.requestJson(operation(operationId), { body, signal });
|
|
155
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* The full search primitive (POST `/api/v1/search`). Layer a hard
|
|
55
|
+
* specification gate onto a ranked (colour / visual / lexical / seed) search
|
|
56
|
+
* via `input.filters` — each entry is a typed {@link SearchFilter} criterion:
|
|
57
|
+
* a term match (`{ attributeId, term }`), a presence check
|
|
58
|
+
* (`{ attributeId, hasValue: true }`), or a certification programme
|
|
59
|
+
* (`{ attributeId: "certification_programme", term }`). This is the seam for
|
|
60
|
+
* "a GREENGUARD-certified alternative to THIS material" (seed by `similarTo`,
|
|
61
|
+
* gate by `filters`) or "the same fire class, with an EPD on record". Build the
|
|
62
|
+
* filters with the `certificationProgramme` / `specTerm` / `hasSpec` helpers.
|
|
63
|
+
*/
|
|
156
64
|
search(input, signal) {
|
|
157
65
|
return this.#post("search_materials_post", input, signal);
|
|
158
66
|
}
|
|
@@ -264,8 +172,15 @@ export class MaterialGraphClient {
|
|
|
264
172
|
return this.#get("catalog_vocabulary_search", { q: query, limit: options?.limit, kind: options?.kind }, signal);
|
|
265
173
|
}
|
|
266
174
|
/**
|
|
267
|
-
* Find materials by canonical specification
|
|
268
|
-
*
|
|
175
|
+
* Find materials by canonical specification across every brand — the
|
|
176
|
+
* value-anchored, spec-only primitive (GET `/api/v1/search/specifications`).
|
|
177
|
+
* Each `spec` is an `attribute:value` criterion; a variant must satisfy ALL of
|
|
178
|
+
* them, and each match carries per-criterion trust (`confidence` +
|
|
179
|
+
* `sourceBasis`). Answers "which materials, from anyone, are ASTM E84 Class A
|
|
180
|
+
* AND GREENGUARD-certified?" Build the strings with `specCriterion` /
|
|
181
|
+
* `certificationProgrammeCriterion` (e.g.
|
|
182
|
+
* `certificationProgrammeCriterion("GREENGUARD Gold")`); discover the queryable
|
|
183
|
+
* vocabulary with {@link specificationFacets} and {@link attributeValues}.
|
|
269
184
|
*/
|
|
270
185
|
bySpec(spec, options, signal) {
|
|
271
186
|
return this.#get("catalog_by_spec", { spec: Array.isArray(spec) ? spec : [spec], limit: options?.limit }, signal);
|
|
@@ -295,17 +210,83 @@ export class MaterialGraphClient {
|
|
|
295
210
|
demand(params, signal) {
|
|
296
211
|
return this.#get("get_material_demand", { variantId: params.variantId, material: params.material }, signal);
|
|
297
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Enrich a bounded set of ALREADY-CHOSEN candidate ids with their real
|
|
215
|
+
* specification demand (POST `/api/v1/demand/batch`, 1–48 ids). The bounded
|
|
216
|
+
* batch complement to {@link demand}: no fuzzy resolution, ranking, or
|
|
217
|
+
* reordering — each id yields one discriminated `state` (`available` /
|
|
218
|
+
* `insufficient_data` / `no_signal` / `unknown_variant`) in input order and
|
|
219
|
+
* cardinality. This is the Desk's "show demand momentum for every pick in this
|
|
220
|
+
* scheme" read; an `mg:admin` key additionally sees per-result `evidence`.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* // Rank a shortlist by real specification momentum before locking the scheme.
|
|
224
|
+
* const { results } = await mg.demandBatch({
|
|
225
|
+
* variantIds: scheme.picks.map((p) => p.variantId),
|
|
226
|
+
* signal: "sampled",
|
|
227
|
+
* context: { projectType: "hospitality" },
|
|
228
|
+
* });
|
|
229
|
+
* const rising = results.filter(
|
|
230
|
+
* (r) => r.state === "available" && r.momentum.state === "rising",
|
|
231
|
+
* );
|
|
232
|
+
*/
|
|
233
|
+
demandBatch(input, signal) {
|
|
234
|
+
return this.#post("get_demand_batch", input, signal);
|
|
235
|
+
}
|
|
236
|
+
// ── Codes (advisory building-code compliance) ────────────────────────────────
|
|
237
|
+
/**
|
|
238
|
+
* Badge a set of materials against the advisory building-code registry for one
|
|
239
|
+
* (jurisdiction, occupancy, space, sprinklered) context (POST
|
|
240
|
+
* `/api/v1/codes/advisory`, ≤ 48 ids). Every applicable requirement is badged
|
|
241
|
+
* `meets` / `shortfall` / `unknown` from the material's accepted fire and
|
|
242
|
+
* slip-resistance facts, in input order. ADVISORY ONLY — every verdict and the
|
|
243
|
+
* envelope carry the AHJ caveat; this is model code as adopted, never a legal
|
|
244
|
+
* determination.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* // Badge this scheme's picks for a Texas hospitality fire-code review.
|
|
248
|
+
* const advisory = await mg.codeAdvisory({
|
|
249
|
+
* variantIds: scheme.picks.map((p) => p.variantId),
|
|
250
|
+
* jurisdictionId: "us-tx",
|
|
251
|
+
* occupancyGroup: "A-2", // assembly — restaurant / bar
|
|
252
|
+
* spaceType: "corridor",
|
|
253
|
+
* sprinklered: true,
|
|
254
|
+
* });
|
|
255
|
+
* const shortfalls = advisory.results.filter((r) =>
|
|
256
|
+
* r.requirements.some((req) => req.verdict === "shortfall"),
|
|
257
|
+
* );
|
|
258
|
+
*/
|
|
259
|
+
codeAdvisory(input, signal) {
|
|
260
|
+
return this.#post("resolveCodeAdvisory", input, signal);
|
|
261
|
+
}
|
|
298
262
|
// ── Colour helpers (extract) ─────────────────────────────────────────────────
|
|
299
263
|
/** Extract a named colour palette from an uploaded image (POST `/api/v1/search/palettes/extract`). */
|
|
300
264
|
extractPalette(input, signal) {
|
|
301
265
|
return this.#post("extract_image_palette", input, signal);
|
|
302
266
|
}
|
|
303
267
|
// ── Catalog (facets / applicable specs / glossary) ───────────────────────────
|
|
304
|
-
/**
|
|
268
|
+
/**
|
|
269
|
+
* Discover the live specification vocabulary (GET
|
|
270
|
+
* `/api/v1/catalog/specifications/facets`) — the facetable spec attributes that
|
|
271
|
+
* have real product-graph data, each with its top canonical values and the
|
|
272
|
+
* variants/brands behind them. This is where a caller LEARNS that the data
|
|
273
|
+
* exists: LEED, GREENGUARD, ASTM E84 Class A, FloorScore, EPDs all surface here
|
|
274
|
+
* with counts. Includes the virtual `certification_programme` facet (its values
|
|
275
|
+
* are programme names). Learn an `attribute:value` here, then reuse it as a
|
|
276
|
+
* {@link bySpec} criterion or a {@link search} `filters` entry. NOTE: continuous
|
|
277
|
+
* numeric specs (e.g. `slip_resistance_dcof`, a 0–1 DCOF range) are NOT
|
|
278
|
+
* token-facetable and do not appear here — read those per-attribute.
|
|
279
|
+
*/
|
|
305
280
|
specificationFacets(signal) {
|
|
306
281
|
return this.#get("list_spec_facets", undefined, signal);
|
|
307
282
|
}
|
|
308
|
-
/**
|
|
283
|
+
/**
|
|
284
|
+
* List the live value distribution for ONE canonical attribute (GET
|
|
285
|
+
* `/api/v1/catalog/attributes/{attributeId}/values`), ordered by cross-brand
|
|
286
|
+
* reach with variant + brand counts. The drill-in companion to
|
|
287
|
+
* {@link specificationFacets} — e.g. every certification programme on record,
|
|
288
|
+
* or every fire classification and how many variants carry each.
|
|
289
|
+
*/
|
|
309
290
|
attributeValues(attributeId, signal) {
|
|
310
291
|
return this.#get("list_attribute_values", undefined, signal, {
|
|
311
292
|
attributeId,
|
|
@@ -324,10 +305,89 @@ export class MaterialGraphClient {
|
|
|
324
305
|
brands(options = {}, signal) {
|
|
325
306
|
return this.#get("list_brands", { q: options.q, limit: options.limit, offset: options.offset }, signal);
|
|
326
307
|
}
|
|
327
|
-
/** Resolve a brand and return its profile + trade signals (GET `/api/v1/brands/{
|
|
328
|
-
brand(
|
|
308
|
+
/** Resolve a brand and return its profile + trade signals (GET `/api/v1/brands/{brandSlug}`). */
|
|
309
|
+
brand(brandSlug, options, signal) {
|
|
329
310
|
return this.#get("get_brand_profile", { q: options?.q }, signal, {
|
|
330
|
-
|
|
311
|
+
brandSlug,
|
|
331
312
|
});
|
|
332
313
|
}
|
|
314
|
+
// ── Runs (the hosted agentic surface) ────────────────────────────────────────
|
|
315
|
+
/**
|
|
316
|
+
* Upload one atomic source-material batch (POST `/api/v1/source-material`,
|
|
317
|
+
* multipart). Pass a heterogeneous pile via `{ files }` (any mix of PDF,
|
|
318
|
+
* text/markdown, and images — the server classifies each) or the legacy
|
|
319
|
+
* `{ brief, references }` shape. Requires the `mg:ingest` scope.
|
|
320
|
+
*/
|
|
321
|
+
uploadSourceMaterial(input, signal) {
|
|
322
|
+
return uploadSourceMaterial(this.#transport, input, signal);
|
|
323
|
+
}
|
|
324
|
+
/** List invocable agents, workflows, and tools (GET `/api/v1/capabilities`). */
|
|
325
|
+
capabilities(signal) {
|
|
326
|
+
return capabilitiesRun(this.#transport, signal);
|
|
327
|
+
}
|
|
328
|
+
/** Start an asynchronous capability Run (POST `/api/v1/runs`). */
|
|
329
|
+
createRun(body, signal) {
|
|
330
|
+
return createRun(this.#transport, body, signal);
|
|
331
|
+
}
|
|
332
|
+
/** Get a Run's current state (GET `/api/v1/runs/{runId}`). */
|
|
333
|
+
run(runId, signal) {
|
|
334
|
+
return getRun(this.#transport, runId, signal);
|
|
335
|
+
}
|
|
336
|
+
/** Resume a suspended Run (POST `/api/v1/runs/{runId}/resume`). */
|
|
337
|
+
resumeRun(runId, resumeData, signal) {
|
|
338
|
+
return resumeRun(this.#transport, runId, resumeData, signal);
|
|
339
|
+
}
|
|
340
|
+
/** Request durable Run cancellation (POST `/api/v1/runs/{runId}/cancel`). */
|
|
341
|
+
cancelRun(runId, signal) {
|
|
342
|
+
return cancelRun(this.#transport, runId, signal);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Derive an "option B" scheme from a succeeded compose Run along one
|
|
346
|
+
* constraint axis (POST `/api/v1/schemes/vary`). Deterministic and
|
|
347
|
+
* synchronous — the response IS the hydrated scheme (no Run envelope), stamped
|
|
348
|
+
* with axis + source provenance whose `notes` are the variation's receipts.
|
|
349
|
+
* Requires the `mg:read` scope.
|
|
350
|
+
*/
|
|
351
|
+
varyScheme(body, signal) {
|
|
352
|
+
return this.#post("vary_schemes", body, signal);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Stream ordered Run events as an async iterator (GET
|
|
356
|
+
* `/api/v1/runs/{runId}/events`, SSE). Iterates until the stream ends or the
|
|
357
|
+
* signal aborts; pass `after` to resume from the last observed sequence.
|
|
358
|
+
*/
|
|
359
|
+
streamRunEvents(runId, options) {
|
|
360
|
+
return streamRunEvents(this.#transport, runId, options);
|
|
361
|
+
}
|
|
362
|
+
// ── Projects (the persistent Material Box entity) ────────────────────────────
|
|
363
|
+
/**
|
|
364
|
+
* Create a persistent project owned by the caller (POST `/api/v1/projects`).
|
|
365
|
+
* Returns the freshly assembled state (an empty event log). Requires the
|
|
366
|
+
* `mg:ingest` scope.
|
|
367
|
+
*/
|
|
368
|
+
createProject(body = {}, signal) {
|
|
369
|
+
return createProjectCall(this.#transport, body, signal);
|
|
370
|
+
}
|
|
371
|
+
/** Read a project's assembled state (GET `/api/v1/projects/{projectId}`). */
|
|
372
|
+
getProject(projectId, signal) {
|
|
373
|
+
return getProjectCall(this.#transport, projectId, signal);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Append one event to a project's append-only process trail (POST
|
|
377
|
+
* `/api/v1/projects/{projectId}/events`). Requires the `mg:ingest` scope.
|
|
378
|
+
*/
|
|
379
|
+
appendProjectEvent(projectId, body, signal) {
|
|
380
|
+
return appendProjectEventCall(this.#transport, projectId, body, signal);
|
|
381
|
+
}
|
|
382
|
+
/** List the caller's own projects, newest first (GET `/api/v1/projects`). */
|
|
383
|
+
listProjects(signal) {
|
|
384
|
+
return listProjectsCall(this.#transport, signal);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Collect Run events until a terminal event, the deadline, or the stream ends
|
|
388
|
+
* — the bounded events-log pattern (a single pass over {@link streamRunEvents}).
|
|
389
|
+
*/
|
|
390
|
+
drainRunEvents(runId, options) {
|
|
391
|
+
return drainRunEvents(this.#transport, runId, options);
|
|
392
|
+
}
|
|
333
393
|
}
|
package/codes-types.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-mirrored wire types for the advisory building-code surface
|
|
3
|
+
* (`POST /api/v1/codes/advisory`, MG-433).
|
|
4
|
+
*
|
|
5
|
+
* Like the rest of the SDK these interfaces have NO runtime dependency on any
|
|
6
|
+
* `@materialgraph/*` package — they mirror the `codeAdvisoryInputSchema` /
|
|
7
|
+
* `codeAdvisoryResultSchema` Zod contracts the route is built from. Response
|
|
8
|
+
* enums follow the SDK's TOLERANT-PARSING contract (widened with `OpenEnum` so a
|
|
9
|
+
* new server value still type-checks); `requirement.scale` is an open IBC-scale
|
|
10
|
+
* id and `caveat` is the AHJ advisory sentence, both read as plain strings.
|
|
11
|
+
*
|
|
12
|
+
* ADVISORY ONLY: every verdict and the envelope carry the AHJ caveat — this is
|
|
13
|
+
* model code as adopted, never a legal determination.
|
|
14
|
+
*/
|
|
15
|
+
import type { OpenEnum } from "./types.js";
|
|
16
|
+
/**
|
|
17
|
+
* Badge a bounded set of materials against the code-requirement registry for one
|
|
18
|
+
* (jurisdiction, occupancy, space, sprinklered) context. `results` preserve
|
|
19
|
+
* input order and cardinality (duplicate ids are echoed).
|
|
20
|
+
*/
|
|
21
|
+
export interface CodeAdvisoryInput {
|
|
22
|
+
/** 1–48 opaque variant ids to badge, echoed back in `results` in this exact order. */
|
|
23
|
+
variantIds: string[];
|
|
24
|
+
/** A seeded jurisdiction id (e.g. `"us-tx"`); an unknown id is a 400. */
|
|
25
|
+
jurisdictionId: string;
|
|
26
|
+
/** The IBC occupancy group for the space (e.g. `"I-2"` hospital, `"A-2"` restaurant). */
|
|
27
|
+
occupancyGroup: string;
|
|
28
|
+
/** The finish-location / space type (e.g. `"corridor"`, `"exit_enclosure"`). */
|
|
29
|
+
spaceType: string;
|
|
30
|
+
/** Whether the space is sprinklered — relaxes several fire-finish floors. */
|
|
31
|
+
sprinklered: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** The resolved jurisdiction the verdicts were evaluated against. */
|
|
34
|
+
export interface CodeAdvisoryJurisdiction {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
codeFamily: string;
|
|
38
|
+
edition: string;
|
|
39
|
+
adoptionNote: string;
|
|
40
|
+
}
|
|
41
|
+
/** The echoed evaluation context (the space the badges apply to). */
|
|
42
|
+
export interface CodeAdvisoryContext {
|
|
43
|
+
occupancyGroup: string;
|
|
44
|
+
spaceType: string;
|
|
45
|
+
sprinklered: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** The threshold one requirement imposes: a class floor and/or a numeric floor. */
|
|
48
|
+
export interface CodeRequirementThreshold {
|
|
49
|
+
/** The IBC classification scale this requirement is measured on (open id). */
|
|
50
|
+
scale: string;
|
|
51
|
+
/** The minimum acceptable class on `scale`, or null when only a numeric floor applies. */
|
|
52
|
+
minClass: string | null;
|
|
53
|
+
/** A numeric floor (e.g. a DCOF minimum), or null when only a class floor applies. */
|
|
54
|
+
numericFloor: {
|
|
55
|
+
metric: string;
|
|
56
|
+
value: number;
|
|
57
|
+
unit: string;
|
|
58
|
+
} | null;
|
|
59
|
+
}
|
|
60
|
+
/** The accepted fact a verdict was decided from (null when the material carried none). */
|
|
61
|
+
export interface CodeRequirementEvidence {
|
|
62
|
+
attributeId: OpenEnum<"fire_classifications" | "slip_resistance_dcof">;
|
|
63
|
+
value: string;
|
|
64
|
+
confidence: number | null;
|
|
65
|
+
sourceBasis: string | null;
|
|
66
|
+
}
|
|
67
|
+
/** One requirement badged against one material. */
|
|
68
|
+
export interface CodeRequirementVerdict {
|
|
69
|
+
requirementId: string;
|
|
70
|
+
dimension: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
|
|
71
|
+
section: string;
|
|
72
|
+
requirement: CodeRequirementThreshold;
|
|
73
|
+
/** `meets` / `shortfall` / `unknown` — `unknown` when no defensible fact maps onto `scale`. */
|
|
74
|
+
verdict: OpenEnum<"meets" | "shortfall" | "unknown">;
|
|
75
|
+
evidence: CodeRequirementEvidence | null;
|
|
76
|
+
/** The AHJ advisory caveat — model code as adopted, never a legal determination. */
|
|
77
|
+
caveat: string;
|
|
78
|
+
}
|
|
79
|
+
/** Per-variant advisory outcome, in input order. */
|
|
80
|
+
export interface CodeAdvisoryVariantResult {
|
|
81
|
+
variantId: string;
|
|
82
|
+
/** `evaluated` when the id resolved; `unknown_variant` (with `requirements: []`) when it did not. */
|
|
83
|
+
state: OpenEnum<"evaluated" | "unknown_variant">;
|
|
84
|
+
requirements: CodeRequirementVerdict[];
|
|
85
|
+
}
|
|
86
|
+
/** The `POST /api/v1/codes/advisory` envelope — per-variant, per-requirement verdicts. */
|
|
87
|
+
export interface CodeAdvisoryResponse {
|
|
88
|
+
jurisdiction: CodeAdvisoryJurisdiction;
|
|
89
|
+
context: CodeAdvisoryContext;
|
|
90
|
+
/** The AHJ advisory caveat, at the envelope level too. */
|
|
91
|
+
caveat: string;
|
|
92
|
+
results: CodeAdvisoryVariantResult[];
|
|
93
|
+
}
|
package/codes-types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|