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
|
@@ -8,11 +8,13 @@ import {
|
|
|
8
8
|
array,
|
|
9
9
|
bigint,
|
|
10
10
|
boolean,
|
|
11
|
+
open,
|
|
11
12
|
never as neverRtti,
|
|
12
13
|
number,
|
|
13
14
|
option,
|
|
14
15
|
or,
|
|
15
16
|
record,
|
|
17
|
+
rest,
|
|
16
18
|
string,
|
|
17
19
|
unknown as unknownRtti,
|
|
18
20
|
} from '../module.f.mjs'
|
|
@@ -114,10 +116,34 @@ const identityRec = f => f
|
|
|
114
116
|
/** @type {_Rec} */
|
|
115
117
|
const anon = identityRec(() => ['array', anon])
|
|
116
118
|
|
|
119
|
+
/** A cycle through a closed tuple: `_ClosedNode = [number, readonly _ClosedNode[]]`. */
|
|
120
|
+
/** @typedef {() => readonly ['const', readonly [typeof number, _ClosedChildren]]} _ClosedNode */
|
|
121
|
+
/** @typedef {() => readonly ['array', _ClosedNode]} _ClosedChildren */
|
|
122
|
+
/** @type {_ClosedNode} */
|
|
123
|
+
const closedNode = () => ['const', [number, closedChildren]]
|
|
124
|
+
/** @type {_ClosedChildren} */
|
|
125
|
+
const closedChildren = () => ['array', closedNode]
|
|
126
|
+
|
|
127
|
+
/** A cycle through a struct's stated `rest`. */
|
|
128
|
+
/** @typedef {() => readonly ['rest', { readonly a: typeof number }, _NestedRest]} _NestedRest */
|
|
129
|
+
/** @type {_NestedRest} */
|
|
130
|
+
const nestedRest = () => ['rest', { a: number }, nestedRest]
|
|
131
|
+
|
|
117
132
|
const tupleNumber = /** @type {const} */ ([number])
|
|
118
133
|
const tupleString = /** @type {const} */ ([string])
|
|
134
|
+
const tupleNumberNumber = /** @type {const} */ ([number, number])
|
|
119
135
|
const emptyTuple = /** @type {const} */ ([])
|
|
120
136
|
|
|
137
|
+
/**
|
|
138
|
+
* The exact-length array patterns — what a bare, closed `Tuple` converts to,
|
|
139
|
+
* and what an `open` one never reaches. Written as data as well so the
|
|
140
|
+
* `rest`-less arm of every array operation is exercised from both ends.
|
|
141
|
+
*/
|
|
142
|
+
/** @type {Data} */
|
|
143
|
+
const exactlyOneNumber = [{}, { array: [{ prefix: [{ number: true }] }] }]
|
|
144
|
+
/** @type {Data} */
|
|
145
|
+
const exactlyTwoNumbers = [{}, { array: [{ prefix: [{ number: true }, { number: true }] }] }]
|
|
146
|
+
|
|
121
147
|
export const proof = {
|
|
122
148
|
withoutUnits: [
|
|
123
149
|
// Removing the last unit bit removes the key: an empty kind is an
|
|
@@ -176,16 +202,83 @@ export const proof = {
|
|
|
176
202
|
assertData(toData(record(unknownRtti)))([{}, { object: true }])
|
|
177
203
|
assertData(toData(array(neverRtti)))([{}, { array: [{ prefix: [] }] }])
|
|
178
204
|
assertData(toData(record(neverRtti)))([{}, { object: [{ props: {}, rest: {} }] }])
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
205
|
+
// both const containers are closed, so each carries the `rest`
|
|
206
|
+
// that says so — `never`, on either kind. `open` is what supplies
|
|
207
|
+
// the `unknown` one, which the two kinds spell differently: a
|
|
208
|
+
// `rest: unknown` past a tuple's prefix, and no `rest` at all on a
|
|
209
|
+
// struct. An open tuple declaring nothing is therefore every array
|
|
210
|
+
// — and so is one whose every position restates that `rest`, which
|
|
211
|
+
// is trimmed away so that one set keeps one spelling
|
|
212
|
+
assertData(toData(open(emptyTuple)))([{}, { array: true }])
|
|
213
|
+
assertData(toData(open(/** @type {const} */ ([unknownRtti]))))([{}, { array: true }])
|
|
214
|
+
assertData(toData(open(/** @type {const} */ ([unknownRtti, unknownRtti]))))([{}, { array: true }])
|
|
215
|
+
assertData(toData(open(/** @type {const} */ ([number, unknownRtti]))))(toData(open(tupleNumber)))
|
|
216
|
+
assertData(toData(open(/** @type {const} */ ([number, 42]))))(
|
|
217
|
+
[{}, { array: [{ prefix: [{ number: true }, { number: [42] }], rest: unknown }] }])
|
|
218
|
+
assertData(toData(open({})))([{}, { object: true }])
|
|
219
|
+
assertData(toData(open({ b: string, a: number })))(
|
|
184
220
|
[{}, { object: [{ props: { a: { number: true }, b: { string: true } } }] }])
|
|
185
|
-
assertData(toData({ a: unknownRtti }))([{}, { object: true }])
|
|
221
|
+
assertData(toData(open({ a: unknownRtti })))([{}, { object: true }])
|
|
186
222
|
assertData(toData(/** @type {const} */ ([neverRtti])))([{}, never])
|
|
187
223
|
assertData(toData({ a: neverRtti }))([{}, never])
|
|
188
224
|
},
|
|
225
|
+
// `rest` needs no new concept underneath: it is the schema-form
|
|
226
|
+
// spelling of a `rest` the data form already carries — `never` for the
|
|
227
|
+
// bare, closed container, `R` for a stated one — and `open` is the
|
|
228
|
+
// `unknown` one.
|
|
229
|
+
rest: () => {
|
|
230
|
+
assertData(toData(tupleNumber))(exactlyOneNumber)
|
|
231
|
+
assertData(toData(tupleNumberNumber))(exactlyTwoNumbers)
|
|
232
|
+
assertData(toData(emptyTuple))([{}, { array: [{ prefix: [] }] }])
|
|
233
|
+
assertData(toData({ a: number }))(
|
|
234
|
+
[{}, { object: [{ props: { a: { number: true } }, rest: never }] }])
|
|
235
|
+
assertData(toData({}))([{}, { object: [{ props: {}, rest: never }] }])
|
|
236
|
+
// the bare form and an explicit empty rest are one set
|
|
237
|
+
assertData(toData(tupleNumber))(toData(rest(tupleNumber, neverRtti)))
|
|
238
|
+
// a stated rest is that rest
|
|
239
|
+
assertData(toData(rest(tupleNumber, string)))(
|
|
240
|
+
[{}, { array: [{ prefix: [{ number: true }], rest: { string: true } }] }])
|
|
241
|
+
assertData(toData(rest({ a: number }, string)))(
|
|
242
|
+
[{}, { object: [{ props: { a: { number: true } }, rest: { string: true } }] }])
|
|
243
|
+
// an unconstrained rest is `open`, and normalizes the same way on
|
|
244
|
+
// both kinds
|
|
245
|
+
assertData(toData(rest(tupleNumber, unknownRtti)))(toData(open(tupleNumber)))
|
|
246
|
+
assertData(toData(rest({ a: number }, unknownRtti)))(toData(open({ a: number })))
|
|
247
|
+
// a `never` member empties the whole pattern, whatever the rest
|
|
248
|
+
assertData(toData(/** @type {const} */ ([neverRtti])))([{}, never])
|
|
249
|
+
assertData(toData({ a: neverRtti }))([{}, never])
|
|
250
|
+
// an unconstrained key is dropped only once the rest is gone: with
|
|
251
|
+
// one present, "anything at `a`" says strictly more than leaving
|
|
252
|
+
// `a` out, which `open({ a: unknown })` alone does not
|
|
253
|
+
assertData(toData(open({ a: unknownRtti })))([{}, { object: true }])
|
|
254
|
+
assertData(toData({ a: unknownRtti }))(
|
|
255
|
+
[{}, { object: [{ props: { a: unknown }, rest: never }] }])
|
|
256
|
+
// the same container bare and opened are two sets, so the
|
|
257
|
+
// by-identity memo for const containers must not answer for both
|
|
258
|
+
assert(!equal(toData(/** @type {const} */ ([tupleNumber, open(tupleNumber)])))(
|
|
259
|
+
toData(/** @type {const} */ ([tupleNumber, tupleNumber]))))
|
|
260
|
+
},
|
|
261
|
+
// A cycle closes through a stated `rest` the same way it closes
|
|
262
|
+
// through a bare container: the enclosing thunk is what becomes the
|
|
263
|
+
// named rule.
|
|
264
|
+
restRecursion: () => {
|
|
265
|
+
assertData(toData(closedNode))([
|
|
266
|
+
{
|
|
267
|
+
closedNode: {
|
|
268
|
+
array: [{
|
|
269
|
+
prefix: [
|
|
270
|
+
{ number: true },
|
|
271
|
+
{ array: [{ prefix: [], rest: 'closedNode' }] },
|
|
272
|
+
],
|
|
273
|
+
}],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
'closedNode',
|
|
277
|
+
])
|
|
278
|
+
// and a cycle whose recursive position is the rest itself
|
|
279
|
+
assertData(toData(nestedRest))(
|
|
280
|
+
[{ nestedRest: { object: [{ props: { a: { number: true } }, rest: 'nestedRest' }] } }, 'nestedRest'])
|
|
281
|
+
},
|
|
189
282
|
constSchemas: () => {
|
|
190
283
|
assertData(toData(() => /** @type {const} */ (['const', 42])))([{}, { number: [42] }])
|
|
191
284
|
assertData(toData(() => /** @type {const} */ (['const', null])))([{}, { unit: unitBit(null) }])
|
|
@@ -242,11 +335,16 @@ export const proof = {
|
|
|
242
335
|
])
|
|
243
336
|
assertData(toData({ p: list }))([
|
|
244
337
|
{ list: { array: [{ prefix: [], rest: 'list' }] } },
|
|
245
|
-
{ object: [{ props: { p: 'list' } }] },
|
|
338
|
+
{ object: [{ props: { p: 'list' }, rest: never }] },
|
|
246
339
|
])
|
|
247
340
|
assertData(toData(a2))([
|
|
248
341
|
{
|
|
249
|
-
a2: {
|
|
342
|
+
a2: {
|
|
343
|
+
array: [{
|
|
344
|
+
prefix: [],
|
|
345
|
+
rest: { array: [{ prefix: ['a2', 'b2'] }] },
|
|
346
|
+
}],
|
|
347
|
+
},
|
|
250
348
|
b2: { array: [{ prefix: [], rest: 'b2' }] },
|
|
251
349
|
},
|
|
252
350
|
'a2',
|
|
@@ -312,24 +410,43 @@ export const proof = {
|
|
|
312
410
|
assertData(toData(anon))([{ '': { array: [{ prefix: [], rest: '' }] } }, ''])
|
|
313
411
|
},
|
|
314
412
|
collapse: () => {
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
//
|
|
318
|
-
assertData(toData(or(
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
assertData(toData(or(
|
|
322
|
-
|
|
413
|
+
// a longer tuple pattern is included in a shorter one when both
|
|
414
|
+
// are open — the array counterpart of a wider struct in a narrower
|
|
415
|
+
// one. Closed, neither includes the other: they differ in length.
|
|
416
|
+
assertData(toData(or(open(tupleNumberNumber), open(tupleNumber))))(toData(open(tupleNumber)))
|
|
417
|
+
// the empty-array pattern is a member of every uniform array set
|
|
418
|
+
assertData(toData(or(array(neverRtti), array(number))))(toData(array(number)))
|
|
419
|
+
assertData(toData(or(array(number), array(neverRtti))))(toData(array(number)))
|
|
420
|
+
// an open tuple declaring nothing absorbs the whole kind
|
|
421
|
+
assertData(toData(or(open(emptyTuple), array(number))))(toData(array(unknownRtti)))
|
|
422
|
+
// kept when neither pattern subsumes the other: an open `[number]`
|
|
423
|
+
// admits `[1, 'x']`, which `readonly number[]` does not, and the
|
|
424
|
+
// uniform set admits `[]`, which the tuple does not
|
|
425
|
+
assertData(toData(or(open(tupleNumber), array(number))))([{}, {
|
|
426
|
+
array: [
|
|
427
|
+
{ prefix: [], rest: { number: true } },
|
|
428
|
+
{ prefix: [{ number: true }], rest: unknown },
|
|
429
|
+
],
|
|
430
|
+
}])
|
|
431
|
+
assertData(toData(or(open(tupleNumber), open(tupleString))))([{}, {
|
|
432
|
+
array: [
|
|
433
|
+
{ prefix: [{ string: true }], rest: unknown },
|
|
434
|
+
{ prefix: [{ number: true }], rest: unknown },
|
|
435
|
+
],
|
|
436
|
+
}])
|
|
323
437
|
// the collapse recurses into inline positions
|
|
324
|
-
assertData(toData(array(or(
|
|
325
|
-
|
|
326
|
-
assertData(toData(
|
|
438
|
+
assertData(toData(array(or(open(tupleNumberNumber), open(tupleNumber)))))(
|
|
439
|
+
toData(array(open(tupleNumber))))
|
|
440
|
+
assertData(toData(record(or(open(tupleNumberNumber), open(tupleNumber)))))(
|
|
441
|
+
toData(record(open(tupleNumber))))
|
|
442
|
+
assertData(toData({ a: or(open(tupleNumberNumber), open(tupleNumber)) }))(
|
|
443
|
+
toData({ a: open(tupleNumber) }))
|
|
327
444
|
// collapsing innards can make two patterns identical — deduplicated
|
|
328
|
-
assertData(toData(or(array(or(
|
|
329
|
-
toData(array(
|
|
445
|
+
assertData(toData(or(array(or(open(tupleNumberNumber), open(tupleNumber))), array(open(tupleNumber)))))(
|
|
446
|
+
toData(array(open(tupleNumber))))
|
|
330
447
|
// subsumed object patterns are dropped, `true` absorbs patterns
|
|
331
448
|
assertData(toData(or({ a: 42 }, { a: number })))(toData({ a: number }))
|
|
332
|
-
assertData(toData(or({ a: number }, {})))(toData({}))
|
|
449
|
+
assertData(toData(or(open({ a: number }), open({}))))(toData(open({})))
|
|
333
450
|
},
|
|
334
451
|
serializable: () => {
|
|
335
452
|
const d = toData(or(string, array(number), null))
|
|
@@ -363,6 +480,16 @@ export const proof = {
|
|
|
363
480
|
assert(cmp(toData(number))(toData(list)) < 0)
|
|
364
481
|
assert(cmp(toData(list))(toData(number)) > 0)
|
|
365
482
|
},
|
|
483
|
+
// Two spellings of one set must not compare unequal, and two sets must
|
|
484
|
+
// not compare equal — the data form is content-addressed.
|
|
485
|
+
rest: () => {
|
|
486
|
+
assertEq(cmp(toData(tupleNumber))(toData(rest(tupleNumber, neverRtti))), 0)
|
|
487
|
+
assertEq(cmp(toData(rest(tupleNumber, unknownRtti)))(toData(open(tupleNumber))), 0)
|
|
488
|
+
assert(cmp(toData(tupleNumber))(toData(open(tupleNumber))) !== 0)
|
|
489
|
+
assert(cmp(toData({ a: number }))(toData(open({ a: number }))) !== 0)
|
|
490
|
+
assert(cmp(toData(tupleNumber))(toData(rest(tupleNumber, string))) !== 0)
|
|
491
|
+
assert(equal(toData(tupleNumber))(exactlyOneNumber))
|
|
492
|
+
},
|
|
366
493
|
restOrder: () => {
|
|
367
494
|
// a pattern without a rest sorts before one with a rest
|
|
368
495
|
/** @type {Data} */
|
|
@@ -383,7 +510,7 @@ export const proof = {
|
|
|
383
510
|
assertData(toData(or(record(number), record(string))))(
|
|
384
511
|
[{}, { object: [{ props: {}, rest: { string: true } }, { props: {}, rest: { number: true } }] }])
|
|
385
512
|
// fewer props sort before more props
|
|
386
|
-
assertData(toData(or({ a: number }, record(number))))(
|
|
513
|
+
assertData(toData(or(open({ a: number }), record(number))))(
|
|
387
514
|
[{}, { object: [{ props: {}, rest: { number: true } }, { props: { a: { number: true } } }] }])
|
|
388
515
|
},
|
|
389
516
|
rules: () => {
|
|
@@ -412,37 +539,110 @@ export const proof = {
|
|
|
412
539
|
assert(subset(toData(or(number, string)))(toData(unknownRtti)))
|
|
413
540
|
},
|
|
414
541
|
arrays: () => {
|
|
415
|
-
|
|
416
|
-
|
|
542
|
+
// a longer open tuple is included in a shorter one, and neither is
|
|
543
|
+
// included in the uniform set: an open `[number]` admits `[1, 'x']`
|
|
544
|
+
assert(subset(toData(open(tupleNumberNumber)))(toData(open(tupleNumber))))
|
|
545
|
+
assert(!subset(toData(open(tupleNumber)))(toData(open(tupleNumberNumber))))
|
|
546
|
+
assert(!subset(toData(open(tupleNumber)))(toData(array(number))))
|
|
547
|
+
assert(!subset(toData(array(number)))(toData(open(tupleNumber))))
|
|
548
|
+
// the exact-length pattern — the bare, closed tuple — is the one
|
|
549
|
+
// the uniform set contains
|
|
550
|
+
assert(subset(exactlyOneNumber)(toData(array(number))))
|
|
551
|
+
assert(subset(exactlyOneNumber)(toData(open(tupleNumber))))
|
|
552
|
+
// and, admitting one length only, contains just its own
|
|
553
|
+
assert(subset(exactlyOneNumber)(exactlyOneNumber))
|
|
554
|
+
assert(!subset(exactlyOneNumber)(exactlyTwoNumbers))
|
|
555
|
+
assert(!subset(toData(open(tupleNumber)))(exactlyOneNumber))
|
|
417
556
|
assert(subset(toData(array(number)))(toData(unknownRtti)))
|
|
418
557
|
assert(!subset(toData(array(unknownRtti)))(toData(array(number))))
|
|
419
558
|
assert(subset(toData(array(number)))(toData(array(or(number, string)))))
|
|
420
|
-
assert(!subset(toData(tupleNumber))(toData(tupleString)))
|
|
421
|
-
assert(!subset(toData(tupleNumber))(toData(/** @type {const} */ ([number, number]))))
|
|
559
|
+
assert(!subset(toData(open(tupleNumber)))(toData(open(tupleString))))
|
|
422
560
|
},
|
|
423
561
|
arrayRest: () => {
|
|
424
562
|
/** @type {Data} */
|
|
425
563
|
const oneOrMoreNumbers = [{}, { array: [{ prefix: [{ number: true }], rest: { number: true } }] }]
|
|
564
|
+
// a position restating a `rest` that excludes `undefined` is not
|
|
565
|
+
// redundant — it is what makes this "one or more" rather than the
|
|
566
|
+
// uniform set, so nothing trims it away
|
|
426
567
|
assert(subset(oneOrMoreNumbers)(toData(array(number))))
|
|
427
568
|
assert(!subset(toData(array(number)))(oneOrMoreNumbers))
|
|
428
|
-
assert(subset(
|
|
429
|
-
|
|
569
|
+
assert(subset(exactlyTwoNumbers)(oneOrMoreNumbers))
|
|
570
|
+
// the open tuple of two numbers admits a third element of any kind
|
|
571
|
+
assert(!subset(toData(open(tupleNumberNumber)))(oneOrMoreNumbers))
|
|
572
|
+
// an open tuple declaring nothing is the whole kind
|
|
573
|
+
assert(!subset(toData(open(emptyTuple)))(oneOrMoreNumbers))
|
|
430
574
|
/** @type {Data} */
|
|
431
575
|
const oneNumberThenStrings = [{}, { array: [{ prefix: [{ number: true }], rest: { string: true } }] }]
|
|
432
576
|
assert(!subset(oneOrMoreNumbers)(oneNumberThenStrings))
|
|
433
577
|
},
|
|
578
|
+
// The closed default makes a `rest`-less array pattern and an object
|
|
579
|
+
// pattern with an empty `rest` the *ordinary* output of the thunk
|
|
580
|
+
// form, where hand-written data used to be the only way to reach
|
|
581
|
+
// either. Both directions of each.
|
|
582
|
+
rest: () => {
|
|
583
|
+
// closed is included in open, never the other way round
|
|
584
|
+
assert(subset(toData(tupleNumber))(toData(open(tupleNumber))))
|
|
585
|
+
assert(!subset(toData(open(tupleNumber)))(toData(tupleNumber)))
|
|
586
|
+
assert(subset(toData({ a: number }))(toData(open({ a: number }))))
|
|
587
|
+
assert(!subset(toData(open({ a: number })))(toData({ a: number })))
|
|
588
|
+
// an exact length is its own, and contains no other
|
|
589
|
+
assert(subset(toData(tupleNumber))(toData(tupleNumber)))
|
|
590
|
+
assert(!subset(toData(tupleNumber))(toData(tupleNumberNumber)))
|
|
591
|
+
assert(!subset(toData(tupleNumberNumber))(toData(tupleNumber)))
|
|
592
|
+
// a stated rest widens it, and an unconstrained one is openness
|
|
593
|
+
assert(subset(toData(tupleNumber))(toData(rest(tupleNumber, string))))
|
|
594
|
+
assert(!subset(toData(rest(tupleNumber, string)))(toData(tupleNumber)))
|
|
595
|
+
assert(subset(toData(rest(tupleNumber, string)))(toData(rest(tupleNumber, unknownRtti))))
|
|
596
|
+
assert(subset(toData({ a: number }))(toData(rest({ a: number }, string))))
|
|
597
|
+
assert(!subset(toData(rest({ a: number }, string)))(toData({ a: number })))
|
|
598
|
+
assert(subset(toData(rest({ a: number }, number)))(toData(record(number))))
|
|
599
|
+
assert(!subset(toData(record(number)))(toData(rest({ a: number }, number))))
|
|
600
|
+
// and the members are still compared pointwise
|
|
601
|
+
assert(subset(toData(/** @type {const} */ ([42])))(toData(tupleNumber)))
|
|
602
|
+
assert(!subset(toData(tupleNumber))(toData(tupleString)))
|
|
603
|
+
assert(subset(toData(closedNode))(toData(closedNode)))
|
|
604
|
+
},
|
|
605
|
+
// A key present holding `undefined` and a key absent are two different
|
|
606
|
+
// objects, and the two sides of a pattern read them differently: a
|
|
607
|
+
// declared key constrains the value *read* at it, so absence passes
|
|
608
|
+
// when its set holds `undefined`; an undeclared key is checked as an
|
|
609
|
+
// *entry*, so a present `undefined` must belong to `rest` itself.
|
|
610
|
+
//
|
|
611
|
+
// One "read set" of `rest ∪ undefined` folded the two together, which
|
|
612
|
+
// stayed sound only while a struct's `rest` was `unknown` and failed
|
|
613
|
+
// the trailing rest check. A bare struct now supplies `never` there,
|
|
614
|
+
// so the fold is reachable from every schema and answered `true` for a
|
|
615
|
+
// non-inclusion.
|
|
616
|
+
presenceIsNotAbsence: () => {
|
|
617
|
+
const p = toData({ a: option(number) })
|
|
618
|
+
const q = toData(record(number))
|
|
619
|
+
assert(!subset(p)(q))
|
|
620
|
+
// the witness, and the acceptance that makes it one
|
|
621
|
+
assertEq(validate(p)({ a: undefined })[0], 'ok')
|
|
622
|
+
assertEq(validate(q)({ a: undefined })[0], 'error')
|
|
623
|
+
// both halves of the per-key check are load-bearing, and neither
|
|
624
|
+
// implies the other: this pair agrees on every present value and
|
|
625
|
+
// differs only on whether the key may be missing
|
|
626
|
+
assert(!subset(toData(record(number)))(toData(rest({ a: number }, number))))
|
|
627
|
+
assertEq(validate(toData(record(number)))({})[0], 'ok')
|
|
628
|
+
assertEq(validate(toData(rest({ a: number }, number)))({})[0], 'error')
|
|
629
|
+
// and the open-struct spelling, which was sound before, still is
|
|
630
|
+
assert(!subset(toData({ a: option(number) }))(toData(record(number))))
|
|
631
|
+
},
|
|
434
632
|
objects: () => {
|
|
435
|
-
assert(subset(toData({ a: number }))(toData({})))
|
|
633
|
+
assert(subset(toData(open({ a: number })))(toData(open({}))))
|
|
436
634
|
assert(subset(toData({ a: 42 }))(toData({ a: number })))
|
|
437
635
|
assert(!subset(toData({ a: number }))(toData({ a: 42 })))
|
|
438
|
-
assert(subset(toData({ a: number, b: string }))(toData({ a: number })))
|
|
636
|
+
assert(subset(toData(open({ a: number, b: string })))(toData(open({ a: number }))))
|
|
439
637
|
assert(subset(toData(record(number)))(toData(record(or(number, string)))))
|
|
440
638
|
assert(!subset(toData(record(or(number, string))))(toData(record(number))))
|
|
441
639
|
// a record's keys may be absent, a required key excludes that
|
|
442
|
-
assert(!subset(toData(record(number)))(toData({ a: number })))
|
|
443
|
-
assert(subset(toData(record(number)))(toData({ a: option(number) })))
|
|
444
|
-
//
|
|
445
|
-
|
|
640
|
+
assert(!subset(toData(record(number)))(toData(open({ a: number }))))
|
|
641
|
+
assert(subset(toData(record(number)))(toData(open({ a: option(number) }))))
|
|
642
|
+
// an open struct leaves undeclared keys unconstrained, a record
|
|
643
|
+
// does not — while a closed one names them all, so it is included
|
|
644
|
+
assert(!subset(toData(open({ a: number })))(toData(record(number))))
|
|
645
|
+
assert(subset(toData({ a: number }))(toData(record(number))))
|
|
446
646
|
// a key inherited from Object.prototype is not a declared prop
|
|
447
647
|
assert(!subset(toData({ a: number }))(toData({ toString: number })))
|
|
448
648
|
assert(subset(toData({ toString: /** @type {const} */ (42) }))(toData({ toString: number })))
|
|
@@ -517,10 +717,43 @@ export const proof = {
|
|
|
517
717
|
assertEq(
|
|
518
718
|
JSON.stringify(v([1, 'x'])),
|
|
519
719
|
'["error",{"path":["1"],"message":"unexpected value"}]')
|
|
520
|
-
|
|
720
|
+
// an open tuple admits a longer array, and a position is required
|
|
721
|
+
// exactly when its set excludes `undefined`, so a shorter one is a
|
|
722
|
+
// member whenever the positions past its end admit absence
|
|
723
|
+
const vt = validate(toData(open(/** @type {const} */ ([number, string]))))
|
|
521
724
|
assertEq(vt([1, 'a'])[0], 'ok')
|
|
522
|
-
assertEq(vt([1])[0], '
|
|
523
|
-
assertEq(
|
|
725
|
+
assertEq(vt([1, 'a', 2])[0], 'ok')
|
|
726
|
+
assertEq(
|
|
727
|
+
JSON.stringify(vt([1])),
|
|
728
|
+
'["error",{"path":["1"],"message":"unexpected value"}]')
|
|
729
|
+
const vo = validate(toData(open(/** @type {const} */ ([number, option(string)]))))
|
|
730
|
+
assertEq(vo([1])[0], 'ok')
|
|
731
|
+
assertEq(vo([1, 'a'])[0], 'ok')
|
|
732
|
+
assertEq(vo([])[0], 'error')
|
|
733
|
+
// the exact-length pattern admits neither, and counts a hole past
|
|
734
|
+
// the prefix: not an entry, but the array is still that long
|
|
735
|
+
const vx = validate(exactlyOneNumber)
|
|
736
|
+
assertEq(vx([1])[0], 'ok')
|
|
737
|
+
assertEq(vx([1, 2])[0], 'error')
|
|
738
|
+
assertEq(vx([])[0], 'error')
|
|
739
|
+
assertEq(vx([1, , ])[0], 'error')
|
|
740
|
+
assertEq(validate(toData(array(neverRtti)))([, ])[0], 'error')
|
|
741
|
+
assertEq(validate(toData(array(neverRtti)))([])[0], 'ok')
|
|
742
|
+
assertEq(validate(toData(array(neverRtti)))([1])[0], 'error')
|
|
743
|
+
// an enumerable non-index key is an entry the prefix has not
|
|
744
|
+
// spoken for, like an index past it: the `rest` answers it, and
|
|
745
|
+
// an exact-length pattern, having no `rest`, rejects it
|
|
746
|
+
assertEq(validate(toData(array(number)))(Object.assign([1], { foo: 2 }))[0], 'ok')
|
|
747
|
+
assertEq(validate(toData(array(number)))(Object.assign([1], { foo: 'x' }))[0], 'error')
|
|
748
|
+
assertEq(validate(toData(array(neverRtti)))(Object.assign([], { foo: 'x' }))[0], 'error')
|
|
749
|
+
assertEq(validate(exactlyOneNumber)(Object.assign([1], { foo: 1 }))[0], 'error')
|
|
750
|
+
// only the canonical spelling of a non-negative integer is a
|
|
751
|
+
// position; every other key is a property of the array object,
|
|
752
|
+
// whatever `Number` makes of it
|
|
753
|
+
for (const k of ['-1', '01', '1.5', ' 1', '1e0']) {
|
|
754
|
+
assertEq(validate(toData(array(number)))(Object.assign([1], { [k]: 'x' }))[0], 'error')
|
|
755
|
+
assertEq(validate(exactlyTwoNumbers)(Object.assign([1, 2], { [k]: 2 }))[0], 'error')
|
|
756
|
+
}
|
|
524
757
|
/** @type {Data} */
|
|
525
758
|
const onePlus = [{}, { array: [{ prefix: [{ number: true }], rest: { string: true } }] }]
|
|
526
759
|
const vp = validate(onePlus)
|
|
@@ -536,7 +769,7 @@ export const proof = {
|
|
|
536
769
|
'["error",{"path":[],"message":"no match"}]')
|
|
537
770
|
},
|
|
538
771
|
objects: () => {
|
|
539
|
-
const v = validate(toData({ a: number, b: option(string) }))
|
|
772
|
+
const v = validate(toData(open({ a: number, b: option(string) })))
|
|
540
773
|
assertEq(v({ a: 1 })[0], 'ok')
|
|
541
774
|
assertEq(v({ a: 1, b: 's' })[0], 'ok')
|
|
542
775
|
assertEq(v({ a: 1, extra: true })[0], 'ok')
|
|
@@ -19,14 +19,21 @@ export type KindSet<T> = true | readonly T[];
|
|
|
19
19
|
/**
|
|
20
20
|
* A set of arrays: a tuple with an optional rest.
|
|
21
21
|
*
|
|
22
|
-
* - `prefix` constrains the value
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* - `prefix` constrains, per leading position, the value *read* at that
|
|
23
|
+
* position — a position past the array's end reads as `undefined`, so a
|
|
24
|
+
* position is required exactly when its set excludes `undefined`. This is
|
|
25
|
+
* the array half of the rule {@link ObjectSet} states for keys.
|
|
26
|
+
* - `rest` present: the value at every position past the prefix belongs to
|
|
27
|
+
* `rest`.
|
|
28
|
+
* - `rest` absent: there is nothing past the prefix.
|
|
26
29
|
*
|
|
27
|
-
* A tuple schema is `{ prefix }
|
|
28
|
-
*
|
|
29
|
-
* `
|
|
30
|
+
* A bare tuple schema is `{ prefix }` alone — the exact-length set, since
|
|
31
|
+
* tuples are closed (see "Structs and tuples are closed" in `../README.md`) —
|
|
32
|
+
* an `open` one is `{ prefix, rest: unknown }`, and a uniform array schema is
|
|
33
|
+
* `{ prefix: [], rest }`.
|
|
34
|
+
* All are points of the same kind, so a longer tuple pattern included in a
|
|
35
|
+
* shorter one is a plain pattern inclusion, the array counterpart of a wider
|
|
36
|
+
* struct included in a narrower one.
|
|
30
37
|
*/
|
|
31
38
|
export type ArraySet = {
|
|
32
39
|
readonly prefix: readonly Node[];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rtti readers against values **FunctionalScript cannot build**.
|
|
3
|
+
*
|
|
4
|
+
* A `proof.mjs` rather than a `proof.f.mjs`, and deliberately: the fixtures
|
|
5
|
+
* here need in-place mutation — `Object.setPrototypeOf` to give an array a
|
|
6
|
+
* prototype that supplies an index, `Object.assign` to put a key past the
|
|
7
|
+
* index range on one — which [`../../AGENTS.md`](../../AGENTS.md) §3.1 forbids
|
|
8
|
+
* in authored FunctionalScript. `shouldLoad` in
|
|
9
|
+
* [`../../dev/module.f.mjs`](../../dev/module.f.mjs) makes a plain
|
|
10
|
+
* `proof.mjs` the opt-in home for exactly that, so the mutation stays out of
|
|
11
|
+
* the `.f.mjs` proofs rather than being smuggled through them.
|
|
12
|
+
*
|
|
13
|
+
* The values are worth pinning even though the subset cannot express them: a
|
|
14
|
+
* caller in ordinary JavaScript can hand one to an exported reader, and both
|
|
15
|
+
* are cases where reading a member by *entry* and reading it by *index*
|
|
16
|
+
* disagree — which is what these readers had wrong.
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*
|
|
20
|
+
* @import { Type } from './types.ts'
|
|
21
|
+
* @import { ValidateE } from './common/types.ts'
|
|
22
|
+
* @import { Unknown } from './ts/types.ts'
|
|
23
|
+
*/
|
|
24
|
+
export declare const proof: {
|
|
25
|
+
undeclaredMembers: {
|
|
26
|
+
inheritedIndexIsAMember: () => void;
|
|
27
|
+
shadowedIndexIsTheOwnOne: () => void;
|
|
28
|
+
beyondTheIndexRangeIsAMember: () => void;
|
|
29
|
+
};
|
|
30
|
+
readersAgree: () => void;
|
|
31
|
+
inheritedIndexMeetsTheRest: () => void;
|
|
32
|
+
beyondTheIndexRangeIsAMember: () => void;
|
|
33
|
+
};
|