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
@@ -7,7 +7,7 @@
7
7
  * @import { Nullable } from '../../types/nullable/types.ts'
8
8
  */
9
9
 
10
- import { msb, lsb, length, vec, empty } from '../../types/bit_vec/module.f.mjs'
10
+ import { msb, lsb, length, maxLength, vec, empty } from '../../types/bit_vec/module.f.mjs'
11
11
  import { baseN } from '../module.f.mjs'
12
12
 
13
13
  // 0123456789abcdef
@@ -47,15 +47,23 @@ export const vecToCBase32 = v => {
47
47
 
48
48
  /** @type {(s: string) => Nullable<Vec>} */
49
49
  export const cBase32ToVec = s => {
50
- let v = cBase32ToVec5x(s)
51
- if (v === null) { return null }
52
- // Strip the padding: trailing zeros up to and including the sentinel `1` bit.
53
- // A string with no sentinel only zero symbols (`0`/`o`), or empty — exhausts
54
- // to `empty` and is rejected as `null` rather than looping forever.
55
- while (v !== empty) {
56
- const [last, rest] = popBack1(v)
57
- v = rest
58
- if (last === 1n) { return v }
50
+ // Locate the sentinel from the end one character at a time. This preserves
51
+ // accepted non-canonical spellings with trailing zero characters without
52
+ // ever materialising their padded body as one oversized vector.
53
+ for (let i = s.length - 1; i >= 0; i--) {
54
+ let tail = cBase32ToVec5x(s[i])
55
+ if (tail === null) { return null }
56
+ while (tail !== empty) {
57
+ const [last, rest] = popBack1(tail)
58
+ tail = rest
59
+ if (last === 1n) {
60
+ const head = cBase32ToVec5x(s.slice(0, i))
61
+ if (head === null || length(head) + length(rest) > maxLength) {
62
+ return null
63
+ }
64
+ return concat(head)(rest)
65
+ }
66
+ }
59
67
  }
60
68
  return null
61
69
  }
@@ -7,4 +7,7 @@ export declare const proof: {
7
7
  unalignedBits: () => void;
8
8
  caseInsensitive: () => void;
9
9
  unterminated: () => void;
10
+ trailingZeroSymbols: () => void;
11
+ decodeAtMaxLengthSucceeds: () => void;
12
+ decodeOverflow: () => void;
10
13
  };
@@ -2,7 +2,7 @@
2
2
  * @import { Vec } from '../../types/bit_vec/types.ts'
3
3
  */
4
4
 
5
- import { empty, vec } from '../../types/bit_vec/module.f.mjs'
5
+ import { empty, maxLength, vec } from '../../types/bit_vec/module.f.mjs'
6
6
  import { cBase32ToVec, cBase32ToVec5x, vec5xToCBase32, vecToCBase32 } from './module.f.mjs'
7
7
  import { assertEq } from '../../asserts/module.f.mjs'
8
8
 
@@ -75,5 +75,27 @@ export const proof = {
75
75
  assertEq(cBase32ToVec("0"), null, 'single zero symbol must be null')
76
76
  assertEq(cBase32ToVec("00"), null, 'all-zero symbols must be null')
77
77
  assertEq(cBase32ToVec("o"), null, 'o (maps to 0) must be null')
78
- }
78
+ assertEq(cBase32ToVec('u'), null, 'invalid trailing symbol must be null')
79
+ assertEq(cBase32ToVec('u8'), null, 'invalid symbol before sentinel must be null')
80
+ assertEq(cBase32ToVec('8u'), null, 'invalid symbol after sentinel must be null')
81
+ },
82
+ trailingZeroSymbols: () => {
83
+ assertEq(cBase32ToVec('g0'), empty)
84
+ assertEq(cBase32ToVec('80'), vec(1n)(0n))
85
+ },
86
+ decodeAtMaxLengthSucceeds: () => {
87
+ const value = vec(maxLength)(0n)
88
+ // Construct the boundary encoding directly. Encoding a `maxLength`
89
+ // vector exceeds Bun's own BigInt size limit while adding the stop
90
+ // bit, independently of this decoder regression.
91
+ const encoded = '0'.repeat(209_715) + '8'
92
+ assertEq(cBase32ToVec(encoded), value)
93
+ },
94
+ decodeOverflow: () => {
95
+ // Reject both an independently oversized head and a valid head whose
96
+ // retained tail would push the combined result over `maxLength`.
97
+ assertEq(cBase32ToVec('0'.repeat(209_717) + 'g'), null)
98
+ assertEq(cBase32ToVec('0'.repeat(209_715) + '4'), null)
99
+ assertEq(cBase32ToVec('0'.repeat(209_715) + '1'), null)
100
+ },
79
101
  }
@@ -4,15 +4,10 @@ import { baseN } from './module.f.mjs'
4
4
 
5
5
  const hex = baseN(4n, '0123456789abcdef')
6
6
 
7
- const cb32 = baseN(5n, '0123456789abcdefghjkmnpqrstvwxyz', c => {
8
- const lower = c.toLowerCase()
9
- switch (lower) {
10
- case 'i': { return '1' }
11
- case 'l': { return '1' }
12
- case 'o': { return '0' }
13
- default: { return lower }
14
- }
15
- })
7
+ // A synthetic normalizer keeps this proof focused on `baseN`'s mechanism
8
+ // rather than duplicating the rules owned by a concrete codec.
9
+ const normalizedHex = baseN(4n, '0123456789abcdef', c =>
10
+ c === 'x' ? 'a' : c === 'y' ? 'z' : c.toLowerCase())
16
11
 
17
12
  // Sample input for the `big` proof below: 262 144 `f` characters decode into a
18
13
  // 1 Mibit (`maxLength`) vector.
@@ -48,16 +43,13 @@ export const proof = {
48
43
  },
49
44
  normalizeHit: () => {
50
45
  // 'A' lowercases to 'a' — same vector as the lowercase input.
51
- const a = cb32.stringToVec('A')
52
- const b = cb32.stringToVec('a')
46
+ const a = normalizedHex.stringToVec('A')
47
+ const b = normalizedHex.stringToVec('a')
53
48
  assertEq(a, b, [a, b])
54
- // Crockford folds: i,l 1 and o → 0.
55
- assertEq(cb32.stringToVec('I'), cb32.stringToVec('1'), 'I→1')
56
- assertEq(cb32.stringToVec('l'), cb32.stringToVec('1'), 'l→1')
57
- assertEq(cb32.stringToVec('o'), cb32.stringToVec('0'), 'o→0')
49
+ assertEq(normalizedHex.stringToVec('x'), a, 'xa')
58
50
  },
59
51
  normalizeMiss: () => {
60
- assertEq(cb32.stringToVec('u'), null, 'unknown char should return null')
52
+ assertEq(normalizedHex.stringToVec('y'), null, 'normalizing to an unknown char should return null')
61
53
  },
62
54
  // Decodes a 1 Mibit hex string. With the O(n log n) `listToVec` builder this
63
55
  // runs in well under a second (was ~13 s node / ~43 s bun under the old
@@ -3,6 +3,7 @@
3
3
  * @import { RuleSet } from '../data/types.ts'
4
4
  * @import { CodePointMeta } from '../descent/types.ts'
5
5
  * @import { Rule as FRule } from '../types.ts'
6
+ * @import { Match } from './types.ts'
6
7
  * @import { MatchResult } from './types.ts'
7
8
  */
8
9
  export declare const proof: {
@@ -28,4 +29,5 @@ export declare const proof: {
28
29
  leftRecursiveVariant: () => void;
29
30
  };
30
31
  cyclicNullability: () => void;
32
+ ssn: () => void;
31
33
  };
@@ -3,12 +3,13 @@
3
3
  * @import { RuleSet } from '../data/types.ts'
4
4
  * @import { CodePointMeta } from '../descent/types.ts'
5
5
  * @import { Rule as FRule } from '../types.ts'
6
+ * @import { Match } from './types.ts'
6
7
  * @import { MatchResult } from './types.ts'
7
8
  */
8
9
 
9
10
  import { stringToCodePointList } from '../../text/utf16/module.f.mjs'
10
11
  import { map, toArray } from '../../types/list/module.f.mjs'
11
- import { commaJoin0Plus, eof, option, range, repeat0Plus, set } from '../module.f.mjs'
12
+ import { commaJoin0Plus, eof, option, range, repeat, repeat0Plus, set } from '../module.f.mjs'
12
13
  import { toData } from '../data/module.f.mjs'
13
14
  import { descentParser } from '../descent/module.f.mjs'
14
15
  import { dispatchMap, parser, parserRuleSet } from './module.f.mjs'
@@ -18,6 +19,15 @@ import { deterministic, showAst } from '../testlib.f.mjs'
18
19
  /** @type {(cp: CodePoint) => CodePointMeta<unknown>} */
19
20
  const mapCodePoint = cp => [cp, undefined]
20
21
 
22
+ /** @type {(mr: MatchResult) => boolean} */
23
+ const isMatchSuccess = ([, success, remainder]) => success && remainder?.length === 0
24
+
25
+ /** @type {(m: Match) => (s: string, success: boolean) => void} */
26
+ const expectMatch = m => (s, success) => {
27
+ const mr = m('', toArray(stringToCodePointList(s)))
28
+ assertEq(isMatchSuccess(mr), success, mr)
29
+ }
30
+
21
31
  /**
22
32
  * One grammar matched by both backends, which must build the same AST for it:
23
33
  * the shape is pinned once and asserted against each backend, so neither can
@@ -241,13 +251,7 @@ export const proof = {
241
251
  },
242
252
  () => {
243
253
  const m = parser(option('a'))
244
-
245
- const isSuccess = (/** @type {MatchResult} */mr) => mr[1] && mr[2]?.length === 0
246
- /** @type {(s: string, success: boolean) => void} */
247
- const expect = (s, success) => {
248
- const mr = m('', toArray(stringToCodePointList(s)))
249
- assertEq(isSuccess(mr), success, mr)
250
- }
254
+ const expect = expectMatch(m)
251
255
 
252
256
  expect('a', true)
253
257
  expect('', true)
@@ -268,12 +272,10 @@ export const proof = {
268
272
 
269
273
  const m = parser(value)
270
274
 
271
- /** @type {(mr: MatchResult) => boolean} */
272
- const isSuccess = mr => mr[1] && mr[2]?.length === 0
273
275
  /** @type {(s: string, success: boolean) => void} */
274
276
  const expect = (s, success) => {
275
277
  const mr = m('value', toArray(stringToCodePointList(s)))
276
- assertEq(isSuccess(mr), success, mr)
278
+ assertEq(isMatchSuccess(mr), success, mr)
277
279
  }
278
280
 
279
281
  expect('', false)
@@ -305,7 +307,7 @@ export const proof = {
305
307
  /** @type {(s: string) => void} */
306
308
  const expectNoMatch = s => {
307
309
  const mr = ml(entry, toArray(stringToCodePointList(s)))
308
- assertEq(mr[1] && mr[2]?.length === 0, false, s)
310
+ assertEq(isMatchSuccess(mr), false, s)
309
311
  }
310
312
 
311
313
  expectSameAst(' true ')
@@ -570,12 +572,7 @@ export const proof = {
570
572
  const ws = option(' ')
571
573
  const value = () => ({ array: ['[', value, ']'], leaf: 'a' })
572
574
  const m = parser([ws, value, ws]) // must not throw 'can not merge'
573
-
574
- /** @type {(s: string, success: boolean) => void} */
575
- const expect = (s, success) => {
576
- const mr = m('', toArray(stringToCodePointList(s)))
577
- assertEq(mr[1] && mr[2]?.length === 0, success, mr)
578
- }
575
+ const expect = expectMatch(m)
579
576
 
580
577
  expect('a', true)
581
578
  expect(' a ', true)
@@ -584,4 +581,22 @@ export const proof = {
584
581
  expect(' [[a]] ', true)
585
582
  expect('b', false)
586
583
  },
584
+ // A Social Security Number, `ddd-dd-dddd`.
585
+ ssn: () => {
586
+ const ws = repeat0Plus(' ')
587
+ const d = range('09')
588
+ const ssn = /** @type {const} */([
589
+ ws, repeat(3)(d), ws, '-', ws, repeat(2)(d), ws, '-', ws, repeat(4)(d), ws])
590
+
591
+ const m = parser(ssn) // must not throw 'can not merge'
592
+ const expect = expectMatch(m)
593
+
594
+ expect('123-45-6789', true)
595
+ expect(' 123 - 45 - 6789 ', true)
596
+ expect('22', false) // too short
597
+ expect('123456789', false) // no dashes
598
+ expect('123-3456-78', false) // wrong grouping
599
+ expect('123-345-6789', false) // wrong grouping
600
+ expect('12a-34-5678', false) // a letter where a digit is required
601
+ }
587
602
  }
@@ -106,9 +106,9 @@ export declare const none: None;
106
106
  /**
107
107
  * Creates an option value from a required branch.
108
108
  */
109
- export declare const option: <S extends Rule>(
109
+ export declare const option: <const S extends Rule>(
110
110
  /**
111
- * @template {Rule} S
111
+ * @template {Rule} const S
112
112
  * @param {S} some
113
113
  * @returns {Option<S>}
114
114
  */
@@ -236,7 +236,7 @@ export const none = []
236
236
  */
237
237
  export const option =
238
238
  /**
239
- * @template {Rule} S
239
+ * @template {Rule} const S
240
240
  * @param {S} some
241
241
  * @returns {Option<S>}
242
242
  */
@@ -261,7 +261,7 @@ export const repeat0Plus =
261
261
  * @returns {Repeat0Plus<T>}
262
262
  */
263
263
  some => {
264
- const r = () => option(/** @type {const} */ ([some, r]))
264
+ const r = () => option([some, r])
265
265
  return r
266
266
  }
267
267
 
@@ -215,20 +215,9 @@ export const proof = {
215
215
  const hash = writeResult[1]
216
216
  assertEq(length(hash), 256n, ['expected 256-bit hash', length(hash)])
217
217
  assertEq(msb.cmp(hash)(computeSync(sha256)([content])), 0, 'write hash mismatch')
218
- /** @type {(acc: readonly Vec[]) => (stream: List<FileCasOperation, Vec, IoChannel>) => Effect<FileCasOperation, readonly Vec[], IoChannel>} */
219
- const drain = acc =>
220
- stream =>
221
- ioStep(
222
- stream,
223
- (node) => {
224
- if (node === undefined) { return pureOk(acc) }
225
- const { first, tail } = node
226
- return drain([...acc, first])(tail)
227
- },
228
- )
229
- const [, readResult] = virtual(state1)(drain([])(c.read(hash)))
218
+ const [, readResult] = virtual(state1)(collectRead(c.read(hash)))
230
219
  assert(readResult[0] === 'ok', ['expected read ok', readResult])
231
- assertEq(msb.cmp(msb.listToVec(readResult[1]))(content), 0, 'read content mismatch')
220
+ assertEq(msb.cmp(readResult[1])(content), 0, 'read content mismatch')
232
221
  },
233
222
  casReadMissingShard: () => {
234
223
  // A missing shard fails the stream; it is never the `ok(undefined)` that
@@ -252,21 +241,10 @@ export const proof = {
252
241
  assert(writeResult[0] === 'ok', ['expected write ok', writeResult])
253
242
  const hash = writeResult[1]
254
243
  assertEq(msb.cmp(hash)(computeSync(sha256)(chunks)), 0, 'multi-chunk write hash mismatch')
255
- /** @type {(acc: readonly Vec[]) => (stream: List<FileCasOperation, Vec, IoChannel>) => Effect<FileCasOperation, readonly Vec[], IoChannel>} */
256
- const drain = acc =>
257
- stream =>
258
- ioStep(
259
- stream,
260
- (node) => {
261
- if (node === undefined) { return pureOk(acc) }
262
- const { first, tail } = node
263
- return drain([...acc, first])(tail)
264
- },
265
- )
266
- const [, readResult] = virtual(state1)(drain([])(c.read(hash)))
244
+ const [, readResult] = virtual(state1)(collectRead(c.read(hash)))
267
245
  assert(readResult[0] === 'ok', ['expected read ok', readResult])
268
246
  const expected = msb.concat(msb.concat(chunks[0])(chunks[1]))(chunks[2])
269
- assertEq(msb.cmp(msb.listToVec(readResult[1]))(expected), 0, 'multi-chunk read content mismatch')
247
+ assertEq(msb.cmp(readResult[1])(expected), 0, 'multi-chunk read content mismatch')
270
248
  },
271
249
  casWriteDedup: () => {
272
250
  // Same content ⇒ same hash; the second upload's replace-`rename` publishes over the
@@ -18,6 +18,12 @@ export const os = /** @type {const} */ (['ubuntu', 'macos', 'windows'])
18
18
 
19
19
  export const architecture = /** @type {const} */ (['intel', 'arm'])
20
20
 
21
+ // These three are **closed**, which is the bare form's meaning and the right
22
+ // one here: `parseGitHubAction` reads back a workflow this repo generates, so
23
+ // a key the schema does not name is generator drift rather than a field a
24
+ // third party added. Reading a hand-written workflow — which carries `name`,
25
+ // `if`, `env` and much else — would need `open`.
26
+
21
27
  export const stepSchema = /** @type {const} */ ({
22
28
  run: option(string),
23
29
  uses: option(string),
@@ -18,20 +18,20 @@ export declare const images: {
18
18
  readonly arm: 'windows-11-arm';
19
19
  };
20
20
  };
21
- export declare const functionalscript = /** @type {const} */ "0.45.0";
22
- export declare const bun = "1.3.14";
21
+ export declare const functionalscript = /** @type {const} */ "0.46.1";
22
+ export declare const bun = "1.4.0";
23
23
  export declare const deno = "2.9.5";
24
24
  export declare const node: {
25
25
  readonly default: '26.7.0';
26
26
  readonly node22: '22.23.2';
27
- readonly node24: '24.18.1';
27
+ readonly node24: '24.19.0';
28
28
  };
29
29
  export declare const nixpkgs: {
30
30
  readonly ref: 'nixos-26.05';
31
- readonly commit: '02e08985a27c65ffd33d434eeb2e660a2e4dc84d';
31
+ readonly commit: 'f4f698677b11021a8f84f452e23ae9ef2427bec3';
32
32
  };
33
- export declare const wasmtime = "47.0.3";
34
- export declare const wasmer = "7.2.1";
33
+ export declare const wasmtime = "48.0.1";
34
+ export declare const wasmer = "7.3.0";
35
35
  export declare const actions: {
36
36
  readonly 'actions/checkout': 'v7.0.1';
37
37
  readonly 'actions/setup-node': 'v7.0.0';
@@ -41,5 +41,5 @@ export declare const actions: {
41
41
  readonly 'bytecodealliance/actions/wasmtime/setup': 'v1.1.3';
42
42
  readonly 'wasmerio/setup-wasmer': 'v3.1';
43
43
  readonly 'cachix/install-nix-action': 'v31.11.1';
44
- readonly 'dtolnay/rust-toolchain': '1.97.1';
44
+ readonly 'dtolnay/rust-toolchain': '1.98.0';
45
45
  };
@@ -25,10 +25,10 @@ export const images = /** @type {const} */({
25
25
  // published FunctionalScript release; do not tie it to package.json's current
26
26
  // in-repo version.
27
27
  // https://www.npmjs.com/package/functionalscript
28
- export const functionalscript = /** @type {const} */ '0.45.0'
28
+ export const functionalscript = /** @type {const} */ '0.46.1'
29
29
 
30
30
  // https://bun.sh/
31
- export const bun = '1.3.14'
31
+ export const bun = '1.4.0'
32
32
 
33
33
  // https://deno.com/
34
34
  export const deno = '2.9.5'
@@ -43,7 +43,7 @@ export const deno = '2.9.5'
43
43
  export const node = /** @type {const} */({
44
44
  default: '26.7.0',
45
45
  node22: '22.23.2',
46
- node24: '24.18.1',
46
+ node24: '24.19.0',
47
47
  })
48
48
 
49
49
  // Official Nixpkgs snapshot used by the generated CI flakes. `ref` is the
@@ -53,14 +53,14 @@ export const node = /** @type {const} */({
53
53
  // https://channels.nixos.org/nixos-26.05/git-revision
54
54
  export const nixpkgs = /** @type {const} */({
55
55
  ref: 'nixos-26.05',
56
- commit: '02e08985a27c65ffd33d434eeb2e660a2e4dc84d',
56
+ commit: 'f4f698677b11021a8f84f452e23ae9ef2427bec3',
57
57
  })
58
58
 
59
59
  // https://github.com/bytecodealliance/wasmtime/releases
60
- export const wasmtime = '47.0.3'
60
+ export const wasmtime = '48.0.1'
61
61
 
62
62
  // https://github.com/wasmerio/wasmer/releases
63
- export const wasmer = '7.2.1'
63
+ export const wasmer = '7.3.0'
64
64
 
65
65
  // GitHub Action versions used by CI step builders. The key is the action
66
66
  // `owner/name`; call sites compose the full ref as
@@ -85,5 +85,5 @@ export const actions = /** @type {const} */({
85
85
  // Enables the `nix-command` and `flakes` experimental features by default.
86
86
  'cachix/install-nix-action': 'v31.11.1',
87
87
  // https://rust-lang.org/ - value is Rust version, not action version
88
- 'dtolnay/rust-toolchain': '1.97.1',
88
+ 'dtolnay/rust-toolchain': '1.98.0',
89
89
  })
@@ -14,19 +14,23 @@ import { at, fromEntries } from '../types/object/module.f.mjs'
14
14
 
15
15
  const helpMeta = { names: ['help', 'h', '?'], description: 'Print this help message' }
16
16
 
17
- /** @type {<O extends NodeOp>(commands: Commands<O>) => Program<O | Write>} */
18
- export const dispatch = commands => options => {
19
- const [cmd, ...rest] = options.args
17
+ /** @type {<O extends NodeOp>(commands: Commands<O>) => string} */
18
+ const renderHelp = commands => {
20
19
  const rows = [...commands, helpMeta]
21
- const nameCol = rows.map(({names}) => names.join(', '))
22
- const width = Math.max(...nameCol.map(({length}) => length))
23
- const helpText = [
20
+ const nameCol = rows.map(({ names }) => names.join(', '))
21
+ const width = Math.max(...nameCol.map(({ length }) => length))
22
+ return [
24
23
  'Available commands:',
25
- ...rows.map(({description}, i) => ` ${nameCol[i].padEnd(width)} ${description}`)
24
+ ...rows.map(({ description }, i) => ` ${nameCol[i].padEnd(width)} ${description}`)
26
25
  ].join('\n')
26
+ }
27
+
28
+ /** @type {<O extends NodeOp>(commands: Commands<O>) => Program<O | Write>} */
29
+ export const dispatch = commands => options => {
30
+ const [cmd, ...rest] = options.args
27
31
  const map = fromEntries(commands.flatMap(c => c.names.map(n => /** @type {const} */ ([n, c]))))
28
32
  if (cmd === undefined) {
29
- return errorExit(`Error: command is required.\n${helpText}`)
33
+ return errorExit(`Error: command is required.\n${renderHelp(commands)}`)
30
34
  }
31
35
  if (helpMeta.names.includes(cmd)) {
32
36
  const [target] = rest
@@ -36,11 +40,11 @@ export const dispatch = commands => options => {
36
40
  return dispatch(targetCmd.handler)({ ...options, args: ['help'] })
37
41
  }
38
42
  }
39
- return exitStep(log(helpText))
43
+ return exitStep(log(renderHelp(commands)))
40
44
  }
41
45
  const found = at(cmd)(map)
42
46
  if (found === null) {
43
- return errorExit(`Error: unknown command "${cmd}".\n${helpText}`)
47
+ return errorExit(`Error: unknown command "${cmd}".\n${renderHelp(commands)}`)
44
48
  }
45
49
  const { handler } = found
46
50
  return (typeof handler === 'function' ? handler : dispatch(handler))({ ...options, args: rest })
@@ -18,7 +18,6 @@
18
18
  *
19
19
  * @import { PrimeField } from '../../types/prime_field/types.ts'
20
20
  * @import { Nullable } from '../../types/nullable/types.ts'
21
- * @import { Unary } from '../../types/bigint/types.ts'
22
21
  * @import { Sloth } from './types.ts'
23
22
  */
24
23
  import type { Sloth } from './types.ts';
@@ -18,27 +18,16 @@
18
18
  *
19
19
  * @import { PrimeField } from '../../types/prime_field/types.ts'
20
20
  * @import { Nullable } from '../../types/nullable/types.ts'
21
- * @import { Unary } from '../../types/bigint/types.ts'
22
21
  * @import { Sloth } from './types.ts'
23
22
  */
24
23
 
24
+ import { iterate } from '../../types/function/module.f.mjs'
25
25
  import { modSqrt, prime_field } from '../../types/prime_field/module.f.mjs'
26
26
 
27
27
  /** Sloth VDF modulus (3072-bit safe prime, same as reference implementations). */
28
28
  export const p =
29
29
  0xf2346eae06a23388_2814ff16f6a076d3_b8f2161c5c92171c_0b7b84eed4e9475b_cce0c13bde34512a_fdf90f41ab9b86dc_f834f85e04b27fad_ee712eed23a1d4e5_8cd1b09d9bfb1069_6d614f119179a40c_49dc8762edc29e81_15263913237e1471_8cbcd4dc6b35bace_13f8cdb1b5156c50_c47b4aaee0820c87_4e2864cb854367c3n
30
30
 
31
- /** @type {(steps: bigint) => (f: Unary) => (value: bigint) => bigint} */
32
- const repeatSeq = steps => f => value => {
33
- let v = value
34
- let i = 0n
35
- while (i < steps) {
36
- v = f(v)
37
- i = i + 1n
38
- }
39
- return v
40
- }
41
-
42
31
  /**
43
32
  * Builds Sloth VDF operations over `modulus`.
44
33
  *
@@ -52,11 +41,11 @@ export const sloth_vdf = modulus => {
52
41
 
53
42
  /** @type {(steps: bigint) => (value: bigint) => bigint} */
54
43
  const squareLoop = steps => value =>
55
- repeatSeq(steps)(pow2)(reduce(value))
44
+ iterate(steps)(reduce(value))(pow2)
56
45
 
57
46
  /** @type {(steps: bigint) => (value: bigint) => bigint} */
58
47
  const modSqrtLoop = steps => value =>
59
- repeatSeq(steps)(root)(reduce(value))
48
+ iterate(steps)(reduce(value))(root)
60
49
 
61
50
  /** @type {(steps: bigint) => (x: bigint) => Nullable<bigint>} */
62
51
  const evalSteps = steps => x =>
@@ -11,6 +11,7 @@
11
11
 
12
12
  import { allOk, import_, readdir } from '../effects/node/module.f.mjs'
13
13
  import { cmp as strCmp } from '../types/string/module.f.mjs'
14
+ import { fromEntries } from '../types/object/module.f.mjs'
14
15
  import { unwrap } from '../types/result/module.f.mjs'
15
16
  import { mapStep, pureOk, step } from '../effects/module.f.mjs'
16
17
  import { join, relativize, toPosix } from '../path/module.f.mjs'
@@ -55,24 +56,14 @@ const allFiles = (s, predicate) => {
55
56
  const load = p => {
56
57
  const listed = step(
57
58
  readdir(p, {}),
58
- d => {
59
- /** @type {readonly Effect<Readdir | All, readonly string[], IoChannel>[]} */
60
- let result = []
61
- for (const i of d) {
62
- const { name } = i
63
- if (name.startsWith('.')) { continue }
64
- const file = join(p, name)
65
- if (!i.isFile) {
66
- if (name === 'node_modules') { continue }
67
- result = [...result, load(file)]
68
- continue
69
- }
70
- if (predicate(file)) {
71
- result = [...result, pureOk([file])]
72
- }
73
- }
74
- return allOk(...result)
75
- })
59
+ d => allOk(...d.flatMap(i => {
60
+ const { name } = i
61
+ if (name.startsWith('.')) { return [] }
62
+ const file = join(p, name)
63
+ return i.isFile
64
+ ? (predicate(file) ? [pureOk([file])] : [])
65
+ : (name === 'node_modules' ? [] : [load(file)])
66
+ })))
76
67
  return mapStep(listed, v => v.flat())
77
68
  }
78
69
  return load(s)
@@ -82,8 +73,6 @@ const allFiles = (s, predicate) => {
82
73
  const loadFile = f =>
83
74
  mapStep(import_(f), m => [/** @type {const} */ ([f, m])])
84
75
 
85
- const { fromEntries } = Object
86
-
87
76
  /**
88
77
  * Discovers all source files under `INIT_CWD` (or `.` if unset) that match
89
78
  * `predicate`, imports them, and returns a map from relative path to module
@@ -142,6 +131,7 @@ export const proof = {
142
131
  // still skipped.
143
132
  /** @type {Dir} */
144
133
  const root = {
134
+ '.hidden.f.ts': [],
145
135
  'a.f.ts': [],
146
136
  'b.f.mjs': [],
147
137
  'c.f.mts': [],