@instruments/materialgraph 0.2.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.
- package/by-spec-types.d.ts +73 -0
- package/by-spec-types.js +7 -0
- package/client.d.ts +68 -23
- package/client.js +82 -25
- package/codes-client.d.ts +82 -0
- package/codes-client.js +73 -0
- package/codes-types.d.ts +154 -9
- package/colour-aggregate-types.d.ts +93 -0
- package/colour-aggregate-types.js +1 -0
- package/colours-client.d.ts +52 -0
- package/colours-client.js +39 -0
- package/composition-types.d.ts +228 -2
- package/http-transport.d.ts +19 -2
- package/http-transport.js +72 -2
- package/index.d.ts +7 -0
- package/index.js +3 -0
- package/match-stream-types.d.ts +150 -0
- package/match-stream-types.js +28 -0
- package/match-stream.d.ts +22 -0
- package/match-stream.js +280 -0
- package/material-block-types.d.ts +23 -2
- package/material-types.d.ts +92 -0
- package/material-types.js +1 -0
- package/ndjson.d.ts +46 -0
- package/ndjson.js +103 -0
- package/operations.d.ts +1 -1
- package/operations.js +6 -0
- package/package.json +1 -1
- package/run-types.d.ts +65 -1
- package/runs.d.ts +45 -3
- package/runs.js +82 -43
- package/schedule-types.d.ts +88 -0
- package/schedule-types.js +12 -0
- package/search-refinement-types.d.ts +13 -0
- package/search-result-validation.d.ts +27 -0
- package/search-result-validation.js +174 -0
- package/source-material-validation.d.ts +3 -0
- package/source-material-validation.js +109 -0
- package/spec-types.d.ts +4 -0
- package/spec-types.js +4 -0
- package/specifications.d.ts +9 -0
- package/specifications.js +9 -0
- package/types.d.ts +59 -73
- package/validation.d.ts +119 -0
- package/validation.js +241 -0
package/codes-types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hand-mirrored wire types for the advisory building-code surface
|
|
3
|
-
* (`
|
|
3
|
+
* (`GET /api/v1/codes/context`, `GET /api/v1/codes/requirements`,
|
|
4
|
+
* `POST /api/v1/codes/advisory` — MG-433, MG-674).
|
|
4
5
|
*
|
|
5
6
|
* Like the rest of the SDK these interfaces have NO runtime dependency on any
|
|
6
7
|
* `@materialgraph/*` package — they mirror the `codeAdvisoryInputSchema` /
|
|
@@ -18,18 +19,32 @@ import type { OpenEnum } from "./types.js";
|
|
|
18
19
|
* (jurisdiction, occupancy, space, sprinklered) context. `results` preserve
|
|
19
20
|
* input order and cardinality (duplicate ids are echoed).
|
|
20
21
|
*/
|
|
21
|
-
export interface
|
|
22
|
-
/** 1–48 opaque variant ids to badge, echoed back in `results` in this exact order. */
|
|
23
|
-
variantIds: string[];
|
|
22
|
+
export interface CodeContextRequest {
|
|
24
23
|
/** A seeded jurisdiction id (e.g. `"us-tx"`); an unknown id is a 400. */
|
|
25
24
|
jurisdictionId: string;
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The IBC occupancy group for the space (e.g. `"I-2"` hospital, `"A-2"`
|
|
27
|
+
* restaurant). Required unless `roomType` correlates to one.
|
|
28
|
+
*/
|
|
29
|
+
occupancyGroup?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The finish location: `"corridors"`, `"exit_stairs_passageways"`, or
|
|
32
|
+
* `"rooms_enclosed_spaces"`. Required unless `roomType` correlates to one.
|
|
33
|
+
*/
|
|
34
|
+
spaceType?: string;
|
|
35
|
+
/**
|
|
36
|
+
* An `ms_room` term id (e.g. `"hotel_room"`) to correlate the regulatory axes
|
|
37
|
+
* from. Explicit `occupancyGroup` / `spaceType` always win. A room that
|
|
38
|
+
* correlates to neither axis is a 400 naming the axis — never a guess.
|
|
39
|
+
*/
|
|
40
|
+
roomType?: string;
|
|
41
|
+
/** Whether the space is sprinklered — relaxes several fire-finish floors. No default. */
|
|
31
42
|
sprinklered: boolean;
|
|
32
43
|
}
|
|
44
|
+
export interface CodeAdvisoryInput extends CodeContextRequest {
|
|
45
|
+
/** 1–100 opaque variant ids to badge, echoed back in `results` in this exact order. */
|
|
46
|
+
variantIds: string[];
|
|
47
|
+
}
|
|
33
48
|
/** The resolved jurisdiction the verdicts were evaluated against. */
|
|
34
49
|
export interface CodeAdvisoryJurisdiction {
|
|
35
50
|
id: string;
|
|
@@ -44,6 +59,35 @@ export interface CodeAdvisoryContext {
|
|
|
44
59
|
spaceType: string;
|
|
45
60
|
sprinklered: boolean;
|
|
46
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* What one material is, and therefore what it was held to. `dimensions` are the
|
|
64
|
+
* regulatory dimensions the material can be scored on; `basis` names which
|
|
65
|
+
* source(s) produced them: a mapped `productTypeId`, `applicationIds`, both, or
|
|
66
|
+
* `unresolved` when neither yielded anything. `unresolved` does NOT mean the
|
|
67
|
+
* material is answerable for nothing — it means we could not tell what the
|
|
68
|
+
* material is, so every requirement the space imposes was scored against it and
|
|
69
|
+
* `notApplicable` on the result is empty. That is a coverage gap in the
|
|
70
|
+
* product-type/application map, not a narrow material. A short `requirements`
|
|
71
|
+
* list under `product_type` / `application` / `both` is a genuine intersection;
|
|
72
|
+
* a full list under `unresolved` is the map falling short.
|
|
73
|
+
*/
|
|
74
|
+
export interface MaterialCodeProfile {
|
|
75
|
+
/** The variant's accepted primary product type, or null if it has none. */
|
|
76
|
+
productTypeId: string | null;
|
|
77
|
+
/** The variant's accepted `applications` terms, as stored. */
|
|
78
|
+
applicationIds: string[];
|
|
79
|
+
/** Regulatory dimensions this material can be held to. Empty ONLY when `basis` is `unresolved`. */
|
|
80
|
+
dimensions: OpenEnum<"fire" | "floor" | "slip" | "decorative">[];
|
|
81
|
+
basis: OpenEnum<"product_type" | "application" | "both" | "unresolved">;
|
|
82
|
+
}
|
|
83
|
+
/** Why one requirement the space imposes was excluded from this material's `requirements`. */
|
|
84
|
+
export interface CodeRequirementExclusion {
|
|
85
|
+
requirementId: string;
|
|
86
|
+
dimension: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
|
|
87
|
+
reason: OpenEnum<"not_a_material_dimension">;
|
|
88
|
+
/** One readable line, e.g. `"Slip resistance is not a dimension wallcovering answers for."`. */
|
|
89
|
+
detail: string;
|
|
90
|
+
}
|
|
47
91
|
/** The threshold one requirement imposes: a class floor and/or a numeric floor. */
|
|
48
92
|
export interface CodeRequirementThreshold {
|
|
49
93
|
/** The IBC classification scale this requirement is measured on (open id). */
|
|
@@ -76,18 +120,119 @@ export interface CodeRequirementVerdict {
|
|
|
76
120
|
/** The AHJ advisory caveat — model code as adopted, never a legal determination. */
|
|
77
121
|
caveat: string;
|
|
78
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* One candidate's requirement verdicts folded into a single status and sentence.
|
|
125
|
+
* A shortfall outranks an unknown, and an unknown NEVER rolls up to a pass:
|
|
126
|
+
* `not_applicable` (nothing applied) is deliberately distinct from `meets`.
|
|
127
|
+
*/
|
|
128
|
+
export interface CodeAdvisorySummary {
|
|
129
|
+
status: OpenEnum<"meets" | "shortfall" | "unverified" | "not_applicable">;
|
|
130
|
+
/** One deterministic sentence naming what failed, e.g. `"Falls short on fire for A-2 corridors."`. */
|
|
131
|
+
headline: string;
|
|
132
|
+
shortfallDimensions: OpenEnum<"fire" | "floor" | "slip" | "decorative">[];
|
|
133
|
+
unverifiedDimensions: OpenEnum<"fire" | "floor" | "slip" | "decorative">[];
|
|
134
|
+
}
|
|
79
135
|
/** Per-variant advisory outcome, in input order. */
|
|
80
136
|
export interface CodeAdvisoryVariantResult {
|
|
81
137
|
variantId: string;
|
|
82
138
|
/** `evaluated` when the id resolved; `unknown_variant` (with `requirements: []`) when it did not. */
|
|
83
139
|
state: OpenEnum<"evaluated" | "unknown_variant">;
|
|
140
|
+
/** What this material is and what it was scored against. Null only for `state: "unknown_variant"`. */
|
|
141
|
+
material: MaterialCodeProfile | null;
|
|
142
|
+
/** Verdicts for the requirements this material is answerable for — the answerable subset only. */
|
|
84
143
|
requirements: CodeRequirementVerdict[];
|
|
144
|
+
/** Requirements the space imposes that this material cannot answer. Empty when nothing was excluded. */
|
|
145
|
+
notApplicable: CodeRequirementExclusion[];
|
|
146
|
+
/** The rolled-up card, or null for an id that resolved to nothing. */
|
|
147
|
+
summary: CodeAdvisorySummary | null;
|
|
85
148
|
}
|
|
86
149
|
/** The `POST /api/v1/codes/advisory` envelope — per-variant, per-requirement verdicts. */
|
|
87
150
|
export interface CodeAdvisoryResponse {
|
|
88
151
|
jurisdiction: CodeAdvisoryJurisdiction;
|
|
152
|
+
/** The context AS SUPPLIED, echoed verbatim. */
|
|
89
153
|
context: CodeAdvisoryContext;
|
|
154
|
+
/** The canonical axes the registry was queried with, and where they came from. */
|
|
155
|
+
resolvedContext: ResolvedCodeContext;
|
|
90
156
|
/** The AHJ advisory caveat, at the envelope level too. */
|
|
91
157
|
caveat: string;
|
|
92
158
|
results: CodeAdvisoryVariantResult[];
|
|
93
159
|
}
|
|
160
|
+
/** Where a resolved axis came from: stated outright, or correlated from a room type. */
|
|
161
|
+
export interface ResolvedCodeContext {
|
|
162
|
+
jurisdictionId: string;
|
|
163
|
+
occupancyGroup: string;
|
|
164
|
+
/** The canonical finish-location term id the registry was queried with. */
|
|
165
|
+
spaceType: string;
|
|
166
|
+
sprinklered: boolean;
|
|
167
|
+
basis: OpenEnum<"explicit" | "derived_from_room">;
|
|
168
|
+
/** The `ms_room` term the axes were correlated from, or null. */
|
|
169
|
+
roomType: string | null;
|
|
170
|
+
}
|
|
171
|
+
/** How far a jurisdiction's rows have been reviewed against that AHJ's own tables. */
|
|
172
|
+
export type CodeCoverageTier = OpenEnum<"thorough" | "model_baseline">;
|
|
173
|
+
export interface CodeJurisdictionSummary {
|
|
174
|
+
id: string;
|
|
175
|
+
name: string;
|
|
176
|
+
codeFamily: string;
|
|
177
|
+
edition: string;
|
|
178
|
+
coverage: CodeCoverageTier;
|
|
179
|
+
/** One honest line on how the model code actually applies in this jurisdiction. */
|
|
180
|
+
adoptionNote: string;
|
|
181
|
+
}
|
|
182
|
+
/** The `GET /api/v1/codes/context` envelope — the vocabulary for stating a context. */
|
|
183
|
+
export interface CodeContextResponse {
|
|
184
|
+
jurisdictions: CodeJurisdictionSummary[];
|
|
185
|
+
occupancyGroups: {
|
|
186
|
+
id: string;
|
|
187
|
+
label: string;
|
|
188
|
+
}[];
|
|
189
|
+
finishLocations: {
|
|
190
|
+
id: string;
|
|
191
|
+
label: string;
|
|
192
|
+
shortLabel: string;
|
|
193
|
+
}[];
|
|
194
|
+
dimensions: {
|
|
195
|
+
id: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
|
|
196
|
+
label: string;
|
|
197
|
+
description: string;
|
|
198
|
+
/** The MaterialGraph attribute this dimension is scored against. */
|
|
199
|
+
attributeId: string;
|
|
200
|
+
}[];
|
|
201
|
+
scales: {
|
|
202
|
+
id: string;
|
|
203
|
+
label: string;
|
|
204
|
+
detail: string;
|
|
205
|
+
/** Ordered BEST FIRST: index 0 outranks index 1. */
|
|
206
|
+
classes: string[];
|
|
207
|
+
}[];
|
|
208
|
+
/** `ms_room` → regulatory axes. `null` where the registry refuses to correlate. */
|
|
209
|
+
roomCorrelations: {
|
|
210
|
+
roomType: string;
|
|
211
|
+
label: string;
|
|
212
|
+
occupancyGroup: string | null;
|
|
213
|
+
finishLocation: string | null;
|
|
214
|
+
}[];
|
|
215
|
+
/** The AHJ advisory caveat. */
|
|
216
|
+
caveat: string;
|
|
217
|
+
}
|
|
218
|
+
/** One requirement a context imposes, independent of any candidate material. */
|
|
219
|
+
export interface CodeRequirementListing {
|
|
220
|
+
requirementId: string;
|
|
221
|
+
dimension: OpenEnum<"fire" | "floor" | "slip" | "decorative">;
|
|
222
|
+
/** The code section this demand is cited from. */
|
|
223
|
+
section: string;
|
|
224
|
+
attributeId: string;
|
|
225
|
+
requirement: CodeRequirementThreshold;
|
|
226
|
+
/** One readable line, e.g. `"Class A or better · ASTM E84 / UL 723"`. */
|
|
227
|
+
label: string;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The `GET /api/v1/codes/requirements` envelope. An empty `requirements` array
|
|
231
|
+
* means the registry holds nothing for this context — it is not a pass.
|
|
232
|
+
*/
|
|
233
|
+
export interface CodeRequirementsResponse {
|
|
234
|
+
jurisdiction: CodeJurisdictionSummary;
|
|
235
|
+
context: ResolvedCodeContext;
|
|
236
|
+
requirements: CodeRequirementListing[];
|
|
237
|
+
caveat: string;
|
|
238
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types for the colour AGGREGATE surface — `GET /api/v1/colours/profiles`
|
|
3
|
+
* and `GET /api/v1/colours/stats`.
|
|
4
|
+
*
|
|
5
|
+
* Kept out of `types.ts` for the same reason `material-types.ts` and
|
|
6
|
+
* `codes-types.ts` are: the shared mirror is at its source-size budget. Same
|
|
7
|
+
* tolerant-parsing contract as the rest of the mirror — response enums are
|
|
8
|
+
* widened so a new server value still type-checks.
|
|
9
|
+
*/
|
|
10
|
+
import type { OpenEnum } from "./types.js";
|
|
11
|
+
/** Which taxonomy tier a colour profile's `categoryKey` names. */
|
|
12
|
+
export type CategoryTier = OpenEnum<"group" | "type">;
|
|
13
|
+
/** How the endpoint picks which colours to keep. */
|
|
14
|
+
export type ColourProfileSelect = OpenEnum<"frequency" | "diverse">;
|
|
15
|
+
/** One representative colour bucket of a category. */
|
|
16
|
+
export interface CategoryColour {
|
|
17
|
+
hex: string;
|
|
18
|
+
oklab: {
|
|
19
|
+
l: number;
|
|
20
|
+
a: number;
|
|
21
|
+
b: number;
|
|
22
|
+
};
|
|
23
|
+
variantCount: number;
|
|
24
|
+
/** This bucket's share of the category's coloured variants, 0–1. */
|
|
25
|
+
proportion: number;
|
|
26
|
+
}
|
|
27
|
+
export interface CategoryColourProfile {
|
|
28
|
+
categoryKey: string;
|
|
29
|
+
level: CategoryTier;
|
|
30
|
+
name: string;
|
|
31
|
+
path: string;
|
|
32
|
+
variantCount: number;
|
|
33
|
+
avgLightness: number;
|
|
34
|
+
avgChroma: number;
|
|
35
|
+
colours: CategoryColour[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The snapshot a profile response was computed from. Compare `variantCount`
|
|
39
|
+
* against `colours/stats`' `corpus.withColour` to see how complete the
|
|
40
|
+
* underlying materialized view currently is, and `refreshedAt` for its age.
|
|
41
|
+
*/
|
|
42
|
+
export interface CategoryColourProfilesBasis {
|
|
43
|
+
variantCount: number;
|
|
44
|
+
refreshedAt: string | null;
|
|
45
|
+
}
|
|
46
|
+
export interface CategoryColourProfilesResponse {
|
|
47
|
+
profiles: CategoryColourProfile[];
|
|
48
|
+
basis: CategoryColourProfilesBasis;
|
|
49
|
+
}
|
|
50
|
+
/** Request parameters for `colours.profiles()`. */
|
|
51
|
+
export interface ColourProfilesParams {
|
|
52
|
+
/** 1–8 category keys, at the tier named by `groupBy`. */
|
|
53
|
+
productTypeIds: readonly string[];
|
|
54
|
+
/** `"group"` merges each group's leaf product types into one palette. */
|
|
55
|
+
groupBy?: "group" | "type";
|
|
56
|
+
/** Representative colours per category, 1–64. */
|
|
57
|
+
topN?: number;
|
|
58
|
+
/** `"diverse"` maximises visual distinctness instead of frequency. */
|
|
59
|
+
select?: "frequency" | "diverse";
|
|
60
|
+
}
|
|
61
|
+
export interface ColourStatsCorpus {
|
|
62
|
+
variants: number;
|
|
63
|
+
withColour: number;
|
|
64
|
+
/** `withColour / variants`, 0–1. */
|
|
65
|
+
coverage: number;
|
|
66
|
+
}
|
|
67
|
+
export interface ColourStatsFamily {
|
|
68
|
+
id: string;
|
|
69
|
+
label: string;
|
|
70
|
+
variants: number;
|
|
71
|
+
brands: number;
|
|
72
|
+
}
|
|
73
|
+
export interface ColourStatsGroup {
|
|
74
|
+
key: string;
|
|
75
|
+
name: string;
|
|
76
|
+
productTypes: number;
|
|
77
|
+
variants: number;
|
|
78
|
+
}
|
|
79
|
+
/** Where each section came from, how fresh it is, and what it does NOT answer. */
|
|
80
|
+
export interface ColourStatsBasis {
|
|
81
|
+
corpus: string;
|
|
82
|
+
families: string;
|
|
83
|
+
groups: string;
|
|
84
|
+
groupsRefreshedAt: string | null;
|
|
85
|
+
groupsVariantCount: number;
|
|
86
|
+
omissions: string[];
|
|
87
|
+
}
|
|
88
|
+
export interface ColourStatsResponse {
|
|
89
|
+
corpus: ColourStatsCorpus;
|
|
90
|
+
families: ColourStatsFamily[];
|
|
91
|
+
groups: ColourStatsGroup[];
|
|
92
|
+
basis: ColourStatsBasis;
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The colour AGGREGATE surface, namespaced as `mg.colours`.
|
|
3
|
+
*
|
|
4
|
+
* Two reads that answer the same question at two altitudes — "how much colour
|
|
5
|
+
* does the graph hold at all" (`stats`) and "what colours does this category
|
|
6
|
+
* come in" (`profiles`) — so they live together rather than as two more flat
|
|
7
|
+
* methods on an already-large client facade. `mg.codes` and `mg.browse` set the
|
|
8
|
+
* precedent for a namespace when a capability is a small family rather than a
|
|
9
|
+
* single verb.
|
|
10
|
+
*
|
|
11
|
+
* The namespace holds no transport of its own: the client hands it a bound
|
|
12
|
+
* `get`, so auth, rate-limit accounting and error shaping stay in one place.
|
|
13
|
+
*
|
|
14
|
+
* Both responses carry a `basis`. These are reads of a materialized view that is
|
|
15
|
+
* refreshed on a schedule, not on write, so a caller can and should check how
|
|
16
|
+
* much of the corpus the answer actually covers before treating it as complete.
|
|
17
|
+
*/
|
|
18
|
+
import type { CategoryColourProfilesResponse, ColourProfilesParams, ColourStatsResponse } from "./colour-aggregate-types.js";
|
|
19
|
+
import type { QueryParams } from "./http-transport.js";
|
|
20
|
+
import type { SdkOperationId } from "./operations.js";
|
|
21
|
+
/** The client's own request primitive, handed down so this file owns no transport. */
|
|
22
|
+
export interface ColoursTransport {
|
|
23
|
+
get<T>(operationId: SdkOperationId, query?: QueryParams, signal?: AbortSignal): Promise<T>;
|
|
24
|
+
}
|
|
25
|
+
export declare class ColoursClient {
|
|
26
|
+
#private;
|
|
27
|
+
constructor(transport: ColoursTransport);
|
|
28
|
+
/**
|
|
29
|
+
* The representative colour palette of 1–8 categories in ONE call
|
|
30
|
+
* (GET `/api/v1/colours/profiles`).
|
|
31
|
+
*
|
|
32
|
+
* `groupBy: "group"` takes product-type GROUP keys and merges every leaf type
|
|
33
|
+
* under a group into one histogram BEFORE ranking — a group's palette, not the
|
|
34
|
+
* union of its leaves' heads. `select: "diverse"` returns visually spread
|
|
35
|
+
* swatches (greedy max-min over OKLab, 0.02 minimum separation, backfilled by
|
|
36
|
+
* frequency if fewer survive) instead of the most frequent ones; a client
|
|
37
|
+
* cannot recover that spread from a truncated frequency list, which is why it
|
|
38
|
+
* is a server mode rather than a local sort.
|
|
39
|
+
*
|
|
40
|
+
* A selected category with no colour data comes back as a zero-count profile,
|
|
41
|
+
* not a missing key.
|
|
42
|
+
*/
|
|
43
|
+
profiles(params: ColourProfilesParams, signal?: AbortSignal): Promise<CategoryColourProfilesResponse>;
|
|
44
|
+
/**
|
|
45
|
+
* Corpus colour coverage (GET `/api/v1/colours/stats`): how much of the graph
|
|
46
|
+
* carries colour at all, the split across the canonical colour families, and
|
|
47
|
+
* coverage per product-type group. `basis` states the source and freshness of
|
|
48
|
+
* each section plus `basis.omissions` — what this call deliberately does not
|
|
49
|
+
* answer, listed rather than silently absent.
|
|
50
|
+
*/
|
|
51
|
+
stats(signal?: AbortSignal): Promise<ColourStatsResponse>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export class ColoursClient {
|
|
2
|
+
#transport;
|
|
3
|
+
constructor(transport) {
|
|
4
|
+
this.#transport = transport;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The representative colour palette of 1–8 categories in ONE call
|
|
8
|
+
* (GET `/api/v1/colours/profiles`).
|
|
9
|
+
*
|
|
10
|
+
* `groupBy: "group"` takes product-type GROUP keys and merges every leaf type
|
|
11
|
+
* under a group into one histogram BEFORE ranking — a group's palette, not the
|
|
12
|
+
* union of its leaves' heads. `select: "diverse"` returns visually spread
|
|
13
|
+
* swatches (greedy max-min over OKLab, 0.02 minimum separation, backfilled by
|
|
14
|
+
* frequency if fewer survive) instead of the most frequent ones; a client
|
|
15
|
+
* cannot recover that spread from a truncated frequency list, which is why it
|
|
16
|
+
* is a server mode rather than a local sort.
|
|
17
|
+
*
|
|
18
|
+
* A selected category with no colour data comes back as a zero-count profile,
|
|
19
|
+
* not a missing key.
|
|
20
|
+
*/
|
|
21
|
+
profiles(params, signal) {
|
|
22
|
+
return this.#transport.get("get_category_colour_profiles", {
|
|
23
|
+
productTypeIds: params.productTypeIds.join(","),
|
|
24
|
+
groupBy: params.groupBy,
|
|
25
|
+
topN: params.topN,
|
|
26
|
+
select: params.select,
|
|
27
|
+
}, signal);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Corpus colour coverage (GET `/api/v1/colours/stats`): how much of the graph
|
|
31
|
+
* carries colour at all, the split across the canonical colour families, and
|
|
32
|
+
* coverage per product-type group. `basis` states the source and freshness of
|
|
33
|
+
* each section plus `basis.omissions` — what this call deliberately does not
|
|
34
|
+
* answer, listed rather than silently absent.
|
|
35
|
+
*/
|
|
36
|
+
stats(signal) {
|
|
37
|
+
return this.#transport.get("get_colour_stats", undefined, signal);
|
|
38
|
+
}
|
|
39
|
+
}
|
package/composition-types.d.ts
CHANGED
|
@@ -45,13 +45,95 @@ export interface VarySchemesRequest {
|
|
|
45
45
|
locks?: SchemeLock[];
|
|
46
46
|
exclusions?: SchemeExclusions;
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* One anchor (MG-613): a variant the designer has already pinned to a place in
|
|
50
|
+
* the programme, supplied on the `compose-material-schemes` run input's
|
|
51
|
+
* `anchors[]`. Exactly ONE of `variantId` (an exact catalog variant) or `query`
|
|
52
|
+
* (free-text "Brand Product Colour", resolved server-side) is required. An
|
|
53
|
+
* anchored slot is LOCKED — composition completes the OPEN slots in relation to
|
|
54
|
+
* it. Anchors that cannot be resolved leave the slot open and surface a run
|
|
55
|
+
* warning (never a failure).
|
|
56
|
+
*/
|
|
57
|
+
export interface ComposeAnchorInput {
|
|
58
|
+
/** The building element the anchor fills (flooring, wall, textile, …). */
|
|
59
|
+
element: SlotElement;
|
|
60
|
+
/** Optional named space to scope the anchor to (matched by name/slug). */
|
|
61
|
+
spaceName?: string;
|
|
62
|
+
/** An exact catalog variant id. Mutually exclusive with `query`. */
|
|
63
|
+
variantId?: string;
|
|
64
|
+
/** Free-text "Brand Product Colour". Mutually exclusive with `variantId`. */
|
|
65
|
+
query?: string;
|
|
66
|
+
/** Display name for slot naming and warnings. */
|
|
67
|
+
label?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* One standing rule in a project's compliance preset (MG-615): free-text
|
|
71
|
+
* `criteria` phrases that apply to a `scope` of slots. `scope: "all"` reaches
|
|
72
|
+
* every slot (including slots with no element and slots classified `other`); a
|
|
73
|
+
* named element reaches only slots carrying exactly that element, so an
|
|
74
|
+
* unclassified slot receives `"all"` rules only. Phrases are resolved
|
|
75
|
+
* server-side through the same vocabulary as brief criteria — sustainability
|
|
76
|
+
* wording ("LEED", "GREENGUARD Gold") resolves to a named certification
|
|
77
|
+
* programme. A phrase that grounds to nothing is dropped with a run warning.
|
|
78
|
+
*/
|
|
79
|
+
export interface CompliancePresetRule {
|
|
80
|
+
scope: "all" | SlotElement;
|
|
81
|
+
criteria: string[];
|
|
82
|
+
}
|
|
83
|
+
/** A project's standing rules, declared once on the compose run (max 12 rules). */
|
|
84
|
+
export interface CompliancePreset {
|
|
85
|
+
rules: CompliancePresetRule[];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The `inputJson` for a `compose-material-schemes` run. `batchId` is required;
|
|
89
|
+
* everything else has a server default. `anchors` (max 24) carry externally
|
|
90
|
+
* decided picks (MG-613); `compliancePreset` carries the project's standing
|
|
91
|
+
* rules (MG-615), which cascade into slots as PREFERRED criteria — they grade
|
|
92
|
+
* every candidate but never exclude one.
|
|
93
|
+
*/
|
|
94
|
+
export interface ComposeMaterialSchemesInput {
|
|
95
|
+
batchId: string;
|
|
96
|
+
count?: number;
|
|
97
|
+
seed?: number;
|
|
98
|
+
exclusions?: SchemeExclusions;
|
|
99
|
+
anchors?: ComposeAnchorInput[];
|
|
100
|
+
compliancePreset?: CompliancePreset;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The state of one criterion verdict cell (MG-627: the shared MaterialGraph
|
|
104
|
+
* verdict states). The six LIVE states are `matched` (accepted value satisfies
|
|
105
|
+
* the criterion), `deviated` (directionally off but named), `failed` (an
|
|
106
|
+
* accepted value exists and does not satisfy it), `unverified` (a real value
|
|
107
|
+
* that did not clear the trust bar), `missing` (no accepted value to evaluate),
|
|
108
|
+
* and `not_evaluated` (the engine did not check it). `mismatched`/`unknown` are
|
|
109
|
+
* the two LEGACY tokens old persisted runs still carry (never emitted again).
|
|
110
|
+
* Open per the response-enum policy: treat unknown states forward-compatibly.
|
|
111
|
+
*/
|
|
112
|
+
export type CompositionMatchState = OpenEnum<"matched" | "deviated" | "failed" | "unverified" | "missing" | "not_evaluated" | "mismatched" | "unknown">;
|
|
113
|
+
/** How a criterion cell was baselined — what the candidate was compared against. */
|
|
114
|
+
export type CompositionMatchBaseline = {
|
|
115
|
+
kind: "term";
|
|
116
|
+
term: string;
|
|
117
|
+
} | {
|
|
118
|
+
kind: "presence";
|
|
119
|
+
} | {
|
|
120
|
+
kind: "seed";
|
|
121
|
+
variantId: string;
|
|
122
|
+
};
|
|
123
|
+
/** One resolved criterion → material verdict cell, as graded during composition. */
|
|
50
124
|
export interface CompositionMatch {
|
|
51
125
|
attributeId: string;
|
|
52
126
|
state: CompositionMatchState;
|
|
53
127
|
confidence: number | null;
|
|
54
128
|
sourceBasis: string | null;
|
|
129
|
+
/** Required vs preferred criterion (MG-627). Absent on legacy persisted runs. */
|
|
130
|
+
strength?: "required" | "preferred";
|
|
131
|
+
/** What the cell was evaluated against (MG-627). Absent on legacy persisted runs. */
|
|
132
|
+
baseline?: CompositionMatchBaseline;
|
|
133
|
+
/** The candidate's own accepted value label, when the cell carries one (MG-627). */
|
|
134
|
+
value?: string | null;
|
|
135
|
+
/** Why an `unverified` cell missed the trust bar (MG-627), else null. */
|
|
136
|
+
trustGap?: "below_confidence" | "basis_unmet" | "proposed" | null;
|
|
55
137
|
}
|
|
56
138
|
/** One slot's chosen material plus the reason it was picked. */
|
|
57
139
|
export interface CompositionPick {
|
|
@@ -72,6 +154,12 @@ export interface CompositionPick {
|
|
|
72
154
|
matched: CompositionMatch[];
|
|
73
155
|
notes: string[];
|
|
74
156
|
};
|
|
157
|
+
/**
|
|
158
|
+
* True when this pick is an ANCHOR the designer pinned to the slot (MG-613):
|
|
159
|
+
* composition never replaced it and vary keeps it fixed. Absent for ordinary
|
|
160
|
+
* composed picks.
|
|
161
|
+
*/
|
|
162
|
+
locked?: boolean;
|
|
75
163
|
}
|
|
76
164
|
/** The multi-objective score for one scheme (each component in [0, 1]). */
|
|
77
165
|
export interface CompositionObjective {
|
|
@@ -115,3 +203,141 @@ export interface VarySchemesResponse {
|
|
|
115
203
|
provenance: VaryProvenance;
|
|
116
204
|
shortfall: CompositionShortfall | null;
|
|
117
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Whether a programme element (space, slot, or palette) was `stated` in a text
|
|
208
|
+
* source or `inferred` from imagery/mood. Only inferred elements raise a review
|
|
209
|
+
* question. Absent → treated as `stated`.
|
|
210
|
+
*/
|
|
211
|
+
export type ProgrammeProvenance = "stated" | "inferred";
|
|
212
|
+
/**
|
|
213
|
+
* The broad building element a slot fills — a pragmatically-extended Uniclass EF
|
|
214
|
+
* vocabulary. Widened with {@link OpenEnum} so a newer server term still
|
|
215
|
+
* type-checks.
|
|
216
|
+
*/
|
|
217
|
+
export type SlotElement = OpenEnum<"flooring" | "wall" | "ceiling" | "textile" | "surface" | "furniture" | "lighting" | "other">;
|
|
218
|
+
/** One colour cue: a hex swatch with an optional human name. */
|
|
219
|
+
export interface BriefColourCue {
|
|
220
|
+
hex: string;
|
|
221
|
+
name?: string;
|
|
222
|
+
}
|
|
223
|
+
/** A source anchor (document id + optional page) attributing derived content. */
|
|
224
|
+
export interface CompositionSourceRef {
|
|
225
|
+
sourceId: string;
|
|
226
|
+
page: number | null;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* One candidate colour direction the pile's colour evidence implies when no
|
|
230
|
+
* palette was explicitly committed. `directionId` is a stable kebab-case handle;
|
|
231
|
+
* `sourceRefs` attribute the direction to the palette source(s) it was drawn
|
|
232
|
+
* from (empty when unattributable).
|
|
233
|
+
*/
|
|
234
|
+
export interface PaletteDirection {
|
|
235
|
+
directionId: string;
|
|
236
|
+
name: string;
|
|
237
|
+
colours: BriefColourCue[];
|
|
238
|
+
sourceRefs: CompositionSourceRef[];
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Suspend payload for the compose `review-programme` step: the FULL programme
|
|
242
|
+
* picture — every space and slot tagged with its provenance — so the card can
|
|
243
|
+
* show the whole thing and highlight what was inferred.
|
|
244
|
+
*/
|
|
245
|
+
export interface ReviewProgrammeSuspendPayload {
|
|
246
|
+
spaces: Array<{
|
|
247
|
+
spaceId: string;
|
|
248
|
+
name: string;
|
|
249
|
+
spaceType?: string;
|
|
250
|
+
provenance: ProgrammeProvenance;
|
|
251
|
+
}>;
|
|
252
|
+
slots: Array<{
|
|
253
|
+
slotId: string;
|
|
254
|
+
name: string;
|
|
255
|
+
spaceId?: string;
|
|
256
|
+
element?: SlotElement;
|
|
257
|
+
provenance: ProgrammeProvenance;
|
|
258
|
+
}>;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Resume grammar for the `review-programme` step: drop a slot, drop a space
|
|
262
|
+
* (cascade-dropping its slots), or rename a space.
|
|
263
|
+
*/
|
|
264
|
+
export interface ReviewProgrammeResumeData {
|
|
265
|
+
corrections: Array<{
|
|
266
|
+
action: "drop-slot";
|
|
267
|
+
slotId: string;
|
|
268
|
+
} | {
|
|
269
|
+
action: "drop-space";
|
|
270
|
+
spaceId: string;
|
|
271
|
+
} | {
|
|
272
|
+
action: "rename-space";
|
|
273
|
+
spaceId: string;
|
|
274
|
+
name: string;
|
|
275
|
+
}>;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Suspend payload for the compose `commit-palette` step. Today only the
|
|
279
|
+
* `directions` mode ships; the shape carries `mode` so a later `ab-images` mode
|
|
280
|
+
* is an additive member, not a migration.
|
|
281
|
+
*/
|
|
282
|
+
export interface CommitPaletteSuspendPayload {
|
|
283
|
+
mode: OpenEnum<"directions">;
|
|
284
|
+
directions: PaletteDirection[];
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Resume grammar for the `commit-palette` step: commit to one direction (by id)
|
|
288
|
+
* or adopt the union of every direction's colours.
|
|
289
|
+
*/
|
|
290
|
+
export type CommitPaletteResumeData = {
|
|
291
|
+
choice: "direction";
|
|
292
|
+
directionId: string;
|
|
293
|
+
} | {
|
|
294
|
+
choice: "all";
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* Resume grammar for the `review-material-criteria` step: accept a resolved
|
|
298
|
+
* attribute/term for an unresolved phrase, or drop it.
|
|
299
|
+
*/
|
|
300
|
+
export interface ReviewCriteriaResumeData {
|
|
301
|
+
corrections: Array<{
|
|
302
|
+
action: "accept";
|
|
303
|
+
slotId: string;
|
|
304
|
+
raw: string;
|
|
305
|
+
attributeId: string;
|
|
306
|
+
term?: string;
|
|
307
|
+
} | {
|
|
308
|
+
action: "drop";
|
|
309
|
+
slotId: string;
|
|
310
|
+
raw: string;
|
|
311
|
+
}>;
|
|
312
|
+
}
|
|
313
|
+
/** The union of every socratic-step resume payload, keyed by the suspended step. */
|
|
314
|
+
export type InterviewResumeData = ReviewCriteriaResumeData | ReviewProgrammeResumeData | CommitPaletteResumeData;
|
|
315
|
+
/** The three socratic interview steps, in procedure order. */
|
|
316
|
+
export type InterviewStepId = "review-programme" | "commit-palette" | "review-material-criteria";
|
|
317
|
+
/**
|
|
318
|
+
* Payload of a `composition.answered` event — the pile answered a question with
|
|
319
|
+
* no human turn, or a suspension resolved. `answer` is a stable slug; `citation`
|
|
320
|
+
* attributes an answer to its source when the brief stated it.
|
|
321
|
+
*/
|
|
322
|
+
export interface CompositionAnsweredPayload {
|
|
323
|
+
step: InterviewStepId;
|
|
324
|
+
answer: string;
|
|
325
|
+
citation: CompositionSourceRef | null;
|
|
326
|
+
counts?: Record<string, number>;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Payload of a `composition.slot-pool` event — the candidate theater for one
|
|
330
|
+
* slot: how many were considered and the top picks (≤5) hydrated for identity.
|
|
331
|
+
*/
|
|
332
|
+
export interface SlotPoolEventPayload {
|
|
333
|
+
slotId: string;
|
|
334
|
+
slotName: string;
|
|
335
|
+
considered: number;
|
|
336
|
+
top: Array<{
|
|
337
|
+
variantId: string;
|
|
338
|
+
brand: string;
|
|
339
|
+
product: string;
|
|
340
|
+
imageUrl: string | null;
|
|
341
|
+
score: number;
|
|
342
|
+
}>;
|
|
343
|
+
}
|