@instruments/materialgraph 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/by-spec-types.d.ts +73 -0
  2. package/by-spec-types.js +7 -0
  3. package/client.d.ts +186 -7
  4. package/client.js +242 -125
  5. package/codes-client.d.ts +82 -0
  6. package/codes-client.js +73 -0
  7. package/codes-types.d.ts +238 -0
  8. package/codes-types.js +1 -0
  9. package/colour-aggregate-types.d.ts +93 -0
  10. package/colour-aggregate-types.js +1 -0
  11. package/colours-client.d.ts +52 -0
  12. package/colours-client.js +39 -0
  13. package/composition-types.d.ts +343 -0
  14. package/composition-types.js +10 -0
  15. package/http-transport.d.ts +74 -0
  16. package/http-transport.js +201 -0
  17. package/index.d.ts +19 -0
  18. package/index.js +9 -0
  19. package/match-stream-types.d.ts +150 -0
  20. package/match-stream-types.js +28 -0
  21. package/match-stream.d.ts +22 -0
  22. package/match-stream.js +280 -0
  23. package/material-block-types.d.ts +23 -2
  24. package/material-types.d.ts +92 -0
  25. package/material-types.js +1 -0
  26. package/ndjson.d.ts +46 -0
  27. package/ndjson.js +103 -0
  28. package/operations.d.ts +6 -2
  29. package/operations.js +30 -2
  30. package/package.json +1 -1
  31. package/project-types.d.ts +67 -0
  32. package/project-types.js +16 -0
  33. package/projects.d.ts +13 -0
  34. package/projects.js +46 -0
  35. package/run-events.d.ts +82 -0
  36. package/run-events.js +29 -0
  37. package/run-types.d.ts +266 -0
  38. package/run-types.js +6 -0
  39. package/runs.d.ts +84 -0
  40. package/runs.js +253 -0
  41. package/schedule-types.d.ts +88 -0
  42. package/schedule-types.js +12 -0
  43. package/search-refinement-types.d.ts +26 -1
  44. package/search-result-validation.d.ts +27 -0
  45. package/search-result-validation.js +174 -0
  46. package/source-material-validation.d.ts +3 -0
  47. package/source-material-validation.js +109 -0
  48. package/spec-types.d.ts +82 -0
  49. package/spec-types.js +44 -0
  50. package/specifications.d.ts +72 -0
  51. package/specifications.js +82 -0
  52. package/sse.d.ts +35 -0
  53. package/sse.js +147 -0
  54. package/types.d.ts +204 -77
  55. package/validation.d.ts +119 -0
  56. package/validation.js +241 -0
@@ -0,0 +1,73 @@
1
+ export interface BySpecMatchedCell {
2
+ attributeId: string;
3
+ confidence: number | null;
4
+ sourceBasis: string | null;
5
+ }
6
+ /** The six states a verdict cell can report; open per the response-enum policy. */
7
+ export type BySpecVerdictState = "matched" | "deviated" | "failed" | "unverified" | "missing" | "not_evaluated";
8
+ /**
9
+ * One cell of a match's report card — how a single requested criterion scored.
10
+ * Hand-mirrors the `AttributeVerdict` contract from `@materialgraph/contracts`;
11
+ * the OpenAPI `CatalogBySpecResponse` schema is the source of truth on the wire.
12
+ */
13
+ export interface BySpecVerdictCell {
14
+ attributeId: string;
15
+ state: BySpecVerdictState;
16
+ strength: "required" | "preferred";
17
+ baseline: {
18
+ kind: "term";
19
+ term: string;
20
+ } | {
21
+ kind: "presence";
22
+ } | {
23
+ kind: "seed";
24
+ variantId: string;
25
+ } | {
26
+ kind: "image";
27
+ source: "variant" | "vector";
28
+ variantId: string | null;
29
+ };
30
+ value: string | null;
31
+ deviation: {
32
+ direction: "above" | "below" | "other";
33
+ delta: number | null;
34
+ unit: string | null;
35
+ label: string;
36
+ } | null;
37
+ confidence: number | null;
38
+ sourceBasis: string | null;
39
+ trustGap: "below_confidence" | "basis_unmet" | "proposed" | null;
40
+ }
41
+ /** A match's full report card: every cell, the rolled-up grade, weakest confidence. */
42
+ export interface BySpecVerdict {
43
+ cells: BySpecVerdictCell[];
44
+ grade: "meets_all" | "close" | "hard_no";
45
+ minConfidence: number | null;
46
+ }
47
+ /**
48
+ * The verdict a materials-search hit carries (MG-625). It IS the shared
49
+ * {@link BySpecVerdict} shape — the single CandidateVerdict mirror — aliased here
50
+ * so search-surface code can name it for what it is rather than duplicating it.
51
+ */
52
+ export type MaterialVerdict = BySpecVerdict;
53
+ export interface BySpecMatch {
54
+ variantId: string;
55
+ brand: string;
56
+ product: string;
57
+ variant: string;
58
+ color: string | null;
59
+ imageUrl: string | null;
60
+ hex: string | null;
61
+ matched: BySpecMatchedCell[];
62
+ minConfidence: number | null;
63
+ verdict: BySpecVerdict;
64
+ }
65
+ export interface BySpecResponse {
66
+ criteria: Array<{
67
+ attribute: string;
68
+ value: string;
69
+ }>;
70
+ totalMatches: number;
71
+ brandsSpanned: number;
72
+ matches: BySpecMatch[];
73
+ }
@@ -0,0 +1,7 @@
1
+ // ── Catalog / spec — find-by-spec wire types ─────────────────────────────────
2
+ //
3
+ // Hand-maintained mirror of the `CatalogBySpecResponse` OpenAPI schema (the
4
+ // source of truth on the wire); the `sdk-openapi-parity` test gates this against
5
+ // the generated spec. Split out of `types.ts` to keep that file under its growth
6
+ // baseline; re-exported from `types.ts` so importers see no change.
7
+ export {};
package/client.d.ts CHANGED
@@ -1,9 +1,17 @@
1
1
  import { type RateLimit } from "./errors.js";
2
+ import type { CapabilityManifestResponse, CreatePublicRunRequest, CreatePublicRunResponse, DrainRunEventsOptions, PublicRunResponse, ResumeRunOptions, RunEvent, SourceMaterialBatchDetail, SourceMaterialBatchReceipt, StreamRunEventsOptions, UploadSourceMaterialInput } from "./run-types.js";
3
+ import type { FinishScheduleResponse, GetRunScheduleOptions } from "./schedule-types.js";
4
+ import type { MatchStreamEvent } from "./match-stream-types.js";
5
+ import type { AppendProjectEventRequest, CreateProjectRequest, ProjectEventReceipt, ProjectListResponse, ProjectState } from "./project-types.js";
6
+ import type { VarySchemesRequest, VarySchemesResponse } from "./composition-types.js";
7
+ import { CodesClient } from "./codes-client.js";
8
+ import { ColoursClient } from "./colours-client.js";
9
+ import type { CodeAdvisoryInput, CodeAdvisoryResponse } from "./codes-types.js";
2
10
  import type { BrandCatalogListPage, BrandsOptions } from "./brand-types.js";
3
11
  import type { MaterialAssetProjection } from "./material-asset-types.js";
4
12
  import type { MaterialBlockProjection } from "./material-block-types.js";
5
13
  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";
14
+ 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
15
  export declare const DEFAULT_BASE_URL = "https://beta.materialgraph.com";
8
16
  export declare const MAX_BATCH_IDS = 100;
9
17
  export interface MaterialGraphClientOptions {
@@ -22,11 +30,57 @@ export declare class MaterialGraphClient {
22
30
  #private;
23
31
  readonly baseUrl: string;
24
32
  readonly browse: BrowseNamespace;
33
+ /**
34
+ * The advisory building-code surface: `codes.context()` to discover the
35
+ * vocabulary, `codes.requirements()` to state a space, `codes.advisory()` to
36
+ * badge candidates against it.
37
+ */
38
+ readonly codes: CodesClient;
39
+ /**
40
+ * The colour AGGREGATE surface: `colours.profiles()` for a category palette,
41
+ * `colours.stats()` for corpus colour coverage.
42
+ */
43
+ readonly colours: ColoursClient;
25
44
  constructor(options: MaterialGraphClientOptions);
26
45
  get lastRateLimit(): RateLimit | null;
46
+ /**
47
+ * The full search primitive (POST `/api/v1/search`). Layer a hard
48
+ * specification gate onto a ranked (colour / visual / lexical / seed) search
49
+ * via `input.filters` — each entry is a typed {@link SearchFilter} criterion:
50
+ * a term match (`{ attributeId, term }`), a presence check
51
+ * (`{ attributeId, hasValue: true }`), or a certification programme
52
+ * (`{ attributeId: "certification_programme", term }`). This is the seam for
53
+ * "a GREENGUARD-certified alternative to THIS material" (seed by `similarTo`,
54
+ * gate by `filters`) or "the same fire class, with an EPD on record". Build the
55
+ * filters with the `certificationProgramme` / `specTerm` / `hasSpec` helpers.
56
+ */
27
57
  search(input: SearchInput, signal?: AbortSignal): Promise<SearchResponse>;
28
58
  searchGet(params: SearchGetParams, signal?: AbortSignal): Promise<SearchResponse>;
29
59
  match(input: MatchInput, signal?: AbortSignal): Promise<MatchResponse>;
60
+ /**
61
+ * Material Match as a PROGRESSIVE stream — the same POST
62
+ * `/api/v1/search/match` endpoint negotiated with `Accept:
63
+ * application/x-ndjson`, which runs the scene workflow (classify → decompose →
64
+ * one match per surface) and emits an event as each surface resolves. Use this
65
+ * wherever the user watches a photo come apart; use {@link match} when you only
66
+ * want the finished answer.
67
+ *
68
+ * Iterate it directly — the request opens on the first `next()`:
69
+ *
70
+ * ```ts
71
+ * for await (const event of client.matchStream({ imageBase64 })) {
72
+ * if (event.type === "surface") render(event.surface);
73
+ * }
74
+ * ```
75
+ *
76
+ * Every frame is validated before it is yielded, and the iterator ends only on
77
+ * a `complete` or `error` event — a truncated connection raises rather than
78
+ * passing for a short run. `signal` cancels the in-flight request itself, as
79
+ * does breaking out of the loop; a cancelled stream ends without raising.
80
+ * Requires an image (`imageBase64` or `imageUrl`); a `vector` input is
81
+ * buffered-only.
82
+ */
83
+ matchStream(input: MatchInput, signal?: AbortSignal): AsyncGenerator<MatchStreamEvent, void, unknown>;
30
84
  byColour(params: ByColourParams, signal?: AbortSignal): Promise<ByColourResponse>;
31
85
  /** Get a single material's detail record (GET `/api/v1/materials/{variantId}`). */
32
86
  material(variantId: string, options?: {
@@ -83,8 +137,15 @@ export declare class MaterialGraphClient {
83
137
  /** Search canonical attributes and value-dictionary terms together. */
84
138
  searchVocabulary(query: string, options?: VocabularySearchOptions, signal?: AbortSignal): Promise<VocabularySearchResponse>;
85
139
  /**
86
- * Find materials by canonical specification (GET `/api/v1/search/specifications`).
87
- * Each `spec` is an `attribute:value` criterion; a variant must satisfy ALL.
140
+ * Find materials by canonical specification across every brand — the
141
+ * value-anchored, spec-only primitive (GET `/api/v1/search/specifications`).
142
+ * Each `spec` is an `attribute:value` criterion; a variant must satisfy ALL of
143
+ * them, and each match carries per-criterion trust (`confidence` +
144
+ * `sourceBasis`). Answers "which materials, from anyone, are ASTM E84 Class A
145
+ * AND GREENGUARD-certified?" Build the strings with `specCriterion` /
146
+ * `certificationProgrammeCriterion` (e.g.
147
+ * `certificationProgrammeCriterion("GREENGUARD Gold")`); discover the queryable
148
+ * vocabulary with {@link specificationFacets} and {@link attributeValues}.
88
149
  */
89
150
  bySpec(spec: string | string[], options?: BySpecOptions, signal?: AbortSignal): Promise<BySpecResponse>;
90
151
  /** The product-category tree, flattened (GET `/api/v1/taxonomy/categories`). */
@@ -102,11 +163,55 @@ export declare class MaterialGraphClient {
102
163
  * `/api/v1/demand`). Provide EXACTLY ONE of `variantId` or `material`.
103
164
  */
104
165
  demand(params: DemandParams, signal?: AbortSignal): Promise<DemandResponse>;
166
+ /**
167
+ * Enrich a bounded set of ALREADY-CHOSEN candidate ids with their real
168
+ * specification demand (POST `/api/v1/demand/batch`, 1–48 ids). The bounded
169
+ * batch complement to {@link demand}: no fuzzy resolution, ranking, or
170
+ * reordering — each id yields one discriminated `state` (`available` /
171
+ * `insufficient_data` / `no_signal` / `unknown_variant`) in input order and
172
+ * cardinality. This is the Desk's "show demand momentum for every pick in this
173
+ * scheme" read; an `mg:admin` key additionally sees per-result `evidence`.
174
+ *
175
+ * @example
176
+ * // Rank a shortlist by real specification momentum before locking the scheme.
177
+ * const { results } = await mg.demandBatch({
178
+ * variantIds: scheme.picks.map((p) => p.variantId),
179
+ * signal: "sampled",
180
+ * context: { projectType: "hospitality" },
181
+ * });
182
+ * const rising = results.filter(
183
+ * (r) => r.state === "available" && r.momentum.state === "rising",
184
+ * );
185
+ */
186
+ demandBatch(input: DemandBatchInput, signal?: AbortSignal): Promise<DemandBatchResponse>;
187
+ /**
188
+ * Badge materials against the advisory building-code registry (POST
189
+ * `/api/v1/codes/advisory`). Identical to {@link CodesClient.advisory}, kept
190
+ * flat because it shipped before the `codes` namespace existed.
191
+ */
192
+ codeAdvisory(input: CodeAdvisoryInput, signal?: AbortSignal): Promise<CodeAdvisoryResponse>;
105
193
  /** Extract a named colour palette from an uploaded image (POST `/api/v1/search/palettes/extract`). */
106
194
  extractPalette(input: ExtractPaletteInput, signal?: AbortSignal): Promise<ExtractPaletteResponse>;
107
- /** Discover the live specification-facet collection. */
195
+ /**
196
+ * Discover the live specification vocabulary (GET
197
+ * `/api/v1/catalog/specifications/facets`) — the facetable spec attributes that
198
+ * have real product-graph data, each with its top canonical values and the
199
+ * variants/brands behind them. This is where a caller LEARNS that the data
200
+ * exists: LEED, GREENGUARD, ASTM E84 Class A, FloorScore, EPDs all surface here
201
+ * with counts. Includes the virtual `certification_programme` facet (its values
202
+ * are programme names). Learn an `attribute:value` here, then reuse it as a
203
+ * {@link bySpec} criterion or a {@link search} `filters` entry. NOTE: continuous
204
+ * numeric specs (e.g. `slip_resistance_dcof`, a 0–1 DCOF range) are NOT
205
+ * token-facetable and do not appear here — read those per-attribute.
206
+ */
108
207
  specificationFacets(signal?: AbortSignal): Promise<SpecFacetsListResponse>;
109
- /** List the live values for one canonical attribute. */
208
+ /**
209
+ * List the live value distribution for ONE canonical attribute (GET
210
+ * `/api/v1/catalog/attributes/{attributeId}/values`), ordered by cross-brand
211
+ * reach with variant + brand counts. The drill-in companion to
212
+ * {@link specificationFacets} — e.g. every certification programme on record,
213
+ * or every fire classification and how many variants carry each.
214
+ */
110
215
  attributeValues(attributeId: string, signal?: AbortSignal): Promise<SpecFacetsDistributionResponse>;
111
216
  /** The specs a product type COULD carry (GET `/api/v1/taxonomy/categories/{key}/specs`). */
112
217
  applicableSpecs(key: string, signal?: AbortSignal): Promise<ApplicableSpecsResponse>;
@@ -114,6 +219,80 @@ export declare class MaterialGraphClient {
114
219
  glossary(term: string, signal?: AbortSignal): Promise<GlossaryTermResponse>;
115
220
  /** List registered brands (GET `/api/v1/brands`). */
116
221
  brands(options?: BrandsOptions, signal?: AbortSignal): Promise<BrandCatalogListPage>;
117
- /** Resolve a brand and return its profile + trade signals (GET `/api/v1/brands/{slug}`). */
118
- brand(slug: string, options?: BrandOptions, signal?: AbortSignal): Promise<BrandProfileResponse>;
222
+ /** Resolve a brand and return its profile + trade signals (GET `/api/v1/brands/{brandSlug}`). */
223
+ brand(brandSlug: string, options?: BrandOptions, signal?: AbortSignal): Promise<BrandProfileResponse>;
224
+ /**
225
+ * Upload one atomic source-material batch (POST `/api/v1/source-material`,
226
+ * multipart). Pass a heterogeneous pile via `{ files }` (any mix of PDF,
227
+ * text/markdown, and images — the server classifies each) or the legacy
228
+ * `{ brief, references }` shape. Requires the `mg:ingest` scope.
229
+ */
230
+ uploadSourceMaterial(input: UploadSourceMaterialInput, signal?: AbortSignal): Promise<SourceMaterialBatchReceipt>;
231
+ /**
232
+ * Read back an uploaded batch (GET `/api/v1/source-material/{batchId}`): every
233
+ * file with a fetchable `url` and its analyses inlined. `analyses` is always
234
+ * an array — `[]` means nothing has been computed yet. Analyses are shared by
235
+ * content hash, so re-uploading an already-analysed image returns its analysis
236
+ * immediately. A batch you do not own raises the same 404 as one that does not
237
+ * exist. Requires the `mg:compose` scope.
238
+ */
239
+ sourceMaterialBatch(batchId: string, signal?: AbortSignal): Promise<SourceMaterialBatchDetail>;
240
+ /** List invocable agents, workflows, and tools (GET `/api/v1/capabilities`). */
241
+ capabilities(signal?: AbortSignal): Promise<CapabilityManifestResponse>;
242
+ /** Start an asynchronous capability Run (POST `/api/v1/runs`). */
243
+ createRun(body: CreatePublicRunRequest, signal?: AbortSignal): Promise<CreatePublicRunResponse>;
244
+ /** Get a Run's current state (GET `/api/v1/runs/{runId}`). */
245
+ run(runId: string, signal?: AbortSignal): Promise<PublicRunResponse>;
246
+ /**
247
+ * Export a completed compose Run's scheme as a finish schedule — one row per
248
+ * slot, every attribute value carrying its source and verification state
249
+ * (GET `/api/v1/runs/{runId}/schedule`). Unknown values are explicit
250
+ * `missing` cells, never blanks. The `.xlsx` form is a direct-URL download:
251
+ * `GET /api/v1/runs/{runId}/schedule.xlsx` with the same bearer key.
252
+ */
253
+ runSchedule(runId: string, options?: GetRunScheduleOptions): Promise<FinishScheduleResponse>;
254
+ /**
255
+ * Resume a suspended Run (POST `/api/v1/runs/{runId}/resume`).
256
+ *
257
+ * The third argument accepts either an `AbortSignal` (the published form) or
258
+ * {@link ResumeRunOptions}, whose `stepId` guards against resuming a step the
259
+ * run has already moved past — the API answers 409 on a mismatch (MG-608).
260
+ */
261
+ resumeRun(runId: string, resumeData: unknown, signalOrOptions?: AbortSignal | ResumeRunOptions): Promise<PublicRunResponse>;
262
+ /** Request durable Run cancellation (POST `/api/v1/runs/{runId}/cancel`). */
263
+ cancelRun(runId: string, signal?: AbortSignal): Promise<PublicRunResponse>;
264
+ /**
265
+ * Derive an "option B" scheme from a succeeded compose Run along one
266
+ * constraint axis (POST `/api/v1/schemes/vary`). Deterministic and
267
+ * synchronous — the response IS the hydrated scheme (no Run envelope), stamped
268
+ * with axis + source provenance whose `notes` are the variation's receipts.
269
+ * Requires the `mg:read` scope.
270
+ */
271
+ varyScheme(body: VarySchemesRequest, signal?: AbortSignal): Promise<VarySchemesResponse>;
272
+ /**
273
+ * Stream ordered Run events as an async iterator (GET
274
+ * `/api/v1/runs/{runId}/events`, SSE). Iterates until the stream ends or the
275
+ * signal aborts; pass `after` to resume from the last observed sequence.
276
+ */
277
+ streamRunEvents(runId: string, options?: StreamRunEventsOptions): AsyncGenerator<RunEvent>;
278
+ /**
279
+ * Create a persistent project owned by the caller (POST `/api/v1/projects`).
280
+ * Returns the freshly assembled state (an empty event log). Requires the
281
+ * `mg:ingest` scope.
282
+ */
283
+ createProject(body?: CreateProjectRequest, signal?: AbortSignal): Promise<ProjectState>;
284
+ /** Read a project's assembled state (GET `/api/v1/projects/{projectId}`). */
285
+ getProject(projectId: string, signal?: AbortSignal): Promise<ProjectState>;
286
+ /**
287
+ * Append one event to a project's append-only process trail (POST
288
+ * `/api/v1/projects/{projectId}/events`). Requires the `mg:ingest` scope.
289
+ */
290
+ appendProjectEvent(projectId: string, body: AppendProjectEventRequest, signal?: AbortSignal): Promise<ProjectEventReceipt>;
291
+ /** List the caller's own projects, newest first (GET `/api/v1/projects`). */
292
+ listProjects(signal?: AbortSignal): Promise<ProjectListResponse>;
293
+ /**
294
+ * Collect Run events until a terminal event, the deadline, or the stream ends
295
+ * — the bounded events-log pattern (a single pass over {@link streamRunEvents}).
296
+ */
297
+ drainRunEvents(runId: string, options: DrainRunEventsOptions): Promise<RunEvent[]>;
119
298
  }