@oh-my-pi/omptype 17.2.6 → 17.2.7

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.
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Validation error containers mirroring ArkType's observable error surface:
3
+ * `result instanceof type.errors` / `instanceof OmpErrors`, lazy `.summary`,
4
+ * array iteration, and per-entry `.path` / `.problem` / `.message`.
5
+ *
6
+ * Failure-path cost matters: schemas reject untrusted input constantly, so
7
+ * construction stores only the path, the expectation, and the offending value.
8
+ * All human-readable strings are built lazily on property access.
9
+ */
10
+ function format(override, context, fallback) {
11
+ return typeof override === "function" ? override(context) : (override ?? fallback);
12
+ }
13
+ /** A single validation failure at one path. */
14
+ export class OmpError {
15
+ path;
16
+ data;
17
+ #rawExpected;
18
+ #config;
19
+ constructor(
20
+ /** Property path from the root to the failing value (empty at root). */
21
+ path, expected,
22
+ /** The value that failed validation. */
23
+ data, config) {
24
+ this.path = path;
25
+ this.data = data;
26
+ this.#rawExpected = expected;
27
+ this.#config = config;
28
+ }
29
+ /** Stable category for programmatic error handling. */
30
+ get code() {
31
+ return errorCode(this.#rawExpected, this.data);
32
+ }
33
+ #context(expected, actual, problem = "") {
34
+ return { code: this.code, path: this.path, data: this.data, expected, actual, problem };
35
+ }
36
+ /** Human-readable expectation, including a configured override. */
37
+ get expected() {
38
+ const actual = describeValue(this.data);
39
+ return format(this.#config?.expected, this.#context(this.#rawExpected, actual), this.#rawExpected);
40
+ }
41
+ /** Short description of the received value, e.g. `"a number"` or `"missing"`. */
42
+ get actual() {
43
+ const actual = describeValue(this.data);
44
+ return format(this.#config?.actual, this.#context(this.expected, actual), actual);
45
+ }
46
+ /** Path-less problem statement: `must be <expected> (was <actual>)`. */
47
+ get problem() {
48
+ const expected = this.expected;
49
+ const actual = this.actual;
50
+ const fallback = this.data === MISSING ? `must be ${expected} (was missing)` : `must be ${expected} (was ${actual})`;
51
+ return format(this.#config?.problem, this.#context(expected, actual, fallback), fallback);
52
+ }
53
+ /** Full message including the path prefix. */
54
+ get message() {
55
+ const expected = this.expected;
56
+ const actual = this.actual;
57
+ const problem = this.problem;
58
+ const at = this.path.length === 0 ? "" : `${this.path.map(String).join(".")} `;
59
+ return format(this.#config?.message, this.#context(expected, actual, problem), `${at}${problem}`);
60
+ }
61
+ toString() {
62
+ return this.message;
63
+ }
64
+ }
65
+ /** Sentinel for a required key that was absent (distinguishes from `undefined`). */
66
+ export const MISSING = Symbol("omptype.missing");
67
+ function describeValue(data) {
68
+ if (data === null)
69
+ return "null";
70
+ if (Array.isArray(data))
71
+ return "an array";
72
+ switch (typeof data) {
73
+ case "string":
74
+ return data.length <= 40 ? JSON.stringify(data) : `a string (length ${data.length})`;
75
+ case "number":
76
+ return String(data);
77
+ case "bigint":
78
+ return `${data}n`;
79
+ case "boolean":
80
+ return String(data);
81
+ case "undefined":
82
+ return "undefined";
83
+ case "object":
84
+ return "an object";
85
+ case "function":
86
+ return "a function";
87
+ default:
88
+ return "a symbol";
89
+ }
90
+ }
91
+ function errorCode(expected, data) {
92
+ if (data === MISSING)
93
+ return "required";
94
+ if (expected.includes("divisible by"))
95
+ return "divisor";
96
+ if (expected.includes("at least") || expected.includes("more than"))
97
+ return "min";
98
+ if (expected.includes("at most") || expected.includes("less than"))
99
+ return "max";
100
+ if (expected.includes("matching") || expected.includes("format") || expected.includes("email"))
101
+ return "pattern";
102
+ if (expected.includes("predicate") || expected.includes("satisfying"))
103
+ return "predicate";
104
+ if (expected.startsWith('"') || expected.startsWith("the date "))
105
+ return "unit";
106
+ return "domain";
107
+ }
108
+ export class OmpErrors {
109
+ #path;
110
+ #expected;
111
+ #data;
112
+ #entry;
113
+ #config;
114
+ /** Number of failures; omptype validators fast-fail on the first error. */
115
+ length = 1;
116
+ constructor(path, expected, data, config) {
117
+ this.#path = path;
118
+ this.#expected = expected;
119
+ this.#data = data;
120
+ this.#config = config;
121
+ }
122
+ /** First and only validation failure, materialized on demand. */
123
+ get 0() {
124
+ return this.#getEntry();
125
+ }
126
+ static single(path, expected, data, config) {
127
+ return new OmpErrors(path, expected, data, config);
128
+ }
129
+ #getEntry() {
130
+ if (this.#entry)
131
+ return this.#entry;
132
+ const path = this.#path === undefined ? [] : Array.isArray(this.#path) ? [...this.#path] : [this.#path];
133
+ const entry = new OmpError(path, this.#expected, this.#data, this.#config);
134
+ this.#entry = entry;
135
+ return entry;
136
+ }
137
+ /** Prefix the failure path with `key` when nesting sub-schemas. */
138
+ prefix(key) {
139
+ const path = this.#path;
140
+ this.#path = path === undefined ? [key] : Array.isArray(path) ? [key, ...path] : [key, path];
141
+ this.#entry = undefined;
142
+ return this;
143
+ }
144
+ /** Apply schema-local message formatting without rebuilding the failure. */
145
+ configure(config) {
146
+ this.#config = { ...this.#config, ...config };
147
+ this.#entry = undefined;
148
+ return this;
149
+ }
150
+ /** Index the failure by its dotted property path (`""` for the root). */
151
+ get byPath() {
152
+ const entry = this.#getEntry();
153
+ return { [entry.path.map(String).join(".")]: entry };
154
+ }
155
+ /** Transform the failure entry into a plain array. */
156
+ map(fn) {
157
+ return [fn(this.#getEntry(), 0, this)];
158
+ }
159
+ /** Select the failure entry into a plain array. */
160
+ filter(fn) {
161
+ const entry = this.#getEntry();
162
+ return fn(entry, 0, this) ? [entry] : [];
163
+ }
164
+ /** Iterate over the single failure entry. */
165
+ *[Symbol.iterator]() {
166
+ yield this.#getEntry();
167
+ }
168
+ /** Human-readable failure text, materialized only when requested. */
169
+ get summary() {
170
+ return this.#getEntry().message;
171
+ }
172
+ toString() {
173
+ return this.summary;
174
+ }
175
+ /** Throw a `TraversalError` carrying this result. */
176
+ throw() {
177
+ throw new TraversalError(this);
178
+ }
179
+ }
180
+ /** Error thrown by `Type.assert` on invalid input. */
181
+ export class TraversalError extends Error {
182
+ errors;
183
+ constructor(errors) {
184
+ super(errors.summary);
185
+ this.errors = errors;
186
+ this.name = "TraversalError";
187
+ }
188
+ }
189
+ /**
190
+ * Definition/usage error thrown while building a schema — malformed string
191
+ * DSL, unsupported composition, or an illegal builder call. Distinct from
192
+ * validation failures, which are returned as {@link OmpErrors}.
193
+ */
194
+ export class OmpTypeError extends Error {
195
+ constructor(message) {
196
+ super(message);
197
+ this.name = "OmpTypeError";
198
+ }
199
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * omptype — ArkType-compatible schema validation with a lazy JIT runtime.
3
+ *
4
+ * ArkType-compatible `type()`/`Type`, keyword modules, recursive scopes,
5
+ * composition and morph APIs, structured errors, input/output inference, and
6
+ * JSON Schema emission.
7
+ */
8
+ export * from "./errors.js";
9
+ export * from "./infer.js";
10
+ export * from "./ir.js";
11
+ export * from "./json-schema.js";
12
+ export * from "./type.js";
@@ -0,0 +1,2 @@
1
+ /** Type-level input and output inference for definitions accepted by omptype. */
2
+ export {};
@@ -0,0 +1,476 @@
1
+ /**
2
+ * Tree-walking validator used for a schema's first few calls and as the
3
+ * targeted fallback for recursive or predicate-only JIT subtrees.
4
+ *
5
+ * Semantics must stay in lockstep with `compile.ts`:
6
+ * - success returns the output value; the input is returned as-is unless the
7
+ * schema morphs (defaults, `"+": "delete"`, embedded stepped schemas), in
8
+ * which case a fresh object/array is produced and the input is untouched
9
+ * - failure returns an `OmpErrors` with a single fast-fail entry
10
+ */
11
+ import { MISSING, OmpErrors } from "./errors.js";
12
+ import { expectedOf, hasMorph } from "./ir.js";
13
+ const own = Object.prototype.hasOwnProperty;
14
+ /** Validate `value` against `ir`; returns output value or `OmpErrors`. */
15
+ export function walk(ir, value) {
16
+ const path = [];
17
+ const out = visit(ir, value, path);
18
+ return out;
19
+ }
20
+ function fail(path, expected, data) {
21
+ const storedPath = path.length === 0 ? undefined : path.length === 1 ? path[0] : [...path];
22
+ return new OmpErrors(storedPath, expected, data);
23
+ }
24
+ /** Pure predicate used for union-member scanning (no morphs, no errors). */
25
+ function checks(ir, v) {
26
+ switch (ir.k) {
27
+ case "unknown":
28
+ return true;
29
+ case "null":
30
+ return v === null;
31
+ case "undefined":
32
+ return v === undefined;
33
+ case "boolean":
34
+ return typeof v === "boolean";
35
+ case "bigint":
36
+ return typeof v === "bigint";
37
+ case "symbol":
38
+ return typeof v === "symbol";
39
+ case "never":
40
+ return false;
41
+ case "anyobject":
42
+ return typeof v === "object" && v !== null;
43
+ case "string":
44
+ return (typeof v === "string" &&
45
+ (ir.min === undefined || v.length >= ir.min) &&
46
+ (ir.max === undefined || v.length <= ir.max) &&
47
+ (!ir.url || URL.canParse(v)));
48
+ case "number":
49
+ if (typeof v !== "number")
50
+ return false;
51
+ return ((ir.int ? Number.isInteger(v) : Number.isFinite(v)) &&
52
+ (ir.divisor === undefined || v % ir.divisor === 0) &&
53
+ (ir.min === undefined || (ir.xmin ? v > ir.min : v >= ir.min)) &&
54
+ (ir.max === undefined || (ir.xmax ? v < ir.max : v <= ir.max)));
55
+ case "lit":
56
+ return ir.v instanceof Date ? v instanceof Date && v.valueOf() === ir.v.valueOf() : v === ir.v;
57
+ case "union":
58
+ return ir.members.some(m => checks(m, v));
59
+ case "intersection":
60
+ return ir.members.every(member => checks(member, v));
61
+ case "array": {
62
+ if (!Array.isArray(v))
63
+ return false;
64
+ if (ir.min !== undefined && v.length < ir.min)
65
+ return false;
66
+ if (ir.max !== undefined && v.length > ir.max)
67
+ return false;
68
+ for (const el of v)
69
+ if (!checks(ir.el, el))
70
+ return false;
71
+ return true;
72
+ }
73
+ case "tuple": {
74
+ if (!Array.isArray(v))
75
+ return false;
76
+ let required = ir.postfix.length;
77
+ for (const item of ir.prefix)
78
+ if (!item.opt && !item.hasDefault)
79
+ required++;
80
+ if (v.length < required)
81
+ return false;
82
+ if (ir.variadic === undefined && v.length > ir.prefix.length + ir.postfix.length)
83
+ return false;
84
+ const postfixStart = v.length - ir.postfix.length;
85
+ const prefixCount = Math.min(ir.prefix.length, postfixStart);
86
+ for (let index = 0; index < prefixCount; index++) {
87
+ if (!checks(ir.prefix[index].val, v[index]))
88
+ return false;
89
+ }
90
+ for (let index = prefixCount; index < ir.prefix.length; index++) {
91
+ const item = ir.prefix[index];
92
+ if (!item.opt && !item.hasDefault)
93
+ return false;
94
+ }
95
+ if (ir.variadic !== undefined) {
96
+ for (let index = prefixCount; index < postfixStart; index++) {
97
+ if (!checks(ir.variadic, v[index]))
98
+ return false;
99
+ }
100
+ }
101
+ for (let index = 0; index < ir.postfix.length; index++) {
102
+ if (!checks(ir.postfix[index], v[postfixStart + index]))
103
+ return false;
104
+ }
105
+ return true;
106
+ }
107
+ case "object": {
108
+ if (typeof v !== "object" || v === null || Array.isArray(v))
109
+ return false;
110
+ const rec = v;
111
+ for (const p of ir.props) {
112
+ const present = p.key in rec;
113
+ if (!present) {
114
+ if (!p.opt && !p.hasDefault)
115
+ return false;
116
+ continue;
117
+ }
118
+ if (!checks(p.val, rec[p.key]))
119
+ return false;
120
+ }
121
+ if (ir.index) {
122
+ for (const key in rec) {
123
+ if (own.call(rec, key) && !checks(ir.index, rec[key]))
124
+ return false;
125
+ }
126
+ }
127
+ else if (ir.extras === "reject") {
128
+ for (const key in rec) {
129
+ if (!own.call(rec, key))
130
+ continue;
131
+ let declared = false;
132
+ for (const p of ir.props) {
133
+ if (p.key === key) {
134
+ declared = true;
135
+ break;
136
+ }
137
+ }
138
+ if (!declared)
139
+ return false;
140
+ }
141
+ }
142
+ return true;
143
+ }
144
+ case "instance":
145
+ return v instanceof ir.ctor;
146
+ case "refine":
147
+ if (!checks(ir.base, v))
148
+ return false;
149
+ try {
150
+ return ir.pred(v);
151
+ }
152
+ catch {
153
+ return false;
154
+ }
155
+ case "alias":
156
+ return checks(ir.resolve(), v);
157
+ case "morph":
158
+ return checks(ir.input, v);
159
+ case "sub":
160
+ return !(ir.schema.run(v) instanceof OmpErrors);
161
+ }
162
+ }
163
+ function visit(ir, v, path) {
164
+ switch (ir.k) {
165
+ case "alias":
166
+ return visit(ir.resolve(), v, path);
167
+ case "refine": {
168
+ const base = visit(ir.base, v, path);
169
+ if (base instanceof OmpErrors)
170
+ return base;
171
+ try {
172
+ return ir.pred(base) ? base : fail(path, ir.expected, base);
173
+ }
174
+ catch {
175
+ return fail(path, ir.expected, base);
176
+ }
177
+ }
178
+ case "morph": {
179
+ const input = visit(ir.input, v, path);
180
+ if (input instanceof OmpErrors)
181
+ return input;
182
+ const context = {
183
+ error: (expected, data = input) => fail(path, expected, data),
184
+ reject: (problem, data = input) => fail(path, problem, data),
185
+ };
186
+ const output = ir.fn(input, context);
187
+ if (output instanceof OmpErrors)
188
+ return output;
189
+ return ir.out === undefined ? output : visit(ir.out, output, path);
190
+ }
191
+ case "intersection": {
192
+ let output = v;
193
+ for (const member of ir.members) {
194
+ output = visit(member, output, path);
195
+ if (output instanceof OmpErrors)
196
+ return output;
197
+ }
198
+ return output;
199
+ }
200
+ case "sub": {
201
+ const out = ir.schema.run(v);
202
+ if (out instanceof OmpErrors) {
203
+ return path.length === 0 ? out : prefixAll(out, path);
204
+ }
205
+ return out;
206
+ }
207
+ case "union": {
208
+ // fast path: any pure member matching returns the input unchanged
209
+ for (const m of ir.members) {
210
+ if (m.k !== "sub" && checks(m, v)) {
211
+ if (hasMorph(m))
212
+ break;
213
+ return v;
214
+ }
215
+ }
216
+ for (const m of ir.members) {
217
+ if (m.k === "sub" || hasMorph(m)) {
218
+ const out = visit(m, v, path);
219
+ if (!(out instanceof OmpErrors))
220
+ return out;
221
+ }
222
+ }
223
+ return ir.members.some(canRefineUnionFailure) ? unionFail(ir, v, path) : fail(path, expectedOf(ir), v);
224
+ }
225
+ case "array": {
226
+ if (!Array.isArray(v))
227
+ return fail(path, "an array", v);
228
+ if (ir.min !== undefined && v.length < ir.min)
229
+ return fail(path, `at least length ${ir.min}`, v);
230
+ if (ir.max !== undefined && v.length > ir.max)
231
+ return fail(path, `at most length ${ir.max}`, v);
232
+ if (!hasMorph(ir.el)) {
233
+ for (let i = 0; i < v.length; i++) {
234
+ if (!checks(ir.el, v[i])) {
235
+ path.push(i);
236
+ const err = visit(ir.el, v[i], path);
237
+ path.pop();
238
+ return err instanceof OmpErrors ? err : fail([...path, i], expectedOf(ir.el), v[i]);
239
+ }
240
+ }
241
+ return v;
242
+ }
243
+ const out = new Array(v.length);
244
+ for (let i = 0; i < v.length; i++) {
245
+ path.push(i);
246
+ const el = visit(ir.el, v[i], path);
247
+ path.pop();
248
+ if (el instanceof OmpErrors)
249
+ return el;
250
+ out[i] = el;
251
+ }
252
+ return out;
253
+ }
254
+ case "tuple": {
255
+ if (!Array.isArray(v))
256
+ return fail(path, "an array", v);
257
+ let required = ir.postfix.length;
258
+ for (const item of ir.prefix)
259
+ if (!item.opt && !item.hasDefault)
260
+ required++;
261
+ if (v.length < required)
262
+ return fail(path, `an array of at least length ${required}`, v);
263
+ const maximum = ir.prefix.length + ir.postfix.length;
264
+ if (ir.variadic === undefined && v.length > maximum) {
265
+ return fail(path, `an array of at most length ${maximum}`, v);
266
+ }
267
+ const postfixStart = v.length - ir.postfix.length;
268
+ const prefixCount = Math.min(ir.prefix.length, postfixStart);
269
+ const morph = hasMorph(ir);
270
+ const output = morph ? [...v] : v;
271
+ for (let index = 0; index < prefixCount; index++) {
272
+ path.push(index);
273
+ const item = visit(ir.prefix[index].val, v[index], path);
274
+ path.pop();
275
+ if (item instanceof OmpErrors)
276
+ return item;
277
+ if (morph)
278
+ output[index] = item;
279
+ }
280
+ for (let index = prefixCount; index < ir.prefix.length; index++) {
281
+ const item = ir.prefix[index];
282
+ if (item.hasDefault && morph) {
283
+ const payload = item.def;
284
+ output[index] = item.defFactory && typeof payload === "function" ? payload() : payload;
285
+ }
286
+ else if (!item.opt) {
287
+ path.push(index);
288
+ const error = fail(path, expectedOf(item.val), MISSING);
289
+ path.pop();
290
+ return error;
291
+ }
292
+ }
293
+ if (ir.variadic !== undefined) {
294
+ for (let index = prefixCount; index < postfixStart; index++) {
295
+ path.push(index);
296
+ const item = visit(ir.variadic, v[index], path);
297
+ path.pop();
298
+ if (item instanceof OmpErrors)
299
+ return item;
300
+ if (morph)
301
+ output[index] = item;
302
+ }
303
+ }
304
+ for (let index = 0; index < ir.postfix.length; index++) {
305
+ const inputIndex = postfixStart + index;
306
+ path.push(inputIndex);
307
+ const item = visit(ir.postfix[index], v[inputIndex], path);
308
+ path.pop();
309
+ if (item instanceof OmpErrors)
310
+ return item;
311
+ if (morph)
312
+ output[inputIndex] = item;
313
+ }
314
+ return output;
315
+ }
316
+ case "object": {
317
+ if (typeof v !== "object" || v === null || Array.isArray(v))
318
+ return fail(path, "an object", v);
319
+ const rec = v;
320
+ const morph = hasMorph(ir);
321
+ let out;
322
+ if (morph) {
323
+ if (ir.extras === "delete" && !ir.index) {
324
+ out = {};
325
+ }
326
+ else {
327
+ out = { ...rec };
328
+ }
329
+ }
330
+ for (const p of ir.props) {
331
+ if (!(p.key in rec)) {
332
+ if (p.hasDefault && out) {
333
+ // defFactory guarantees a callable default payload
334
+ const payload = p.def;
335
+ out[p.key] = p.defFactory && typeof payload === "function" ? payload() : payload;
336
+ continue;
337
+ }
338
+ if (p.opt || p.hasDefault)
339
+ continue;
340
+ path.push(p.key);
341
+ const err = fail(path, expectedOf(p.val), MISSING);
342
+ path.pop();
343
+ return err;
344
+ }
345
+ path.push(p.key);
346
+ const res = visit(p.val, rec[p.key], path);
347
+ path.pop();
348
+ if (res instanceof OmpErrors)
349
+ return res;
350
+ if (out)
351
+ out[p.key] = res;
352
+ }
353
+ if (ir.index) {
354
+ for (const key in rec) {
355
+ if (!own.call(rec, key))
356
+ continue;
357
+ path.push(key);
358
+ const res = visit(ir.index, rec[key], path);
359
+ path.pop();
360
+ if (res instanceof OmpErrors)
361
+ return res;
362
+ if (out)
363
+ out[key] = res;
364
+ }
365
+ }
366
+ else if (ir.extras === "reject") {
367
+ for (const key in rec) {
368
+ if (!own.call(rec, key))
369
+ continue;
370
+ let declared = false;
371
+ for (const p of ir.props) {
372
+ if (p.key === key) {
373
+ declared = true;
374
+ break;
375
+ }
376
+ }
377
+ if (!declared) {
378
+ path.push(key);
379
+ const err = fail(path, "removed (undeclared key)", rec[key]);
380
+ path.pop();
381
+ return err;
382
+ }
383
+ }
384
+ }
385
+ return out ?? v;
386
+ }
387
+ default:
388
+ return checks(ir, v) ? v : fail(path, expectedOf(ir), v);
389
+ }
390
+ }
391
+ function prefixAll(errs, path) {
392
+ for (let i = path.length - 1; i >= 0; i--)
393
+ errs.prefix(path[i]);
394
+ return errs;
395
+ }
396
+ /** True when a union failure can be replaced with a more specific nested error. */
397
+ export function canRefineUnionFailure(member) {
398
+ const base = member.k === "sub" ? member.schema.ir : member;
399
+ if (member.k === "sub") {
400
+ return (base.k === "array" ||
401
+ base.k === "object" ||
402
+ base.k === "anyobject" ||
403
+ base.k === "string" ||
404
+ base.k === "number");
405
+ }
406
+ if (base.k === "array" || base.k === "object")
407
+ return true;
408
+ if (base.k === "string")
409
+ return base.min !== undefined || base.max !== undefined || base.url === true;
410
+ return base.k === "number" && (base.int === true || base.min !== undefined || base.max !== undefined);
411
+ }
412
+ /**
413
+ * Detailed failure for a union: descend into the member the value was clearly
414
+ * aimed at — unique runtime-kind match, else an object member whose literal
415
+ * discriminant property (e.g. `type: "'computer_call'"`) equals the value's —
416
+ * for a precise nested error (paths, narrow messages) instead of the coarse
417
+ * "A or B" expectation.
418
+ */
419
+ export function unionFail(ir, v, path, expected) {
420
+ let best;
421
+ for (const m of ir.members) {
422
+ const base = m.k === "sub" ? m.schema.ir : m;
423
+ if (!kindMatches(base, v))
424
+ continue;
425
+ if (best !== undefined) {
426
+ best = undefined;
427
+ break;
428
+ }
429
+ best = m;
430
+ }
431
+ if (best === undefined)
432
+ best = discriminate(ir.members, v);
433
+ if (best) {
434
+ const out = visit(best, v, path);
435
+ if (out instanceof OmpErrors)
436
+ return out;
437
+ }
438
+ return fail(path, expected ?? expectedOf(ir), v);
439
+ }
440
+ /** Pick the sole object member whose literal-typed property matches the value's. */
441
+ function discriminate(members, v) {
442
+ if (typeof v !== "object" || v === null || Array.isArray(v))
443
+ return undefined;
444
+ const rec = v;
445
+ let match;
446
+ for (const m of members) {
447
+ const base = m.k === "sub" ? m.schema.ir : m;
448
+ if (base.k !== "object")
449
+ continue;
450
+ for (const p of base.props) {
451
+ if (p.val.k !== "lit" || rec[p.key] !== p.val.v)
452
+ continue;
453
+ if (match !== undefined)
454
+ return undefined; // ambiguous
455
+ match = m;
456
+ break;
457
+ }
458
+ }
459
+ return match;
460
+ }
461
+ /** True when a value's runtime shape could only be aimed at this member. */
462
+ function kindMatches(base, v) {
463
+ switch (base.k) {
464
+ case "array":
465
+ return Array.isArray(v);
466
+ case "object":
467
+ case "anyobject":
468
+ return typeof v === "object" && v !== null && !Array.isArray(v);
469
+ case "string":
470
+ return typeof v === "string";
471
+ case "number":
472
+ return typeof v === "number";
473
+ default:
474
+ return false;
475
+ }
476
+ }