@instruments/materialgraph 0.2.0 → 0.4.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-browse.d.ts +46 -0
- package/client-browse.js +42 -0
- package/client.d.ts +83 -28
- package/client.js +99 -47
- 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-asset-types.d.ts +1 -1
- 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 +7 -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/suggest-types.d.ts +58 -0
- package/suggest-types.js +21 -0
- package/types.d.ts +60 -73
- package/validation.d.ts +119 -0
- package/validation.js +241 -0
package/validation.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SDK's shared wire-validation primitives — the vocabulary every hand-rolled
|
|
3
|
+
* mirror of a canonical Zod schema is built from.
|
|
4
|
+
*
|
|
5
|
+
* The SDK publishes with ZERO runtime dependencies, so it cannot call
|
|
6
|
+
* `schema.parse()` on anything. Where a wire payload must be proven rather than
|
|
7
|
+
* trusted, it is checked with these helpers and the result is pinned against the
|
|
8
|
+
* real schema in `contract-parity.test.ts`. Each helper mirrors one Zod
|
|
9
|
+
* construct, named for it, so a reviewer can read the mirror against the schema
|
|
10
|
+
* line by line.
|
|
11
|
+
*
|
|
12
|
+
* Every helper takes the same three shapes: the value, a `what` path used in the
|
|
13
|
+
* diagnostic (`results[3].verdict.cells[0].state`), and a {@link ValidationFailure}
|
|
14
|
+
* owned by the calling module — so one payload's failures all read alike without
|
|
15
|
+
* this file knowing what a Match frame or a Run event is.
|
|
16
|
+
*/
|
|
17
|
+
import { MaterialGraphError } from "./errors.js";
|
|
18
|
+
export function isRecord(value) {
|
|
19
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
/** A `z.object({…})`. */
|
|
22
|
+
export function requireRecord(value, what, fail) {
|
|
23
|
+
if (!isRecord(value))
|
|
24
|
+
throw fail(`${what} is not a JSON object`);
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
/** A `z.string()`. */
|
|
28
|
+
export function requireString(value, what, fail) {
|
|
29
|
+
if (typeof value !== "string")
|
|
30
|
+
throw fail(`${what} is not a string`);
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
/** A `z.string().min(1)`. */
|
|
34
|
+
export function requireNonEmptyString(value, what, fail) {
|
|
35
|
+
const text = requireString(value, what, fail);
|
|
36
|
+
if (text.length === 0)
|
|
37
|
+
throw fail(`${what} is an empty string`);
|
|
38
|
+
return text;
|
|
39
|
+
}
|
|
40
|
+
/** A `z.string().optional()`: absent is fine, present-but-wrong is not. */
|
|
41
|
+
export function requireOptionalString(value, what, fail) {
|
|
42
|
+
if (value === undefined)
|
|
43
|
+
return;
|
|
44
|
+
requireString(value, what, fail);
|
|
45
|
+
}
|
|
46
|
+
/** A `z.string().nullable()`. */
|
|
47
|
+
export function requireNullableString(value, what, fail) {
|
|
48
|
+
if (value === null)
|
|
49
|
+
return null;
|
|
50
|
+
return requireString(value, what, fail);
|
|
51
|
+
}
|
|
52
|
+
/** A `z.string().min(1).nullable()`. */
|
|
53
|
+
export function requireNullableNonEmptyString(value, what, fail) {
|
|
54
|
+
if (value === null)
|
|
55
|
+
return null;
|
|
56
|
+
return requireNonEmptyString(value, what, fail);
|
|
57
|
+
}
|
|
58
|
+
/** A `z.boolean()`. */
|
|
59
|
+
export function requireBoolean(value, what, fail) {
|
|
60
|
+
if (typeof value !== "boolean")
|
|
61
|
+
throw fail(`${what} is not a boolean`);
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
/** A `z.boolean().optional()`. */
|
|
65
|
+
export function requireOptionalBoolean(value, what, fail) {
|
|
66
|
+
if (value === undefined)
|
|
67
|
+
return;
|
|
68
|
+
requireBoolean(value, what, fail);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A `z.number()`. Zod 4 rejects `NaN` AND `±Infinity` for it, and `1e999` parses
|
|
72
|
+
* out of JSON as `Infinity`, so finiteness is a reachable check.
|
|
73
|
+
*/
|
|
74
|
+
export function requireNumber(value, what, fail) {
|
|
75
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
76
|
+
throw fail(`${what} is not a finite number`);
|
|
77
|
+
}
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
/** A `z.number().optional()`. */
|
|
81
|
+
export function requireOptionalNumber(value, what, fail) {
|
|
82
|
+
if (value === undefined)
|
|
83
|
+
return;
|
|
84
|
+
requireNumber(value, what, fail);
|
|
85
|
+
}
|
|
86
|
+
/** A `z.number().nullable()`. */
|
|
87
|
+
export function requireNullableNumber(value, what, fail) {
|
|
88
|
+
if (value === null)
|
|
89
|
+
return null;
|
|
90
|
+
return requireNumber(value, what, fail);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A `z.number().min(min).max(max)`. `NaN` and both infinities fail the
|
|
94
|
+
* comparisons, exactly as the bounded Zod schema rejects them.
|
|
95
|
+
*/
|
|
96
|
+
export function requireBoundedNumber(value, what, fail, min, max) {
|
|
97
|
+
if (typeof value !== "number" || !(value >= min && value <= max)) {
|
|
98
|
+
throw fail(`${what} is not a number within [${min}, ${max}]`);
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
/** A `z.number().min(min).max(max).nullable()`. */
|
|
103
|
+
export function requireNullableBoundedNumber(value, what, fail, min, max) {
|
|
104
|
+
if (value === null)
|
|
105
|
+
return null;
|
|
106
|
+
return requireBoundedNumber(value, what, fail, min, max);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A bare `z.number().int()` — integer, finite, and within the SAFE range, with
|
|
110
|
+
* NO lower bound.
|
|
111
|
+
*
|
|
112
|
+
* Measured against the pinned Zod (4.4.3), `.int()` is exactly
|
|
113
|
+
* `Number.isSafeInteger`: it accepts `0`, `-1`, and `±MAX_SAFE_INTEGER`, and
|
|
114
|
+
* rejects `1.5`, `±2**53`, `NaN`, and `±Infinity`.
|
|
115
|
+
*
|
|
116
|
+
* Two ways to get this wrong, both of which break parity in a different
|
|
117
|
+
* direction. Adding a `min: 0` REJECTS a negative the schema accepts — a client
|
|
118
|
+
* refusing a legitimate response. Using `Number.isInteger` ACCEPTS `2**53`,
|
|
119
|
+
* which the schema rejects — so `requireIntegerAtLeast` is not this helper with
|
|
120
|
+
* the bound dropped, and the safe-range test is why.
|
|
121
|
+
*/
|
|
122
|
+
export function requireSafeInteger(value, what, fail) {
|
|
123
|
+
if (!Number.isSafeInteger(value))
|
|
124
|
+
throw fail(`${what} is not a safe integer`);
|
|
125
|
+
return requireNumber(value, what, fail);
|
|
126
|
+
}
|
|
127
|
+
/** A `z.number().int().min(min)` — the integer constraint AND the lower bound. */
|
|
128
|
+
export function requireIntegerAtLeast(value, what, fail, min) {
|
|
129
|
+
const numeric = requireNumber(value, what, fail);
|
|
130
|
+
if (!Number.isInteger(numeric))
|
|
131
|
+
throw fail(`${what} is not an integer`);
|
|
132
|
+
if (numeric < min)
|
|
133
|
+
throw fail(`${what} is below the minimum of ${min}`);
|
|
134
|
+
return numeric;
|
|
135
|
+
}
|
|
136
|
+
/** A `z.enum([…])` — CLOSED, exactly like the canonical member list. */
|
|
137
|
+
export function requireEnum(value, allowed, what, fail) {
|
|
138
|
+
const text = requireString(value, what, fail);
|
|
139
|
+
if (!allowed.includes(text)) {
|
|
140
|
+
throw fail(`${what} is not one of ${allowed.join(" | ")} (${text})`);
|
|
141
|
+
}
|
|
142
|
+
return text;
|
|
143
|
+
}
|
|
144
|
+
/** A `z.enum([…]).nullable()`. */
|
|
145
|
+
export function requireNullableEnum(value, allowed, what, fail) {
|
|
146
|
+
if (value === null)
|
|
147
|
+
return null;
|
|
148
|
+
return requireEnum(value, allowed, what, fail);
|
|
149
|
+
}
|
|
150
|
+
/** A `z.array(…)`. */
|
|
151
|
+
export function requireArray(value, what, fail) {
|
|
152
|
+
if (!Array.isArray(value))
|
|
153
|
+
throw fail(`${what} is not an array`);
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* A `z.array(…).default([])`: an OMITTED array is valid and materialises as
|
|
158
|
+
* `[]`; a present non-array is not.
|
|
159
|
+
*/
|
|
160
|
+
export function requireDefaultedArray(value, what, fail) {
|
|
161
|
+
if (value === undefined)
|
|
162
|
+
return [];
|
|
163
|
+
return requireArray(value, what, fail);
|
|
164
|
+
}
|
|
165
|
+
/** A `z.array(z.string()).min(1)` — non-empty, and every element a string. */
|
|
166
|
+
export function requireNonEmptyStringArray(value, what, fail) {
|
|
167
|
+
const items = requireArray(value, what, fail);
|
|
168
|
+
if (items.length === 0)
|
|
169
|
+
throw fail(`${what} is empty`);
|
|
170
|
+
return items.map((item, index) => requireString(item, `${what}[${index}]`, fail));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* A `z.json()` — a JSON value, recursively: `string | number | boolean | null`,
|
|
174
|
+
* an array of them, or an object of them, at ANY depth.
|
|
175
|
+
*
|
|
176
|
+
* WHY THIS EXISTS, given it can never fire over the wire. Every SDK response
|
|
177
|
+
* has already been through `JSON.parse`, so a `Date`, `bigint`, `undefined`, or
|
|
178
|
+
* function CANNOT be present by construction — this check is unreachable in
|
|
179
|
+
* practice. It is here for PARITY: the canonical `sourceImageAnalysisResultSchema`
|
|
180
|
+
* uses `z.json()` precisely because those four break differently on the way to
|
|
181
|
+
* `jsonb` (a `bigint` throws in `JSON.stringify`, an `undefined` is silently
|
|
182
|
+
* dropped, and a `Date` round-trips as an ISO *string* — a different type read
|
|
183
|
+
* back than written). The mirror has to reject what the schema rejects or the
|
|
184
|
+
* parity test is lying. Do not delete this as dead code.
|
|
185
|
+
*/
|
|
186
|
+
export function requireJsonValue(value, what, fail) {
|
|
187
|
+
if (value === null)
|
|
188
|
+
return null;
|
|
189
|
+
if (typeof value === "string" || typeof value === "boolean")
|
|
190
|
+
return value;
|
|
191
|
+
if (typeof value === "number") {
|
|
192
|
+
// `z.json()`'s number leaf rejects NaN and ±Infinity, and `1e999` parses out
|
|
193
|
+
// of JSON as `Infinity` — so finiteness IS reachable, unlike the rest.
|
|
194
|
+
if (!Number.isFinite(value))
|
|
195
|
+
throw fail(`${what} is not a finite number`);
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
if (Array.isArray(value)) {
|
|
199
|
+
return value.map((item, index) => requireJsonValue(item, `${what}[${index}]`, fail));
|
|
200
|
+
}
|
|
201
|
+
if (isRecord(value)) {
|
|
202
|
+
// A PLAIN object only. `isRecord` alone is not enough: it answers true for a
|
|
203
|
+
// `Date`, a `Map`, and any class instance, none of which have own enumerable
|
|
204
|
+
// entries — so recursing over them would silently accept them as `{}` while
|
|
205
|
+
// `z.json()` rejects them outright. A `Date` reaching `jsonb` is exactly the
|
|
206
|
+
// failure this check exists to stop.
|
|
207
|
+
const prototype = Object.getPrototypeOf(value);
|
|
208
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
209
|
+
throw fail(`${what} is not a JSON value`);
|
|
210
|
+
}
|
|
211
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
212
|
+
key,
|
|
213
|
+
requireJsonValue(item, `${what}.${key}`, fail),
|
|
214
|
+
]));
|
|
215
|
+
}
|
|
216
|
+
throw fail(`${what} is not a JSON value`);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* A `z.record(z.string(), z.json())` — a PLAIN JSON object of JSON values.
|
|
220
|
+
*
|
|
221
|
+
* Deliberately NOT `requireRecord` + walk the entries. That combination has a
|
|
222
|
+
* top-level hole: `Object.entries(new Date())` and `Object.entries(new Map())`
|
|
223
|
+
* are both `[]`, so there is nothing to recurse over and the value is accepted
|
|
224
|
+
* as `{}` — while `z.record(z.string(), z.json())` rejects both outright. A
|
|
225
|
+
* "non-array object" guard would close those two and still miss a class instance
|
|
226
|
+
* with real own properties, which canonical also rejects. Only a prototype test
|
|
227
|
+
* matches, and {@link requireJsonValue} already applies it at every depth — so
|
|
228
|
+
* this delegates there and then narrows to an object, rather than reimplementing
|
|
229
|
+
* the test and acquiring the same hole one level up.
|
|
230
|
+
*
|
|
231
|
+
* `requireRecord` is left alone: its other callers mirror `z.object({…})`, which
|
|
232
|
+
* genuinely does accept any record, and tightening a shared helper under them
|
|
233
|
+
* would be a change none of them asked for.
|
|
234
|
+
*/
|
|
235
|
+
export function requireJsonRecord(value, what, fail) {
|
|
236
|
+
const json = requireJsonValue(value, what, fail);
|
|
237
|
+
if (json === null || typeof json !== "object" || Array.isArray(json)) {
|
|
238
|
+
throw fail(`${what} is not a JSON object`);
|
|
239
|
+
}
|
|
240
|
+
return json;
|
|
241
|
+
}
|