functionalscript 0.45.0 → 0.46.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 (296) hide show
  1. package/README.md +5 -3
  2. package/fjs/asn.1/module.f.mjs +8 -1
  3. package/fjs/asn.1/proof.f.d.mts +10 -0
  4. package/fjs/asn.1/proof.f.mjs +16 -0
  5. package/fjs/basen/base128/module.f.mjs +22 -5
  6. package/fjs/bnf/data/module.f.d.mts +32 -6
  7. package/fjs/bnf/data/module.f.mjs +112 -7
  8. package/fjs/bnf/data/proof.f.d.mts +2 -0
  9. package/fjs/bnf/data/proof.f.mjs +74 -3
  10. package/fjs/bnf/data/types.d.ts +20 -2
  11. package/fjs/bnf/descent/module.f.d.mts +28 -14
  12. package/fjs/bnf/descent/module.f.mjs +122 -77
  13. package/fjs/bnf/descent/proof.f.d.mts +2 -0
  14. package/fjs/bnf/descent/proof.f.mjs +117 -31
  15. package/fjs/bnf/descent/types.d.ts +12 -14
  16. package/fjs/bnf/ll1/module.f.d.mts +64 -22
  17. package/fjs/bnf/ll1/module.f.mjs +214 -154
  18. package/fjs/bnf/ll1/proof.f.d.mts +15 -2
  19. package/fjs/bnf/ll1/proof.f.mjs +323 -149
  20. package/fjs/bnf/ll1/types.d.ts +16 -24
  21. package/fjs/bnf/matcher/module.f.d.mts +66 -0
  22. package/fjs/bnf/matcher/module.f.mjs +81 -0
  23. package/fjs/bnf/matcher/proof.f.d.mts +10 -0
  24. package/fjs/bnf/matcher/proof.f.mjs +79 -0
  25. package/fjs/bnf/matcher/types.d.ts +54 -0
  26. package/fjs/bnf/testlib.f.d.mts +31 -0
  27. package/fjs/bnf/testlib.f.mjs +80 -0
  28. package/fjs/cas/cli/module.f.d.mts +1 -1
  29. package/fjs/cas/cli/module.f.mjs +14 -20
  30. package/fjs/cas/cli/proof.f.d.mts +1 -3
  31. package/fjs/cas/cli/proof.f.mjs +44 -33
  32. package/fjs/cas/evo/module.f.d.mts +64 -18
  33. package/fjs/cas/evo/module.f.mjs +148 -70
  34. package/fjs/cas/evo/proof.f.d.mts +10 -1
  35. package/fjs/cas/evo/proof.f.mjs +305 -223
  36. package/fjs/cas/evo/types.d.ts +45 -9
  37. package/fjs/cas/module.f.d.mts +18 -32
  38. package/fjs/cas/module.f.mjs +129 -128
  39. package/fjs/cas/proof.f.d.mts +11 -8
  40. package/fjs/cas/proof.f.mjs +259 -147
  41. package/fjs/cas/types.d.ts +24 -10
  42. package/fjs/ci/config/module.f.d.mts +2 -2
  43. package/fjs/ci/config/module.f.mjs +2 -2
  44. package/fjs/ci/module.f.d.mts +5 -5
  45. package/fjs/ci/module.f.mjs +8 -7
  46. package/fjs/ci/nix/module.f.d.mts +7 -5
  47. package/fjs/ci/nix/module.f.mjs +13 -12
  48. package/fjs/ci/nix/proof.f.mjs +2 -2
  49. package/fjs/ci/proof.f.mjs +9 -5
  50. package/fjs/cli/module.f.d.mts +4 -6
  51. package/fjs/cli/module.f.mjs +4 -8
  52. package/fjs/cli/proof.f.mjs +17 -16
  53. package/fjs/cli/types.d.ts +2 -3
  54. package/fjs/common/monoid/types.d.ts +1 -1
  55. package/fjs/crypto/hmac/module.f.mjs +2 -2
  56. package/fjs/crypto/sha2/module.f.mjs +3 -1
  57. package/fjs/crypto/sha2/proof.f.d.mts +1 -0
  58. package/fjs/crypto/sha2/proof.f.mjs +24 -0
  59. package/fjs/crypto/sha2/types.d.ts +11 -0
  60. package/fjs/crypto/sign/module.f.mjs +2 -2
  61. package/fjs/dev/module.f.d.mts +13 -4
  62. package/fjs/dev/module.f.mjs +56 -27
  63. package/fjs/dev/update/module.f.d.mts +9 -4
  64. package/fjs/dev/update/module.f.mjs +14 -10
  65. package/fjs/dev/update/proof.f.d.mts +1 -3
  66. package/fjs/dev/update/proof.f.mjs +10 -5
  67. package/fjs/djs/module.f.d.mts +13 -5
  68. package/fjs/djs/module.f.mjs +31 -16
  69. package/fjs/djs/parser/module.f.d.mts +13 -3
  70. package/fjs/djs/parser/module.f.mjs +117 -16
  71. package/fjs/djs/parser/proof.f.d.mts +5 -1
  72. package/fjs/djs/parser/proof.f.mjs +274 -12
  73. package/fjs/djs/parser/types.d.ts +7 -1
  74. package/fjs/djs/proof.f.d.mts +18 -2
  75. package/fjs/djs/proof.f.mjs +187 -12
  76. package/fjs/djs/serializer/module.f.d.mts +25 -8
  77. package/fjs/djs/serializer/module.f.mjs +61 -15
  78. package/fjs/djs/serializer/proof.f.d.mts +5 -0
  79. package/fjs/djs/serializer/proof.f.mjs +23 -1
  80. package/fjs/djs/tokenizer/module.f.d.mts +17 -5
  81. package/fjs/djs/tokenizer/module.f.mjs +99 -60
  82. package/fjs/djs/tokenizer/proof.f.d.mts +1 -1
  83. package/fjs/djs/tokenizer/proof.f.mjs +74 -67
  84. package/fjs/djs/transpiler/module.f.d.mts +12 -7
  85. package/fjs/djs/transpiler/module.f.mjs +82 -56
  86. package/fjs/djs/transpiler/types.d.ts +7 -3
  87. package/fjs/djs/types.d.ts +7 -1
  88. package/fjs/effects/list/module.f.d.mts +14 -11
  89. package/fjs/effects/list/module.f.mjs +13 -11
  90. package/fjs/effects/list/types.d.ts +27 -7
  91. package/fjs/effects/memory/module.f.d.mts +2 -1
  92. package/fjs/effects/memory/module.f.mjs +3 -4
  93. package/fjs/effects/memory/proof.f.mjs +13 -7
  94. package/fjs/effects/memory/types.d.ts +4 -3
  95. package/fjs/effects/mock/module.f.d.mts +20 -4
  96. package/fjs/effects/mock/module.f.mjs +37 -5
  97. package/fjs/effects/mock/types.d.ts +10 -1
  98. package/fjs/effects/module.d.mts +4 -2
  99. package/fjs/effects/module.f.d.mts +466 -280
  100. package/fjs/effects/module.f.mjs +537 -299
  101. package/fjs/effects/module.mjs +2 -1
  102. package/fjs/effects/node/memory/module.d.mts +4 -2
  103. package/fjs/effects/node/memory/module.mjs +6 -3
  104. package/fjs/effects/node/memory/proof.mjs +10 -4
  105. package/fjs/effects/node/module.d.mts +4 -3
  106. package/fjs/effects/node/module.f.d.mts +168 -33
  107. package/fjs/effects/node/module.f.mjs +256 -52
  108. package/fjs/effects/node/module.mjs +57 -34
  109. package/fjs/effects/node/proof.f.d.mts +28 -2
  110. package/fjs/effects/node/proof.f.mjs +161 -42
  111. package/fjs/effects/node/types.d.ts +106 -18
  112. package/fjs/effects/node/virtual/module.f.d.mts +18 -4
  113. package/fjs/effects/node/virtual/module.f.mjs +110 -68
  114. package/fjs/effects/node/virtual/proof.f.d.mts +28 -2
  115. package/fjs/effects/node/virtual/proof.f.mjs +190 -9
  116. package/fjs/effects/proof.f.d.mts +69 -37
  117. package/fjs/effects/proof.f.mjs +410 -130
  118. package/fjs/effects/types.d.ts +161 -33
  119. package/fjs/emergent_testing/module.f.d.mts +19 -12
  120. package/fjs/emergent_testing/module.f.mjs +93 -33
  121. package/fjs/emergent_testing/proof.f.d.mts +21 -7
  122. package/fjs/emergent_testing/proof.f.mjs +166 -32
  123. package/fjs/emergent_testing/types.d.ts +22 -4
  124. package/fjs/fsm/module.f.d.mts +14 -4
  125. package/fjs/fsm/module.f.mjs +54 -37
  126. package/fjs/fsm/proof.f.d.mts +2 -0
  127. package/fjs/fsm/proof.f.mjs +83 -114
  128. package/fjs/js/keywords/module.f.d.mts +52 -0
  129. package/fjs/js/keywords/module.f.mjs +72 -0
  130. package/fjs/js/keywords/proof.f.d.mts +3 -0
  131. package/fjs/js/keywords/proof.f.mjs +13 -0
  132. package/fjs/js/tokenizer/module.f.d.mts +26 -6
  133. package/fjs/js/tokenizer/module.f.mjs +145 -159
  134. package/fjs/js/tokenizer/proof.f.d.mts +1 -0
  135. package/fjs/js/tokenizer/proof.f.mjs +54 -24
  136. package/fjs/js/tokenizer/types.d.ts +33 -24
  137. package/fjs/mcp/cas/module.f.d.mts +1 -6
  138. package/fjs/mcp/cas/module.f.mjs +55 -51
  139. package/fjs/mcp/cas/proof.f.d.mts +15 -0
  140. package/fjs/mcp/cas/proof.f.mjs +174 -0
  141. package/fjs/mcp/evo/module.f.d.mts +19 -10
  142. package/fjs/mcp/evo/module.f.mjs +48 -27
  143. package/fjs/mcp/evo/proof.f.d.mts +6 -1
  144. package/fjs/mcp/evo/proof.f.mjs +115 -31
  145. package/fjs/mcp/module.f.d.mts +4 -4
  146. package/fjs/mcp/module.f.mjs +6 -6
  147. package/fjs/mcp/proof.f.d.mts +5 -3
  148. package/fjs/mcp/proof.f.mjs +112 -46
  149. package/fjs/media/html/module.f.mjs +1 -1
  150. package/fjs/media/json/extended/module.f.d.mts +82 -0
  151. package/fjs/media/json/extended/module.f.mjs +153 -0
  152. package/fjs/media/json/extended/proof.f.d.mts +42 -0
  153. package/fjs/media/json/extended/proof.f.mjs +127 -0
  154. package/fjs/media/json/extended/types.d.ts +23 -0
  155. package/fjs/media/json/module.f.d.mts +8 -2
  156. package/fjs/media/json/module.f.mjs +43 -41
  157. package/fjs/media/json/number/module.f.d.mts +59 -0
  158. package/fjs/media/json/number/module.f.mjs +136 -0
  159. package/fjs/media/json/number/proof.f.d.mts +24 -0
  160. package/fjs/media/json/number/proof.f.mjs +86 -0
  161. package/fjs/media/json/number/types.d.ts +28 -0
  162. package/fjs/media/json/parser/module.f.d.mts +25 -13
  163. package/fjs/media/json/parser/module.f.mjs +114 -70
  164. package/fjs/media/json/parser/proof.f.d.mts +5 -0
  165. package/fjs/media/json/parser/proof.f.mjs +31 -1
  166. package/fjs/media/json/parser/types.d.ts +33 -14
  167. package/fjs/media/json/rtti/module.f.d.mts +1 -1
  168. package/fjs/media/json/rtti/module.f.mjs +1 -1
  169. package/fjs/media/json/rtti/proof.f.mjs +9 -9
  170. package/fjs/media/json/schema/module.f.mjs +3 -13
  171. package/fjs/media/json/schema/proof.f.d.mts +0 -1
  172. package/fjs/media/json/schema/proof.f.mjs +1 -2
  173. package/fjs/media/json/serializer/module.f.d.mts +32 -1
  174. package/fjs/media/json/serializer/module.f.mjs +64 -2
  175. package/fjs/media/json/tokenizer/module.f.mjs +7 -3
  176. package/fjs/media/json/tokenizer/proof.f.d.mts +6 -0
  177. package/fjs/media/json/tokenizer/proof.f.mjs +62 -21
  178. package/fjs/media/json/types.d.ts +36 -10
  179. package/fjs/media/lock/module.f.d.mts +100 -0
  180. package/fjs/media/lock/module.f.mjs +125 -0
  181. package/fjs/media/lock/proof.f.d.mts +33 -0
  182. package/fjs/media/lock/proof.f.mjs +196 -0
  183. package/fjs/media/lock/types.d.ts +15 -0
  184. package/fjs/media/module.f.d.mts +6 -5
  185. package/fjs/media/module.f.mjs +8 -7
  186. package/fjs/media/note/module.f.d.mts +121 -0
  187. package/fjs/media/note/module.f.mjs +131 -0
  188. package/fjs/media/note/proof.f.d.mts +29 -0
  189. package/fjs/media/note/proof.f.mjs +150 -0
  190. package/fjs/media/note/types.d.ts +10 -0
  191. package/fjs/media/proof.f.d.mts +4 -1
  192. package/fjs/media/proof.f.mjs +40 -21
  193. package/fjs/media/revision/module.f.d.mts +78 -7
  194. package/fjs/media/revision/module.f.mjs +119 -12
  195. package/fjs/media/revision/proof.f.d.mts +10 -0
  196. package/fjs/media/revision/proof.f.mjs +88 -0
  197. package/fjs/media/revision/types.d.ts +34 -5
  198. package/fjs/media/type/module.f.d.mts +33 -15
  199. package/fjs/media/type/module.f.mjs +35 -29
  200. package/fjs/media/type/proof.f.d.mts +2 -1
  201. package/fjs/media/type/proof.f.mjs +30 -10
  202. package/fjs/module.f.mjs +29 -8
  203. package/fjs/nanvm/proof.f.mjs +3 -3
  204. package/fjs/nanvm/rust/module.f.mjs +1 -1
  205. package/fjs/nanvm/update/module.f.d.mts +4 -4
  206. package/fjs/nanvm/update/module.f.mjs +8 -9
  207. package/fjs/nanvm/update/proof.f.mjs +4 -3
  208. package/fjs/proof.f.d.mts +3 -3
  209. package/fjs/proof.f.mjs +36 -10
  210. package/fjs/protocol/json_rpc/module.f.d.mts +2 -2
  211. package/fjs/protocol/json_rpc/module.f.mjs +3 -3
  212. package/fjs/protocol/json_rpc/proof.f.mjs +4 -4
  213. package/fjs/protocol/mcp/module.f.d.mts +10 -13
  214. package/fjs/protocol/mcp/module.f.mjs +74 -58
  215. package/fjs/protocol/mcp/proof.f.d.mts +13 -4
  216. package/fjs/protocol/mcp/proof.f.mjs +193 -61
  217. package/fjs/protocol/mcp/stdio/module.f.d.mts +15 -7
  218. package/fjs/protocol/mcp/stdio/module.f.mjs +38 -25
  219. package/fjs/protocol/mcp/stdio/proof.f.d.mts +4 -2
  220. package/fjs/protocol/mcp/stdio/proof.f.mjs +45 -12
  221. package/fjs/protocol/mcp/stdio/types.d.ts +1 -1
  222. package/fjs/protocol/mcp/types.d.ts +17 -6
  223. package/fjs/sul/id/module.f.d.mts +0 -1
  224. package/fjs/sul/id/module.f.mjs +2 -3
  225. package/fjs/sul/level/hash/module.f.mjs +2 -1
  226. package/fjs/sul/level/hash/proof.f.mjs +2 -2
  227. package/fjs/sul/module.f.mjs +18 -13
  228. package/fjs/text/code_point/module.f.d.mts +8 -0
  229. package/fjs/text/code_point/module.f.mjs +8 -1
  230. package/fjs/text/code_point/proof.f.d.mts +1 -0
  231. package/fjs/text/code_point/proof.f.mjs +11 -0
  232. package/fjs/text/sgr/module.f.d.mts +4 -6
  233. package/fjs/text/sgr/module.f.mjs +6 -7
  234. package/fjs/text/utf16/module.f.mjs +7 -2
  235. package/fjs/text/utf16/proof.f.mjs +3 -3
  236. package/fjs/text/utf8/module.f.mjs +4 -2
  237. package/fjs/types/array/module.f.mjs +14 -2
  238. package/fjs/types/bit_vec/module.f.d.mts +0 -2
  239. package/fjs/types/bit_vec/module.f.mjs +46 -32
  240. package/fjs/types/bit_vec/proof.f.mjs +2 -2
  241. package/fjs/types/btree/remove/module.f.mjs +1 -1
  242. package/fjs/types/btree/set/module.f.mjs +9 -11
  243. package/fjs/types/btree/set/proof.f.mjs +12 -0
  244. package/fjs/types/byte_set/module.f.d.mts +11 -4
  245. package/fjs/types/byte_set/module.f.mjs +14 -6
  246. package/fjs/types/byte_set/proof.f.mjs +7 -7
  247. package/fjs/types/function/compare/module.f.mjs +10 -3
  248. package/fjs/types/list/module.f.d.mts +1 -1
  249. package/fjs/types/list/module.f.mjs +1 -1
  250. package/fjs/types/nullable/module.f.d.mts +18 -4
  251. package/fjs/types/nullable/module.f.mjs +21 -4
  252. package/fjs/types/nullable/proof.f.d.mts +4 -0
  253. package/fjs/types/nullable/proof.f.mjs +15 -0
  254. package/fjs/types/object/module.f.d.mts +12 -2
  255. package/fjs/types/object/module.f.mjs +11 -1
  256. package/fjs/types/patricia_trie/module.f.mjs +26 -11
  257. package/fjs/types/result/module.f.d.mts +3 -3
  258. package/fjs/types/result/module.f.mjs +3 -3
  259. package/fjs/types/rtti/common/module.f.d.mts +25 -28
  260. package/fjs/types/rtti/common/module.f.mjs +36 -32
  261. package/fjs/types/rtti/common/proof.f.mjs +5 -5
  262. package/fjs/types/rtti/data/module.f.d.mts +14 -0
  263. package/fjs/types/rtti/data/module.f.mjs +25 -2
  264. package/fjs/types/rtti/data/proof.f.d.mts +1 -0
  265. package/fjs/types/rtti/data/proof.f.mjs +25 -2
  266. package/fjs/types/rtti/parse/module.f.d.mts +24 -14
  267. package/fjs/types/rtti/parse/module.f.mjs +37 -28
  268. package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
  269. package/fjs/types/rtti/parse/proof.f.mjs +33 -14
  270. package/fjs/types/rtti/proof.f.mjs +3 -1
  271. package/fjs/types/rtti/ts/module.f.mjs +13 -17
  272. package/fjs/types/rtti/ts/types.d.ts +14 -1
  273. package/fjs/types/sorted_set/module.f.d.mts +18 -0
  274. package/fjs/types/sorted_set/module.f.mjs +22 -0
  275. package/fjs/types/sorted_set/proof.f.d.mts +1 -0
  276. package/fjs/types/sorted_set/proof.f.mjs +16 -1
  277. package/fjs/types/uint8array/module.f.d.mts +1 -1
  278. package/fjs/types/uint8array/module.f.mjs +1 -1
  279. package/fjs/website/module.f.d.mts +3 -3
  280. package/fjs/website/module.f.mjs +4 -7
  281. package/fjs/website/proof.f.mjs +2 -1
  282. package/package.json +2 -2
  283. package/fjs/dev/package_json/module.f.d.mts +0 -39
  284. package/fjs/dev/package_json/module.f.mjs +0 -40
  285. package/fjs/dev/package_json/proof.f.d.mts +0 -6
  286. package/fjs/dev/package_json/proof.f.mjs +0 -32
  287. package/fjs/effects/eff/module.f.d.mts +0 -20
  288. package/fjs/effects/eff/module.f.mjs +0 -70
  289. package/fjs/effects/eff/proof.f.d.mts +0 -15
  290. package/fjs/effects/eff/proof.f.mjs +0 -69
  291. package/fjs/effects/eff/types.d.ts +0 -71
  292. package/fjs/types/rtti/validate/module.f.d.mts +0 -41
  293. package/fjs/types/rtti/validate/module.f.mjs +0 -171
  294. package/fjs/types/rtti/validate/proof.f.d.mts +0 -125
  295. package/fjs/types/rtti/validate/proof.f.mjs +0 -437
  296. package/fjs/types/rtti/validate/types.d.ts +0 -6
@@ -1,8 +1,13 @@
1
1
  /**
2
2
  * @import { JsonToken } from '../tokenizer/types.ts'
3
+ * @import { NumberPolicy } from './types.ts'
3
4
  */
4
5
  export declare const proof: {
5
6
  valid: (() => void)[];
6
7
  invalid: (() => void)[];
7
8
  siblingContainers: (() => void)[];
9
+ policy: {
10
+ exactLexeme: () => void;
11
+ rejected: () => void;
12
+ };
8
13
  };
@@ -1,18 +1,26 @@
1
1
  /**
2
2
  * @import { JsonToken } from '../tokenizer/types.ts'
3
+ * @import { NumberPolicy } from './types.ts'
3
4
  */
4
5
 
5
- import { parse } from './module.f.mjs'
6
+ import { parse as parseWithPolicy } from './module.f.mjs'
6
7
  import { tokenize } from '../tokenizer/module.f.mjs'
7
8
  import { toArray } from '../../../types/list/module.f.mjs'
8
9
  import { stringify as jsonStringify } from '../module.f.mjs'
9
10
  import { sort } from '../../../types/object/module.f.mjs'
10
11
  import { stringToList } from '../../../text/utf16/module.f.mjs'
12
+ import { error, ok } from '../../../types/result/module.f.mjs'
11
13
  import { assertEq } from '../../../asserts/module.f.mjs'
12
14
 
13
15
  /** @type {(s: string) => readonly JsonToken[]} */
14
16
  const tokenizeString = s => toArray(tokenize(stringToList(s)))
15
17
 
18
+ /** The structural machine is proved through the ordinary `number` policy. */
19
+ /** @type {NumberPolicy<number>} */
20
+ const numberPolicy = token => ok(parseFloat(token.value))
21
+
22
+ const parse = parseWithPolicy(numberPolicy)
23
+
16
24
  const stringify = jsonStringify(sort)
17
25
 
18
26
  export const proof = {
@@ -311,4 +319,26 @@ export const proof = {
311
319
  assertEq(Array.isArray(value) ? value.length : -1, 12000)
312
320
  },
313
321
  ],
322
+ // The numeric policy is the parser's only opinion about numbers: it is
323
+ // handed the token, so it reads the exact lexeme, and it may reject one.
324
+ policy: {
325
+ // the lexeme reaches the policy unrounded — `1.0`, `1e0` and `1` are
326
+ // one `number` but three tokens
327
+ exactLexeme: () => {
328
+ /** @type {NumberPolicy<string>} */
329
+ const lexemePolicy = token => ok(token.value)
330
+ const [tag, value] = parseWithPolicy(lexemePolicy)(tokenizeString('[1.0,1e0,1,-0]'))
331
+ assertEq(tag, 'ok')
332
+ assertEq(stringify(value), '["1.0","1e0","1","-0"]')
333
+ },
334
+ // a policy that cannot represent the number fails the parse as an
335
+ // ordinary `Result`, with its own message
336
+ rejected: () => {
337
+ /** @type {NumberPolicy<never>} */
338
+ const rejectPolicy = () => error('no numbers here')
339
+ const [tag, message] = parseWithPolicy(rejectPolicy)(tokenizeString('{"a":[1]}'))
340
+ assertEq(tag, 'error')
341
+ assertEq(message, 'no numbers here')
342
+ },
343
+ },
314
344
  }
@@ -1,39 +1,58 @@
1
1
  /**
2
- * Internal state types for the JSON parser.
2
+ * Types for the shared structural JSON parser: its numeric policy, the tree
3
+ * that policy produces, and the parser's internal state.
3
4
  *
4
5
  * @module
5
6
  */
6
- import type { Unknown } from '../types.ts';
7
+ import type { Tree } from '../types.ts';
7
8
  import type { OrderedMap } from '../../../types/ordered_map/types.ts';
8
9
  import type { List } from '../../../types/list/types.ts';
10
+ import type { Result } from '../../../types/result/types.ts';
9
11
  import type { JsonToken } from '../tokenizer/types.ts';
12
+ import type { NumberToken } from '../../../js/tokenizer/types.ts';
10
13
  /** JSON tokens that carry a directly-usable value. */
11
14
  export type _ValueToken = Extract<JsonToken, {
12
15
  readonly kind: 'null' | 'false' | 'true' | 'string' | 'number';
13
16
  }>;
14
- export type _JsonObject = {
17
+ /**
18
+ * A numeric policy: how one codec materializes a JSON number token into its
19
+ * own numeric domain `P`.
20
+ *
21
+ * The policy is handed the token itself, so it decides from the exact lexeme —
22
+ * before any narrowing the parser might otherwise have imposed. It may fail:
23
+ * a valid JSON number can be outside the domain the codec offers (the extended
24
+ * codec has no non-finite `number`), and that has to be an ordinary parse
25
+ * error rather than an escaping runtime exception.
26
+ */
27
+ export type NumberPolicy<P> = (token: NumberToken) => Result<P, string>;
28
+ /**
29
+ * The tree a numeric policy `P` parses into: JSON's containers over `P` plus
30
+ * the primitives every policy shares.
31
+ */
32
+ export type ParseUnknown<P> = Tree<P | null | boolean | string>;
33
+ export type _JsonObject<P> = {
15
34
  readonly kind: 'object';
16
- readonly values: OrderedMap<Unknown>;
35
+ readonly values: OrderedMap<ParseUnknown<P>>;
17
36
  readonly key: string;
18
37
  };
19
- export type _JsonArray = {
38
+ export type _JsonArray<P> = {
20
39
  readonly kind: 'array';
21
- readonly values: List<Unknown>;
40
+ readonly values: List<ParseUnknown<P>>;
22
41
  };
23
- type _JsonStackElement = _JsonObject | _JsonArray;
24
- export type _JsonStack = List<_JsonStackElement>;
25
- export type _StateParse = {
42
+ type _JsonStackElement<P> = _JsonObject<P> | _JsonArray<P>;
43
+ export type _JsonStack<P> = List<_JsonStackElement<P>>;
44
+ export type _StateParse<P> = {
26
45
  readonly status: '' | '[' | '[v' | '[,' | '{' | '{k' | '{:' | '{v' | '{,';
27
- readonly top: _JsonStackElement | null;
28
- readonly stack: _JsonStack;
46
+ readonly top: _JsonStackElement<P> | null;
47
+ readonly stack: _JsonStack<P>;
29
48
  };
30
- type _StateResult = {
49
+ type _StateResult<P> = {
31
50
  readonly status: 'result';
32
- readonly value: Unknown;
51
+ readonly value: ParseUnknown<P>;
33
52
  };
34
53
  type _StateError = {
35
54
  readonly status: 'error';
36
55
  readonly message: string;
37
56
  };
38
- export type _JsonState = _StateParse | _StateResult | _StateError;
57
+ export type _JsonState<P> = _StateParse<P> | _StateResult<P> | _StateError;
39
58
  export {};
@@ -8,7 +8,7 @@
8
8
  * `typeof`. That spelling is deliberate: `@type {const}` also type-checks here,
9
9
  * but leaves declaration emit no name for the recursive positions, so it
10
10
  * inlines the structure, gives up at depth, and degrades the emitted `.d.mts`
11
- * to `any`. See AGENTS.md §6.2.
11
+ * to `any`. See `fjs/AGENTS.md` §3.2.
12
12
  *
13
13
  * The TypeScript counterparts live in the sibling
14
14
  * [`../types.ts`](../types.ts), which pins them against these schemas with
@@ -8,7 +8,7 @@
8
8
  * `typeof`. That spelling is deliberate: `@type {const}` also type-checks here,
9
9
  * but leaves declaration emit no name for the recursive positions, so it
10
10
  * inlines the structure, gives up at depth, and degrades the emitted `.d.mts`
11
- * to `any`. See AGENTS.md §6.2.
11
+ * to `any`. See `fjs/AGENTS.md` §3.2.
12
12
  *
13
13
  * The TypeScript counterparts live in the sibling
14
14
  * [`../types.ts`](../types.ts), which pins them against these schemas with
@@ -8,20 +8,20 @@
8
8
  */
9
9
 
10
10
  import { assertEq } from '../../../asserts/module.f.mjs'
11
- import { validate } from '../../../types/rtti/validate/module.f.mjs'
11
+ import { parse } from '../../../types/rtti/parse/module.f.mjs'
12
12
  import { primitive, unknown, object, array } from './module.f.mjs'
13
13
 
14
- // Reduces a validation to its `ok`/`error` tag: these schemas are about what is
15
- // accepted, not about the payload, which validation returns unchanged. The
16
- // erased `ValidateE` keeps the shared helper from re-instantiating each
17
- // schema's deep recursive result type (TS2589).
14
+ // Reduces a parse to its `ok`/`error` tag: these schemas are about what is
15
+ // accepted, not about the payload `parse` rebuilds. The erased `ValidateE`
16
+ // keeps the shared helper from re-instantiating each schema's deep recursive
17
+ // result type (TS2589).
18
18
  /** @type {(v: ValidateE) => (value: Unknown) => string} */
19
19
  const tag = v => value => v(value)[0]
20
20
 
21
- const primitiveAccepts = tag(validate(primitive))
22
- const unknownAccepts = tag(validate(unknown))
23
- const objectAccepts = tag(validate(object))
24
- const arrayAccepts = tag(validate(array))
21
+ const primitiveAccepts = tag(parse(primitive))
22
+ const unknownAccepts = tag(parse(unknown))
23
+ const objectAccepts = tag(parse(object))
24
+ const arrayAccepts = tag(parse(array))
25
25
 
26
26
  export const proof = {
27
27
  primitive: {
@@ -26,7 +26,7 @@
26
26
  import { assert, assertNotNullish } from '../../../asserts/module.f.mjs'
27
27
  import { at, definedEntries } from '../../../types/object/module.f.mjs'
28
28
  import { array, number, option, or, record, string } from '../../../types/rtti/module.f.mjs'
29
- import { cmp, toData, unitBit, unknown as top } from '../../../types/rtti/data/module.f.mjs'
29
+ import { cmp, toData, unitBit, unknown as top, withoutUnits } from '../../../types/rtti/data/module.f.mjs'
30
30
  import { unknown as jsonUnknown } from '../rtti/module.f.mjs'
31
31
 
32
32
  /** @type {() => readonly ['const', typeof unknownConst]} */
@@ -199,18 +199,8 @@ const admitsUndefined = rules => n => {
199
199
  *
200
200
  * @type {(n: Node) => Node}
201
201
  */
202
- const stripUndefined = n => {
203
- if (typeof n === 'string') { return n }
204
- const unit = (n.unit ?? 0) & ~undefinedBit
205
- return {
206
- ...(unit === 0 ? {} : { unit }),
207
- ...(n.number === undefined ? {} : { number: n.number }),
208
- ...(n.string === undefined ? {} : { string: n.string }),
209
- ...(n.bigint === undefined ? {} : { bigint: n.bigint }),
210
- ...(n.array === undefined ? {} : { array: n.array }),
211
- ...(n.object === undefined ? {} : { object: n.object }),
212
- }
213
- }
202
+ const stripUndefined = n =>
203
+ typeof n === 'string' ? n : withoutUnits(undefinedBit)(n)
214
204
 
215
205
  /**
216
206
  * A set of objects: `properties` for the declared keys — a key admitting
@@ -1,5 +1,4 @@
1
1
  /**
2
- * @import { Unknown as JsonValue } from '../types.ts'
3
2
  * @import { Unknown } from './module.f.mjs'
4
3
  * @import { Data } from '../../../types/rtti/data/types.ts'
5
4
  */
@@ -1,5 +1,4 @@
1
1
  /**
2
- * @import { Unknown as JsonValue } from '../types.ts'
3
2
  * @import { Unknown } from './module.f.mjs'
4
3
  * @import { Data } from '../../../types/rtti/data/types.ts'
5
4
  */
@@ -11,7 +10,7 @@ import { unitBit } from '../../../types/rtti/data/module.f.mjs'
11
10
  import { assert, assertEq } from '../../../asserts/module.f.mjs'
12
11
 
13
12
  /** @type {(v: Unknown) => string} */
14
- const serialize = v => stringify(e => e)(/** @type {JsonValue} */ (/** @type {unknown} */ (v)))
13
+ const serialize = v => stringify(e => e)(v)
15
14
 
16
15
  /** @type {(rtti: Parameters<typeof toJsonSchema>[0], expected: Unknown) => () => void} */
17
16
  const eq = (rtti, expected) => () => {
@@ -1,5 +1,10 @@
1
1
  /**
2
- * JSON serializer for deterministic string output.
2
+ * JSON serializer for deterministic string output: the atoms every JSON leaf
3
+ * is spelled with, and the one recursive walk over JSON's containers.
4
+ *
5
+ * `treeSerialize` is that walk. It knows objects and arrays and nothing about
6
+ * leaves, so a codec supplies its own leaf spelling — standard JSON and
7
+ * extended JSON differ only there, not in a second object/array walker.
3
8
  *
4
9
  * `stringSerialize` is FunctionalScript, not the host's `JSON.stringify`: it
5
10
  * escapes over this repository's own UTF-16 decoder and reproduces the
@@ -10,8 +15,10 @@
10
15
  * @import { List } from '../../../types/list/types.ts'
11
16
  * @import { Reduce } from '../../../types/function/operator/types.ts'
12
17
  * @import { CodePoint } from '../../../text/utf16/types.ts'
18
+ * @import { Tree, TreeObject, TreeArray, TreeEntry, TreeEntries, TreeMapEntries } from '../types.ts'
13
19
  */
14
20
  import type { List } from '../../../types/list/types.ts';
21
+ import type { Tree, TreeMapEntries } from '../types.ts';
15
22
  /**
16
23
  * Serializes a string as a JSON string literal.
17
24
  *
@@ -38,3 +45,27 @@ export declare const objectWrap: (input: List<List<string>>) => List<string>;
38
45
  * @type {(input: List<List<string>>) => List<string>}
39
46
  */
40
47
  export declare const arrayWrap: (input: List<List<string>>) => List<string>;
48
+ /**
49
+ * The separator between a serialized property key and its value. Shared with
50
+ * the DJS serializer, which builds the same `key : value` fragment.
51
+ *
52
+ * @type {List<string>}
53
+ */
54
+ export declare const colon: List<string>;
55
+ /**
56
+ * The recursive walk over a JSON-shaped tree: objects, arrays, and a leaf
57
+ * spelling supplied by the codec.
58
+ *
59
+ * Every leaf of `Tree<P>` reaches `leafSerialize` — containers are the only
60
+ * thing this function recognizes — so a codec that adds a leaf type (extended
61
+ * JSON's `bigint`) or respells one (extended JSON's `3.0` for a whole-valued
62
+ * `number`) changes `leafSerialize` alone.
63
+ *
64
+ * Object entries whose value is `undefined` are dropped: `undefined` is a
65
+ * missing property, not a leaf of any of these trees.
66
+ *
67
+ * @template P
68
+ * @param {(value: P) => List<string>} leafSerialize
69
+ * @returns {(sort: TreeMapEntries<P>) => (value: Tree<P>) => List<string>}
70
+ */
71
+ export declare const treeSerialize: <P>(leafSerialize: (value: P) => List<string>) => (sort: TreeMapEntries<P>) => (value: Tree<P>) => List<string>;
@@ -1,5 +1,10 @@
1
1
  /**
2
- * JSON serializer for deterministic string output.
2
+ * JSON serializer for deterministic string output: the atoms every JSON leaf
3
+ * is spelled with, and the one recursive walk over JSON's containers.
4
+ *
5
+ * `treeSerialize` is that walk. It knows objects and arrays and nothing about
6
+ * leaves, so a codec supplies its own leaf spelling — standard JSON and
7
+ * extended JSON differ only there, not in a second object/array walker.
3
8
  *
4
9
  * `stringSerialize` is FunctionalScript, not the host's `JSON.stringify`: it
5
10
  * escapes over this repository's own UTF-16 decoder and reproduces the
@@ -10,12 +15,15 @@
10
15
  * @import { List } from '../../../types/list/types.ts'
11
16
  * @import { Reduce } from '../../../types/function/operator/types.ts'
12
17
  * @import { CodePoint } from '../../../text/utf16/types.ts'
18
+ * @import { Tree, TreeObject, TreeArray, TreeEntry, TreeEntries, TreeMapEntries } from '../types.ts'
13
19
  */
14
20
 
15
21
  import { flat, map, reduce, empty } from '../../../types/list/module.f.mjs'
16
22
  import { concat } from '../../../types/string/module.f.mjs'
17
23
  import { codePointToString, stringToCodePointList } from '../../../text/utf16/module.f.mjs'
18
24
  import { errorMask } from '../../../text/code_point/module.f.mjs'
25
+ import { definedEntries, isObject } from '../../../types/object/module.f.mjs'
26
+ import { compose, fn } from '../../../types/function/module.f.mjs'
19
27
  import {
20
28
  backspace,
21
29
  cr,
@@ -68,7 +76,7 @@ const unicodeEscape = unit =>
68
76
  const escapeCodePoint = codePoint =>
69
77
  (codePoint & errorMask) !== 0
70
78
  ? unicodeEscape(codePoint & 0xffff)
71
- : escapeTable[/** @type {keyof typeof escapeTable} */ (codePoint)]
79
+ : escapeTable[codePoint]
72
80
  ?? (codePoint < space ? unicodeEscape(codePoint) : codePointToString(codePoint))
73
81
 
74
82
  /**
@@ -129,3 +137,57 @@ export const objectWrap
129
137
  */
130
138
  export const arrayWrap
131
139
  = wrap('[')(']')
140
+
141
+ /**
142
+ * The separator between a serialized property key and its value. Shared with
143
+ * the DJS serializer, which builds the same `key : value` fragment.
144
+ *
145
+ * @type {List<string>}
146
+ */
147
+ export const colon = [':']
148
+
149
+ /**
150
+ * The recursive walk over a JSON-shaped tree: objects, arrays, and a leaf
151
+ * spelling supplied by the codec.
152
+ *
153
+ * Every leaf of `Tree<P>` reaches `leafSerialize` — containers are the only
154
+ * thing this function recognizes — so a codec that adds a leaf type (extended
155
+ * JSON's `bigint`) or respells one (extended JSON's `3.0` for a whole-valued
156
+ * `number`) changes `leafSerialize` alone.
157
+ *
158
+ * Object entries whose value is `undefined` are dropped: `undefined` is a
159
+ * missing property, not a leaf of any of these trees.
160
+ *
161
+ * @template P
162
+ * @param {(value: P) => List<string>} leafSerialize
163
+ * @returns {(sort: TreeMapEntries<P>) => (value: Tree<P>) => List<string>}
164
+ */
165
+ export const treeSerialize = leafSerialize => sort => {
166
+ // `definedEntries` is generic; naming it at `P` here is what keeps the leaf
167
+ // type through the `fn(...)` composition below, which would otherwise
168
+ // instantiate it at `unknown`.
169
+ /** @type {(object: TreeObject<P>) => TreeEntries<P>} */
170
+ const objectEntries = definedEntries
171
+ /** @type {(kv: TreeEntry<P>) => List<string>} */
172
+ const propertySerialize = ([k, v]) => flat([
173
+ stringSerialize(k),
174
+ colon,
175
+ f(v)
176
+ ])
177
+ const mapPropertySerialize = map(propertySerialize)
178
+ /** @type {(object: TreeObject<P>) => List<string>} */
179
+ const objectSerialize = fn(objectEntries)
180
+ .map(sort)
181
+ .map(mapPropertySerialize)
182
+ .map(objectWrap)
183
+ .result
184
+ /** @type {(value: Tree<P>) => List<string>} */
185
+ const f = value => {
186
+ if (value instanceof Array) { return arraySerialize(value) }
187
+ if (isObject(value)) { return objectSerialize(value) }
188
+ return leafSerialize(value)
189
+ }
190
+ /** @type {(value: TreeArray<P>) => List<string>} */
191
+ const arraySerialize = compose(map(f))(arrayWrap)
192
+ return f
193
+ }
@@ -10,7 +10,6 @@
10
10
  */
11
11
 
12
12
  import { concat, empty, flat, stateScan, toArray } from '../../../types/list/module.f.mjs'
13
- import { multiply } from '../../../types/bigfloat/module.f.mjs'
14
13
  import { tokenize as jsTokenize } from '../../../js/tokenizer/module.f.mjs'
15
14
  import { assertEq } from '../../../asserts/module.f.mjs'
16
15
 
@@ -49,8 +48,13 @@ const parseDefaultState = input => {
49
48
  const parseMinusState = input => {
50
49
  if (input === null) return [[{ kind: 'error', message: 'invalid token' }], { kind: 'def' }]
51
50
  switch (input.token.kind) {
52
- case '-': return [[{ kind: 'error', message: 'invalid token' }], { kind: '-' }]
53
- case 'number': return [[{ kind: 'number', bf: multiply(input.token.bf)(-1n), value: `-${input.token.value}` }], { kind: 'def' }]
51
+ // negation is lexical: the minus sign joins the lexeme, so the token
52
+ // stays the exact source text of the JSON number.
53
+ case 'number': return [[{ kind: 'number', value: `-${input.token.value}` }], { kind: 'def' }]
54
+ // No `'-'` case: the underlying JS tokenizer always merges adjacent
55
+ // `-` characters into a single `'--'` token (see `js/tokenizer`), so
56
+ // this state never sees a second `'-'` — a run of `-` past the first
57
+ // always falls through to the `default` arm below instead.
54
58
  default: return [{ first: { kind: 'error', message: 'invalid token' }, tail: mapToken(input.token) }, { kind: 'def' }]
55
59
  }
56
60
  }
@@ -5,4 +5,10 @@ export declare const proof: {
5
5
  json: (() => void)[];
6
6
  id: (() => void)[];
7
7
  keywords: (() => void)[];
8
+ lossless: {
9
+ oversizedCoefficient: () => void;
10
+ unboundedExponent: () => void;
11
+ negativeUnboundedExponent: () => void;
12
+ negativeOversized: () => void;
13
+ };
8
14
  };
@@ -7,6 +7,7 @@ import { toArray } from '../../../types/list/module.f.mjs'
7
7
  import { stringifyAsTree } from '../../../djs/serializer/module.f.mjs'
8
8
  import { sort } from '../../../types/object/module.f.mjs'
9
9
  import { stringToList } from '../../../text/utf16/module.f.mjs'
10
+ import { assertStructurallySame } from '../../../asserts/module.f.mjs'
10
11
 
11
12
  /** @type {(s: string) => readonly JsonToken[]} */
12
13
  const tokenizeString = s => toArray(tokenize(stringToList(s)))
@@ -109,11 +110,11 @@ export const proof = {
109
110
  },
110
111
  () => {
111
112
  const result = stringify(tokenizeString('0'))
112
- if (result !== '[{"bf":[0n,0],"kind":"number","value":"0"},{"kind":"eof"}]') { throw result }
113
+ if (result !== '[{"kind":"number","value":"0"},{"kind":"eof"}]') { throw result }
113
114
  },
114
115
  () => {
115
116
  const result = stringify(tokenizeString('[0]'))
116
- if (result !== '[{"kind":"["},{"bf":[0n,0],"kind":"number","value":"0"},{"kind":"]"},{"kind":"eof"}]') { throw result }
117
+ if (result !== '[{"kind":"["},{"kind":"number","value":"0"},{"kind":"]"},{"kind":"eof"}]') { throw result }
117
118
  },
118
119
  () => {
119
120
  const result = stringify(tokenizeString('00'))
@@ -125,23 +126,23 @@ export const proof = {
125
126
  },
126
127
  () => {
127
128
  const result = stringify(tokenizeString('123456789012345678901234567890'))
128
- if (result !== '[{"bf":[123456789012345678901234567890n,0],"kind":"number","value":"123456789012345678901234567890"},{"kind":"eof"}]') { throw result }
129
+ if (result !== '[{"kind":"number","value":"123456789012345678901234567890"},{"kind":"eof"}]') { throw result }
129
130
  },
130
131
  () => {
131
132
  const result = stringify(tokenizeString('{90}'))
132
- if (result !== '[{"kind":"{"},{"bf":[90n,0],"kind":"number","value":"90"},{"kind":"}"},{"kind":"eof"}]') { throw result }
133
+ if (result !== '[{"kind":"{"},{"kind":"number","value":"90"},{"kind":"}"},{"kind":"eof"}]') { throw result }
133
134
  },
134
135
  () => {
135
136
  const result = stringify(tokenizeString('1 2'))
136
- if (result !== '[{"bf":[1n,0],"kind":"number","value":"1"},{"bf":[2n,0],"kind":"number","value":"2"},{"kind":"eof"}]') { throw result }
137
+ if (result !== '[{"kind":"number","value":"1"},{"kind":"number","value":"2"},{"kind":"eof"}]') { throw result }
137
138
  },
138
139
  () => {
139
140
  const result = stringify(tokenizeString('0. 2'))
140
- if (result !== '[{"kind":"error","message":"invalid number"},{"bf":[2n,0],"kind":"number","value":"2"},{"kind":"eof"}]') { throw result }
141
+ if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"number","value":"2"},{"kind":"eof"}]') { throw result }
141
142
  },
142
143
  () => {
143
144
  const result = stringify(tokenizeString('10-0'))
144
- if (result !== '[{"bf":[10n,0],"kind":"number","value":"10"},{"bf":[0n,0],"kind":"number","value":"-0"},{"kind":"eof"}]') { throw result }
145
+ if (result !== '[{"kind":"number","value":"10"},{"kind":"number","value":"-0"},{"kind":"eof"}]') { throw result }
145
146
  },
146
147
  () => {
147
148
  const result = stringify(tokenizeString('9a:'))
@@ -149,7 +150,7 @@ export const proof = {
149
150
  },
150
151
  () => {
151
152
  const result = stringify(tokenizeString('-10'))
152
- if (result !== '[{"bf":[-10n,0],"kind":"number","value":"-10"},{"kind":"eof"}]') { throw result }
153
+ if (result !== '[{"kind":"number","value":"-10"},{"kind":"eof"}]') { throw result }
153
154
  },
154
155
  () => {
155
156
  const result = stringify(tokenizeString('-'))
@@ -165,7 +166,7 @@ export const proof = {
165
166
  },
166
167
  () => {
167
168
  const result = stringify(tokenizeString('-0'))
168
- if (result !== '[{"bf":[0n,0],"kind":"number","value":"-0"},{"kind":"eof"}]') { throw result }
169
+ if (result !== '[{"kind":"number","value":"-0"},{"kind":"eof"}]') { throw result }
169
170
  },
170
171
  () => {
171
172
  const result = stringify(tokenizeString('-00'))
@@ -173,15 +174,15 @@ export const proof = {
173
174
  },
174
175
  () => {
175
176
  const result = stringify(tokenizeString('-.123'))
176
- if (result !== '[{"kind":"error","message":"invalid token"},{"kind":"error","message":"invalid token"},{"bf":[123n,0],"kind":"number","value":"123"},{"kind":"eof"}]') { throw result }
177
+ if (result !== '[{"kind":"error","message":"invalid token"},{"kind":"error","message":"invalid token"},{"kind":"number","value":"123"},{"kind":"eof"}]') { throw result }
177
178
  },
178
179
  () => {
179
180
  const result = stringify(tokenizeString('0.01'))
180
- if (result !== '[{"bf":[1n,-2],"kind":"number","value":"0.01"},{"kind":"eof"}]') { throw result }
181
+ if (result !== '[{"kind":"number","value":"0.01"},{"kind":"eof"}]') { throw result }
181
182
  },
182
183
  () => {
183
184
  const result = stringify(tokenizeString('-0.9'))
184
- if (result !== '[{"bf":[-9n,-1],"kind":"number","value":"-0.9"},{"kind":"eof"}]') { throw result }
185
+ if (result !== '[{"kind":"number","value":"-0.9"},{"kind":"eof"}]') { throw result }
185
186
  },
186
187
  () => {
187
188
  const result = stringify(tokenizeString('-0.'))
@@ -193,11 +194,11 @@ export const proof = {
193
194
  },
194
195
  () => {
195
196
  const result = stringify(tokenizeString('12.34'))
196
- if (result !== '[{"bf":[1234n,-2],"kind":"number","value":"12.34"},{"kind":"eof"}]') { throw result }
197
+ if (result !== '[{"kind":"number","value":"12.34"},{"kind":"eof"}]') { throw result }
197
198
  },
198
199
  () => {
199
200
  const result = stringify(tokenizeString('-12.00'))
200
- if (result !== '[{"bf":[-1200n,-2],"kind":"number","value":"-12.00"},{"kind":"eof"}]') { throw result }
201
+ if (result !== '[{"kind":"number","value":"-12.00"},{"kind":"eof"}]') { throw result }
201
202
  },
202
203
  () => {
203
204
  const result = stringify(tokenizeString('-12.'))
@@ -209,27 +210,27 @@ export const proof = {
209
210
  },
210
211
  () => {
211
212
  const result = stringify(tokenizeString('0e1'))
212
- if (result !== '[{"bf":[0n,1],"kind":"number","value":"0e1"},{"kind":"eof"}]') { throw result }
213
+ if (result !== '[{"kind":"number","value":"0e1"},{"kind":"eof"}]') { throw result }
213
214
  },
214
215
  () => {
215
216
  const result = stringify(tokenizeString('0e+2'))
216
- if (result !== '[{"bf":[0n,2],"kind":"number","value":"0e+2"},{"kind":"eof"}]') { throw result }
217
+ if (result !== '[{"kind":"number","value":"0e+2"},{"kind":"eof"}]') { throw result }
217
218
  },
218
219
  () => {
219
220
  const result = stringify(tokenizeString('0e-0'))
220
- if (result !== '[{"bf":[0n,0],"kind":"number","value":"0e-0"},{"kind":"eof"}]') { throw result }
221
+ if (result !== '[{"kind":"number","value":"0e-0"},{"kind":"eof"}]') { throw result }
221
222
  },
222
223
  () => {
223
224
  const result = stringify(tokenizeString('12e0000'))
224
- if (result !== '[{"bf":[12n,0],"kind":"number","value":"12e0000"},{"kind":"eof"}]') { throw result }
225
+ if (result !== '[{"kind":"number","value":"12e0000"},{"kind":"eof"}]') { throw result }
225
226
  },
226
227
  () => {
227
228
  const result = stringify(tokenizeString('-12e-0001'))
228
- if (result !== '[{"bf":[-12n,-1],"kind":"number","value":"-12e-0001"},{"kind":"eof"}]') { throw result }
229
+ if (result !== '[{"kind":"number","value":"-12e-0001"},{"kind":"eof"}]') { throw result }
229
230
  },
230
231
  () => {
231
232
  const result = stringify(tokenizeString('-12.34e1234'))
232
- if (result !== '[{"bf":[-1234n,1232],"kind":"number","value":"-12.34e1234"},{"kind":"eof"}]') { throw result }
233
+ if (result !== '[{"kind":"number","value":"-12.34e1234"},{"kind":"eof"}]') { throw result }
233
234
  },
234
235
  () => {
235
236
  const result = stringify(tokenizeString('0e'))
@@ -287,5 +288,45 @@ export const proof = {
287
288
  const result = stringify(tokenizeString('[null]'))
288
289
  if (result !== '[{"kind":"["},{"kind":"null"},{"kind":"]"},{"kind":"eof"}]') { throw result }
289
290
  },
290
- ]
291
+ ],
292
+ // Losslessness starts at the tokenizer boundary: a syntactically valid
293
+ // JSON number reaches `NumberToken.value` as its exact lexeme. Nothing
294
+ // derived from it — a coefficient bigint, an exponent number — is built
295
+ // while scanning, so no valid input can fail to tokenize because such a
296
+ // derived value would exceed a runtime numeric limit.
297
+ //
298
+ // The runtime's own bigint limit (V8 rejects magnitudes above 2^30 bits,
299
+ // roughly 3.2e8 decimal digits) is not exercised here: the smallest input
300
+ // that reaches it is a JSON document of some hundreds of megabytes. These
301
+ // cases stay far below that while still being far above what `number` and
302
+ // `Number.MAX_SAFE_INTEGER` can hold.
303
+ lossless: {
304
+ // a coefficient with more digits than any JavaScript number can carry
305
+ oversizedCoefficient: () => {
306
+ const value = `1${'0'.repeat(100000)}1`
307
+ assertStructurallySame(
308
+ tokenizeString(value),
309
+ [{ kind: 'number', value }, { kind: 'eof' }])
310
+ },
311
+ // exponent text far beyond JavaScript-number precision: `Number` of it
312
+ // is `Infinity`, yet every digit still has to reach the token
313
+ unboundedExponent: () => {
314
+ const value = '1e999999999999999999999'
315
+ assertStructurallySame(
316
+ tokenizeString(value),
317
+ [{ kind: 'number', value }, { kind: 'eof' }])
318
+ },
319
+ negativeUnboundedExponent: () => {
320
+ const value = '-1.5e-999999999999999999999'
321
+ assertStructurallySame(
322
+ tokenizeString(value),
323
+ [{ kind: 'number', value }, { kind: 'eof' }])
324
+ },
325
+ negativeOversized: () => {
326
+ const value = `-1${'0'.repeat(100000)}1`
327
+ assertStructurallySame(
328
+ tokenizeString(value),
329
+ [{ kind: 'number', value }, { kind: 'eof' }])
330
+ },
331
+ },
291
332
  }