functionalscript 0.46.1 → 0.47.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/fjs/basen/cbase32/module.f.mjs +18 -10
- package/fjs/basen/cbase32/proof.f.d.mts +3 -0
- package/fjs/basen/cbase32/proof.f.mjs +24 -2
- package/fjs/basen/proof.f.mjs +8 -16
- package/fjs/bnf/ll1/proof.f.d.mts +2 -0
- package/fjs/bnf/ll1/proof.f.mjs +33 -18
- package/fjs/bnf/module.f.d.mts +2 -2
- package/fjs/bnf/module.f.mjs +2 -2
- package/fjs/cas/proof.f.mjs +4 -26
- package/fjs/ci/common/module.f.mjs +6 -0
- package/fjs/ci/config/module.f.d.mts +7 -7
- package/fjs/ci/config/module.f.mjs +7 -7
- package/fjs/cli/module.f.mjs +14 -10
- package/fjs/crypto/vdf/module.f.d.mts +0 -1
- package/fjs/crypto/vdf/module.f.mjs +3 -14
- package/fjs/dev/module.f.mjs +10 -20
- package/fjs/djs/parser/module.f.d.mts +101 -53
- package/fjs/djs/parser/module.f.mjs +732 -525
- package/fjs/djs/parser/proof.f.d.mts +6 -0
- package/fjs/djs/parser/proof.f.mjs +315 -11
- package/fjs/djs/parser/types.d.ts +29 -2
- package/fjs/djs/tokenizer/module.f.mjs +122 -105
- package/fjs/djs/tokenizer/proof.f.mjs +17 -0
- package/fjs/djs/transpiler/module.f.mjs +4 -5
- package/fjs/djs/transpiler/proof.f.d.mts +2 -0
- package/fjs/djs/transpiler/proof.f.mjs +25 -0
- package/fjs/edag/amnesia/module.f.d.mts +20 -0
- package/fjs/edag/amnesia/module.f.mjs +303 -0
- package/fjs/edag/amnesia/proof.f.d.mts +79 -0
- package/fjs/edag/amnesia/proof.f.mjs +573 -0
- package/fjs/edag/amnesia/types.d.ts +15 -0
- package/fjs/edag/module.f.d.mts +410 -0
- package/fjs/edag/module.f.mjs +559 -0
- package/fjs/edag/proof.f.d.mts +116 -0
- package/fjs/edag/proof.f.mjs +686 -0
- package/fjs/edag/types.d.ts +55 -0
- package/fjs/effects/node/memory/module.d.mts +37 -3
- package/fjs/effects/node/memory/module.mjs +38 -4
- package/fjs/effects/node/memory/proof.d.mts +5 -2
- package/fjs/effects/node/memory/proof.mjs +36 -6
- package/fjs/effects/node/module.d.mts +9 -1
- package/fjs/effects/node/module.f.d.mts +39 -0
- package/fjs/effects/node/module.f.mjs +46 -0
- package/fjs/effects/node/module.mjs +234 -32
- package/fjs/effects/node/types.d.ts +28 -2
- package/fjs/effects/node/virtual/module.f.d.mts +19 -10
- package/fjs/effects/node/virtual/module.f.mjs +246 -32
- package/fjs/effects/node/virtual/proof.f.d.mts +17 -2
- package/fjs/effects/node/virtual/proof.f.mjs +232 -8
- package/fjs/effects/node/virtual/types.d.ts +57 -1
- package/fjs/emergent_testing/browser/proof.d.mts +55 -0
- package/fjs/emergent_testing/browser/proof.mjs +352 -0
- package/fjs/emergent_testing/browser/species.proof.d.mts +4 -0
- package/fjs/emergent_testing/browser/species.proof.mjs +45 -0
- package/fjs/emergent_testing/browser.d.mts +79 -0
- package/fjs/emergent_testing/browser.mjs +432 -0
- package/fjs/emergent_testing/proof.f.d.mts +1 -1
- package/fjs/emergent_testing/proof.f.mjs +2 -2
- package/fjs/js/string_escape/module.f.d.mts +47 -0
- package/fjs/js/string_escape/module.f.mjs +95 -0
- package/fjs/js/string_escape/proof.f.d.mts +8 -0
- package/fjs/js/string_escape/proof.f.mjs +68 -0
- package/fjs/js/tokenizer/module.f.mjs +14 -12
- package/fjs/mcp/cas/module.f.d.mts +1 -7
- package/fjs/mcp/cas/module.f.mjs +24 -15
- package/fjs/mcp/evo/module.f.mjs +9 -24
- package/fjs/mcp/module.f.d.mts +3 -1
- package/fjs/mcp/module.f.mjs +4 -2
- package/fjs/media/json/rtti/module.f.d.mts +2 -2
- package/fjs/media/json/rtti/module.f.mjs +1 -1
- package/fjs/media/json/schema/module.f.d.mts +15 -5
- package/fjs/media/json/schema/module.f.mjs +42 -15
- package/fjs/media/json/schema/proof.f.d.mts +10 -1
- package/fjs/media/json/schema/proof.f.mjs +88 -16
- package/fjs/media/json/serializer/module.f.mjs +14 -26
- package/fjs/media/json/types.d.ts +1 -1
- package/fjs/media/lock/module.f.d.mts +8 -3
- package/fjs/media/lock/module.f.mjs +9 -3
- package/fjs/media/lock/proof.f.d.mts +1 -1
- package/fjs/media/lock/proof.f.mjs +2 -2
- package/fjs/media/lock/types.d.ts +1 -3
- package/fjs/media/module.f.d.mts +20 -13
- package/fjs/media/module.f.mjs +29 -16
- package/fjs/media/note/module.f.d.mts +14 -7
- package/fjs/media/note/module.f.mjs +14 -7
- package/fjs/media/note/proof.f.mjs +1 -1
- package/fjs/media/proof.f.mjs +5 -5
- package/fjs/media/revision/module.f.d.mts +8 -2
- package/fjs/media/revision/module.f.mjs +9 -3
- package/fjs/media/revision/proof.f.mjs +1 -1
- package/fjs/media/revision/types.d.ts +1 -1
- package/fjs/media/type/module.f.d.mts +20 -0
- package/fjs/media/type/module.f.mjs +74 -0
- package/fjs/media/type/proof.f.d.mts +5 -0
- package/fjs/media/type/proof.f.mjs +24 -1
- package/fjs/module.f.mjs +6 -0
- package/fjs/path/module.f.d.mts +60 -3
- package/fjs/path/module.f.mjs +138 -14
- package/fjs/path/proof.f.d.mts +3 -0
- package/fjs/path/proof.f.mjs +298 -2
- package/fjs/protocol/json_rpc/module.f.d.mts +65 -28
- package/fjs/protocol/json_rpc/module.f.mjs +49 -13
- package/fjs/protocol/json_rpc/proof.f.d.mts +4 -0
- package/fjs/protocol/json_rpc/proof.f.mjs +29 -0
- package/fjs/protocol/json_rpc/types.d.ts +12 -2
- package/fjs/protocol/mcp/module.f.d.mts +89 -54
- package/fjs/protocol/mcp/module.f.mjs +110 -56
- package/fjs/protocol/mcp/proof.f.d.mts +13 -0
- package/fjs/protocol/mcp/proof.f.mjs +107 -7
- package/fjs/protocol/mcp/stdio/module.f.mjs +3 -4
- package/fjs/protocol/mcp/stdio/proof.f.mjs +2 -2
- package/fjs/protocol/mcp/types.d.ts +14 -2
- package/fjs/text/utf8/module.f.mjs +27 -1
- package/fjs/text/utf8/proof.f.mjs +37 -0
- package/fjs/types/array/module.f.mjs +18 -11
- package/fjs/types/array/proof.f.mjs +27 -1
- package/fjs/types/bigfloat/module.f.d.mts +48 -9
- package/fjs/types/bigfloat/module.f.mjs +147 -59
- package/fjs/types/bigfloat/proof.f.d.mts +2 -0
- package/fjs/types/bigfloat/proof.f.mjs +136 -4
- package/fjs/types/bigfloat/types.d.ts +17 -0
- package/fjs/types/function/module.f.d.mts +11 -2
- package/fjs/types/function/module.f.mjs +20 -2
- package/fjs/types/function/proof.f.mjs +5 -1
- package/fjs/types/phantom/types.d.ts +21 -0
- package/fjs/types/result/module.f.d.mts +4 -4
- package/fjs/types/result/module.f.mjs +2 -2
- package/fjs/types/result/proof.f.d.mts +6 -2
- package/fjs/types/result/proof.f.mjs +17 -1
- package/fjs/types/rtti/common/module.f.d.mts +78 -5
- package/fjs/types/rtti/common/module.f.mjs +168 -3
- package/fjs/types/rtti/common/proof.f.d.mts +14 -0
- package/fjs/types/rtti/common/proof.f.mjs +61 -2
- package/fjs/types/rtti/common/types.d.ts +23 -1
- package/fjs/types/rtti/data/module.f.d.mts +57 -1
- package/fjs/types/rtti/data/module.f.mjs +241 -47
- package/fjs/types/rtti/data/proof.f.d.mts +10 -0
- package/fjs/types/rtti/data/proof.f.mjs +272 -39
- package/fjs/types/rtti/data/types.d.ts +14 -7
- package/fjs/types/rtti/host.proof.d.mts +33 -0
- package/fjs/types/rtti/host.proof.mjs +170 -0
- package/fjs/types/rtti/module.f.d.mts +51 -6
- package/fjs/types/rtti/module.f.mjs +50 -3
- package/fjs/types/rtti/parse/module.f.d.mts +31 -24
- package/fjs/types/rtti/parse/module.f.mjs +197 -59
- package/fjs/types/rtti/parse/proof.f.d.mts +37 -2
- package/fjs/types/rtti/parse/proof.f.mjs +171 -47
- package/fjs/types/rtti/proof.f.d.mts +6 -0
- package/fjs/types/rtti/proof.f.mjs +42 -1
- package/fjs/types/rtti/ts/module.f.d.mts +9 -3
- package/fjs/types/rtti/ts/module.f.mjs +68 -15
- package/fjs/types/rtti/ts/proof.f.d.mts +42 -2
- package/fjs/types/rtti/ts/proof.f.mjs +139 -6
- package/fjs/types/rtti/ts/types.d.ts +164 -16
- package/fjs/types/rtti/types.d.ts +31 -3
- package/fjs/types/rtti/validate/module.f.d.mts +42 -25
- package/fjs/types/rtti/validate/module.f.mjs +187 -53
- package/fjs/types/rtti/validate/proof.f.d.mts +28 -2
- package/fjs/types/rtti/validate/proof.f.mjs +430 -75
- package/fjs/types/ts/types.d.ts +4 -0
- package/fjs/web/module.f.d.mts +115 -0
- package/fjs/web/module.f.mjs +581 -0
- package/fjs/web/proof.f.d.mts +39 -0
- package/fjs/web/proof.f.mjs +413 -0
- package/fjs/web/types.d.ts +39 -0
- package/fjs/website/browser-prepare.d.mts +5 -0
- package/fjs/website/browser-prepare.mjs +115 -0
- package/fjs/website/browser-source.d.mts +37 -0
- package/fjs/website/browser-source.mjs +202 -0
- package/fjs/website/browser-source.proof.d.mts +31 -0
- package/fjs/website/browser-source.proof.mjs +162 -0
- package/fjs/website/browser.d.mts +5 -0
- package/fjs/website/browser.mjs +8 -0
- package/fjs/website/module.f.d.mts +3 -3
- package/fjs/website/module.f.mjs +62 -9
- package/fjs/website/proof.f.d.mts +4 -0
- package/fjs/website/proof.f.mjs +18 -3
- package/package.json +2 -3
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import { assert, assertNotNullish } from '../../../asserts/module.f.mjs'
|
|
26
26
|
import { at, definedEntries, definedValues } from '../../object/module.f.mjs'
|
|
27
27
|
import { ok } from '../../result/module.f.mjs'
|
|
28
|
-
import { eachEntry, isArray, verror } from '../common/module.f.mjs'
|
|
28
|
+
import { eachEntry, isArray, undeclaredMembers, verror } from '../common/module.f.mjs'
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* The unit kind's enumeration: bit `1 << i` of a {@link UnionSet}'s `unit`
|
|
@@ -286,33 +286,76 @@ const isNever = n => typeof n !== 'string' && cmpUnion(n, never) === 0
|
|
|
286
286
|
/** @type {(n: Node) => boolean} */
|
|
287
287
|
const isTop = n => typeof n !== 'string' && cmpUnion(n, unknown) === 0
|
|
288
288
|
|
|
289
|
+
/**
|
|
290
|
+
* The prefix with its redundant tail removed: a last position stating exactly
|
|
291
|
+
* the `rest` says nothing the `rest` does not already say, *provided* the
|
|
292
|
+
* `rest` admits `undefined`.
|
|
293
|
+
*
|
|
294
|
+
* Every array carrying a value at that position is read against the same set
|
|
295
|
+
* either way, so the two spellings can only differ on the arrays with nothing
|
|
296
|
+
* there — one that ends before it, and one holding a hole at it. Both read
|
|
297
|
+
* `undefined`, which the `rest` alone imposes nothing on, so dropping the
|
|
298
|
+
* position widens the set unless the `rest` admits `undefined` too. That is
|
|
299
|
+
* why `{ prefix: [number], rest: number }` keeps its position and stays "one
|
|
300
|
+
* or more numbers": `[]` and `[ , 1]` belong to `{ prefix: [], rest: number }`
|
|
301
|
+
* and not to it.
|
|
302
|
+
*
|
|
303
|
+
* This is what keeps one set to one spelling: the open tuples `[]` and
|
|
304
|
+
* `[unknown]` are both every array and have to produce one `Node`.
|
|
305
|
+
*
|
|
306
|
+
* A referenced `rest` is left alone — reading its unit bits would need the
|
|
307
|
+
* rule set, and the form already declines to see through a reference (see
|
|
308
|
+
* `./README.md`).
|
|
309
|
+
*
|
|
310
|
+
* @type {(prefix: readonly Node[], rest: Node) => readonly Node[]}
|
|
311
|
+
*/
|
|
312
|
+
const trimPrefix = (prefix, rest) =>
|
|
313
|
+
typeof rest === 'string' || ((rest.unit ?? 0) & unitBit(undefined)) === 0
|
|
314
|
+
? prefix
|
|
315
|
+
: prefix.slice(0, prefix.findLastIndex(n => cmpNode(n, rest) !== 0) + 1)
|
|
316
|
+
|
|
289
317
|
/**
|
|
290
318
|
* Canonical array-kind singleton. A syntactically empty position makes the
|
|
291
|
-
* whole pattern empty
|
|
292
|
-
*
|
|
319
|
+
* whole pattern empty (a position past the array's end reads as `undefined`,
|
|
320
|
+
* which the empty set excludes, so no length escapes it); an empty `rest`
|
|
321
|
+
* admits nothing past the prefix, which is what no `rest` already says; a
|
|
322
|
+
* prefix restating its `rest` is {@link trimPrefix}'d away; an unconstrained
|
|
323
|
+
* `rest` with nothing left before it is every array.
|
|
324
|
+
*
|
|
325
|
+
* Every array set is stated with a `rest` — `never` for a bare tuple,
|
|
326
|
+
* `unknown` for an `open` one, the element set for a uniform array — so this
|
|
327
|
+
* takes one rather than an optional one; the absent `rest` is what it
|
|
328
|
+
* normalizes an empty one *to*.
|
|
293
329
|
*
|
|
294
|
-
* @type {(prefix: readonly Node[], rest: Node
|
|
330
|
+
* @type {(prefix: readonly Node[], rest: Node) => UnionSet}
|
|
295
331
|
*/
|
|
296
332
|
const arraySet = (prefix, rest) => {
|
|
297
333
|
if (prefix.some(isNever)) { return never }
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
: { array: [r === undefined ? { prefix } : { prefix, rest: r }] }
|
|
334
|
+
if (isNever(rest)) { return { array: [{ prefix }] } }
|
|
335
|
+
const p = trimPrefix(prefix, rest)
|
|
336
|
+
return p.length === 0 && isTop(rest) ? { array: true } : { array: [{ prefix: p, rest }] }
|
|
302
337
|
}
|
|
303
338
|
|
|
304
339
|
/**
|
|
305
|
-
* Canonical object-kind singleton. An unconstrained
|
|
306
|
-
*
|
|
307
|
-
* whole pattern empty;
|
|
308
|
-
*
|
|
340
|
+
* Canonical object-kind singleton. An unconstrained `rest` is the same set as
|
|
341
|
+
* no `rest`; an unconstrained key is then dropped too; a syntactically empty
|
|
342
|
+
* key set makes the whole pattern empty; with nothing left, the pattern is
|
|
343
|
+
* every object.
|
|
344
|
+
*
|
|
345
|
+
* A key is dropped only once the `rest` is gone, and that order is the whole
|
|
346
|
+
* rule: an undeclared key may be absent, or must belong to `rest`, which
|
|
347
|
+
* leaves it unconstrained exactly when there is no `rest` — so with one
|
|
348
|
+
* present a key saying "anything" says strictly more than leaving it out.
|
|
349
|
+
* A bare struct's empty `rest` is where the two part company —
|
|
350
|
+
* `{ props: { a: unknown }, rest: never }` admits `{ a: 1 }` and
|
|
351
|
+
* `{ props: {}, rest: never }` admits only `{}`.
|
|
309
352
|
*
|
|
310
353
|
* @type {(props: readonly (readonly [string, Node])[], rest: Node | undefined) => UnionSet}
|
|
311
354
|
*/
|
|
312
355
|
const objectSet = (props, rest) => {
|
|
313
|
-
const constrained = props.filter(([, v]) => !isTop(v))
|
|
314
|
-
if (constrained.some(([, v]) => isNever(v))) { return never }
|
|
315
356
|
const r = rest !== undefined && isTop(rest) ? undefined : rest
|
|
357
|
+
const constrained = r === undefined ? props.filter(([, v]) => !isTop(v)) : props
|
|
358
|
+
if (constrained.some(([, v]) => isNever(v))) { return never }
|
|
316
359
|
if (constrained.length === 0 && r === undefined) { return { object: true } }
|
|
317
360
|
/** @type {StringMap<Node>} */
|
|
318
361
|
const sorted = Object.fromEntries(constrained.toSorted(([ak], [bk]) => cmpString(ak, bk)))
|
|
@@ -367,12 +410,22 @@ const kindSubset = le => (a, b) => {
|
|
|
367
410
|
return a.every(x => b.some(y => le(x, y)))
|
|
368
411
|
}
|
|
369
412
|
|
|
370
|
-
/**
|
|
413
|
+
/**
|
|
414
|
+
* Only the *longest* array each side admits is tested here — `pn` without a
|
|
415
|
+
* `rest`, unbounded with one. The shortest needs no test of its own: a
|
|
416
|
+
* position `q` insists on (one whose set excludes `undefined`) is a position
|
|
417
|
+
* `p` insists on too as soon as the pointwise check below passes, since
|
|
418
|
+
* otherwise `undefined` would be a member of `p.prefix[i]` and not of
|
|
419
|
+
* `q.prefix[i]`. Sound, and incomplete in the way `subset` is elsewhere: a
|
|
420
|
+
* `p` shorter than `q` is answered `false` even when every position past its
|
|
421
|
+
* end is one `q` admits as absent.
|
|
422
|
+
*
|
|
423
|
+
* @type {(ctx: _Ctx) => (assumed: _Assumed) => (p: ArraySet, q: ArraySet) => boolean}
|
|
424
|
+
*/
|
|
371
425
|
const arraySetSubset = ctx => assumed => (p, q) => {
|
|
372
426
|
const le = nodeSubset(ctx)(assumed)
|
|
373
427
|
const pn = p.prefix.length
|
|
374
428
|
const qn = q.prefix.length
|
|
375
|
-
// `p` admits lengths `{pn}` or `[pn, ∞)`; they must fit `q`'s admitted lengths
|
|
376
429
|
const lengthOk = q.rest !== undefined
|
|
377
430
|
? qn <= pn
|
|
378
431
|
: p.rest === undefined && qn === pn
|
|
@@ -387,38 +440,65 @@ const arraySetSubset = ctx => assumed => (p, q) => {
|
|
|
387
440
|
const keyed = n => [n, typeof n === 'string' ? `r:${n}` : undefined]
|
|
388
441
|
|
|
389
442
|
/**
|
|
390
|
-
* The set of values
|
|
391
|
-
* declared set, else
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
443
|
+
* The set of values the pattern admits at key `k` when the key is **present**:
|
|
444
|
+
* the declared set, else the `rest`, else anything.
|
|
445
|
+
*
|
|
446
|
+
* Presence is the whole point of splitting this from {@link objectMayOmit}. An
|
|
447
|
+
* absent key and a key present holding `undefined` are not the same object, and
|
|
448
|
+
* the two sides of a pattern read them differently: a *declared* key constrains
|
|
449
|
+
* the value read at it, so absence reads `undefined` and passes when the set
|
|
450
|
+
* holds it, whereas an *undeclared* key is checked as an entry, so a present
|
|
451
|
+
* `undefined` must belong to `rest` itself (see {@link objectSetValidate}).
|
|
452
|
+
* Folding the two into one "read set" of `rest ∪ undefined` made
|
|
453
|
+
* `{ a: option(number) }` a subset of `record(number)`, which admits
|
|
454
|
+
* `{ a: undefined }` on the left and rejects it on the right.
|
|
395
455
|
*
|
|
396
|
-
* @type {(
|
|
456
|
+
* @type {(pattern: ObjectSet) => (k: string) => _Keyed}
|
|
397
457
|
*/
|
|
398
|
-
const
|
|
458
|
+
const objectPresentSet = pattern => k => {
|
|
399
459
|
const n = at(k)(pattern.props)
|
|
400
460
|
if (n !== null) { return keyed(n) }
|
|
401
461
|
const { rest } = pattern
|
|
402
|
-
return rest === undefined
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
462
|
+
return rest === undefined ? [unknown, 't'] : keyed(rest)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Whether the pattern admits an object carrying no `k` at all: an undeclared
|
|
467
|
+
* key may always be missing, and a declared one exactly when its set holds
|
|
468
|
+
* `undefined`, since an absent property reads as `undefined`.
|
|
469
|
+
*
|
|
470
|
+
* This is the half of the old read-set that the `∪ undefined` stood for, now
|
|
471
|
+
* asked as its own question — a local unit-bit test, so it needs no memo.
|
|
472
|
+
*
|
|
473
|
+
* @type {(rules: RuleSet) => (pattern: ObjectSet) => (k: string) => boolean}
|
|
474
|
+
*/
|
|
475
|
+
const objectMayOmit = rules => pattern => k => {
|
|
476
|
+
const n = at(k)(pattern.props)
|
|
477
|
+
return n === null || ((resolve(rules)(n).unit ?? 0) & unitBit(undefined)) !== 0
|
|
408
478
|
}
|
|
409
479
|
|
|
410
480
|
/** @type {(list: readonly string[]) => readonly string[]} */
|
|
411
481
|
const dedup = list => list.filter((n, i) => list.indexOf(n) === i)
|
|
412
482
|
|
|
413
|
-
/**
|
|
483
|
+
/**
|
|
484
|
+
* Every key either side declares is checked twice — what `p` may hold there
|
|
485
|
+
* must be something `q` holds there, and `p` may leave it out only where `q`
|
|
486
|
+
* lets it. Neither question implies the other: the first alone lets `p` admit
|
|
487
|
+
* an object `q` rejects for a *missing* key, the second alone for a *present*
|
|
488
|
+
* one.
|
|
489
|
+
*
|
|
490
|
+
* @type {(ctx: _Ctx) => (assumed: _Assumed) => (p: ObjectSet, q: ObjectSet) => boolean}
|
|
491
|
+
*/
|
|
414
492
|
const objectSetSubset = ctx => assumed => (p, q) => {
|
|
415
493
|
const le = keyedSubset(ctx)(assumed)
|
|
416
494
|
const keys = dedup([
|
|
417
495
|
...definedEntries(p.props).map(([k]) => k),
|
|
418
496
|
...definedEntries(q.props).map(([k]) => k),
|
|
419
497
|
])
|
|
420
|
-
return keys.every(k =>
|
|
421
|
-
|
|
498
|
+
return keys.every(k =>
|
|
499
|
+
le(objectPresentSet(p)(k), objectPresentSet(q)(k))
|
|
500
|
+
&& (!objectMayOmit(ctx[0])(p)(k) || objectMayOmit(ctx[1])(q)(k)))
|
|
501
|
+
// values at the keys declared by neither side, which both may omit
|
|
422
502
|
&& (q.rest === undefined || nodeSubset(ctx)(assumed)(p.rest ?? unknown, q.rest))
|
|
423
503
|
}
|
|
424
504
|
|
|
@@ -459,6 +539,23 @@ const nodeSubset = ctx => assumed => (a, b) =>
|
|
|
459
539
|
export const subset = ([aRules, aNode]) => ([bRules, bNode]) =>
|
|
460
540
|
nodeSubset([aRules, bRules])({})(aNode, bNode)
|
|
461
541
|
|
|
542
|
+
/**
|
|
543
|
+
* Whether `a` and `b` denote the same set: {@link subset} both ways.
|
|
544
|
+
*
|
|
545
|
+
* Weaker as a test than {@link equal} and stronger as an answer. `equal`
|
|
546
|
+
* compares canonical *structure*, so it reports two α-equivalent recursive
|
|
547
|
+
* definitions — the same shape under different rule names — as different;
|
|
548
|
+
* `subset` resolves references coinductively and sees through the renaming
|
|
549
|
+
* (see `./README.md`). It inherits `subset`'s incompleteness in exchange: an
|
|
550
|
+
* equality that holds only by distributing a union, or through a non-syntactic
|
|
551
|
+
* empty set, is answered `false`. That direction is the safe one — a `false`
|
|
552
|
+
* here never merges two memberships — so a caller may treat a `true` as
|
|
553
|
+
* conclusive and a `false` as "not established".
|
|
554
|
+
*
|
|
555
|
+
* @type {(a: Data) => (b: Data) => boolean}
|
|
556
|
+
*/
|
|
557
|
+
export const equivalent = a => b => subset(a)(b) && subset(b)(a)
|
|
558
|
+
|
|
462
559
|
// ── coverage collapse ────────────────────────────────────────────────────────
|
|
463
560
|
|
|
464
561
|
/**
|
|
@@ -685,12 +782,24 @@ const primitiveUnion = p => {
|
|
|
685
782
|
const containerMemo = (state, c) => {
|
|
686
783
|
const done = assoc(state.done, c)
|
|
687
784
|
if (done !== undefined) { return [state, done] }
|
|
688
|
-
const [state1, u] = containerUnion(state, c)
|
|
785
|
+
const [state1, u] = containerUnion(state, c, never)
|
|
689
786
|
return [{ ...state1, done: [...state1.done, [c, u]] }, u]
|
|
690
787
|
}
|
|
691
788
|
|
|
692
|
-
/**
|
|
693
|
-
|
|
789
|
+
/**
|
|
790
|
+
* The union of a const container, with `rest` the set every member it does not
|
|
791
|
+
* declare belongs to.
|
|
792
|
+
*
|
|
793
|
+
* Used bare, both kinds are **closed** — the declared members and no others —
|
|
794
|
+
* so the default maps onto the `rest` that says so, `never` on either kind.
|
|
795
|
+
* `rest`/`open` is what supplies one of its own; `unknown` is the open form,
|
|
796
|
+
* which the two kinds spell differently in the canonical data (a tuple's
|
|
797
|
+
* `rest: unknown`, a struct's absent `rest`) and {@link arraySet} /
|
|
798
|
+
* {@link objectSet} normalize to.
|
|
799
|
+
*
|
|
800
|
+
* @type {(state: _State, c: ConstObject, rest: Node) => readonly [_State, UnionSet]}
|
|
801
|
+
*/
|
|
802
|
+
const containerUnion = (state, c, rest) => {
|
|
694
803
|
let s = state
|
|
695
804
|
if (c instanceof Array) {
|
|
696
805
|
/** @type {readonly Node[]} */
|
|
@@ -700,7 +809,7 @@ const containerUnion = (state, c) => {
|
|
|
700
809
|
s = s1
|
|
701
810
|
prefix = [...prefix, n]
|
|
702
811
|
}
|
|
703
|
-
return [s, arraySet(prefix,
|
|
812
|
+
return [s, arraySet(prefix, rest)]
|
|
704
813
|
}
|
|
705
814
|
/** @type {readonly (readonly [string, Node])[]} */
|
|
706
815
|
let props = []
|
|
@@ -709,7 +818,22 @@ const containerUnion = (state, c) => {
|
|
|
709
818
|
s = s1
|
|
710
819
|
props = [...props, [k, n]]
|
|
711
820
|
}
|
|
712
|
-
return [s, objectSet(props,
|
|
821
|
+
return [s, objectSet(props, rest)]
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* The union of a `rest` schema. The container is walked here rather than
|
|
826
|
+
* through {@link containerMemo}: that memo is keyed by the container's
|
|
827
|
+
* identity alone, and the same object with two different rests is two
|
|
828
|
+
* different sets. The enclosing thunk is memoized by {@link convertThunk}
|
|
829
|
+
* either way, so nothing is recomputed and a cycle through a `rest` still
|
|
830
|
+
* closes.
|
|
831
|
+
*
|
|
832
|
+
* @type {(state: _State, c: ConstObject, r: Type) => readonly [_State, UnionSet]}
|
|
833
|
+
*/
|
|
834
|
+
const restUnion = (state, c, r) => {
|
|
835
|
+
const [state1, restNode] = nodeOf(state)(r)
|
|
836
|
+
return containerUnion(state1, c, restNode)
|
|
713
837
|
}
|
|
714
838
|
|
|
715
839
|
/** @type {(state: _State, c: Const) => readonly [_State, UnionSet]} */
|
|
@@ -784,6 +908,11 @@ const thunkUnion = (state, t) => {
|
|
|
784
908
|
const [state1, value] = nodeOf(state)(rest[0])
|
|
785
909
|
return [state1, objectSet([], value)]
|
|
786
910
|
}
|
|
911
|
+
case 'rest': {
|
|
912
|
+
const [c, r] = rest
|
|
913
|
+
assert(typeof c === 'object' && c !== null, c)
|
|
914
|
+
return restUnion(state, c, r)
|
|
915
|
+
}
|
|
787
916
|
default: { return orUnion(state, t, rest) }
|
|
788
917
|
}
|
|
789
918
|
}
|
|
@@ -987,21 +1116,44 @@ const patternsValidate = (k, item, value) => {
|
|
|
987
1116
|
return verror('no match')
|
|
988
1117
|
}
|
|
989
1118
|
|
|
990
|
-
/**
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1119
|
+
/**
|
|
1120
|
+
* The declared positions are checked by reading the value at each — a
|
|
1121
|
+
* position past the end reads as `undefined`, so a position is required
|
|
1122
|
+
* exactly when its set excludes `undefined`, and no minimum length is tested
|
|
1123
|
+
* for. What is left over is tested against `rest`, or, with no `rest`, must
|
|
1124
|
+
* not be there at all. Same shape as {@link objectSetValidate}, one kind
|
|
1125
|
+
* over.
|
|
1126
|
+
*
|
|
1127
|
+
* `undeclaredMembers` is what the schema-form readers walk too, so "what is
|
|
1128
|
+
* left over" is one rule rather than two that happen to coincide — including
|
|
1129
|
+
* an index the prototype supplies, which an own-entry filter here answered
|
|
1130
|
+
* `ok` for while the rendered tail claimed the `rest`'s type over it.
|
|
1131
|
+
*
|
|
1132
|
+
* @type {(rules: RuleSet) => (p: ArraySet) => (value: readonly Unknown[]) => ResultE}
|
|
1133
|
+
*/
|
|
994
1134
|
const arraySetValidate = rules => p => value => {
|
|
995
1135
|
const pn = p.prefix.length
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
Object.entries(value),
|
|
1001
|
-
(k, v) => nodeValidate(rules)(atIndex(p, Number(k)))(v),
|
|
1136
|
+
const { rest } = p
|
|
1137
|
+
const declared = eachEntry(
|
|
1138
|
+
Object.entries(p.prefix),
|
|
1139
|
+
(k, n) => nodeValidate(rules)(n)(value[Number(k)]),
|
|
1002
1140
|
undefined,
|
|
1003
1141
|
noAccumulate,
|
|
1004
1142
|
)
|
|
1143
|
+
if (declared[0] === 'error') { return declared }
|
|
1144
|
+
const extra = undeclaredMembers(p.prefix.map((_, i) => String(i)), value)
|
|
1145
|
+
if (rest === undefined) {
|
|
1146
|
+
// Nothing past the prefix, by length as well as by entry: a hole past
|
|
1147
|
+
// it is not an entry, but the array is still that long, and this is
|
|
1148
|
+
// the set `Ts<>` renders as a tuple of exactly `pn` positions and JSON
|
|
1149
|
+
// Schema as `items: false`. A *shorter* array is another matter — the
|
|
1150
|
+
// declared loop above has already held every position it left unfilled
|
|
1151
|
+
// to a set admitting `undefined`.
|
|
1152
|
+
return extra.length === 0 && value.length <= pn
|
|
1153
|
+
? ok(value)
|
|
1154
|
+
: verror('unexpected value')
|
|
1155
|
+
}
|
|
1156
|
+
const r = eachEntry(extra, (_k, v) => nodeValidate(rules)(rest)(v), undefined, noAccumulate)
|
|
1005
1157
|
return r[0] === 'error' ? r : ok(value)
|
|
1006
1158
|
}
|
|
1007
1159
|
|
|
@@ -1042,6 +1194,48 @@ const unionValidate = rules => u => value => {
|
|
|
1042
1194
|
/** @type {(rules: RuleSet) => (n: Node) => (value: Unknown) => ResultE} */
|
|
1043
1195
|
const nodeValidate = rules => n => unionValidate(rules)(resolve(rules)(n))
|
|
1044
1196
|
|
|
1197
|
+
/**
|
|
1198
|
+
* Whether `r`, as the rest of the container `c`, admits nothing — so that
|
|
1199
|
+
* `rest(c, r)` and the bare, closed `c` denote one set.
|
|
1200
|
+
*
|
|
1201
|
+
* This is what the schema-form readers bound an array's length by, and it is
|
|
1202
|
+
* asked *here* on purpose: `arraySet` is where an empty rest normalizes away,
|
|
1203
|
+
* so the readers agree with this form by asking it rather than by re-deriving
|
|
1204
|
+
* the same rule and drifting. Stated as **making no difference to the
|
|
1205
|
+
* canonical form** — the conversion of `rest(c, r)` denoting the same set as
|
|
1206
|
+
* the conversion of `c` — and not as a judgement about `r` on its own, nor as
|
|
1207
|
+
* a reading of whether the conversion kept a `rest` key. Five cases fix it
|
|
1208
|
+
* between them, and only this equality satisfies all five:
|
|
1209
|
+
*
|
|
1210
|
+
* - `never`, `or()` and `[or()]` all convert `rest(c, r)` to `c`'s own
|
|
1211
|
+
* conversion, so all three are empty. Keying on the exported `never` alone
|
|
1212
|
+
* would pass a `never`-only proof with the other two spellings still
|
|
1213
|
+
* disagreeing.
|
|
1214
|
+
* - `const r = () => ['rest', [r], never]` has no finite inhabitant, yet the
|
|
1215
|
+
* conversion keeps `rest: "r"`, so it is *not* empty here. A reader
|
|
1216
|
+
* recognizing that emptiness would start rejecting what this form accepts.
|
|
1217
|
+
* - `const a = () => ['or', b]; const b = () => ['or', a]` rules out the
|
|
1218
|
+
* rest's *own* canonical data as the test: {@link toData} of `a` is `never`,
|
|
1219
|
+
* and the conversion still keeps `rest: "a"`.
|
|
1220
|
+
* - `unknown` rules out "the conversion kept no `rest` key" as the test:
|
|
1221
|
+
* `toData(rest([], unknown))` is `{ array: true }` — the whole kind, with no
|
|
1222
|
+
* `rest` key — because a top rest collapses the pattern rather than being
|
|
1223
|
+
* dropped from it.
|
|
1224
|
+
* - Two separately constructed copies of one recursive rule, one in `c` and
|
|
1225
|
+
* one in `r`, rule out {@link equal} as the comparison: converting the rest
|
|
1226
|
+
* reserves the name, so `c`'s rule is named `r0` where converting `c` alone
|
|
1227
|
+
* names it `r`, and `equal` compares recursive definitions by rule name.
|
|
1228
|
+
*
|
|
1229
|
+
* {@link equivalent} answers all five. It is incomplete, and in the direction
|
|
1230
|
+
* that costs nothing here: an unrecognized empty rest leaves the length
|
|
1231
|
+
* unbounded, which is what a *kept* `rest` key means in this form anyway, so
|
|
1232
|
+
* the readers still agree with it.
|
|
1233
|
+
*
|
|
1234
|
+
* @type {(c: ConstObject, r: Type) => boolean}
|
|
1235
|
+
*/
|
|
1236
|
+
export const emptyRest = (c, r) =>
|
|
1237
|
+
equivalent(toData(/** @type {Type} */ (() => ['rest', c, r])))(toData(c))
|
|
1238
|
+
|
|
1045
1239
|
/**
|
|
1046
1240
|
* Data-driven validation — the counterpart of `../validate` that consumes a
|
|
1047
1241
|
* {@link Data} produced by {@link toData} instead of walking the thunk
|
|
@@ -30,6 +30,11 @@ export type _Odd = () => readonly ['const', {
|
|
|
30
30
|
readonly next: Or<readonly [_Even, undefined]>;
|
|
31
31
|
}];
|
|
32
32
|
export type _Rec = () => readonly ['array', _Rec];
|
|
33
|
+
export type _ClosedNode = () => readonly ['const', readonly [typeof number, _ClosedChildren]];
|
|
34
|
+
export type _ClosedChildren = () => readonly ['array', _ClosedNode];
|
|
35
|
+
export type _NestedRest = () => readonly ['rest', {
|
|
36
|
+
readonly a: typeof number;
|
|
37
|
+
}, _NestedRest];
|
|
33
38
|
export declare const proof: {
|
|
34
39
|
withoutUnits: (() => void)[];
|
|
35
40
|
unitBits: () => void;
|
|
@@ -37,6 +42,8 @@ export declare const proof: {
|
|
|
37
42
|
primitives: () => void;
|
|
38
43
|
tag0: () => void;
|
|
39
44
|
containers: () => void;
|
|
45
|
+
rest: () => void;
|
|
46
|
+
restRecursion: () => void;
|
|
40
47
|
constSchemas: () => void;
|
|
41
48
|
or: () => void;
|
|
42
49
|
orCanonicalIdentity: () => void;
|
|
@@ -50,6 +57,7 @@ export declare const proof: {
|
|
|
50
57
|
};
|
|
51
58
|
cmp: {
|
|
52
59
|
totalOrder: () => void;
|
|
60
|
+
rest: () => void;
|
|
53
61
|
restOrder: () => void;
|
|
54
62
|
rules: () => void;
|
|
55
63
|
};
|
|
@@ -57,6 +65,8 @@ export declare const proof: {
|
|
|
57
65
|
literals: () => void;
|
|
58
66
|
arrays: () => void;
|
|
59
67
|
arrayRest: () => void;
|
|
68
|
+
rest: () => void;
|
|
69
|
+
presenceIsNotAbsence: () => void;
|
|
60
70
|
objects: () => void;
|
|
61
71
|
recursion: () => void;
|
|
62
72
|
mixedObjectRecursion: () => void;
|