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.
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 +42 -25
  157. package/fjs/types/rtti/validate/module.f.mjs +187 -53
  158. package/fjs/types/rtti/validate/proof.f.d.mts +28 -2
  159. package/fjs/types/rtti/validate/proof.f.mjs +430 -75
  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
@@ -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 open
30
- *
31
- * Openness is the shared rule, not a `parse` detail — see "Structs and tuples
32
- * are open" in `../README.md`. `validate` iterates the *schema's* entries, so
33
- * an undeclared key or a longer array is never visited: it is accepted, and it
34
- * is still there afterwards because the value is returned as-is. An absent
35
- * member reads as `undefined`, so a member is required exactly when its set
36
- * excludes `undefined`.
37
- *
38
- * **Do not add a length check for tuples here.** `Ts<readonly [42]>` is the
39
- * exact tuple only because TypeScript cannot express the open one (see
40
- * `../ts/types.ts` `TupleTs`); reading that rendering as the value model is
41
- * what produced #1622, whose check lived in this module's ancestor and was
42
- * deleted with it. A schema that wants exact members says so — see
43
- * `../todo/close-type.md`.
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
- * For `array` and `record` schemas, the inner item validator is instantiated
58
- * lazily — only after confirming the container is non-empty. This prevents
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 { Info1, Struct, Tag1, Tuple, Type } from '../types.ts'
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 => value => {
102
- if (!isContainer(value)) {
103
- return verror('unexpected value')
104
- }
105
- const e = entries(value)
106
- if (e.length === 0) {
107
- return /** @type {any} */ (ok(value))
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(isArray)
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. It iterates the
124
- * *schema's* entries, which is what makes both kinds open: a longer array or
125
- * an undeclared key is never visited, so it is accepted and, the value being
126
- * returned as it came, it survives.
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
- * @returns {<T extends Tuple | Struct>(rtti: T) => Validate<T>}
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
- // Depends on `rtti` alone, so it is computed once per schema rather
189
+ // Depend on `rtti` alone, so they are computed once per schema rather
138
190
  // than once per validated value.
139
- const rttiEntries = entries(rtti)
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 r[0] === 'error' ? r : /** @type {any} */ (ok(value))
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
- * // open, and the extras are still there afterwards
207
- * validate([number, number])([1, 2, 3]) // ['ok', [1, 2, 3]]
208
- * validate({ a: number })({ a: 1, b: 2 }) // ['ok', { 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
- extraItemsAcceptedAndKept: () => void;
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
  };