@instruments/materialgraph 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/association-types.d.ts +20 -20
- package/associations-client.d.ts +4 -4
- package/associations-client.js +3 -10
- package/attribution-types.d.ts +3 -3
- package/brand-most-sampled-types.d.ts +4 -4
- package/brand-types.d.ts +6 -6
- package/brands-client.d.ts +4 -4
- package/brands-client.js +9 -13
- package/browse-types.d.ts +8 -8
- package/by-colour-types.d.ts +18 -18
- package/by-spec-types.d.ts +14 -14
- package/catalog-discovery-types.d.ts +14 -14
- package/catalogue-client.d.ts +4 -4
- package/catalogue-client.js +38 -37
- package/catalogue-types.d.ts +4 -4
- package/citation-types.d.ts +4 -4
- package/client-browse.d.ts +4 -4
- package/client-browse.js +24 -25
- package/client-config.d.ts +2 -2
- package/client-exploration.d.ts +3 -3
- package/client-exploration.js +4 -6
- package/client-source-material-comments.d.ts +4 -4
- package/client-source-material-comments.js +4 -38
- package/client-vocabulary.d.ts +8 -8
- package/client-vocabulary.js +20 -30
- package/client.d.ts +11 -45
- package/client.js +143 -188
- package/codes-client.d.ts +2 -2
- package/codes-client.js +8 -8
- package/codes-types.d.ts +34 -34
- package/colour-aggregate-types.d.ts +24 -24
- package/colours-client.d.ts +2 -2
- package/colours-client.js +15 -15
- package/compare-types.d.ts +10 -10
- package/composition-client.d.ts +3 -3
- package/composition-client.js +8 -7
- package/composition-types.d.ts +12 -12
- package/coordinate-client.d.ts +2 -2
- package/coordinate-client.js +1 -13
- package/corpus-scope-types.d.ts +3 -3
- package/corpus-scope-types.js +1 -25
- package/correction-types.d.ts +10 -10
- package/demand-batch-types.d.ts +18 -18
- package/demand-types.d.ts +24 -24
- package/errors.d.ts +12 -12
- package/errors.js +53 -52
- package/exploration-types.d.ts +29 -22
- package/fan-deck-types.d.ts +6 -6
- package/http-transport.d.ts +13 -21
- package/http-transport.js +31 -28
- package/identify-types.d.ts +10 -10
- package/index.d.ts +0 -6
- package/index.js +0 -2
- package/match-rejection-types.d.ts +6 -6
- package/match-stream-types.d.ts +32 -32
- package/match-stream.d.ts +2 -2
- package/match-stream.js +55 -41
- package/match-types.d.ts +8 -8
- package/material-asset-types.d.ts +26 -26
- package/material-block-types.d.ts +6 -6
- package/material-query-plan-types.d.ts +26 -26
- package/material-resources-client.d.ts +5 -5
- package/material-resources-client.js +10 -20
- package/material-types.d.ts +12 -12
- package/ndjson.js +16 -8
- package/operations.d.ts +6 -6
- package/operations.js +9 -39
- package/package.json +2 -2
- package/palette-completion-types.d.ts +22 -22
- package/palette-search-types.d.ts +28 -28
- package/query-understanding-types.d.ts +43 -36
- package/render-assets-types.d.ts +8 -8
- package/resolve-client.d.ts +2 -2
- package/resolve-client.js +4 -6
- package/resolve-colour-types.d.ts +22 -6
- package/resolve-types.d.ts +10 -10
- package/run-types.d.ts +70 -70
- package/runs.d.ts +13 -26
- package/runs.js +77 -89
- package/samplize-catalogue-types.d.ts +18 -18
- package/samplize-colorvec.d.ts +20 -20
- package/samplize-colorvec.js +66 -42
- package/schema-resource-types.d.ts +38 -38
- package/schema-resource-types.js +5 -5
- package/search-card-types.d.ts +14 -5
- package/search-collapse-types.d.ts +4 -4
- package/search-mood-types.d.ts +2 -2
- package/search-refinement-types.d.ts +8 -8
- package/search-request-types.d.ts +6 -6
- package/search-response-receipt-types.d.ts +135 -34
- package/search-response-types.d.ts +2 -2
- package/search-result-validation.d.ts +3 -3
- package/search-result-validation.js +53 -36
- package/search-window-types.d.ts +2 -2
- package/slot-pool-types.d.ts +20 -20
- package/source-material-validation.d.ts +3 -3
- package/source-material-validation.js +35 -39
- package/spec-facet-types.d.ts +10 -10
- package/spec-types.d.ts +6 -6
- package/specifications.d.ts +9 -25
- package/specifications.js +15 -21
- package/sse.d.ts +2 -2
- package/sse.js +32 -16
- package/substitute-types.d.ts +22 -22
- package/suggest-types.d.ts +7 -7
- package/suggest-types.js +3 -3
- package/taxonomy-coverage-types.d.ts +8 -8
- package/types.d.ts +119 -119
- package/validation.d.ts +26 -26
- package/validation.js +97 -92
- package/webmcp.d.ts +2 -2
- package/webmcp.generated.js +94 -87
- package/project-types.d.ts +0 -79
- package/project-types.js +0 -9
- package/projects.d.ts +0 -13
- package/projects.js +0 -46
- package/run-events.d.ts +0 -106
- package/run-events.js +0 -32
- package/schedule-reference-types.d.ts +0 -46
- package/schedule-reference-types.js +0 -1
- package/schedule-references-client.d.ts +0 -14
- package/schedule-references-client.js +0 -26
- package/schedule-types.d.ts +0 -261
- package/schedule-types.js +0 -1
package/slot-pool-types.d.ts
CHANGED
|
@@ -61,11 +61,11 @@ export type SlotPoolCriterion = {
|
|
|
61
61
|
coverageNote?: string;
|
|
62
62
|
};
|
|
63
63
|
/** A colour cue for a slot. Colours rerank within the retrieved set; they never admit. */
|
|
64
|
-
export
|
|
64
|
+
export type SlotPoolColourCue = {
|
|
65
65
|
/** `#rrggbb`. */
|
|
66
66
|
hex: string;
|
|
67
67
|
name?: string;
|
|
68
|
-
}
|
|
68
|
+
};
|
|
69
69
|
/**
|
|
70
70
|
* What to do with a candidate the catalogue no longer lists. Neither value ever
|
|
71
71
|
* marks such a candidate eligible — the policy decides only whether it is
|
|
@@ -73,7 +73,7 @@ export interface SlotPoolColourCue {
|
|
|
73
73
|
*/
|
|
74
74
|
export type SlotPoolAvailabilityPolicy = "flag-unavailable" | "exclude-unavailable";
|
|
75
75
|
/** One slot of work. Self-contained: nothing in it identifies a project. */
|
|
76
|
-
export
|
|
76
|
+
export type SlotPoolTask = {
|
|
77
77
|
/**
|
|
78
78
|
* YOUR key for this task, opaque to MaterialGraph and returned verbatim on the
|
|
79
79
|
* matching pool. This is how you correlate 24 results; it is never parsed,
|
|
@@ -102,9 +102,9 @@ export interface SlotPoolTask {
|
|
|
102
102
|
availability?: SlotPoolAvailabilityPolicy;
|
|
103
103
|
/** Candidates returned for this task. Max 48, default 12. */
|
|
104
104
|
limit?: number;
|
|
105
|
-
}
|
|
105
|
+
};
|
|
106
106
|
/** Body for `POST /api/v1/schemes/pools` ({@link MaterialGraphClient.slotPools}). */
|
|
107
|
-
export
|
|
107
|
+
export type SlotPoolsRequest = {
|
|
108
108
|
/** Up to 24 tasks. Each is independent: one failing costs that one. */
|
|
109
109
|
tasks: SlotPoolTask[];
|
|
110
110
|
/**
|
|
@@ -112,11 +112,11 @@ export interface SlotPoolsRequest {
|
|
|
112
112
|
* (default true). One bounded read — never one request per candidate.
|
|
113
113
|
*/
|
|
114
114
|
coSpecification?: boolean;
|
|
115
|
-
}
|
|
115
|
+
};
|
|
116
116
|
/** The six live verdict states, plus the two legacy tokens older rows carry. */
|
|
117
117
|
export type SlotPoolVerdictState = OpenEnum<"matched" | "deviated" | "failed" | "unverified" | "missing" | "not_evaluated">;
|
|
118
118
|
/** One cell of a candidate's report card against one criterion. */
|
|
119
|
-
export
|
|
119
|
+
export type SlotPoolVerdictCell = {
|
|
120
120
|
attributeId: string;
|
|
121
121
|
state: SlotPoolVerdictState;
|
|
122
122
|
strength: OpenEnum<"required" | "preferred">;
|
|
@@ -149,12 +149,12 @@ export interface SlotPoolVerdictCell {
|
|
|
149
149
|
sourceBasis: string | null;
|
|
150
150
|
trustGap: OpenEnum<"below_confidence" | "basis_unmet" | "proposed"> | null;
|
|
151
151
|
citation?: VerdictCitation;
|
|
152
|
-
}
|
|
152
|
+
};
|
|
153
153
|
/** Why a candidate is labelled. An eligible candidate carries none of these. */
|
|
154
154
|
export type SlotPoolFlag = OpenEnum<"below-floor" | "required-not-applied" | "discontinued" | "superseded" | "unverified-value">;
|
|
155
155
|
export type SlotPoolAvailability = OpenEnum<"available" | "discontinued" | "superseded" | "unknown">;
|
|
156
156
|
/** The declared successor of a withdrawn material, one hop only. */
|
|
157
|
-
export
|
|
157
|
+
export type SlotPoolSuccessor = {
|
|
158
158
|
variantId: VariantId;
|
|
159
159
|
brandSlug: string;
|
|
160
160
|
productId: string;
|
|
@@ -163,13 +163,13 @@ export interface SlotPoolSuccessor {
|
|
|
163
163
|
variant: string;
|
|
164
164
|
color: string | null;
|
|
165
165
|
availability: SlotPoolAvailability;
|
|
166
|
-
}
|
|
166
|
+
};
|
|
167
167
|
/**
|
|
168
168
|
* The features a composer scores on. Colours are EXTRACTED FROM PRODUCT IMAGERY
|
|
169
169
|
* — visual likeness, never a declared colour specification. An empty `palette`
|
|
170
170
|
* or `attributes` means nothing is on record, never that the material has none.
|
|
171
171
|
*/
|
|
172
|
-
export
|
|
172
|
+
export type SlotPoolCandidateFeatures = {
|
|
173
173
|
colour: {
|
|
174
174
|
dominantHex: string | null;
|
|
175
175
|
hsl: {
|
|
@@ -201,8 +201,8 @@ export interface SlotPoolCandidateFeatures {
|
|
|
201
201
|
term: string;
|
|
202
202
|
}[];
|
|
203
203
|
dimensions: Record<string, unknown>[] | null;
|
|
204
|
-
}
|
|
205
|
-
export
|
|
204
|
+
};
|
|
205
|
+
export type SlotPoolCandidate = {
|
|
206
206
|
variantId: VariantId;
|
|
207
207
|
productId: string;
|
|
208
208
|
brandSlug: string;
|
|
@@ -241,7 +241,7 @@ export interface SlotPoolCandidate {
|
|
|
241
241
|
minConfidence: number | null;
|
|
242
242
|
};
|
|
243
243
|
features: SlotPoolCandidateFeatures;
|
|
244
|
-
}
|
|
244
|
+
};
|
|
245
245
|
/**
|
|
246
246
|
* Why a pool is short. Four different facts, and none of them is "nothing in
|
|
247
247
|
* the catalogue matches your brief".
|
|
@@ -268,7 +268,7 @@ export type SlotPoolShortfall = {
|
|
|
268
268
|
tier: string;
|
|
269
269
|
consideredCount: number;
|
|
270
270
|
};
|
|
271
|
-
export
|
|
271
|
+
export type SlotPool = {
|
|
272
272
|
/** Your own key, verbatim. */
|
|
273
273
|
taskKey: string;
|
|
274
274
|
category: string | null;
|
|
@@ -305,14 +305,14 @@ export interface SlotPool {
|
|
|
305
305
|
variantId: VariantId;
|
|
306
306
|
} | null;
|
|
307
307
|
warnings: string[];
|
|
308
|
-
}
|
|
308
|
+
};
|
|
309
309
|
/**
|
|
310
310
|
* The sparse pairing graph among the returned candidates. Each unordered pair
|
|
311
311
|
* appears ONCE and the relation is symmetric. An empty `edges` under a null
|
|
312
312
|
* `datasetSessionId` means the dataset is absent, NOT that these materials are
|
|
313
313
|
* never specified together.
|
|
314
314
|
*/
|
|
315
|
-
export
|
|
315
|
+
export type SlotPoolCoSpecification = {
|
|
316
316
|
edges: {
|
|
317
317
|
from: VariantId;
|
|
318
318
|
to: VariantId;
|
|
@@ -326,9 +326,9 @@ export interface SlotPoolCoSpecification {
|
|
|
326
326
|
window: string;
|
|
327
327
|
signal: string;
|
|
328
328
|
};
|
|
329
|
-
}
|
|
329
|
+
};
|
|
330
330
|
/** The `200` body from `POST /api/v1/schemes/pools`. */
|
|
331
|
-
export
|
|
331
|
+
export type SlotPoolsResponse = {
|
|
332
332
|
versions: {
|
|
333
333
|
contract: string;
|
|
334
334
|
retrieval: string;
|
|
@@ -369,4 +369,4 @@ export interface SlotPoolsResponse {
|
|
|
369
369
|
coSpecificationQueries: number;
|
|
370
370
|
};
|
|
371
371
|
warnings: string[];
|
|
372
|
-
}
|
|
372
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PostSourceMaterialCommentResult, SourceMaterialBatchDetail, SourceMaterialCommentThread } from "./run-types.js";
|
|
2
2
|
/** Validate a batch-detail body, returning it typed, or throw explaining why. */
|
|
3
|
-
export declare
|
|
3
|
+
export declare const checkSourceMaterialBatchDetail: (value: unknown, batchId: string) => SourceMaterialBatchDetail;
|
|
4
4
|
/** Validate a comment-thread body, returning it typed, or throw explaining why. */
|
|
5
|
-
export declare
|
|
5
|
+
export declare const checkSourceMaterialCommentThread: (value: unknown, sourceId: string) => SourceMaterialCommentThread;
|
|
6
6
|
/** Validate a posted-comment body, returning it typed, or throw explaining why. */
|
|
7
|
-
export declare
|
|
7
|
+
export declare const checkPostSourceMaterialCommentResult: (value: unknown, sourceId: string) => PostSourceMaterialCommentResult;
|
|
@@ -48,23 +48,21 @@ const SOURCE_ROLES = [
|
|
|
48
48
|
"inspiration_image",
|
|
49
49
|
"unclassified",
|
|
50
50
|
];
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
function checkAnalysis(value, what, fail) {
|
|
51
|
+
const detailFailure = (batchId) => (reason) => MaterialGraphError.unexpected(`GET /api/v1/source-material/${batchId} returned a malformed body: ${reason}`);
|
|
52
|
+
const checkAnalysis = (value, what, fail) => {
|
|
55
53
|
const record = requireRecord(value, what, fail);
|
|
56
54
|
// `model` and `analysedAt` ARE `.min(1)` canonically — fields this endpoint
|
|
57
55
|
// introduces, where an empty value is meaningless and `model` already cannot
|
|
58
56
|
// be written empty. Parity here is achieved by the schema being strict, never
|
|
59
57
|
// by this mirror freelancing past it.
|
|
60
58
|
return {
|
|
59
|
+
analysedAt: requireNonEmptyString(record.analysedAt, `${what}.analysedAt`, fail),
|
|
61
60
|
kind: requireEnum(record.kind, ANALYSIS_KINDS, `${what}.kind`, fail),
|
|
62
61
|
model: requireNonEmptyString(record.model, `${what}.model`, fail),
|
|
63
|
-
status: requireEnum(record.status, ANALYSIS_STATUSES, `${what}.status`, fail),
|
|
64
62
|
result: requireJsonRecord(record.result, `${what}.result`, fail),
|
|
65
|
-
|
|
63
|
+
status: requireEnum(record.status, ANALYSIS_STATUSES, `${what}.status`, fail),
|
|
66
64
|
};
|
|
67
|
-
}
|
|
65
|
+
};
|
|
68
66
|
/** `sourceMaterialCommentAuthorSchema`'s `attribution` members, verbatim. */
|
|
69
67
|
const COMMENT_ATTRIBUTIONS = ["reported"];
|
|
70
68
|
/** The canonical cap on both author fields: `.trim().min(1).max(160)`. */
|
|
@@ -84,27 +82,27 @@ const AUTHOR_FIELD_MAX = 160;
|
|
|
84
82
|
* schema caps. A validator that is looser than the contract is a validator that
|
|
85
83
|
* lets the two drift without anything failing.
|
|
86
84
|
*/
|
|
87
|
-
|
|
85
|
+
const checkCommentBase = (record, what, fail) => {
|
|
88
86
|
const author = requireRecord(record.author, `${what}.author`, fail);
|
|
89
87
|
const recordedBy = requireRecord(record.recordedBy, `${what}.recordedBy`, fail);
|
|
90
88
|
return {
|
|
91
|
-
commentId: requireNonEmptyString(record.commentId, `${what}.commentId`, fail),
|
|
92
89
|
author: {
|
|
93
|
-
name: requireTrimmedBoundedString(author.name, `${what}.author.name`, AUTHOR_FIELD_MAX, fail),
|
|
94
|
-
organisation: requireNullableTrimmedBoundedString(author.organisation, `${what}.author.organisation`, AUTHOR_FIELD_MAX, fail),
|
|
95
90
|
// Closed here, matching the canonical `z.literal("reported")`, while the
|
|
96
91
|
// published type is `OpenEnum` so a newer server value keeps
|
|
97
92
|
// type-checking — the same split the analysis enums make.
|
|
98
93
|
attribution: requireEnum(author.attribution, COMMENT_ATTRIBUTIONS, `${what}.author.attribution`, fail),
|
|
94
|
+
name: requireTrimmedBoundedString(author.name, `${what}.author.name`, AUTHOR_FIELD_MAX, fail),
|
|
95
|
+
organisation: requireNullableTrimmedBoundedString(author.organisation, `${what}.author.organisation`, AUTHOR_FIELD_MAX, fail),
|
|
99
96
|
},
|
|
97
|
+
body: requireNonEmptyString(record.body, `${what}.body`, fail),
|
|
98
|
+
commentId: requireNonEmptyString(record.commentId, `${what}.commentId`, fail),
|
|
99
|
+
createdAt: requireNonEmptyString(record.createdAt, `${what}.createdAt`, fail),
|
|
100
100
|
recordedBy: {
|
|
101
101
|
principalId: requireNonEmptyString(recordedBy.principalId, `${what}.recordedBy.principalId`, fail),
|
|
102
102
|
},
|
|
103
|
-
body: requireNonEmptyString(record.body, `${what}.body`, fail),
|
|
104
|
-
createdAt: requireNonEmptyString(record.createdAt, `${what}.createdAt`, fail),
|
|
105
103
|
};
|
|
106
|
-
}
|
|
107
|
-
|
|
104
|
+
};
|
|
105
|
+
const checkComment = (value, what, fail) => {
|
|
108
106
|
const record = requireRecord(value, what, fail);
|
|
109
107
|
// Same reasoning as `analyses`: no `.default([])` canonically, so an absent
|
|
110
108
|
// `replies` is malformed rather than empty.
|
|
@@ -113,8 +111,8 @@ function checkComment(value, what, fail) {
|
|
|
113
111
|
...checkCommentBase(record, what, fail),
|
|
114
112
|
replies: replies.map((reply, index) => checkCommentBase(requireRecord(reply, `${what}.replies[${index}]`, fail), `${what}.replies[${index}]`, fail)),
|
|
115
113
|
};
|
|
116
|
-
}
|
|
117
|
-
|
|
114
|
+
};
|
|
115
|
+
const checkFile = (value, what, fail) => {
|
|
118
116
|
const record = requireRecord(value, what, fail);
|
|
119
117
|
// NOT `requireDefaultedArray`: the schema has no `.default([])`, so an absent
|
|
120
118
|
// `analyses` is a malformed body rather than an empty one. Keeping that
|
|
@@ -133,11 +131,7 @@ function checkFile(value, what, fail) {
|
|
|
133
131
|
// refuse a response the server can legitimately produce, and tightening them
|
|
134
132
|
// is a change to a contract this endpoint does not own.
|
|
135
133
|
return {
|
|
136
|
-
|
|
137
|
-
role: requireEnum(record.role, SOURCE_ROLES, `${what}.role`, fail),
|
|
138
|
-
filename: requireString(record.filename, `${what}.filename`, fail),
|
|
139
|
-
mediaType: requireString(record.mediaType, `${what}.mediaType`, fail),
|
|
140
|
-
sha256: requireString(record.sha256, `${what}.sha256`, fail),
|
|
134
|
+
analyses: analyses.map((analysis, index) => checkAnalysis(analysis, `${what}.analyses[${index}]`, fail)),
|
|
141
135
|
// `requireSafeInteger`, NOT `requireIntegerAtLeast(…, 0)`. Canonically these
|
|
142
136
|
// are a bare `z.number().int()` on the INHERITED
|
|
143
137
|
// `sourceMaterialFileReceiptSchema`, which carries no lower bound — so a
|
|
@@ -150,17 +144,21 @@ function checkFile(value, what, fail) {
|
|
|
150
144
|
// fields is a canonical change to a schema shared with the upload path, and
|
|
151
145
|
// belongs in its own ticket with this mirror following after.
|
|
152
146
|
bytes: requireSafeInteger(record.bytes, `${what}.bytes`, fail),
|
|
147
|
+
comments: comments.map((comment, index) => checkComment(comment, `${what}.comments[${index}]`, fail)),
|
|
148
|
+
commentsTruncated,
|
|
149
|
+
filename: requireString(record.filename, `${what}.filename`, fail),
|
|
150
|
+
mediaType: requireString(record.mediaType, `${what}.mediaType`, fail),
|
|
153
151
|
pages: record.pages === null
|
|
154
152
|
? null
|
|
155
153
|
: requireSafeInteger(record.pages, `${what}.pages`, fail),
|
|
154
|
+
role: requireEnum(record.role, SOURCE_ROLES, `${what}.role`, fail),
|
|
155
|
+
sha256: requireString(record.sha256, `${what}.sha256`, fail),
|
|
156
|
+
sourceId: requireString(record.sourceId, `${what}.sourceId`, fail),
|
|
156
157
|
url: requireString(record.url, `${what}.url`, fail),
|
|
157
|
-
analyses: analyses.map((analysis, index) => checkAnalysis(analysis, `${what}.analyses[${index}]`, fail)),
|
|
158
|
-
comments: comments.map((comment, index) => checkComment(comment, `${what}.comments[${index}]`, fail)),
|
|
159
|
-
commentsTruncated,
|
|
160
158
|
};
|
|
161
|
-
}
|
|
159
|
+
};
|
|
162
160
|
/** Validate a batch-detail body, returning it typed, or throw explaining why. */
|
|
163
|
-
export
|
|
161
|
+
export const checkSourceMaterialBatchDetail = (value, batchId) => {
|
|
164
162
|
const fail = detailFailure(batchId);
|
|
165
163
|
const record = requireRecord(value, "body", fail);
|
|
166
164
|
const sources = requireArray(record.sources, "body.sources", fail);
|
|
@@ -169,21 +167,19 @@ export function checkSourceMaterialBatchDetail(value, batchId) {
|
|
|
169
167
|
createdAt: requireString(record.createdAt, "body.createdAt", fail),
|
|
170
168
|
sources: sources.map((file, index) => checkFile(file, `body.sources[${index}]`, fail)),
|
|
171
169
|
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return (reason) => MaterialGraphError.unexpected(`${verb} .../sources/${sourceId}/comments returned a malformed body: ${reason}`);
|
|
175
|
-
}
|
|
170
|
+
};
|
|
171
|
+
const commentsFailure = (verb, sourceId) => (reason) => MaterialGraphError.unexpected(`${verb} .../sources/${sourceId}/comments returned a malformed body: ${reason}`);
|
|
176
172
|
/** Validate a comment-thread body, returning it typed, or throw explaining why. */
|
|
177
|
-
export
|
|
173
|
+
export const checkSourceMaterialCommentThread = (value, sourceId) => {
|
|
178
174
|
const fail = commentsFailure("GET", sourceId);
|
|
179
175
|
const record = requireRecord(value, "body", fail);
|
|
180
176
|
const comments = requireArray(record.comments, "body.comments", fail);
|
|
181
177
|
return {
|
|
182
|
-
sourceId: requireString(record.sourceId, "body.sourceId", fail),
|
|
183
178
|
comments: comments.map((comment, index) => checkComment(comment, `body.comments[${index}]`, fail)),
|
|
179
|
+
sourceId: requireString(record.sourceId, "body.sourceId", fail),
|
|
184
180
|
truncated: requireBoolean(record.truncated, "body.truncated", fail),
|
|
185
181
|
};
|
|
186
|
-
}
|
|
182
|
+
};
|
|
187
183
|
/** `storedSourceMaterialCommentSchema`'s discriminator members, verbatim. */
|
|
188
184
|
const STORED_COMMENT_KINDS = ["root", "reply"];
|
|
189
185
|
/**
|
|
@@ -195,7 +191,7 @@ const STORED_COMMENT_KINDS = ["root", "reply"];
|
|
|
195
191
|
* defect this discriminated union was added to fix, and a mirror that did it
|
|
196
192
|
* would put the lie back on the client side.
|
|
197
193
|
*/
|
|
198
|
-
|
|
194
|
+
const checkStoredComment = (value, what, fail) => {
|
|
199
195
|
const record = requireRecord(value, what, fail);
|
|
200
196
|
const kind = requireEnum(record.kind, STORED_COMMENT_KINDS, `${what}.kind`, fail);
|
|
201
197
|
if (kind === "reply") {
|
|
@@ -206,13 +202,13 @@ function checkStoredComment(value, what, fail) {
|
|
|
206
202
|
};
|
|
207
203
|
}
|
|
208
204
|
return { kind: "root", ...checkComment(record, what, fail) };
|
|
209
|
-
}
|
|
205
|
+
};
|
|
210
206
|
/** Validate a posted-comment body, returning it typed, or throw explaining why. */
|
|
211
|
-
export
|
|
207
|
+
export const checkPostSourceMaterialCommentResult = (value, sourceId) => {
|
|
212
208
|
const fail = commentsFailure("POST", sourceId);
|
|
213
209
|
const record = requireRecord(value, "body", fail);
|
|
214
210
|
return {
|
|
215
|
-
sourceId: requireString(record.sourceId, "body.sourceId", fail),
|
|
216
211
|
comment: checkStoredComment(record.comment, "body.comment", fail),
|
|
212
|
+
sourceId: requireString(record.sourceId, "body.sourceId", fail),
|
|
217
213
|
};
|
|
218
|
-
}
|
|
214
|
+
};
|
package/spec-facet-types.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type SpecFacetValue = {
|
|
2
2
|
value: string;
|
|
3
3
|
variants: number;
|
|
4
4
|
brands: number;
|
|
5
5
|
label?: string;
|
|
6
|
-
}
|
|
6
|
+
};
|
|
7
7
|
/**
|
|
8
8
|
* A NUMERIC facet's observed span and the coverage behind it.
|
|
9
9
|
*
|
|
@@ -17,13 +17,13 @@ export interface SpecFacetValue {
|
|
|
17
17
|
* `unparseableVariants` is the honesty companion: values recorded here that are
|
|
18
18
|
* not numbers, which a range EXCLUDES rather than reading as zero.
|
|
19
19
|
*/
|
|
20
|
-
export
|
|
20
|
+
export type SpecFacetRange = {
|
|
21
21
|
min: number;
|
|
22
22
|
max: number;
|
|
23
23
|
variants: number;
|
|
24
24
|
unparseableVariants: number;
|
|
25
|
-
}
|
|
26
|
-
export
|
|
25
|
+
};
|
|
26
|
+
export type SpecFacet = {
|
|
27
27
|
attributeId: string;
|
|
28
28
|
name: string;
|
|
29
29
|
isArray: boolean;
|
|
@@ -39,15 +39,15 @@ export interface SpecFacet {
|
|
|
39
39
|
/** Present ONLY on a `numeric` facet. */
|
|
40
40
|
range?: SpecFacetRange;
|
|
41
41
|
description?: string;
|
|
42
|
-
}
|
|
43
|
-
export
|
|
42
|
+
};
|
|
43
|
+
export type SpecFacetsListResponse = {
|
|
44
44
|
mode: "facets";
|
|
45
45
|
count: number;
|
|
46
46
|
facets: SpecFacet[];
|
|
47
|
-
}
|
|
48
|
-
export
|
|
47
|
+
};
|
|
48
|
+
export type SpecFacetsDistributionResponse = {
|
|
49
49
|
mode: "distribution";
|
|
50
50
|
attributeId: string;
|
|
51
51
|
count: number;
|
|
52
52
|
values: SpecFacetValue[];
|
|
53
|
-
}
|
|
53
|
+
};
|
package/spec-types.d.ts
CHANGED
|
@@ -60,15 +60,15 @@ export type CertificationProgrammeAttributeId = typeof CERTIFICATION_PROGRAMME_A
|
|
|
60
60
|
* and `client.attributeValues()`, or resolve free text with
|
|
61
61
|
* `client.resolveAttribute()`.
|
|
62
62
|
*/
|
|
63
|
-
export
|
|
63
|
+
export type SpecTermFilter = {
|
|
64
64
|
attributeId: string;
|
|
65
65
|
term: string;
|
|
66
|
-
}
|
|
66
|
+
};
|
|
67
67
|
/** A negative spec criterion; absence of a value remains unproven. */
|
|
68
|
-
export
|
|
68
|
+
export type SpecExclusionFilter = {
|
|
69
69
|
attributeId: string;
|
|
70
70
|
excludesTerm: string;
|
|
71
|
-
}
|
|
71
|
+
};
|
|
72
72
|
/**
|
|
73
73
|
* A presence spec criterion: the attribute must have ANY accepted value —
|
|
74
74
|
* "has an EPD on record", "has a measured slip figure at all". Not supported for
|
|
@@ -90,10 +90,10 @@ export type SpecPresenceFilter = {
|
|
|
90
90
|
* virtual {@link CERTIFICATION_PROGRAMME_ATTRIBUTE_ID}. Structurally assignable
|
|
91
91
|
* to {@link SpecFilter}, so it drops straight into `search({ filters })`.
|
|
92
92
|
*/
|
|
93
|
-
export
|
|
93
|
+
export type CertificationProgrammeFilter = {
|
|
94
94
|
attributeId: CertificationProgrammeAttributeId;
|
|
95
95
|
term: string;
|
|
96
|
-
}
|
|
96
|
+
};
|
|
97
97
|
/**
|
|
98
98
|
* A hard, canonical-attribute spec criterion — either a term match or a
|
|
99
99
|
* presence check. The union the search engine applies as a restriction BEFORE
|
package/specifications.d.ts
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
* Zero-dependency builders for the specification surface. Turn a plain-language
|
|
3
|
-
* spec intent into a typed criterion the SDK's two spec operations accept:
|
|
4
|
-
*
|
|
5
|
-
* - `search({ filters })` takes {@link SpecFilter} objects — a spec gate layered
|
|
6
|
-
* onto a ranked (colour / visual / lexical) search. Build them with
|
|
7
|
-
* {@link specTerm}, {@link hasSpec}, and {@link certificationProgramme}.
|
|
8
|
-
* - `bySpec(specs)` takes `attribute:value` strings — the value-anchored,
|
|
9
|
-
* spec-only primitive. Build them with {@link specCriterion} and
|
|
10
|
-
* {@link certificationProgrammeCriterion}.
|
|
11
|
-
*
|
|
12
|
-
* The point of these helpers: Desk (and any spec-driven caller) never has to
|
|
13
|
-
* remember that a certification programme lives under a VIRTUAL attribute, nor
|
|
14
|
-
* hand-format the `attribute:value` string the `bySpec` route splits on its
|
|
15
|
-
* first `:`.
|
|
16
|
-
*/
|
|
17
|
-
import { type CertificationProgrammeFilter, type SpecExclusionFilter, type SpecPresenceFilter, type SpecTermFilter } from "./spec-types.js";
|
|
1
|
+
import type { CertificationProgrammeFilter, SpecExclusionFilter, SpecPresenceFilter, SpecTermFilter } from "./spec-types.js";
|
|
18
2
|
/** A hard criterion excluding one known canonical value while retaining only variants with known values. */
|
|
19
|
-
export declare
|
|
3
|
+
export declare const excludeSpec: (attributeId: string, excludesTerm: string) => SpecExclusionFilter;
|
|
20
4
|
/**
|
|
21
5
|
* A term-equality criterion for `search({ filters })` — the attribute must
|
|
22
6
|
* carry this canonical value.
|
|
@@ -25,7 +9,7 @@ export declare function excludeSpec(attributeId: string, excludesTerm: string):
|
|
|
25
9
|
* // "same fire class as the seed"
|
|
26
10
|
* client.search({ similarTo: { variantId }, filters: [specTerm("fire_classifications", "astm_e84_class_a")] });
|
|
27
11
|
*/
|
|
28
|
-
export declare
|
|
12
|
+
export declare const specTerm: (attributeId: string, term: string) => SpecTermFilter;
|
|
29
13
|
/**
|
|
30
14
|
* A presence criterion for `search({ filters })` — the attribute must have ANY
|
|
31
15
|
* accepted value.
|
|
@@ -34,7 +18,7 @@ export declare function specTerm(attributeId: string, term: string): SpecTermFil
|
|
|
34
18
|
* // "…and it must have an EPD on record"
|
|
35
19
|
* client.search({ similarTo: { variantId }, filters: [hasSpec("environmental_product_declaration")] });
|
|
36
20
|
*/
|
|
37
|
-
export declare
|
|
21
|
+
export declare const hasSpec: (attributeId: string) => SpecPresenceFilter;
|
|
38
22
|
/**
|
|
39
23
|
* A certification-programme criterion for `search({ filters })`, pinned to the
|
|
40
24
|
* virtual attribute so a programme matches wherever it landed at ingest
|
|
@@ -49,7 +33,7 @@ export declare function hasSpec(attributeId: string): SpecPresenceFilter;
|
|
|
49
33
|
* // "find a GREENGUARD-certified alternative"
|
|
50
34
|
* client.search({ similarTo: { variantId: seed }, filters: [certificationProgramme("GREENGUARD Gold")] });
|
|
51
35
|
*/
|
|
52
|
-
export declare
|
|
36
|
+
export declare const certificationProgramme: (programme: string) => CertificationProgrammeFilter;
|
|
53
37
|
/**
|
|
54
38
|
* An `attribute:value` criterion string for `bySpec`. The `bySpec` route splits
|
|
55
39
|
* on the FIRST `:`, so a value may itself contain colons; the attribute id may
|
|
@@ -58,7 +42,7 @@ export declare function certificationProgramme(programme: string): Certification
|
|
|
58
42
|
* @example
|
|
59
43
|
* client.bySpec([specCriterion("primary_material", "textile"), specCriterion("fire_classifications", "astm_e84_class_a")]);
|
|
60
44
|
*/
|
|
61
|
-
export declare
|
|
45
|
+
export declare const specCriterion: (attributeId: string, value: string) => string;
|
|
62
46
|
/**
|
|
63
47
|
* An `attribute:min..max` RANGE criterion string for `bySpec`, for a
|
|
64
48
|
* NUMBER-valued spec (`light_reflectance_value`, `nrc_rating`,
|
|
@@ -81,10 +65,10 @@ export declare function specCriterion(attributeId: string, value: string): strin
|
|
|
81
65
|
* // a mid-tone band
|
|
82
66
|
* specRangeCriterion("light_reflectance_value", { min: 30, max: 45 }); // "light_reflectance_value:30..45"
|
|
83
67
|
*/
|
|
84
|
-
export declare
|
|
68
|
+
export declare const specRangeCriterion: (attributeId: string, bounds: {
|
|
85
69
|
min?: number;
|
|
86
70
|
max?: number;
|
|
87
|
-
})
|
|
71
|
+
}) => string;
|
|
88
72
|
/**
|
|
89
73
|
* An `attribute:value` criterion string for `bySpec` that filters by
|
|
90
74
|
* certification PROGRAMME across all four certification buckets.
|
|
@@ -97,4 +81,4 @@ export declare function specRangeCriterion(attributeId: string, bounds: {
|
|
|
97
81
|
* // every brand's GREENGUARD-Gold materials
|
|
98
82
|
* client.bySpec([certificationProgrammeCriterion("GREENGUARD Gold")]);
|
|
99
83
|
*/
|
|
100
|
-
export declare
|
|
84
|
+
export declare const certificationProgrammeCriterion: (programme: string) => string;
|
package/specifications.js
CHANGED
|
@@ -14,11 +14,9 @@
|
|
|
14
14
|
* hand-format the `attribute:value` string the `bySpec` route splits on its
|
|
15
15
|
* first `:`.
|
|
16
16
|
*/
|
|
17
|
-
import { CERTIFICATION_PROGRAMME_ATTRIBUTE_ID
|
|
17
|
+
import { CERTIFICATION_PROGRAMME_ATTRIBUTE_ID } from "./spec-types.js";
|
|
18
18
|
/** A hard criterion excluding one known canonical value while retaining only variants with known values. */
|
|
19
|
-
export
|
|
20
|
-
return { attributeId, excludesTerm };
|
|
21
|
-
}
|
|
19
|
+
export const excludeSpec = (attributeId, excludesTerm) => ({ attributeId, excludesTerm });
|
|
22
20
|
/**
|
|
23
21
|
* A term-equality criterion for `search({ filters })` — the attribute must
|
|
24
22
|
* carry this canonical value.
|
|
@@ -27,9 +25,7 @@ export function excludeSpec(attributeId, excludesTerm) {
|
|
|
27
25
|
* // "same fire class as the seed"
|
|
28
26
|
* client.search({ similarTo: { variantId }, filters: [specTerm("fire_classifications", "astm_e84_class_a")] });
|
|
29
27
|
*/
|
|
30
|
-
export
|
|
31
|
-
return { attributeId, term };
|
|
32
|
-
}
|
|
28
|
+
export const specTerm = (attributeId, term) => ({ attributeId, term });
|
|
33
29
|
/**
|
|
34
30
|
* A presence criterion for `search({ filters })` — the attribute must have ANY
|
|
35
31
|
* accepted value.
|
|
@@ -38,9 +34,10 @@ export function specTerm(attributeId, term) {
|
|
|
38
34
|
* // "…and it must have an EPD on record"
|
|
39
35
|
* client.search({ similarTo: { variantId }, filters: [hasSpec("environmental_product_declaration")] });
|
|
40
36
|
*/
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
export const hasSpec = (attributeId) => ({
|
|
38
|
+
attributeId,
|
|
39
|
+
hasValue: true,
|
|
40
|
+
});
|
|
44
41
|
/**
|
|
45
42
|
* A certification-programme criterion for `search({ filters })`, pinned to the
|
|
46
43
|
* virtual attribute so a programme matches wherever it landed at ingest
|
|
@@ -55,9 +52,10 @@ export function hasSpec(attributeId) {
|
|
|
55
52
|
* // "find a GREENGUARD-certified alternative"
|
|
56
53
|
* client.search({ similarTo: { variantId: seed }, filters: [certificationProgramme("GREENGUARD Gold")] });
|
|
57
54
|
*/
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
export const certificationProgramme = (programme) => ({
|
|
56
|
+
attributeId: CERTIFICATION_PROGRAMME_ATTRIBUTE_ID,
|
|
57
|
+
term: programme,
|
|
58
|
+
});
|
|
61
59
|
/**
|
|
62
60
|
* An `attribute:value` criterion string for `bySpec`. The `bySpec` route splits
|
|
63
61
|
* on the FIRST `:`, so a value may itself contain colons; the attribute id may
|
|
@@ -66,9 +64,7 @@ export function certificationProgramme(programme) {
|
|
|
66
64
|
* @example
|
|
67
65
|
* client.bySpec([specCriterion("primary_material", "textile"), specCriterion("fire_classifications", "astm_e84_class_a")]);
|
|
68
66
|
*/
|
|
69
|
-
export
|
|
70
|
-
return `${attributeId}:${value}`;
|
|
71
|
-
}
|
|
67
|
+
export const specCriterion = (attributeId, value) => `${attributeId}:${value}`;
|
|
72
68
|
/**
|
|
73
69
|
* An `attribute:min..max` RANGE criterion string for `bySpec`, for a
|
|
74
70
|
* NUMBER-valued spec (`light_reflectance_value`, `nrc_rating`,
|
|
@@ -91,7 +87,7 @@ export function specCriterion(attributeId, value) {
|
|
|
91
87
|
* // a mid-tone band
|
|
92
88
|
* specRangeCriterion("light_reflectance_value", { min: 30, max: 45 }); // "light_reflectance_value:30..45"
|
|
93
89
|
*/
|
|
94
|
-
export
|
|
90
|
+
export const specRangeCriterion = (attributeId, bounds) => {
|
|
95
91
|
const { min, max } = bounds;
|
|
96
92
|
if (min === undefined && max === undefined) {
|
|
97
93
|
throw new Error(`specRangeCriterion("${attributeId}") needs a min, a max, or both — a range with neither would match every recorded value. Use hasSpec() for a presence check.`);
|
|
@@ -100,7 +96,7 @@ export function specRangeCriterion(attributeId, bounds) {
|
|
|
100
96
|
throw new Error(`specRangeCriterion("${attributeId}") is inverted (min ${min} > max ${max}) and can only ever match nothing.`);
|
|
101
97
|
}
|
|
102
98
|
return `${attributeId}:${min ?? ""}..${max ?? ""}`;
|
|
103
|
-
}
|
|
99
|
+
};
|
|
104
100
|
/**
|
|
105
101
|
* An `attribute:value` criterion string for `bySpec` that filters by
|
|
106
102
|
* certification PROGRAMME across all four certification buckets.
|
|
@@ -113,6 +109,4 @@ export function specRangeCriterion(attributeId, bounds) {
|
|
|
113
109
|
* // every brand's GREENGUARD-Gold materials
|
|
114
110
|
* client.bySpec([certificationProgrammeCriterion("GREENGUARD Gold")]);
|
|
115
111
|
*/
|
|
116
|
-
export
|
|
117
|
-
return specCriterion(CERTIFICATION_PROGRAMME_ATTRIBUTE_ID, programme);
|
|
118
|
-
}
|
|
112
|
+
export const certificationProgrammeCriterion = (programme) => specCriterion(CERTIFICATION_PROGRAMME_ATTRIBUTE_ID, programme);
|
package/sse.d.ts
CHANGED
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
* chunks are held in an internal buffer until their terminating blank line.
|
|
12
12
|
*/
|
|
13
13
|
/** One dispatched SSE frame. `data` is the joined data lines (no trailing newline). */
|
|
14
|
-
export
|
|
14
|
+
export type SseFrame = {
|
|
15
15
|
id?: string;
|
|
16
16
|
event?: string;
|
|
17
17
|
data: string;
|
|
18
|
-
}
|
|
18
|
+
};
|
|
19
19
|
/**
|
|
20
20
|
* A push-based SSE line assembler. Feed it decoded text with {@link push}; it
|
|
21
21
|
* returns any frames completed by that text. Call {@link flush} once the stream
|