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
@@ -0,0 +1,170 @@
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
+
25
+ import { assert, assertEq, assertStructurallySame } from '../../asserts/module.f.mjs'
26
+ import { undeclaredMembers } from './common/module.f.mjs'
27
+ import { toData, validate as dataValidate } from './data/module.f.mjs'
28
+ import { array, number, rest, string } from './module.f.mjs'
29
+ import { parse } from './parse/module.f.mjs'
30
+ import { validate } from './validate/module.f.mjs'
31
+
32
+ /** The three readers with their payload type erased, as `validate/proof.f.mjs` has them. */
33
+
34
+ /** @type {(t: Type) => ValidateE} */
35
+ const v = t => /** @type {any} */ (validate(t))
36
+
37
+ /** @type {(t: Type) => ValidateE} */
38
+ const p = t => /** @type {any} */ (parse(t))
39
+
40
+ /** @type {(t: Type) => ValidateE} */
41
+ const d = t => dataValidate(toData(t))
42
+
43
+ /** @type {(r: readonly [string, unknown]) => void} */
44
+ const assertOk = ([k]) => { assertEq(k, 'ok', 'expected ok') }
45
+
46
+ /** @type {(r: readonly [string, unknown]) => void} */
47
+ const assertError = ([k]) => { assertEq(k, 'error', 'expected error') }
48
+
49
+ /**
50
+ * An array whose index 1 is readable through the **prototype**: `length` says
51
+ * 2, index 1 is no own entry, and reading it gives `99`. `Array.isArray` looks
52
+ * at the value and not at its prototype, so this is still an array to every
53
+ * reader — and the member is one a schema has to answer for, or the tail
54
+ * `./ts/types.ts` renders would claim the rest's type over a number.
55
+ *
56
+ * @type {() => readonly Unknown[]}
57
+ */
58
+ const inheritedIndex = () => {
59
+ const value = [42, ,]
60
+ Object.setPrototypeOf(value, [0, 99])
61
+ return value
62
+ }
63
+
64
+ /**
65
+ * An array whose prototype supplies an index it **owns anyway**. Nothing about
66
+ * it is unusual to a reader — the member list is the own one — but it is the
67
+ * case that separates "the chain names an index" from "the chain supplies one",
68
+ * which is where the walk decides whether it has anything to merge.
69
+ *
70
+ * @type {() => readonly Unknown[]}
71
+ */
72
+ const shadowedIndex = () => {
73
+ const value = [42]
74
+ Object.setPrototypeOf(value, [7])
75
+ return value
76
+ }
77
+
78
+ /**
79
+ * An array carrying `4294967295` — one past the last index the language has.
80
+ * Assigning it creates an ordinary enumerable property and leaves `length` at
81
+ * `1`, so it is an undeclared member that no `length`-bounded walk reaches; a
82
+ * reader treating it as an index found it on neither path and let it through a
83
+ * closed container.
84
+ *
85
+ * @type {() => readonly Unknown[]}
86
+ */
87
+ const beyondIndexRange = () => Object.assign([1], { '4294967295': 2 })
88
+
89
+ export const proof = {
90
+ // `undeclaredMembers` decides a container's members by what an index
91
+ // *reads*, so both of these are members — one that an own-entry walk
92
+ // misses, and one that an index-keyed walk misses.
93
+ undeclaredMembers: {
94
+ inheritedIndexIsAMember: () => {
95
+ const value = inheritedIndex()
96
+ assertEq(value.length, 2, 'the array reaches that far')
97
+ assert(!Object.hasOwn(value, 1), 'and index 1 is no own entry')
98
+ assertStructurallySame(undeclaredMembers(['0'], value), [['1', 99]])
99
+ },
100
+ // A shadowed one contributes nothing: the value owns the index, so
101
+ // the member list is what the own walk already answered.
102
+ shadowedIndexIsTheOwnOne: () => {
103
+ const value = shadowedIndex()
104
+ assertEq(value[0], 42, 'the own value wins')
105
+ assertStructurallySame(undeclaredMembers([], value), [['0', 42]])
106
+ assertEq(undeclaredMembers(['0'], value).length, 0)
107
+ },
108
+ // `2 ** 32 - 1` is not an index: assigning it creates an ordinary
109
+ // enumerable property and leaves `length` alone, so it is a member by
110
+ // the non-index half. Reading it as an index put it past both halves.
111
+ beyondTheIndexRangeIsAMember: () => {
112
+ const value = beyondIndexRange()
113
+ assertEq(value.length, 1, '`length` never saw the assignment')
114
+ assertStructurallySame(
115
+ undeclaredMembers(['0'], value),
116
+ [['4294967295', 2]],
117
+ )
118
+ },
119
+ },
120
+ // Every row runs through all three readers, which is the agreement
121
+ // `validate/proof.f.mjs`'s table exists to hold: a value one reader
122
+ // accepts and another rejects is a bug in whichever walk differs, and
123
+ // both of these fixtures found one.
124
+ readersAgree: () => {
125
+ /** @type {readonly (readonly [Type, readonly Unknown[]])[]} */
126
+ const rows = [
127
+ // an index read through the prototype is held to the rest like any
128
+ // other member — on the uniform reader as well as the tuple one
129
+ [rest([number], string), inheritedIndex()],
130
+ [rest([number], number), inheritedIndex()],
131
+ [array(string), inheritedIndex()],
132
+ [array(number), inheritedIndex()],
133
+ // a key past the last index the language has is an ordinary
134
+ // property, so it is a member by the non-index half
135
+ [[number], beyondIndexRange()],
136
+ [rest([number], string), beyondIndexRange()],
137
+ [rest([number], number), beyondIndexRange()],
138
+ ]
139
+ for (const [t, value] of rows) {
140
+ const rv = v(t)(value)
141
+ assertEq(rv[0], p(t)(value)[0], 'validate and parse must agree')
142
+ assertEq(rv[0], d(t)(value)[0], 'the data form must agree too')
143
+ }
144
+ },
145
+ // …and what they agree on, which is what the changelog entry claims.
146
+ inheritedIndexMeetsTheRest: () => {
147
+ const value = inheritedIndex()
148
+ for (const read of [v, p, d]) {
149
+ assertError(read(rest([number], string))(value))
150
+ assertOk(read(rest([number], number))(value))
151
+ // `array(t)` is `rest([], t)`, so it walks the value the same way.
152
+ // An own-entry walk here answered `ok` while the data form's
153
+ // reader rejected the same value against the same schema.
154
+ assertError(read(array(string))(value))
155
+ assertOk(read(array(number))(value))
156
+ }
157
+ assert(Object.is(v(rest([number], number))(value)[1], value),
158
+ '`validate` still hands back the value it was given')
159
+ },
160
+ beyondTheIndexRangeIsAMember: () => {
161
+ const value = beyondIndexRange()
162
+ for (const read of [v, p, d]) {
163
+ // a bare container names every member it admits, and not this one
164
+ assertError(read([number])(value))
165
+ // a stated rest answers for it like any other undeclared member
166
+ assertError(read(rest([number], string))(value))
167
+ assertOk(read(rest([number], number))(value))
168
+ }
169
+ },
170
+ }
@@ -7,12 +7,12 @@
7
7
  * @import { Includes } from '../array/types.ts'
8
8
  * @import { Assert } from '../../asserts/types.ts'
9
9
  * @import { Equal } from '../ts/types.ts'
10
- * @import { Tag0, Primitive0, _Type0, Bigint, Unknown, Tag1, _MakeType1, Array, Record, Or, Type } from './types.ts'
10
+ * @import { Tag0, Primitive0, _Type0, Bigint, Unknown, Tag1, _MakeType1, _MakeOpen, _MakeRest, Or, Type } from './types.ts'
11
11
  */
12
12
  import type { Includes } from '../array/types.ts';
13
13
  import type { Assert } from '../../asserts/types.ts';
14
14
  import type { Equal } from '../ts/types.ts';
15
- import type { Primitive0, _Type0, Bigint, Unknown, Tag1, _MakeType1, Or, Type } from './types.ts';
15
+ import type { Primitive0, _Type0, Bigint, Unknown, Tag1, _MakeType1, _MakeOpen, _MakeRest, Or, Type } from './types.ts';
16
16
  declare const primitive0List: readonly ['bigint', 'boolean', 'number', 'string'];
17
17
  export type _Primitive0Pinned = Assert<Equal<Primitive0, typeof primitive0List[number]>>;
18
18
  /** @typedef {Assert<Equal<Primitive0, typeof primitive0List[number]>>} _Primitive0Pinned */
@@ -62,22 +62,67 @@ export declare const record: _MakeType1<'record'>;
62
62
  * or canonical-form work. All such algebra lives on the serializable data form
63
63
  * — see `./data/README.md`.
64
64
  *
65
- * @template {readonly Type[]} T
65
+ * @template {readonly Type[]} const T
66
66
  * @param {T} types
67
67
  * @returns {Or<T>}
68
68
  */
69
- export declare const or: <T extends readonly Type[]>(...types: T) => Or<T>;
69
+ export declare const or: <const T extends readonly Type[]>(...types: T) => Or<T>;
70
70
  /**
71
71
  * Constructs a schema that validates a value matching `T` or `undefined`.
72
72
  *
73
- * @template {Type} T
73
+ * @template {Type} const T
74
74
  * @param {T} t
75
75
  * @returns {Or<readonly [T, undefined]>}
76
76
  */
77
- export declare const option: <T extends Type>(t: T) => Or<readonly [T, undefined]>;
77
+ export declare const option: <const T extends Type>(t: T) => Or<readonly [T, undefined]>;
78
78
  /**
79
79
  * Schema that never matches any value — the empty union, corresponding to TypeScript's `never`.
80
+ *
80
81
  * @type {Or<readonly []>}
81
82
  */
82
83
  export declare const never: Or<readonly []>;
84
+ /**
85
+ * Constructs a schema for a container whose **undeclared** members belong to
86
+ * `r`: the members `c` declares, plus any number of members belonging to `r`.
87
+ *
88
+ * A `Struct` or a `Tuple` used on its own is closed — it admits the members it
89
+ * declares and no others — which is `rest(c, never)` and needs no spelling.
90
+ * This is how a schema says it wants anything else.
91
+ *
92
+ * ```js
93
+ * rest([number], string) // one number, then any number of strings
94
+ * rest({ a: number }, string) // `a`, plus any number of string-valued keys
95
+ * open({ a: number }) // `a`, plus anything else
96
+ * ```
97
+ *
98
+ * Both parameters are required. An optional one would need a sentinel for "no
99
+ * undeclared member", and every candidate — `undefined` most of all — is a
100
+ * `Type` in its own right, so the sentinel would collide with the schema it
101
+ * spells. `never` carries no such ambiguity, and a container whose undeclared
102
+ * members must be the *value* `undefined` states that rest as a wrapped const,
103
+ * `() => ['const', undefined]`.
104
+ *
105
+ * On the array kind `r` constrains the members past the prefix, and a `r` that
106
+ * admits nothing bounds the array's length — which is what the bare form
107
+ * already says, so `rest(c, never)` and `c` are one set.
108
+ *
109
+ * @type {_MakeRest}
110
+ */
111
+ export declare const rest: _MakeRest;
112
+ /**
113
+ * Constructs a schema for an **open** container: the members `c` declares,
114
+ * plus any number of members of any kind. `open(c)` is `rest(c, unknown)`.
115
+ *
116
+ * Openness is what makes a schema forward-compatible with a serialization
117
+ * format that has grown fields, so a schema read against a wire format
118
+ * someone else may extend says `open`.
119
+ *
120
+ * ```js
121
+ * open({ a: number }) // any object whose `a` is a number
122
+ * open([number]) // any array whose position 0 is a number
123
+ * ```
124
+ *
125
+ * @type {_MakeOpen}
126
+ */
127
+ export declare const open: _MakeOpen;
83
128
  export {};
@@ -7,7 +7,7 @@
7
7
  * @import { Includes } from '../array/types.ts'
8
8
  * @import { Assert } from '../../asserts/types.ts'
9
9
  * @import { Equal } from '../ts/types.ts'
10
- * @import { Tag0, Primitive0, _Type0, Bigint, Unknown, Tag1, _MakeType1, Array, Record, Or, Type } from './types.ts'
10
+ * @import { Tag0, Primitive0, _Type0, Bigint, Unknown, Tag1, _MakeType1, _MakeOpen, _MakeRest, Or, Type } from './types.ts'
11
11
  */
12
12
 
13
13
  import { includes } from '../array/module.f.mjs'
@@ -88,7 +88,7 @@ export const record = type1('record')
88
88
  * or canonical-form work. All such algebra lives on the serializable data form
89
89
  * — see `./data/README.md`.
90
90
  *
91
- * @template {readonly Type[]} T
91
+ * @template {readonly Type[]} const T
92
92
  * @param {T} types
93
93
  * @returns {Or<T>}
94
94
  */
@@ -98,7 +98,7 @@ export const or = (...types) =>
98
98
  /**
99
99
  * Constructs a schema that validates a value matching `T` or `undefined`.
100
100
  *
101
- * @template {Type} T
101
+ * @template {Type} const T
102
102
  * @param {T} t
103
103
  * @returns {Or<readonly [T, undefined]>}
104
104
  */
@@ -107,6 +107,53 @@ export const option = t =>
107
107
 
108
108
  /**
109
109
  * Schema that never matches any value — the empty union, corresponding to TypeScript's `never`.
110
+ *
110
111
  * @type {Or<readonly []>}
111
112
  */
112
113
  export const never = or()
114
+
115
+ /**
116
+ * Constructs a schema for a container whose **undeclared** members belong to
117
+ * `r`: the members `c` declares, plus any number of members belonging to `r`.
118
+ *
119
+ * A `Struct` or a `Tuple` used on its own is closed — it admits the members it
120
+ * declares and no others — which is `rest(c, never)` and needs no spelling.
121
+ * This is how a schema says it wants anything else.
122
+ *
123
+ * ```js
124
+ * rest([number], string) // one number, then any number of strings
125
+ * rest({ a: number }, string) // `a`, plus any number of string-valued keys
126
+ * open({ a: number }) // `a`, plus anything else
127
+ * ```
128
+ *
129
+ * Both parameters are required. An optional one would need a sentinel for "no
130
+ * undeclared member", and every candidate — `undefined` most of all — is a
131
+ * `Type` in its own right, so the sentinel would collide with the schema it
132
+ * spells. `never` carries no such ambiguity, and a container whose undeclared
133
+ * members must be the *value* `undefined` states that rest as a wrapped const,
134
+ * `() => ['const', undefined]`.
135
+ *
136
+ * On the array kind `r` constrains the members past the prefix, and a `r` that
137
+ * admits nothing bounds the array's length — which is what the bare form
138
+ * already says, so `rest(c, never)` and `c` are one set.
139
+ *
140
+ * @type {_MakeRest}
141
+ */
142
+ export const rest = (c, r) => () => /** @type {any} */ (['rest', c, r])
143
+
144
+ /**
145
+ * Constructs a schema for an **open** container: the members `c` declares,
146
+ * plus any number of members of any kind. `open(c)` is `rest(c, unknown)`.
147
+ *
148
+ * Openness is what makes a schema forward-compatible with a serialization
149
+ * format that has grown fields, so a schema read against a wire format
150
+ * someone else may extend says `open`.
151
+ *
152
+ * ```js
153
+ * open({ a: number }) // any object whose `a` is a number
154
+ * open([number]) // any array whose position 0 is a number
155
+ * ```
156
+ *
157
+ * @type {_MakeOpen}
158
+ */
159
+ export const open = c => /** @type {any} */ (rest(c, unknown))
@@ -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
  */
@@ -53,5 +60,5 @@ import type { Unknown } from '../ts/types.ts';
53
60
  import type { Parse } from './types.ts';
54
61
  export type _ItemResult = CommonResult<Unknown, ValidationError>;
55
62
  export type _Rebuild = (entries: ReadonlyArray<readonly [string, Unknown]>) => Unknown;
56
- /** @type {<T extends Type>(rtti: T) => Parse<T>} */
57
- export declare const parse: <T extends Type>(rtti: T) => Parse<T>;
63
+ /** @type {<const T extends Type>(rtti: T) => Parse<T>} */
64
+ export declare const parse: <const T extends Type>(rtti: T) => Parse<T>;