functionalscript 0.46.0 → 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 +120 -0
- package/fjs/types/rtti/validate/module.f.mjs +351 -0
- package/fjs/types/rtti/validate/proof.f.d.mts +154 -0
- package/fjs/types/rtti/validate/proof.f.mjs +781 -0
- 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
|
@@ -5,31 +5,38 @@
|
|
|
5
5
|
* a `Parse<T>` function. When called with an unknown value, it returns a `Result`
|
|
6
6
|
* that is either `['ok', newValue]` or `['error', { path, message }]`.
|
|
7
7
|
*
|
|
8
|
-
* **Structs and tuples are
|
|
9
|
-
* declares
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* -
|
|
14
|
-
* - Structs: undeclared properties are accepted; the result has only the
|
|
15
|
-
* schema's keys.
|
|
8
|
+
* **Structs and tuples are closed.** A bare `Struct` or `Tuple` admits the
|
|
9
|
+
* members it declares and no others, so a value carrying more is rejected:
|
|
10
|
+
*
|
|
11
|
+
* - Tuples: an array longer than the schema is rejected — by length as well as
|
|
12
|
+
* by member, since a hole past the prefix is no member.
|
|
13
|
+
* - Structs: an undeclared property is rejected.
|
|
16
14
|
* - Arrays/records: every element/value is itself parsed, so a fresh container is
|
|
17
15
|
* always returned even if the inner type is a primitive.
|
|
18
16
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* Closedness is about *undeclared* members, and leaves the required/optional
|
|
18
|
+
* rule alone: a member is required exactly when its set excludes `undefined` —
|
|
19
|
+
* an absent member reads as `undefined`, on both kinds — so a shorter array
|
|
20
|
+
* whose trailing position admits `undefined` is accepted and the gap is
|
|
21
|
+
* filled.
|
|
22
|
+
*
|
|
23
|
+
* A tuple schema declares by length, so a hole in the *schema* is a declared
|
|
24
|
+
* position whose schema is `undefined` — see "A hole is a declared position"
|
|
25
|
+
* in `../README.md`.
|
|
22
26
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
27
|
+
* The length check is the model rather than an inference from the rendering.
|
|
28
|
+
* #1622 added one by reading `Ts<readonly[42]>`'s exact tuple as the value
|
|
29
|
+
* model while the model said open, and was reverted for that reason; what has
|
|
30
|
+
* changed since is the model, not the reading.
|
|
26
31
|
*
|
|
27
|
-
* **
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
32
|
+
* **A stated rest widens acceptance, not construction.** `rest(c, r)` holds
|
|
33
|
+
* every member `c` does not declare to `r`, and `open(c)` — `rest(c, unknown)`
|
|
34
|
+
* — admits anything else at all; either way the result carries the declared
|
|
35
|
+
* members and nothing else, so a rest says what an undeclared member must be,
|
|
36
|
+
* not that the reader should keep it. Openness is what makes a parser
|
|
37
|
+
* forward-compatible with an extended serialization format, so a schema read
|
|
38
|
+
* against a wire format someone else may extend says `open`. The reader that
|
|
39
|
+
* keeps every member is `../validate/module.f.mjs`.
|
|
33
40
|
*
|
|
34
41
|
* The error shape, path bookkeeping, primitive checks, and schema
|
|
35
42
|
* recognition (`visit`) come from `../common/module.f.mjs`.
|
|
@@ -38,11 +45,11 @@
|
|
|
38
45
|
*
|
|
39
46
|
* @module
|
|
40
47
|
*
|
|
41
|
-
* @import {
|
|
48
|
+
* @import { ConstObject, Info1, Tag1, Type } from '../types.ts'
|
|
42
49
|
* @import { Result as CommonResult } from '../../result/types.ts'
|
|
43
50
|
* @import { StringMap } from '../../object/types.ts'
|
|
44
51
|
* @import { List } from '../../list/types.ts'
|
|
45
|
-
* @import { Container, IsContainer, ValidationError, Visitor } from '../common/types.ts'
|
|
52
|
+
* @import { Container, Fits, IsContainer, SchemaEntries, ValidateE, ValidationError, Visitor } from '../common/types.ts'
|
|
46
53
|
* @import { Unknown } from '../ts/types.ts'
|
|
47
54
|
* @import { Parse } from './types.ts'
|
|
48
55
|
*/
|
|
@@ -56,11 +63,13 @@ import {
|
|
|
56
63
|
isObject,
|
|
57
64
|
orVisit,
|
|
58
65
|
primitive0Validate,
|
|
66
|
+
structSchemaEntries,
|
|
67
|
+
tupleSchemaEntries,
|
|
68
|
+
undeclaredMembers,
|
|
59
69
|
verror,
|
|
60
70
|
visit,
|
|
61
71
|
} from '../common/module.f.mjs'
|
|
62
|
-
|
|
63
|
-
const { entries } = Object
|
|
72
|
+
import { emptyRest } from '../data/module.f.mjs'
|
|
64
73
|
|
|
65
74
|
/** @typedef {CommonResult<Unknown, ValidationError>} _ItemResult */
|
|
66
75
|
|
|
@@ -82,6 +91,10 @@ const emptyEntries = null
|
|
|
82
91
|
const consEntry = (acc, k, v) =>
|
|
83
92
|
({ first: [k, v], tail: acc })
|
|
84
93
|
|
|
94
|
+
/** A uniform container declares no member by name, so every one is undeclared. */
|
|
95
|
+
/** @type {readonly string[]} */
|
|
96
|
+
const noDeclared = []
|
|
97
|
+
|
|
85
98
|
/** Restores forward order from `consEntry`'s reverse-order list, in one linear pass. */
|
|
86
99
|
/** @type {(list: List<readonly [string, Unknown]>) => ReadonlyArray<readonly [string, Unknown]>} */
|
|
87
100
|
const orderedEntries = list =>
|
|
@@ -92,72 +105,190 @@ const orderedEntries = list =>
|
|
|
92
105
|
* from each item's parsed result. The inner item parser is instantiated lazily
|
|
93
106
|
* (only when the container is non-empty) so recursive schemas don't recurse
|
|
94
107
|
* forever on empty containers.
|
|
108
|
+
*
|
|
109
|
+
* The members are `undeclaredMembers`', not `Object.entries`', so `array(t)`
|
|
110
|
+
* and `rest([], t)` walk a value the same way — see `containerValidate` in
|
|
111
|
+
* `../validate/module.f.mjs`, which says what an own-entry walk got wrong.
|
|
112
|
+
*
|
|
113
|
+
* `fits` bounds the array kind's length when its element set admits nothing —
|
|
114
|
+
* the same rule on the other reader.
|
|
95
115
|
*/
|
|
96
116
|
const containerParse =
|
|
97
117
|
/**
|
|
98
118
|
* @template {Tag1} K
|
|
99
119
|
* @param {IsContainer<Container<K>>} isContainer
|
|
100
120
|
* @param {_Rebuild} rebuild
|
|
121
|
+
* @param {(item: Type) => Fits<Container<K>>} restFits
|
|
101
122
|
* @returns {<I extends Type>(item: I) => Parse<Info1<K, I>>}
|
|
102
123
|
*/
|
|
103
|
-
(isContainer, rebuild) =>
|
|
104
|
-
item =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
124
|
+
(isContainer, rebuild, restFits) =>
|
|
125
|
+
item => {
|
|
126
|
+
// Depends on the schema alone, so it is built once per schema rather
|
|
127
|
+
// than once per parsed value.
|
|
128
|
+
const fits = restFits(item)
|
|
129
|
+
return value => {
|
|
130
|
+
if (!isContainer(value)) {
|
|
131
|
+
return verror('unexpected value')
|
|
132
|
+
}
|
|
133
|
+
const e = undeclaredMembers(noDeclared, value)
|
|
134
|
+
if (e.length === 0) {
|
|
135
|
+
return fits(value, 0)
|
|
136
|
+
? /** @type {any} */ (ok(rebuild([])))
|
|
137
|
+
: verror('unexpected value')
|
|
138
|
+
}
|
|
139
|
+
const itemParse = /** @type {any} */ (parse(item))
|
|
140
|
+
const r = eachEntry(e, (_k, v) => itemParse(v), emptyEntries, consEntry)
|
|
141
|
+
return r[0] === 'error' ? r : /** @type {any} */ (ok(rebuild(orderedEntries(r[1]))))
|
|
111
142
|
}
|
|
112
|
-
const itemParse = /** @type {any} */ (parse(item))
|
|
113
|
-
const r = eachEntry(e, (_k, v) => itemParse(v), emptyEntries, consEntry)
|
|
114
|
-
return r[0] === 'error' ? r : /** @type {any} */ (ok(rebuild(orderedEntries(r[1]))))
|
|
115
143
|
}
|
|
116
144
|
|
|
117
|
-
const arrayParse = containerParse(
|
|
145
|
+
const arrayParse = containerParse(
|
|
146
|
+
isArray,
|
|
147
|
+
arrayRebuild,
|
|
148
|
+
// The cast is the price of one factory over two kinds: `Container<K>` is
|
|
149
|
+
// the union until `K` is bound, and only the array arm has a `length`.
|
|
150
|
+
item => (value, declared) =>
|
|
151
|
+
/** @type {ReadonlyArray<Unknown>} */ (value).length <= declared || !emptyRest([], item),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
const recordParse = containerParse(isObject, recordRebuild, () => () => true)
|
|
118
155
|
|
|
119
|
-
|
|
156
|
+
/** A container has nothing to collect from an undeclared member — only pass/fail matters. */
|
|
157
|
+
const noAccumulate = () => undefined
|
|
120
158
|
|
|
121
159
|
/**
|
|
122
|
-
* Builds a parser for `Tuple` or `Struct` const schemas
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
160
|
+
* Builds a parser for `Tuple` or `Struct` const schemas — **closed**: the
|
|
161
|
+
* members the schema declares and no others. It reads each declared member
|
|
162
|
+
* into the rebuilt result, then answers for every member of the value the
|
|
163
|
+
* schema does not name.
|
|
164
|
+
*
|
|
165
|
+
* `fits` is the one thing the two kinds do not share. An undeclared member is
|
|
166
|
+
* a member on both, but an array is also *as long as it is*: a hole past the
|
|
167
|
+
* prefix is no member and would slip through the member check alone, so the
|
|
168
|
+
* array kind answers with its length as well.
|
|
126
169
|
*/
|
|
127
170
|
const constContainerParse =
|
|
128
171
|
/**
|
|
129
|
-
* @template {Unknown} C
|
|
172
|
+
* @template {ReadonlyArray<Unknown> | StringMap<Unknown>} C
|
|
173
|
+
* @template {ConstObject} S
|
|
130
174
|
* @param {IsContainer<C>} isContainer
|
|
175
|
+
* @param {SchemaEntries<S>} schemaEntries
|
|
131
176
|
* @param {(value: C, k: string) => Unknown} getItem
|
|
132
177
|
* @param {_Rebuild} rebuild
|
|
133
|
-
* @
|
|
178
|
+
* @param {Fits<C>} fits
|
|
179
|
+
* @returns {<T extends S>(rtti: T) => Parse<T>}
|
|
134
180
|
*/
|
|
135
|
-
(isContainer, getItem, rebuild) =>
|
|
136
|
-
rtti =>
|
|
137
|
-
|
|
138
|
-
|
|
181
|
+
(isContainer, schemaEntries, getItem, rebuild, fits) =>
|
|
182
|
+
rtti => {
|
|
183
|
+
// Depend on `rtti` alone, so they are computed once per schema.
|
|
184
|
+
const rttiEntries = schemaEntries(rtti)
|
|
185
|
+
const declared = rttiEntries.map(([k]) => k)
|
|
186
|
+
return value => {
|
|
187
|
+
if (!isContainer(value)) {
|
|
188
|
+
return verror('unexpected value')
|
|
189
|
+
}
|
|
190
|
+
const r = eachEntry(
|
|
191
|
+
rttiEntries,
|
|
192
|
+
(k, t) => (/** @type {any} */ (parse(t))(getItem(value, k))),
|
|
193
|
+
emptyEntries,
|
|
194
|
+
consEntry,
|
|
195
|
+
)
|
|
196
|
+
if (r[0] === 'error') { return r }
|
|
197
|
+
return undeclaredMembers(declared, value).length === 0 && fits(value, declared.length)
|
|
198
|
+
? /** @type {any} */ (ok(rebuild(orderedEntries(r[1]))))
|
|
199
|
+
: verror('unexpected value')
|
|
139
200
|
}
|
|
140
|
-
const r = eachEntry(
|
|
141
|
-
entries(rtti),
|
|
142
|
-
(k, t) => (/** @type {any} */ (parse(t))(getItem(value, k))),
|
|
143
|
-
emptyEntries,
|
|
144
|
-
consEntry,
|
|
145
|
-
)
|
|
146
|
-
return r[0] === 'error' ? r : /** @type {any} */ (ok(rebuild(orderedEntries(r[1]))))
|
|
147
201
|
}
|
|
148
202
|
|
|
149
203
|
const tupleParse = constContainerParse(
|
|
150
204
|
isArray,
|
|
205
|
+
tupleSchemaEntries,
|
|
151
206
|
(value, k) => value[Number(k)],
|
|
152
207
|
arrayRebuild,
|
|
208
|
+
(value, declared) => value.length <= declared,
|
|
153
209
|
)
|
|
154
210
|
|
|
155
211
|
const structParse = constContainerParse(
|
|
156
212
|
isObject,
|
|
213
|
+
structSchemaEntries,
|
|
214
|
+
(value, k) => value[k],
|
|
215
|
+
recordRebuild,
|
|
216
|
+
() => true,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Builds a parser for a container with a stated `rest`. The declared members
|
|
221
|
+
* are read exactly as the bare form reads them, and every member the schema
|
|
222
|
+
* does not name is held to `rest` — checked on the way in and absent from what
|
|
223
|
+
* is built, since `rest` says what an undeclared member must be and not that
|
|
224
|
+
* the result should carry it.
|
|
225
|
+
*
|
|
226
|
+
* `restFits` carries the array kind's length bound, which a `rest` removes
|
|
227
|
+
* only while it admits something — see `restContainerValidate` in
|
|
228
|
+
* `../validate/module.f.mjs`, the same rule on the other reader.
|
|
229
|
+
*/
|
|
230
|
+
const restContainerParse =
|
|
231
|
+
/**
|
|
232
|
+
* @template {ReadonlyArray<Unknown> | StringMap<Unknown>} C
|
|
233
|
+
* @template {ConstObject} S
|
|
234
|
+
* @param {IsContainer<C>} isContainer
|
|
235
|
+
* @param {SchemaEntries<S>} schemaEntries
|
|
236
|
+
* @param {(value: C, k: string) => Unknown} getItem
|
|
237
|
+
* @param {_Rebuild} rebuild
|
|
238
|
+
* @param {(rtti: S, r: Type) => Fits<C>} restFits
|
|
239
|
+
* @returns {(rtti: S, r: Type) => ValidateE}
|
|
240
|
+
*/
|
|
241
|
+
(isContainer, schemaEntries, getItem, rebuild, restFits) =>
|
|
242
|
+
(rtti, r) => {
|
|
243
|
+
// Depend on the schema alone, so they are computed once per schema.
|
|
244
|
+
const rttiEntries = schemaEntries(rtti)
|
|
245
|
+
const declared = rttiEntries.map(([k]) => k)
|
|
246
|
+
const fits = restFits(rtti, r)
|
|
247
|
+
return value => {
|
|
248
|
+
if (!isContainer(value)) {
|
|
249
|
+
return verror('unexpected value')
|
|
250
|
+
}
|
|
251
|
+
const d = eachEntry(
|
|
252
|
+
rttiEntries,
|
|
253
|
+
(k, t) => (/** @type {any} */ (parse(t))(getItem(value, k))),
|
|
254
|
+
emptyEntries,
|
|
255
|
+
consEntry,
|
|
256
|
+
)
|
|
257
|
+
if (d[0] === 'error') { return d }
|
|
258
|
+
const extra = undeclaredMembers(declared, value)
|
|
259
|
+
if (extra.length === 0) {
|
|
260
|
+
return fits(value, declared.length)
|
|
261
|
+
? ok(rebuild(orderedEntries(d[1])))
|
|
262
|
+
: verror('unexpected value')
|
|
263
|
+
}
|
|
264
|
+
const restParse = /** @type {any} */ (parse(r))
|
|
265
|
+
const e = eachEntry(extra, (_k, v) => restParse(v), undefined, noAccumulate)
|
|
266
|
+
return e[0] === 'error' ? e : ok(rebuild(orderedEntries(d[1])))
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const restTupleParse = restContainerParse(
|
|
271
|
+
isArray,
|
|
272
|
+
tupleSchemaEntries,
|
|
273
|
+
(value, k) => value[Number(k)],
|
|
274
|
+
arrayRebuild,
|
|
275
|
+
(rtti, r) => (value, declared) => value.length <= declared || !emptyRest(rtti, r),
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
const restStructParse = restContainerParse(
|
|
279
|
+
isObject,
|
|
280
|
+
structSchemaEntries,
|
|
157
281
|
(value, k) => value[k],
|
|
158
282
|
recordRebuild,
|
|
283
|
+
() => () => true,
|
|
159
284
|
)
|
|
160
285
|
|
|
286
|
+
/** @type {(rtti: ConstObject, r: Type) => ValidateE} */
|
|
287
|
+
const restParse = (rtti, r) =>
|
|
288
|
+
rtti instanceof Array
|
|
289
|
+
? restTupleParse(rtti, r)
|
|
290
|
+
: restStructParse(rtti, r)
|
|
291
|
+
|
|
161
292
|
const orParse =
|
|
162
293
|
/**
|
|
163
294
|
* @template {readonly Type[]} T
|
|
@@ -184,16 +315,23 @@ const orParse =
|
|
|
184
315
|
* p([1, 2, 3]) // ['ok', [1, 2, 3]] (a new array)
|
|
185
316
|
* p([1, 'two']) // ['error', { path: ['1'], message: 'unexpected value' }]
|
|
186
317
|
*
|
|
187
|
-
* //
|
|
188
|
-
* parse([number, number])([1, 2, 3]) // ['
|
|
318
|
+
* // closed: a longer array is rejected
|
|
319
|
+
* parse([number, number])([1, 2, 3]) // ['error', …]
|
|
320
|
+
*
|
|
321
|
+
* // closed: the undeclared key is rejected
|
|
322
|
+
* parse({ a: number })({ a: 1, b: 2 }) // ['error', …]
|
|
323
|
+
*
|
|
324
|
+
* // with a rest: it is accepted, checked, and not carried over
|
|
325
|
+
* parse(rest({ a: number }, string))({ a: 1, b: 'x' }) // ['ok', { a: 1 }]
|
|
189
326
|
*
|
|
190
|
-
* // open
|
|
191
|
-
* parse({ a: number })({ a: 1, b: 2 }) // ['ok', { a: 1 }]
|
|
327
|
+
* // `open` admits anything else at all, and carries none of it over
|
|
328
|
+
* parse(open({ a: number }))({ a: 1, b: 2 }) // ['ok', { a: 1 }]
|
|
192
329
|
* ```
|
|
193
330
|
*/
|
|
194
331
|
const parseVisitor = /** @type {any} */ ({
|
|
195
332
|
tuple: tupleParse,
|
|
196
333
|
struct: structParse,
|
|
334
|
+
rest: restParse,
|
|
197
335
|
array: arrayParse,
|
|
198
336
|
record: recordParse,
|
|
199
337
|
or: orParse,
|
|
@@ -202,6 +340,6 @@ const parseVisitor = /** @type {any} */ ({
|
|
|
202
340
|
unknown: () => ok,
|
|
203
341
|
})
|
|
204
342
|
|
|
205
|
-
/** @type {<T extends Type>(rtti: T) => Parse<T>} */
|
|
343
|
+
/** @type {<const T extends Type>(rtti: T) => Parse<T>} */
|
|
206
344
|
export const parse = rtti =>
|
|
207
345
|
(visit(parseVisitor)(rtti))
|
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
* @import { ValidationError } from '../common/types.ts'
|
|
3
3
|
* @import { Equal } from '../../ts/types.ts'
|
|
4
4
|
* @import { Ts } from '../ts/types.ts'
|
|
5
|
+
* @import { Parse } from './types.ts'
|
|
5
6
|
* @import { Unknown as DjsUnknown } from '../../../djs/types.ts'
|
|
6
7
|
* @import { Assert } from '../../../asserts/types.ts'
|
|
8
|
+
* @import { Phantom } from '../../phantom/types.ts'
|
|
7
9
|
*/
|
|
10
|
+
import { number } from '../module.f.mjs';
|
|
11
|
+
export type _Node = readonly [number, _Node | undefined];
|
|
12
|
+
export type _Nest = () => readonly ['rest', {
|
|
13
|
+
readonly a: typeof number;
|
|
14
|
+
}, _Nest];
|
|
8
15
|
export declare const proof: {
|
|
9
16
|
boolean: {
|
|
10
17
|
ok: () => void;
|
|
@@ -64,13 +71,13 @@ export declare const proof: {
|
|
|
64
71
|
};
|
|
65
72
|
tuple: {
|
|
66
73
|
ok: () => void;
|
|
67
|
-
|
|
74
|
+
extraItemsRejected: () => void;
|
|
68
75
|
shortArrayFillsAnOptionalPosition: () => void;
|
|
69
76
|
error: () => void;
|
|
70
77
|
};
|
|
71
78
|
struct: {
|
|
72
79
|
ok: () => void;
|
|
73
|
-
|
|
80
|
+
extraKeysRejected: () => void;
|
|
74
81
|
error: () => void;
|
|
75
82
|
};
|
|
76
83
|
};
|
|
@@ -120,4 +127,32 @@ export declare const proof: {
|
|
|
120
127
|
arrayOfArrays: () => void;
|
|
121
128
|
recordOfRecords: () => void;
|
|
122
129
|
};
|
|
130
|
+
closed: {
|
|
131
|
+
tuple: {
|
|
132
|
+
exact: () => void;
|
|
133
|
+
extraRejected: () => void;
|
|
134
|
+
holePastThePrefixRejected: () => void;
|
|
135
|
+
nonIndexKeyRejected: () => void;
|
|
136
|
+
shortArray: () => void;
|
|
137
|
+
empty: () => void;
|
|
138
|
+
notAnArray: () => void;
|
|
139
|
+
};
|
|
140
|
+
struct: {
|
|
141
|
+
exact: () => void;
|
|
142
|
+
extraRejected: () => void;
|
|
143
|
+
unknownMemberIsStillDeclared: () => void;
|
|
144
|
+
empty: () => void;
|
|
145
|
+
notAnObject: () => void;
|
|
146
|
+
};
|
|
147
|
+
rest: {
|
|
148
|
+
checkedAndDropped: () => void;
|
|
149
|
+
rejected: () => void;
|
|
150
|
+
unknownRestIsOpen: () => void;
|
|
151
|
+
emptyRestBoundsTheLength: () => void;
|
|
152
|
+
};
|
|
153
|
+
path: () => void;
|
|
154
|
+
recursive: () => void;
|
|
155
|
+
recursiveRest: () => void;
|
|
156
|
+
};
|
|
157
|
+
arrayOptional: () => void;
|
|
123
158
|
};
|