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
|
@@ -26,21 +26,33 @@
|
|
|
26
26
|
* it to convention. Which reader a caller wants, and why both exist, is in
|
|
27
27
|
* "The two schema-form readers" in `../README.md`.
|
|
28
28
|
*
|
|
29
|
-
* ## Structs and tuples are
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* are
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* member
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* `../
|
|
29
|
+
* ## Structs and tuples are closed
|
|
30
|
+
*
|
|
31
|
+
* Closedness is the shared rule, not a `parse` detail — see "Structs and
|
|
32
|
+
* tuples are closed" in `../README.md`. A bare `Struct` or `Tuple` admits the
|
|
33
|
+
* members it declares and no others, so an undeclared key or an index past the
|
|
34
|
+
* prefix rejects the value. A tuple answers by **length** as well as by
|
|
35
|
+
* member, because a hole past the prefix is no member and would slip through
|
|
36
|
+
* the member check alone.
|
|
37
|
+
*
|
|
38
|
+
* Closedness is about *undeclared* members and leaves the required/optional
|
|
39
|
+
* rule alone: an absent member reads as `undefined`, so a member is required
|
|
40
|
+
* exactly when its set excludes `undefined`, and a schema whose trailing
|
|
41
|
+
* position admits it still accepts a shorter array. A tuple schema declares by
|
|
42
|
+
* length, so a hole in the *schema* is a position whose schema is `undefined`
|
|
43
|
+
* — see "A hole is a declared position" in `../README.md`.
|
|
44
|
+
*
|
|
45
|
+
* The length check is the model rather than an inference from `Ts<>`. #1622
|
|
46
|
+
* added one by reading `Ts<readonly[42]>`'s exact tuple as the value model
|
|
47
|
+
* while the model said open, and it was reverted for that reason; what has
|
|
48
|
+
* changed since is the model, not the reading.
|
|
49
|
+
*
|
|
50
|
+
* ## Stated rests
|
|
51
|
+
*
|
|
52
|
+
* `rest(c, r)` admits the declared members plus any number of members
|
|
53
|
+
* belonging to `r`, and `open(c)` — `rest(c, unknown)` — admits anything else
|
|
54
|
+
* besides. This widens what is accepted and changes nothing else: a success
|
|
55
|
+
* still carries the very value it was given, undeclared members included.
|
|
44
56
|
*
|
|
45
57
|
* ## Dispatch strategy
|
|
46
58
|
*
|
|
@@ -54,9 +66,9 @@
|
|
|
54
66
|
*
|
|
55
67
|
* ## Recursion safety
|
|
56
68
|
*
|
|
57
|
-
*
|
|
58
|
-
* lazily — only after confirming
|
|
59
|
-
* infinite recursion when validating recursive schemas like
|
|
69
|
+
* The inner validator of an `array`, a `record` or a `rest` is instantiated
|
|
70
|
+
* lazily — only after confirming there is a member for it to read. This
|
|
71
|
+
* prevents infinite recursion when validating recursive schemas like
|
|
60
72
|
* `const list = () => ['array', list]`.
|
|
61
73
|
*
|
|
62
74
|
* See `./types.ts` for the `Path`/`Result`/`Validate`/`ValidationError`
|
|
@@ -65,8 +77,9 @@
|
|
|
65
77
|
* @module
|
|
66
78
|
*
|
|
67
79
|
* @import { Unknown } from '../ts/types.ts'
|
|
68
|
-
* @import {
|
|
69
|
-
* @import { Container, IsContainer, Validate, Visitor } from '../common/types.ts'
|
|
80
|
+
* @import { ConstObject, Info1, Tag1, Type } from '../types.ts'
|
|
81
|
+
* @import { Container, Fits, IsContainer, SchemaEntries, Validate, ValidateE, Visitor } from '../common/types.ts'
|
|
82
|
+
* @import { StringMap } from '../../object/types.ts'
|
|
70
83
|
*/
|
|
71
84
|
|
|
72
85
|
import { ok } from '../../result/module.f.mjs'
|
|
@@ -77,66 +90,106 @@ import {
|
|
|
77
90
|
isObject,
|
|
78
91
|
orVisit,
|
|
79
92
|
primitive0Validate,
|
|
93
|
+
structSchemaEntries,
|
|
94
|
+
tupleSchemaEntries,
|
|
95
|
+
undeclaredMembers,
|
|
80
96
|
verror,
|
|
81
97
|
visit,
|
|
82
98
|
} from '../common/module.f.mjs'
|
|
83
|
-
|
|
84
|
-
const { entries } = Object
|
|
99
|
+
import { emptyRest } from '../data/module.f.mjs'
|
|
85
100
|
|
|
86
101
|
/** `validate` has nothing to collect from a successful entry — only pass/fail matters. */
|
|
87
102
|
const noAccumulate = () => undefined
|
|
88
103
|
|
|
104
|
+
/** A uniform container declares no member by name, so every one is undeclared. */
|
|
105
|
+
/** @type {readonly string[]} */
|
|
106
|
+
const noDeclared = []
|
|
107
|
+
|
|
89
108
|
/**
|
|
90
109
|
* Builds a validator for `array` or `record` schemas.
|
|
91
110
|
* The inner item validator is instantiated lazily (only when the container is
|
|
92
111
|
* non-empty) to avoid infinite recursion with recursive schemas.
|
|
112
|
+
*
|
|
113
|
+
* The members are `undeclaredMembers`', not `Object.entries`': `array(t)` is
|
|
114
|
+
* `rest([], t)`, so the two have to walk a value the same way — an own-entry
|
|
115
|
+
* walk here skipped an index the prototype supplies while the data form's
|
|
116
|
+
* reader found it, which broke the acceptance agreement the three readers are
|
|
117
|
+
* pinned on.
|
|
118
|
+
*
|
|
119
|
+
* `fits` bounds the array kind's length when its element set admits nothing,
|
|
120
|
+
* which is what the data form says by normalizing such a `rest` away: an
|
|
121
|
+
* `array(or())` is the *empty* array and not "any number of holes". It is
|
|
122
|
+
* consulted only where it can change the answer — no member present, and the
|
|
123
|
+
* value reaching further than that — so an ordinary array never asks.
|
|
93
124
|
*/
|
|
94
125
|
const containerValidate =
|
|
95
126
|
/**
|
|
96
127
|
* @template {Tag1} K
|
|
97
128
|
* @param {IsContainer<Container<K>>} isContainer
|
|
129
|
+
* @param {(item: Type) => Fits<Container<K>>} restFits
|
|
98
130
|
* @returns {<I extends Type>(item: I) => Validate<Info1<K, I>>}
|
|
99
131
|
*/
|
|
100
|
-
isContainer =>
|
|
101
|
-
item =>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
132
|
+
(isContainer, restFits) =>
|
|
133
|
+
item => {
|
|
134
|
+
// Depends on the schema alone, so it is built once per schema rather
|
|
135
|
+
// than once per validated value.
|
|
136
|
+
const fits = restFits(item)
|
|
137
|
+
return value => {
|
|
138
|
+
if (!isContainer(value)) {
|
|
139
|
+
return verror('unexpected value')
|
|
140
|
+
}
|
|
141
|
+
const e = undeclaredMembers(noDeclared, value)
|
|
142
|
+
if (e.length === 0) {
|
|
143
|
+
return fits(value, 0)
|
|
144
|
+
? /** @type {any} */ (ok(value))
|
|
145
|
+
: verror('unexpected value')
|
|
146
|
+
}
|
|
147
|
+
// Note: we shouldn't instantiate `itemValidate` until we make sure `entries` is not empty.
|
|
148
|
+
// Otherwise, we can get infinite recursion on empty arrays and objects
|
|
149
|
+
const itemValidate = validate(item)
|
|
150
|
+
const r = eachEntry(e, (_k, v) => itemValidate(v), undefined, noAccumulate)
|
|
151
|
+
// `value` is Container<K>, but Ts<Info1<K,I>> = readonly Ts<I>[] | Record<string,Ts<I>>.
|
|
152
|
+
// TypeScript can't narrow the container's element types through the validation loop.
|
|
153
|
+
return r[0] === 'error' ? r : /** @type {any} */ (ok(value))
|
|
108
154
|
}
|
|
109
|
-
// Note: we shouldn't instantiate `itemValidate` until we make sure `entries` is not empty.
|
|
110
|
-
// Otherwise, we can get infinite recursion on empty arrays and objects
|
|
111
|
-
const itemValidate = validate(item)
|
|
112
|
-
const r = eachEntry(e, (_k, v) => itemValidate(v), undefined, noAccumulate)
|
|
113
|
-
// `value` is Container<K>, but Ts<Info1<K,I>> = readonly Ts<I>[] | Record<string,Ts<I>>.
|
|
114
|
-
// TypeScript can't narrow the container's element types through the validation loop.
|
|
115
|
-
return r[0] === 'error' ? r : /** @type {any} */ (ok(value))
|
|
116
155
|
}
|
|
117
156
|
|
|
118
|
-
const arrayValidate = containerValidate(
|
|
157
|
+
const arrayValidate = containerValidate(
|
|
158
|
+
isArray,
|
|
159
|
+
// The cast is the price of one factory over two kinds: `Container<K>` is
|
|
160
|
+
// the union until `K` is bound, and only the array arm has a `length`.
|
|
161
|
+
item => (value, declared) =>
|
|
162
|
+
/** @type {ReadonlyArray<Unknown>} */ (value).length <= declared || !emptyRest([], item),
|
|
163
|
+
)
|
|
119
164
|
|
|
120
|
-
const recordValidate = containerValidate(isObject)
|
|
165
|
+
const recordValidate = containerValidate(isObject, () => () => true)
|
|
121
166
|
|
|
122
167
|
/**
|
|
123
|
-
* Builds a validator for `Tuple` or `Struct` const schemas
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
168
|
+
* Builds a validator for `Tuple` or `Struct` const schemas — **closed**: the
|
|
169
|
+
* members the schema declares and no others. It reads each declared member,
|
|
170
|
+
* then answers for every member of the value the schema does not name.
|
|
171
|
+
*
|
|
172
|
+
* `fits` is the one thing the two kinds do not share. An undeclared member is
|
|
173
|
+
* a member on both, but an array is also *as long as it is*: a hole past the
|
|
174
|
+
* prefix is no member and would slip through the member check alone, so the
|
|
175
|
+
* array kind answers with its length as well.
|
|
127
176
|
*/
|
|
128
177
|
const constContainerValidate =
|
|
129
178
|
/**
|
|
130
|
-
* @template {Unknown} C
|
|
179
|
+
* @template {ReadonlyArray<Unknown> | StringMap<Unknown>} C
|
|
180
|
+
* @template {ConstObject} S
|
|
131
181
|
* @param {IsContainer<C>} isContainer
|
|
182
|
+
* @param {SchemaEntries<S>} schemaEntries
|
|
132
183
|
* @param {(value: C, k: string) => Unknown} getItem
|
|
133
|
-
* @
|
|
184
|
+
* @param {Fits<C>} fits
|
|
185
|
+
* @returns {<T extends S>(rtti: T) => Validate<T>}
|
|
134
186
|
*/
|
|
135
|
-
(isContainer, getItem) =>
|
|
187
|
+
(isContainer, schemaEntries, getItem, fits) =>
|
|
136
188
|
rtti => {
|
|
137
|
-
//
|
|
189
|
+
// Depend on `rtti` alone, so they are computed once per schema rather
|
|
138
190
|
// than once per validated value.
|
|
139
|
-
const rttiEntries =
|
|
191
|
+
const rttiEntries = schemaEntries(rtti)
|
|
192
|
+
const declared = rttiEntries.map(([k]) => k)
|
|
140
193
|
return value => {
|
|
141
194
|
if (!isContainer(value)) {
|
|
142
195
|
return verror('unexpected value')
|
|
@@ -147,22 +200,98 @@ const constContainerValidate =
|
|
|
147
200
|
undefined,
|
|
148
201
|
noAccumulate,
|
|
149
202
|
)
|
|
203
|
+
if (r[0] === 'error') { return r }
|
|
150
204
|
// `value` is C (Unknown container), but Ts<T> for T extends Tuple|Struct is not
|
|
151
205
|
// structurally equivalent to C — TypeScript can't narrow element types through the loop.
|
|
152
|
-
return
|
|
206
|
+
return undeclaredMembers(declared, value).length === 0 && fits(value, declared.length)
|
|
207
|
+
? /** @type {any} */ (ok(value))
|
|
208
|
+
: verror('unexpected value')
|
|
153
209
|
}
|
|
154
210
|
}
|
|
155
211
|
|
|
156
212
|
const tupleValidate = constContainerValidate(
|
|
157
213
|
isArray,
|
|
214
|
+
tupleSchemaEntries,
|
|
158
215
|
(value, k) => value[Number(k)],
|
|
216
|
+
(value, declared) => value.length <= declared,
|
|
159
217
|
)
|
|
160
218
|
|
|
161
219
|
const structValidate = constContainerValidate(
|
|
162
220
|
isObject,
|
|
221
|
+
structSchemaEntries,
|
|
163
222
|
(value, k) => value[k],
|
|
223
|
+
() => true,
|
|
164
224
|
)
|
|
165
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Builds a validator for a container with a stated `rest`. The declared
|
|
228
|
+
* members are read exactly as the bare form reads them, and every member the
|
|
229
|
+
* schema does not name is held to `rest`.
|
|
230
|
+
*
|
|
231
|
+
* `restFits` carries the array kind's length bound, which a `rest` removes
|
|
232
|
+
* only while it admits something. An empty one says what the bare form says,
|
|
233
|
+
* so `rest(c, or())` and `c` stay one set — the criterion for "empty" is
|
|
234
|
+
* `emptyRest`'s, and it is consulted only when nothing is present past the
|
|
235
|
+
* prefix, since a member that got there and passed is itself the proof that
|
|
236
|
+
* the rest admits something.
|
|
237
|
+
*/
|
|
238
|
+
const restContainerValidate =
|
|
239
|
+
/**
|
|
240
|
+
* @template {ReadonlyArray<Unknown> | StringMap<Unknown>} C
|
|
241
|
+
* @template {ConstObject} S
|
|
242
|
+
* @param {IsContainer<C>} isContainer
|
|
243
|
+
* @param {SchemaEntries<S>} schemaEntries
|
|
244
|
+
* @param {(value: C, k: string) => Unknown} getItem
|
|
245
|
+
* @param {(rtti: S, r: Type) => Fits<C>} restFits
|
|
246
|
+
* @returns {(rtti: S, r: Type) => ValidateE}
|
|
247
|
+
*/
|
|
248
|
+
(isContainer, schemaEntries, getItem, restFits) =>
|
|
249
|
+
(rtti, r) => {
|
|
250
|
+
// Depend on the schema alone, so they are computed once per schema.
|
|
251
|
+
const rttiEntries = schemaEntries(rtti)
|
|
252
|
+
const declared = rttiEntries.map(([k]) => k)
|
|
253
|
+
const fits = restFits(rtti, r)
|
|
254
|
+
return value => {
|
|
255
|
+
if (!isContainer(value)) {
|
|
256
|
+
return verror('unexpected value')
|
|
257
|
+
}
|
|
258
|
+
const d = eachEntry(
|
|
259
|
+
rttiEntries,
|
|
260
|
+
(k, v) => /** @type {any} */ (validate(v))(getItem(value, k)),
|
|
261
|
+
undefined,
|
|
262
|
+
noAccumulate,
|
|
263
|
+
)
|
|
264
|
+
if (d[0] === 'error') { return d }
|
|
265
|
+
const extra = undeclaredMembers(declared, value)
|
|
266
|
+
if (extra.length === 0) {
|
|
267
|
+
return fits(value, declared.length) ? ok(value) : verror('unexpected value')
|
|
268
|
+
}
|
|
269
|
+
const restValidate = /** @type {any} */ (validate(r))
|
|
270
|
+
const e = eachEntry(extra, (_k, v) => restValidate(v), undefined, noAccumulate)
|
|
271
|
+
return e[0] === 'error' ? e : ok(value)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const restTupleValidate = restContainerValidate(
|
|
276
|
+
isArray,
|
|
277
|
+
tupleSchemaEntries,
|
|
278
|
+
(value, k) => value[Number(k)],
|
|
279
|
+
(rtti, r) => (value, declared) => value.length <= declared || !emptyRest(rtti, r),
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
const restStructValidate = restContainerValidate(
|
|
283
|
+
isObject,
|
|
284
|
+
structSchemaEntries,
|
|
285
|
+
(value, k) => value[k],
|
|
286
|
+
() => () => true,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
/** @type {(rtti: ConstObject, r: Type) => ValidateE} */
|
|
290
|
+
const restValidate = (rtti, r) =>
|
|
291
|
+
rtti instanceof Array
|
|
292
|
+
? restTupleValidate(rtti, r)
|
|
293
|
+
: restStructValidate(rtti, r)
|
|
294
|
+
|
|
166
295
|
const orValidate =
|
|
167
296
|
/**
|
|
168
297
|
* @template {readonly Type[]} T
|
|
@@ -175,6 +304,7 @@ const orValidate =
|
|
|
175
304
|
const validateVisitor = /** @type {any} */ ({
|
|
176
305
|
tuple: tupleValidate,
|
|
177
306
|
struct: structValidate,
|
|
307
|
+
rest: restValidate,
|
|
178
308
|
array: arrayValidate,
|
|
179
309
|
record: recordValidate,
|
|
180
310
|
or: orValidate,
|
|
@@ -203,15 +333,19 @@ const validateVisitor = /** @type {any} */ ({
|
|
|
203
333
|
* v(input) // ['ok', input] — the same array, not a copy
|
|
204
334
|
* v([1, 'two']) // ['error', { path: ['1'], message: 'unexpected value' }]
|
|
205
335
|
*
|
|
206
|
-
* //
|
|
207
|
-
* validate([number, number])([1, 2, 3]) // ['
|
|
208
|
-
* validate({ a: number })({ a: 1, b: 2 }) // ['
|
|
336
|
+
* // closed, so a member the schema does not name rejects the value
|
|
337
|
+
* validate([number, number])([1, 2, 3]) // ['error', …]
|
|
338
|
+
* validate({ a: number })({ a: 1, b: 2 }) // ['error', …]
|
|
209
339
|
*
|
|
210
340
|
* // an absent optional member stays absent
|
|
211
341
|
* validate({ a: number, b: option(string) })({ a: 1 }) // ['ok', { a: 1 }]
|
|
342
|
+
*
|
|
343
|
+
* // a stated rest says what the undeclared members may be; `open` says anything
|
|
344
|
+
* validate(rest({ a: number }, number))({ a: 1, b: 2 }) // ['ok', { a: 1, b: 2 }]
|
|
345
|
+
* validate(open({ a: number }))({ a: 1, b: 'x' }) // ['ok', { a: 1, b: 'x' }]
|
|
212
346
|
* ```
|
|
213
347
|
*
|
|
214
|
-
* @type {<T extends Type>(rtti: T) => Validate<T>}
|
|
348
|
+
* @type {<const T extends Type>(rtti: T) => Validate<T>}
|
|
215
349
|
*/
|
|
216
350
|
export const validate = rtti =>
|
|
217
351
|
(visit(validateVisitor)(rtti))
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @import { ValidationError, ValidateE } from '../common/types.ts'
|
|
2
|
+
* @import { ValidationError, ValidateE, Validate } from '../common/types.ts'
|
|
3
3
|
* @import { Type } from '../types.ts'
|
|
4
4
|
* @import { Equal } from '../../ts/types.ts'
|
|
5
5
|
* @import { Ts, Unknown } from '../ts/types.ts'
|
|
6
6
|
* @import { Unknown as DjsUnknown } from '../../../djs/types.ts'
|
|
7
7
|
* @import { Assert } from '../../../asserts/types.ts'
|
|
8
8
|
*/
|
|
9
|
+
import { never } from '../module.f.mjs';
|
|
10
|
+
export type _RecursiveRest = () => readonly ['rest', readonly [_RecursiveRest], typeof never];
|
|
11
|
+
export type _OrCycleA = () => readonly ['or', _OrCycleB];
|
|
12
|
+
export type _OrCycleB = () => readonly ['or', _OrCycleA];
|
|
13
|
+
export type _SelfList = () => readonly ['or', undefined, () => readonly ['array', _SelfList]];
|
|
9
14
|
export declare const proof: {
|
|
10
15
|
verbatim: {
|
|
11
16
|
absentOptionalStaysAbsent: () => void;
|
|
@@ -13,6 +18,14 @@ export declare const proof: {
|
|
|
13
18
|
referenceIdentity: () => void;
|
|
14
19
|
};
|
|
15
20
|
sameAcceptanceAsParse: () => void;
|
|
21
|
+
sameAcceptanceInTheDataForm: () => void;
|
|
22
|
+
optionalPositions: () => void;
|
|
23
|
+
interiorOptionBeforeRequired: () => void;
|
|
24
|
+
sparseTuple: {
|
|
25
|
+
holeIsDeclaredUndefined: () => void;
|
|
26
|
+
arityIsTheSchemaLength: () => void;
|
|
27
|
+
nonIndexPropertyIsNotDeclared: () => void;
|
|
28
|
+
};
|
|
16
29
|
boolean: {
|
|
17
30
|
ok: () => void;
|
|
18
31
|
error: () => void;
|
|
@@ -71,7 +84,7 @@ export declare const proof: {
|
|
|
71
84
|
};
|
|
72
85
|
tuple: {
|
|
73
86
|
ok: () => void;
|
|
74
|
-
|
|
87
|
+
extraItemsRejected: () => void;
|
|
75
88
|
shortArrayKeepsItsLength: () => void;
|
|
76
89
|
empty: () => void;
|
|
77
90
|
error: () => void;
|
|
@@ -125,4 +138,17 @@ export declare const proof: {
|
|
|
125
138
|
arrayOfArrays: () => void;
|
|
126
139
|
recordOfRecords: () => void;
|
|
127
140
|
};
|
|
141
|
+
rest: {
|
|
142
|
+
verbatim: () => void;
|
|
143
|
+
absentOptionalStaysAbsent: () => void;
|
|
144
|
+
path: () => void;
|
|
145
|
+
notAContainer: () => void;
|
|
146
|
+
};
|
|
147
|
+
emptyRests: {
|
|
148
|
+
dropped: () => void;
|
|
149
|
+
kept: () => void;
|
|
150
|
+
};
|
|
151
|
+
beyondAClosedPrefix: () => void;
|
|
152
|
+
lengthDoesNotBoundTheWalk: () => void;
|
|
153
|
+
arrayOptional: () => void;
|
|
128
154
|
};
|