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.
Files changed (178) hide show
  1. package/fjs/basen/cbase32/module.f.mjs +18 -10
  2. package/fjs/basen/cbase32/proof.f.d.mts +3 -0
  3. package/fjs/basen/cbase32/proof.f.mjs +24 -2
  4. package/fjs/basen/proof.f.mjs +8 -16
  5. package/fjs/bnf/ll1/proof.f.d.mts +2 -0
  6. package/fjs/bnf/ll1/proof.f.mjs +33 -18
  7. package/fjs/bnf/module.f.d.mts +2 -2
  8. package/fjs/bnf/module.f.mjs +2 -2
  9. package/fjs/cas/proof.f.mjs +4 -26
  10. package/fjs/ci/common/module.f.mjs +6 -0
  11. package/fjs/ci/config/module.f.d.mts +7 -7
  12. package/fjs/ci/config/module.f.mjs +7 -7
  13. package/fjs/cli/module.f.mjs +14 -10
  14. package/fjs/crypto/vdf/module.f.d.mts +0 -1
  15. package/fjs/crypto/vdf/module.f.mjs +3 -14
  16. package/fjs/dev/module.f.mjs +10 -20
  17. package/fjs/djs/parser/module.f.d.mts +101 -53
  18. package/fjs/djs/parser/module.f.mjs +732 -525
  19. package/fjs/djs/parser/proof.f.d.mts +6 -0
  20. package/fjs/djs/parser/proof.f.mjs +315 -11
  21. package/fjs/djs/parser/types.d.ts +29 -2
  22. package/fjs/djs/tokenizer/module.f.mjs +122 -105
  23. package/fjs/djs/tokenizer/proof.f.mjs +17 -0
  24. package/fjs/djs/transpiler/module.f.mjs +4 -5
  25. package/fjs/djs/transpiler/proof.f.d.mts +2 -0
  26. package/fjs/djs/transpiler/proof.f.mjs +25 -0
  27. package/fjs/edag/amnesia/module.f.d.mts +20 -0
  28. package/fjs/edag/amnesia/module.f.mjs +303 -0
  29. package/fjs/edag/amnesia/proof.f.d.mts +79 -0
  30. package/fjs/edag/amnesia/proof.f.mjs +573 -0
  31. package/fjs/edag/amnesia/types.d.ts +15 -0
  32. package/fjs/edag/module.f.d.mts +410 -0
  33. package/fjs/edag/module.f.mjs +559 -0
  34. package/fjs/edag/proof.f.d.mts +116 -0
  35. package/fjs/edag/proof.f.mjs +686 -0
  36. package/fjs/edag/types.d.ts +55 -0
  37. package/fjs/effects/node/memory/module.d.mts +37 -3
  38. package/fjs/effects/node/memory/module.mjs +38 -4
  39. package/fjs/effects/node/memory/proof.d.mts +5 -2
  40. package/fjs/effects/node/memory/proof.mjs +36 -6
  41. package/fjs/effects/node/module.d.mts +9 -1
  42. package/fjs/effects/node/module.f.d.mts +39 -0
  43. package/fjs/effects/node/module.f.mjs +46 -0
  44. package/fjs/effects/node/module.mjs +234 -32
  45. package/fjs/effects/node/types.d.ts +28 -2
  46. package/fjs/effects/node/virtual/module.f.d.mts +19 -10
  47. package/fjs/effects/node/virtual/module.f.mjs +246 -32
  48. package/fjs/effects/node/virtual/proof.f.d.mts +17 -2
  49. package/fjs/effects/node/virtual/proof.f.mjs +232 -8
  50. package/fjs/effects/node/virtual/types.d.ts +57 -1
  51. package/fjs/emergent_testing/browser/proof.d.mts +55 -0
  52. package/fjs/emergent_testing/browser/proof.mjs +352 -0
  53. package/fjs/emergent_testing/browser/species.proof.d.mts +4 -0
  54. package/fjs/emergent_testing/browser/species.proof.mjs +45 -0
  55. package/fjs/emergent_testing/browser.d.mts +79 -0
  56. package/fjs/emergent_testing/browser.mjs +432 -0
  57. package/fjs/emergent_testing/proof.f.d.mts +1 -1
  58. package/fjs/emergent_testing/proof.f.mjs +2 -2
  59. package/fjs/js/string_escape/module.f.d.mts +47 -0
  60. package/fjs/js/string_escape/module.f.mjs +95 -0
  61. package/fjs/js/string_escape/proof.f.d.mts +8 -0
  62. package/fjs/js/string_escape/proof.f.mjs +68 -0
  63. package/fjs/js/tokenizer/module.f.mjs +14 -12
  64. package/fjs/mcp/cas/module.f.d.mts +1 -7
  65. package/fjs/mcp/cas/module.f.mjs +24 -15
  66. package/fjs/mcp/evo/module.f.mjs +9 -24
  67. package/fjs/mcp/module.f.d.mts +3 -1
  68. package/fjs/mcp/module.f.mjs +4 -2
  69. package/fjs/media/json/rtti/module.f.d.mts +2 -2
  70. package/fjs/media/json/rtti/module.f.mjs +1 -1
  71. package/fjs/media/json/schema/module.f.d.mts +15 -5
  72. package/fjs/media/json/schema/module.f.mjs +42 -15
  73. package/fjs/media/json/schema/proof.f.d.mts +10 -1
  74. package/fjs/media/json/schema/proof.f.mjs +88 -16
  75. package/fjs/media/json/serializer/module.f.mjs +14 -26
  76. package/fjs/media/json/types.d.ts +1 -1
  77. package/fjs/media/lock/module.f.d.mts +8 -3
  78. package/fjs/media/lock/module.f.mjs +9 -3
  79. package/fjs/media/lock/proof.f.d.mts +1 -1
  80. package/fjs/media/lock/proof.f.mjs +2 -2
  81. package/fjs/media/lock/types.d.ts +1 -3
  82. package/fjs/media/module.f.d.mts +20 -13
  83. package/fjs/media/module.f.mjs +29 -16
  84. package/fjs/media/note/module.f.d.mts +14 -7
  85. package/fjs/media/note/module.f.mjs +14 -7
  86. package/fjs/media/note/proof.f.mjs +1 -1
  87. package/fjs/media/proof.f.mjs +5 -5
  88. package/fjs/media/revision/module.f.d.mts +8 -2
  89. package/fjs/media/revision/module.f.mjs +9 -3
  90. package/fjs/media/revision/proof.f.mjs +1 -1
  91. package/fjs/media/revision/types.d.ts +1 -1
  92. package/fjs/media/type/module.f.d.mts +20 -0
  93. package/fjs/media/type/module.f.mjs +74 -0
  94. package/fjs/media/type/proof.f.d.mts +5 -0
  95. package/fjs/media/type/proof.f.mjs +24 -1
  96. package/fjs/module.f.mjs +6 -0
  97. package/fjs/path/module.f.d.mts +60 -3
  98. package/fjs/path/module.f.mjs +138 -14
  99. package/fjs/path/proof.f.d.mts +3 -0
  100. package/fjs/path/proof.f.mjs +298 -2
  101. package/fjs/protocol/json_rpc/module.f.d.mts +65 -28
  102. package/fjs/protocol/json_rpc/module.f.mjs +49 -13
  103. package/fjs/protocol/json_rpc/proof.f.d.mts +4 -0
  104. package/fjs/protocol/json_rpc/proof.f.mjs +29 -0
  105. package/fjs/protocol/json_rpc/types.d.ts +12 -2
  106. package/fjs/protocol/mcp/module.f.d.mts +89 -54
  107. package/fjs/protocol/mcp/module.f.mjs +110 -56
  108. package/fjs/protocol/mcp/proof.f.d.mts +13 -0
  109. package/fjs/protocol/mcp/proof.f.mjs +107 -7
  110. package/fjs/protocol/mcp/stdio/module.f.mjs +3 -4
  111. package/fjs/protocol/mcp/stdio/proof.f.mjs +2 -2
  112. package/fjs/protocol/mcp/types.d.ts +14 -2
  113. package/fjs/text/utf8/module.f.mjs +27 -1
  114. package/fjs/text/utf8/proof.f.mjs +37 -0
  115. package/fjs/types/array/module.f.mjs +18 -11
  116. package/fjs/types/array/proof.f.mjs +27 -1
  117. package/fjs/types/bigfloat/module.f.d.mts +48 -9
  118. package/fjs/types/bigfloat/module.f.mjs +147 -59
  119. package/fjs/types/bigfloat/proof.f.d.mts +2 -0
  120. package/fjs/types/bigfloat/proof.f.mjs +136 -4
  121. package/fjs/types/bigfloat/types.d.ts +17 -0
  122. package/fjs/types/function/module.f.d.mts +11 -2
  123. package/fjs/types/function/module.f.mjs +20 -2
  124. package/fjs/types/function/proof.f.mjs +5 -1
  125. package/fjs/types/phantom/types.d.ts +21 -0
  126. package/fjs/types/result/module.f.d.mts +4 -4
  127. package/fjs/types/result/module.f.mjs +2 -2
  128. package/fjs/types/result/proof.f.d.mts +6 -2
  129. package/fjs/types/result/proof.f.mjs +17 -1
  130. package/fjs/types/rtti/common/module.f.d.mts +78 -5
  131. package/fjs/types/rtti/common/module.f.mjs +168 -3
  132. package/fjs/types/rtti/common/proof.f.d.mts +14 -0
  133. package/fjs/types/rtti/common/proof.f.mjs +61 -2
  134. package/fjs/types/rtti/common/types.d.ts +23 -1
  135. package/fjs/types/rtti/data/module.f.d.mts +57 -1
  136. package/fjs/types/rtti/data/module.f.mjs +241 -47
  137. package/fjs/types/rtti/data/proof.f.d.mts +10 -0
  138. package/fjs/types/rtti/data/proof.f.mjs +272 -39
  139. package/fjs/types/rtti/data/types.d.ts +14 -7
  140. package/fjs/types/rtti/host.proof.d.mts +33 -0
  141. package/fjs/types/rtti/host.proof.mjs +170 -0
  142. package/fjs/types/rtti/module.f.d.mts +51 -6
  143. package/fjs/types/rtti/module.f.mjs +50 -3
  144. package/fjs/types/rtti/parse/module.f.d.mts +31 -24
  145. package/fjs/types/rtti/parse/module.f.mjs +197 -59
  146. package/fjs/types/rtti/parse/proof.f.d.mts +37 -2
  147. package/fjs/types/rtti/parse/proof.f.mjs +171 -47
  148. package/fjs/types/rtti/proof.f.d.mts +6 -0
  149. package/fjs/types/rtti/proof.f.mjs +42 -1
  150. package/fjs/types/rtti/ts/module.f.d.mts +9 -3
  151. package/fjs/types/rtti/ts/module.f.mjs +68 -15
  152. package/fjs/types/rtti/ts/proof.f.d.mts +42 -2
  153. package/fjs/types/rtti/ts/proof.f.mjs +139 -6
  154. package/fjs/types/rtti/ts/types.d.ts +164 -16
  155. package/fjs/types/rtti/types.d.ts +31 -3
  156. package/fjs/types/rtti/validate/module.f.d.mts +120 -0
  157. package/fjs/types/rtti/validate/module.f.mjs +351 -0
  158. package/fjs/types/rtti/validate/proof.f.d.mts +154 -0
  159. package/fjs/types/rtti/validate/proof.f.mjs +781 -0
  160. package/fjs/types/ts/types.d.ts +4 -0
  161. package/fjs/web/module.f.d.mts +115 -0
  162. package/fjs/web/module.f.mjs +581 -0
  163. package/fjs/web/proof.f.d.mts +39 -0
  164. package/fjs/web/proof.f.mjs +413 -0
  165. package/fjs/web/types.d.ts +39 -0
  166. package/fjs/website/browser-prepare.d.mts +5 -0
  167. package/fjs/website/browser-prepare.mjs +115 -0
  168. package/fjs/website/browser-source.d.mts +37 -0
  169. package/fjs/website/browser-source.mjs +202 -0
  170. package/fjs/website/browser-source.proof.d.mts +31 -0
  171. package/fjs/website/browser-source.proof.mjs +162 -0
  172. package/fjs/website/browser.d.mts +5 -0
  173. package/fjs/website/browser.mjs +8 -0
  174. package/fjs/website/module.f.d.mts +3 -3
  175. package/fjs/website/module.f.mjs +62 -9
  176. package/fjs/website/proof.f.d.mts +4 -0
  177. package/fjs/website/proof.f.mjs +18 -3
  178. 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 open.** A value carrying more than the schema
9
- * declares is accepted; `parse` then returns a freshly constructed value that
10
- * contains only the declared fields/elements, so the extras are accepted on
11
- * the way in and absent on the way out:
12
- *
13
- * - Tuples: a longer array is accepted; the result has the schema's length.
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
- * A member is required exactly when its set excludes `undefined` — an absent
20
- * member reads as `undefined`, on both kinds so a shorter array whose
21
- * trailing position admits `undefined` is accepted and the gap is filled.
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
- * Openness is what makes this forward-compatible with extended serialization
24
- * formats: a schema-based parser keeps working when newer versions of the
25
- * format add extra fields or tuple elements.
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
- * **Do not read "the result has the schema's length" as "tuples are closed"
28
- * and add a length check here.** The set a tuple schema describes includes
29
- * longer arrays; `Ts<T>` renders the closed approximation only because
30
- * TypeScript cannot express the open one (see `../ts/types.ts` `TupleTs`), and
31
- * taking that rendering for the model is what produced #1622. A schema that
32
- * wants exact members says so see `../todo/close-type.md`.
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 { Info1, Struct, Tag1, Tuple, Type } from '../types.ts'
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 => value => {
105
- if (!isContainer(value)) {
106
- return verror('unexpected value')
107
- }
108
- const e = entries(value)
109
- if (e.length === 0) {
110
- return /** @type {any} */ (ok(rebuild([])))
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(isArray, arrayRebuild)
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
- const recordParse = containerParse(isObject, recordRebuild)
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. It iterates the
123
- * *schema's* entries, which is what makes both kinds open: a longer array or
124
- * an undeclared key is never visited, so it is accepted and left out of the
125
- * rebuilt result.
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
- * @returns {<T extends Tuple | Struct>(rtti: T) => Parse<T>}
178
+ * @param {Fits<C>} fits
179
+ * @returns {<T extends S>(rtti: T) => Parse<T>}
134
180
  */
135
- (isContainer, getItem, rebuild) =>
136
- rtti => value => {
137
- if (!isContainer(value)) {
138
- return verror('unexpected value')
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
- * // open: a longer array is accepted, and the extra is not carried over
188
- * parse([number, number])([1, 2, 3]) // ['ok', [1, 2]]
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: an undeclared key is accepted, and not carried over
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
- extraItemsAcceptedAndDropped: () => void;
74
+ extraItemsRejected: () => void;
68
75
  shortArrayFillsAnOptionalPosition: () => void;
69
76
  error: () => void;
70
77
  };
71
78
  struct: {
72
79
  ok: () => void;
73
- extraKeysAcceptedAndDropped: () => void;
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
  };