functionalscript 0.18.0 → 0.20.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 (242) hide show
  1. package/fs/asn.1/module.f.js +7 -8
  2. package/fs/asn.1/{test.f.d.ts → proof.f.d.ts} +1 -2
  3. package/fs/asn.1/{test.f.js → proof.f.js} +12 -13
  4. package/fs/base128/proof.f.d.ts +1 -0
  5. package/fs/base128/{test.f.js → proof.f.js} +1 -1
  6. package/fs/bnf/data/{test.f.d.ts → proof.f.d.ts} +1 -2
  7. package/fs/bnf/data/{test.f.js → proof.f.js} +1 -1
  8. package/fs/bnf/proof.f.d.ts +3 -0
  9. package/fs/bnf/{test.f.js → proof.f.js} +1 -1
  10. package/fs/cas/module.f.js +2 -12
  11. package/fs/cas/proof.f.d.ts +1 -0
  12. package/fs/cas/proof.f.js +1 -0
  13. package/fs/cbase32/{test.f.d.ts → proof.f.d.ts} +1 -2
  14. package/fs/cbase32/{test.f.js → proof.f.js} +1 -1
  15. package/fs/ci/config/module.f.d.ts +4 -4
  16. package/fs/ci/config/module.f.js +4 -4
  17. package/fs/ci/node/module.f.js +12 -7
  18. package/fs/ci/{test.f.d.ts → proof.f.d.ts} +1 -2
  19. package/fs/ci/{test.f.js → proof.f.js} +1 -1
  20. package/fs/crypto/hmac/{test.f.d.ts → proof.f.d.ts} +1 -2
  21. package/fs/crypto/hmac/{test.f.js → proof.f.js} +1 -1
  22. package/fs/crypto/secp/{test.f.d.ts → proof.f.d.ts} +1 -2
  23. package/fs/crypto/secp/{test.f.js → proof.f.js} +1 -1
  24. package/fs/crypto/sha2/{test.f.d.ts → proof.f.d.ts} +1 -2
  25. package/fs/crypto/sha2/{test.f.js → proof.f.js} +1 -1
  26. package/fs/crypto/sign/module.f.js +3 -3
  27. package/fs/crypto/sign/{test.f.d.ts → proof.f.d.ts} +1 -2
  28. package/fs/crypto/sign/{test.f.js → proof.f.js} +1 -1
  29. package/fs/dev/module.f.d.ts +28 -2
  30. package/fs/dev/module.f.js +38 -22
  31. package/fs/dev/{test.f.d.ts → proof.f.d.ts} +5 -2
  32. package/fs/dev/{test.f.js → proof.f.js} +25 -2
  33. package/fs/dev/tf/module.d.ts +1 -2
  34. package/fs/dev/tf/module.f.d.ts +70 -7
  35. package/fs/dev/tf/module.f.js +115 -27
  36. package/fs/dev/tf/module.js +4 -103
  37. package/fs/dev/tf/{test.f.d.ts → proof.f.d.ts} +26 -0
  38. package/fs/dev/tf/{test.f.js → proof.f.js} +85 -38
  39. package/fs/dev/tf/scenarios/all.d.ts +1 -0
  40. package/fs/dev/tf/scenarios/all.js +3 -0
  41. package/fs/dev/tf/scenarios/async-subtests.fail.d.ts +4 -0
  42. package/fs/dev/tf/scenarios/async-subtests.fail.js +7 -0
  43. package/fs/dev/tf/scenarios/async-subtests.pass.d.ts +4 -0
  44. package/fs/dev/tf/scenarios/async-subtests.pass.js +7 -0
  45. package/fs/dev/tf/scenarios/async.fail.d.ts +1 -0
  46. package/fs/dev/tf/scenarios/async.fail.js +4 -0
  47. package/fs/dev/tf/scenarios/async.pass.d.ts +1 -0
  48. package/fs/dev/tf/scenarios/async.pass.js +3 -0
  49. package/fs/dev/tf/scenarios/fail.fail.f.d.ts +1 -0
  50. package/fs/dev/tf/scenarios/fail.fail.f.js +1 -0
  51. package/fs/dev/tf/scenarios/return-value.pass.f.d.ts +1 -0
  52. package/fs/dev/tf/scenarios/return-value.pass.f.js +2 -0
  53. package/fs/dev/tf/scenarios/thenable.pass.d.ts +3 -0
  54. package/fs/dev/tf/scenarios/thenable.pass.js +9 -0
  55. package/fs/dev/tf/scenarios/thenable2.pass.f.d.ts +3 -0
  56. package/fs/dev/tf/scenarios/thenable2.pass.f.js +3 -0
  57. package/fs/{bnf/test.f.d.ts → dev/tf/scenarios/throw.pass.f.d.ts} +3 -1
  58. package/fs/dev/tf/scenarios/throw.pass.f.js +3 -0
  59. package/fs/dev/version/proof.f.d.ts +3 -0
  60. package/fs/dev/version/{test.f.js → proof.f.js} +1 -1
  61. package/fs/djs/ast/{test.f.d.ts → proof.f.d.ts} +1 -2
  62. package/fs/djs/ast/{test.f.js → proof.f.js} +1 -1
  63. package/fs/djs/parser/{test.f.d.ts → proof.f.d.ts} +1 -2
  64. package/fs/djs/parser/{test.f.js → proof.f.js} +1 -1
  65. package/fs/djs/{test.f.d.ts → proof.f.d.ts} +1 -2
  66. package/fs/djs/{test.f.js → proof.f.js} +1 -1
  67. package/fs/djs/serializer/module.f.d.ts +2 -2
  68. package/fs/djs/serializer/module.f.js +47 -79
  69. package/fs/djs/serializer/{test.f.d.ts → proof.f.d.ts} +1 -2
  70. package/fs/djs/serializer/{test.f.js → proof.f.js} +8 -8
  71. package/fs/djs/tokenizer/{test.f.d.ts → proof.f.d.ts} +1 -2
  72. package/fs/djs/tokenizer/{test.f.js → proof.f.js} +1 -1
  73. package/fs/djs/tokenizer-new/{test.f.d.ts → proof.f.d.ts} +1 -2
  74. package/fs/djs/tokenizer-new/{test.f.js → proof.f.js} +1 -1
  75. package/fs/djs/transpiler/module.f.d.ts +15 -0
  76. package/fs/djs/transpiler/module.f.js +10 -2
  77. package/fs/djs/transpiler/{test.f.d.ts → proof.f.d.ts} +1 -2
  78. package/fs/djs/transpiler/{test.f.js → proof.f.js} +1 -1
  79. package/fs/fsc/{test.f.d.ts → proof.f.d.ts} +1 -2
  80. package/fs/fsc/{test.f.js → proof.f.js} +1 -1
  81. package/fs/fsm/proof.f.d.ts +4 -0
  82. package/fs/fsm/{test.f.js → proof.f.js} +1 -1
  83. package/fs/html/{test.f.d.ts → proof.f.d.ts} +1 -2
  84. package/fs/html/{test.f.js → proof.f.js} +1 -1
  85. package/fs/io/module.d.ts +1 -1
  86. package/fs/io/module.f.d.ts +8 -3
  87. package/fs/io/module.f.js +12 -12
  88. package/fs/io/module.js +43 -5
  89. package/fs/js/tokenizer/{test.f.d.ts → proof.f.d.ts} +1 -2
  90. package/fs/js/tokenizer/{test.f.js → proof.f.js} +1 -1
  91. package/fs/json/parser/{test.f.d.ts → proof.f.d.ts} +1 -2
  92. package/fs/json/parser/{test.f.js → proof.f.js} +1 -1
  93. package/fs/json/{test.f.d.ts → proof.f.d.ts} +1 -2
  94. package/fs/json/{test.f.js → proof.f.js} +1 -1
  95. package/fs/json/serializer/{test.f.d.ts → proof.f.d.ts} +1 -2
  96. package/fs/json/serializer/{test.f.js → proof.f.js} +1 -1
  97. package/fs/json/tokenizer/{test.f.d.ts → proof.f.d.ts} +1 -2
  98. package/fs/json/tokenizer/{test.f.js → proof.f.js} +1 -1
  99. package/fs/path/proof.f.d.ts +5 -0
  100. package/fs/path/{test.f.js → proof.f.js} +4 -3
  101. package/fs/sul/id/module.f.js +3 -4
  102. package/fs/sul/id/{test.f.d.ts → proof.f.d.ts} +1 -2
  103. package/fs/sul/id/{test.f.js → proof.f.js} +1 -1
  104. package/fs/sul/level/hash/{test.f.d.ts → proof.f.d.ts} +1 -2
  105. package/fs/sul/level/hash/{test.f.js → proof.f.js} +1 -1
  106. package/fs/sul/level/literal/module.f.js +3 -3
  107. package/fs/sul/level/literal/{test.f.d.ts → proof.f.d.ts} +1 -2
  108. package/fs/sul/level/literal/{test.f.js → proof.f.js} +1 -1
  109. package/fs/sul/{test.f.d.ts → proof.f.d.ts} +1 -2
  110. package/fs/sul/{test.f.js → proof.f.js} +1 -1
  111. package/fs/text/ascii/proof.f.d.ts +3 -0
  112. package/fs/text/ascii/{test.f.js → proof.f.js} +1 -1
  113. package/fs/text/code_point/module.f.d.ts +28 -0
  114. package/fs/text/code_point/module.f.js +31 -0
  115. package/fs/text/{test.f.d.ts → proof.f.d.ts} +1 -2
  116. package/fs/text/{test.f.js → proof.f.js} +1 -1
  117. package/fs/text/sgr/proof.f.d.ts +1 -0
  118. package/fs/text/sgr/proof.f.js +23 -0
  119. package/fs/text/utf16/module.f.js +3 -53
  120. package/fs/text/utf16/{test.f.d.ts → proof.f.d.ts} +1 -2
  121. package/fs/text/utf16/{test.f.js → proof.f.js} +1 -1
  122. package/fs/text/utf8/module.f.js +3 -25
  123. package/fs/text/utf8/{test.f.d.ts → proof.f.d.ts} +1 -2
  124. package/fs/text/utf8/{test.f.js → proof.f.js} +1 -1
  125. package/fs/types/array/{test.f.d.ts → proof.f.d.ts} +1 -2
  126. package/fs/types/array/{test.f.js → proof.f.js} +1 -1
  127. package/fs/types/bigfloat/{test.f.d.ts → proof.f.d.ts} +1 -2
  128. package/fs/types/bigfloat/{test.f.js → proof.f.js} +1 -1
  129. package/fs/types/bigint/{test.f.d.ts → proof.f.d.ts} +1 -2
  130. package/fs/types/bigint/{test.f.js → proof.f.js} +1 -1
  131. package/fs/types/bit_vec/module.f.d.ts +9 -4
  132. package/fs/types/bit_vec/module.f.js +7 -9
  133. package/fs/types/bit_vec/{test.f.d.ts → proof.f.d.ts} +1 -2
  134. package/fs/types/bit_vec/{test.f.js → proof.f.js} +1 -1
  135. package/fs/types/btree/find/proof.f.d.ts +1 -0
  136. package/fs/types/btree/find/{test.f.js → proof.f.js} +1 -1
  137. package/fs/types/btree/{test.f.d.ts → proof.f.d.ts} +1 -2
  138. package/fs/types/btree/{test.f.js → proof.f.js} +1 -1
  139. package/fs/types/btree/remove/module.f.d.ts +1 -1
  140. package/fs/types/btree/remove/module.f.js +7 -2
  141. package/fs/types/btree/remove/proof.f.d.ts +4 -0
  142. package/fs/types/btree/remove/{test.f.js → proof.f.js} +1 -1
  143. package/fs/types/btree/set/module.f.d.ts +1 -1
  144. package/fs/types/btree/set/module.f.js +7 -2
  145. package/fs/types/btree/set/proof.f.d.ts +1 -0
  146. package/fs/types/btree/set/{test.f.js → proof.f.js} +1 -1
  147. package/fs/types/btree/types/module.f.d.ts +9 -0
  148. package/fs/types/btree/types/module.f.js +9 -1
  149. package/fs/types/byte_set/{test.f.d.ts → proof.f.d.ts} +1 -2
  150. package/fs/types/byte_set/{test.f.js → proof.f.js} +1 -1
  151. package/fs/types/effects/module.f.d.ts +17 -0
  152. package/fs/types/effects/module.f.js +17 -0
  153. package/fs/types/effects/node/module.f.d.ts +66 -4
  154. package/fs/types/effects/node/module.f.js +5 -1
  155. package/fs/types/effects/node/{test.f.d.ts → proof.f.d.ts} +1 -2
  156. package/fs/types/effects/node/{test.f.js → proof.f.js} +1 -1
  157. package/fs/types/effects/node/virtual/module.f.d.ts +2 -1
  158. package/fs/types/effects/node/virtual/module.f.js +2 -0
  159. package/fs/types/effects/proof.f.d.ts +11 -0
  160. package/fs/types/effects/proof.f.js +57 -0
  161. package/fs/types/function/compare/proof.f.d.ts +1 -0
  162. package/fs/types/function/compare/{test.f.js → proof.f.js} +1 -1
  163. package/fs/types/function/operator/proof.f.d.ts +12 -0
  164. package/fs/types/function/operator/{test.f.js → proof.f.js} +11 -10
  165. package/fs/types/function/proof.f.d.ts +1 -0
  166. package/fs/types/function/{test.f.js → proof.f.js} +1 -1
  167. package/fs/types/list/{test.f.d.ts → proof.f.d.ts} +1 -2
  168. package/fs/types/list/{test.f.js → proof.f.js} +1 -1
  169. package/fs/types/map/proof.f.d.ts +4 -0
  170. package/fs/types/map/{test.f.js → proof.f.js} +1 -1
  171. package/fs/types/monoid/{test.f.d.ts → proof.f.d.ts} +1 -2
  172. package/fs/types/monoid/{test.f.js → proof.f.js} +1 -1
  173. package/fs/types/nibble_set/{test.f.d.ts → proof.f.d.ts} +1 -2
  174. package/fs/types/nibble_set/{test.f.js → proof.f.js} +1 -1
  175. package/fs/types/nominal/proof.f.d.ts +4 -0
  176. package/fs/types/nominal/{test.f.js → proof.f.js} +1 -1
  177. package/fs/types/nullable/proof.f.d.ts +1 -0
  178. package/fs/types/nullable/{test.f.js → proof.f.js} +11 -2
  179. package/fs/types/number/{test.f.d.ts → proof.f.d.ts} +1 -2
  180. package/fs/types/number/{test.f.js → proof.f.js} +1 -1
  181. package/fs/types/object/{test.f.d.ts → proof.f.d.ts} +1 -2
  182. package/fs/types/object/{test.f.js → proof.f.js} +1 -1
  183. package/fs/types/ordered_map/{test.f.d.ts → proof.f.d.ts} +1 -2
  184. package/fs/types/ordered_map/{test.f.js → proof.f.js} +1 -1
  185. package/fs/types/patricia_trie/{test.f.d.ts → proof.f.d.ts} +1 -2
  186. package/fs/types/patricia_trie/{test.f.js → proof.f.js} +1 -1
  187. package/fs/types/prime_field/{test.f.d.ts → proof.f.d.ts} +1 -2
  188. package/fs/types/prime_field/{test.f.js → proof.f.js} +1 -1
  189. package/fs/types/range/proof.f.d.ts +1 -0
  190. package/fs/types/range/{test.f.js → proof.f.js} +1 -1
  191. package/fs/types/range_map/{test.f.d.ts → proof.f.d.ts} +1 -2
  192. package/fs/types/range_map/{test.f.js → proof.f.js} +1 -1
  193. package/fs/types/result/proof.f.d.ts +5 -0
  194. package/fs/types/result/{test.f.js → proof.f.js} +18 -2
  195. package/fs/types/rtti/parse/{test.f.d.ts → proof.f.d.ts} +1 -2
  196. package/fs/types/rtti/parse/{test.f.js → proof.f.js} +1 -1
  197. package/fs/types/rtti/{test.f.d.ts → proof.f.d.ts} +1 -2
  198. package/fs/types/rtti/{test.f.js → proof.f.js} +1 -1
  199. package/fs/types/rtti/ts/{test.f.d.ts → proof.f.d.ts} +1 -2
  200. package/fs/types/rtti/ts/{test.f.js → proof.f.js} +1 -1
  201. package/fs/types/rtti/validate/{test.f.d.ts → proof.f.d.ts} +1 -2
  202. package/fs/types/rtti/validate/{test.f.js → proof.f.js} +1 -1
  203. package/fs/types/sorted_list/{test.f.d.ts → proof.f.d.ts} +1 -2
  204. package/fs/types/sorted_list/{test.f.js → proof.f.js} +1 -1
  205. package/fs/types/sorted_set/{test.f.d.ts → proof.f.d.ts} +1 -2
  206. package/fs/types/sorted_set/{test.f.js → proof.f.js} +1 -1
  207. package/fs/types/string/{test.f.d.ts → proof.f.d.ts} +1 -2
  208. package/fs/types/string/{test.f.js → proof.f.js} +1 -1
  209. package/fs/types/string_set/{test.f.d.ts → proof.f.d.ts} +1 -2
  210. package/fs/types/string_set/{test.f.js → proof.f.js} +1 -1
  211. package/fs/types/ts/{test.f.d.ts → proof.f.d.ts} +20 -0
  212. package/fs/types/ts/{test.f.js → proof.f.js} +1 -0
  213. package/fs/types/uint8array/{test.f.d.ts → proof.f.d.ts} +1 -2
  214. package/fs/types/uint8array/{test.f.js → proof.f.js} +1 -1
  215. package/issues/demo/sample/{test.f.js → proof.f.js} +1 -1
  216. package/issues/{test.f.d.ts → proof.f.d.ts} +1 -2
  217. package/issues/{test.f.js → proof.f.js} +1 -1
  218. package/nanvm-lib/tests/{test.f.d.ts → proof.f.d.ts} +1 -2
  219. package/nanvm-lib/tests/{test.f.js → proof.f.js} +1 -1
  220. package/nanvm-lib/tests/vm/{test.f.d.ts → proof.f.d.ts} +1 -2
  221. package/nanvm-lib/tests/vm/{test.f.js → proof.f.js} +1 -1
  222. package/package.json +5 -5
  223. package/fs/base128/test.f.d.ts +0 -2
  224. package/fs/cas/test.f.d.ts +0 -2
  225. package/fs/cas/test.f.js +0 -1
  226. package/fs/dev/version/test.f.d.ts +0 -4
  227. package/fs/fsm/test.f.d.ts +0 -5
  228. package/fs/path/test.f.d.ts +0 -3
  229. package/fs/text/ascii/test.f.d.ts +0 -4
  230. package/fs/text/sgr/test.f.d.ts +0 -2
  231. package/fs/text/sgr/test.f.js +0 -8
  232. package/fs/types/btree/find/test.f.d.ts +0 -2
  233. package/fs/types/btree/remove/test.f.d.ts +0 -5
  234. package/fs/types/btree/set/test.f.d.ts +0 -2
  235. package/fs/types/function/compare/test.f.d.ts +0 -2
  236. package/fs/types/function/operator/test.f.d.ts +0 -10
  237. package/fs/types/function/test.f.d.ts +0 -2
  238. package/fs/types/map/test.f.d.ts +0 -5
  239. package/fs/types/nominal/test.f.d.ts +0 -5
  240. package/fs/types/nullable/test.f.d.ts +0 -2
  241. package/fs/types/range/test.f.d.ts +0 -2
  242. package/fs/types/result/test.f.d.ts +0 -2
@@ -1,8 +1,9 @@
1
1
  import { pure } from "../../types/effects/module.f.js";
2
2
  import { emptyState } from "../../types/effects/node/virtual/module.f.js";
3
3
  import { virtual } from "../../types/effects/node/virtual/module.f.js";
4
- import { assert, assertEq } from "../module.f.js";
5
- import { test, defaultReporter, fmtPath, fmtTerm, fmtImport, ghEscape, isInteger, isIdentifier, defaultTest, } from "./module.f.js";
4
+ import { assert, assertEq, todo } from "../module.f.js";
5
+ import { testAll, defaultReporter, fmtPath, fmtTerm, fmtImport, ghEscape, isInteger, isIdentifier, defaultTest, } from "./module.f.js";
6
+ import { shouldLoad } from "../module.f.js";
6
7
  const makeReporter = () => {
7
8
  const events = [];
8
9
  const reporter = {
@@ -12,10 +13,15 @@ const makeReporter = () => {
12
13
  };
13
14
  return [reporter, () => events];
14
15
  };
16
+ const noopTestContext = { test: todo };
15
17
  const options = (initCwd, github = false) => ({
16
18
  args: [],
17
19
  env: { INIT_CWD: initCwd, ...(github ? { GITHUB_ACTION: 'true' } : {}) },
18
20
  std: { stdout: { isTTY: false }, stderr: { isTTY: false } },
21
+ testContext: noopTestContext,
22
+ bunTestContext: noopTestContext,
23
+ playwrightTestContext: noopTestContext,
24
+ engine: 'node',
19
25
  });
20
26
  const ok0 = () => ({ result: ['ok', undefined], duration: 0 });
21
27
  const fail0 = () => ({ result: ['error', 'oops'], duration: 0 });
@@ -23,7 +29,7 @@ const ok1 = () => ({ result: ['ok', undefined], duration: 1 });
23
29
  const run = (dir, initCwd = '.') => {
24
30
  const [reporter, getEvents] = makeReporter();
25
31
  const state = { ...emptyState, root: dir };
26
- const [, exitCode] = virtual(state)(test(reporter)(options(initCwd)));
32
+ const [, exitCode] = virtual(state)(testAll(reporter)(options(initCwd)));
27
33
  return [getEvents(), exitCode];
28
34
  };
29
35
  // Runs the real `defaultReporter` and returns its captured stdout/stderr so the
@@ -31,13 +37,13 @@ const run = (dir, initCwd = '.') => {
31
37
  const runMain = (dir, github = false) => {
32
38
  const state = { ...emptyState, root: dir };
33
39
  const opts = options('.', github);
34
- const [finalState, exitCode] = virtual(state)(test(defaultReporter(opts))(opts));
40
+ const [finalState, exitCode] = virtual(state)(testAll(defaultReporter(opts))(opts));
35
41
  return [finalState.stdout, finalState.stderr, exitCode];
36
42
  };
37
43
  // flat object: two passing tests
38
44
  export const flat = () => {
39
45
  const [events, exit] = run({
40
- 'a.test.f.ts': () => ({ a: ok0, b: ok1 }),
46
+ 'a.proof.f.ts': () => ({ proof: { a: ok0, b: ok1 } }),
41
47
  });
42
48
  assertEq(exit, 0);
43
49
  const [e0, e1, e2] = events;
@@ -51,7 +57,7 @@ export const flat = () => {
51
57
  // nested object: leaf tests carry the full path including the sub-tree key
52
58
  export const nested = () => {
53
59
  const [events, exit] = run({
54
- 'n.test.f.ts': () => ({ math: { add: ok0, sub: ok0 } }),
60
+ 'n.proof.f.ts': () => ({ proof: { math: { add: ok0, sub: ok0 } } }),
55
61
  });
56
62
  assertEq(exit, 0);
57
63
  const [e0, e1, e2] = events;
@@ -65,7 +71,7 @@ export const nested = () => {
65
71
  // throw key: tests inside 'throw' pass on error result
66
72
  export const throwKey = () => {
67
73
  const [events, exit] = run({
68
- 't.test.f.ts': () => ({ throw: { a: fail0 } }),
74
+ 't.proof.f.ts': () => ({ proof: { throw: { a: fail0 } } }),
69
75
  });
70
76
  assertEq(exit, 0);
71
77
  const [e0, e1] = events;
@@ -78,7 +84,7 @@ export const throwKey = () => {
78
84
  // throw key fails when test does not throw (returns ok in throw context)
79
85
  export const throwKeyFail = () => {
80
86
  const [events, exit] = run({
81
- 't.test.f.ts': () => ({ throw: { a: ok0 } }),
87
+ 't.proof.f.ts': () => ({ proof: { throw: { a: ok0 } } }),
82
88
  });
83
89
  assertEq(exit, 1);
84
90
  const [e0, e1] = events;
@@ -90,7 +96,7 @@ export const throwKeyFail = () => {
90
96
  // mixed pass/fail updates summary counts
91
97
  export const mixedPassFail = () => {
92
98
  const [events, exit] = run({
93
- 'm.test.f.ts': () => ({ good: ok0, bad: fail0 }),
99
+ 'm.proof.f.ts': () => ({ proof: { good: ok0, bad: fail0 } }),
94
100
  });
95
101
  assertEq(exit, 1);
96
102
  const summary = events[events.length - 1];
@@ -103,11 +109,13 @@ export const mixedPassFail = () => {
103
109
  export const returnValueSubTree = () => {
104
110
  const inner = () => ({ result: ['ok', undefined], duration: 0 });
105
111
  const [events, exit] = run({
106
- 'r.test.f.ts': () => ({
107
- outer: () => ({
108
- result: ['ok', { inner }],
109
- duration: 0,
110
- }),
112
+ 'r.proof.f.ts': () => ({
113
+ proof: {
114
+ outer: () => ({
115
+ result: ['ok', { inner }],
116
+ duration: 0,
117
+ }),
118
+ }
111
119
  }),
112
120
  });
113
121
  // outer passes, then inner (from return value) also passes
@@ -121,7 +129,7 @@ export const returnValueSubTree = () => {
121
129
  // integer-indexed array keys appear as numeric path segments
122
130
  export const arrayKeys = () => {
123
131
  const [events, exit] = run({
124
- 'a.test.f.ts': () => ({ arr: [ok0, ok0] }),
132
+ 'a.proof.f.ts': () => ({ proof: { arr: [ok0, ok0] } }),
125
133
  });
126
134
  assertEq(exit, 0);
127
135
  const passEvents = events.filter(e => e[0] === 'result');
@@ -129,22 +137,24 @@ export const arrayKeys = () => {
129
137
  assertEq(passEvents[0][2][1], '0');
130
138
  assertEq(passEvents[1][2][1], '1');
131
139
  };
132
- // non-test files are skipped (only files ending in test.f.ts/js are loaded)
140
+ // non-proof files are skipped: plain `.ts` is not loaded; `.f.ts` without
141
+ // a `proof` export is loaded but produces no events
133
142
  export const nonTestFilesSkipped = () => {
134
143
  const [events, exit] = run({
135
- 'helper.ts': () => ({ a: ok0 }),
136
- 'b.test.f.ts': () => ({ x: ok0 }),
144
+ 'helper.ts': () => ({ a: ok0 }), // not loaded (plain .ts)
145
+ 'module.f.ts': () => ({ someExport: ok0 }), // loaded, no proof → skipped
146
+ 'b.proof.f.ts': () => ({ proof: { x: ok0 } }), // loaded, has proof → runs
137
147
  });
138
148
  assertEq(exit, 0);
139
149
  const results = events.filter(e => e[0] === 'result');
140
150
  assertEq(results.length, 1);
141
- assertEq(results[0][1], './b.test.f.ts');
151
+ assertEq(results[0][1], './b.proof.f.ts');
142
152
  };
143
153
  // multiple test files each produce result events
144
154
  export const multipleFiles = () => {
145
155
  const [events, exit] = run({
146
- 'a.test.f.ts': () => ({ x: ok0 }),
147
- 'b.test.f.ts': () => ({ y: ok0 }),
156
+ 'a.proof.f.ts': () => ({ proof: { x: ok0 } }),
157
+ 'b.proof.f.ts': () => ({ proof: { y: ok0 } }),
148
158
  });
149
159
  assertEq(exit, 0);
150
160
  const results = events.filter(e => e[0] === 'result');
@@ -159,51 +169,51 @@ export const throwByFunctionName = () => {
159
169
  // const named = ({ throw: () => fail0() }).throw
160
170
  const x = { throw: () => fail0() };
161
171
  const [events, exit] = run({
162
- 't.test.f.ts': () => ({ here: x.throw }),
172
+ 't.proof.f.ts': () => ({ proof: { here: x.throw } }),
163
173
  });
164
174
  assertEq(exit, 0);
165
175
  const passEvents = events.filter(e => e[0] === 'result');
166
176
  assertEq(passEvents.length, 1);
167
177
  assertEq(passEvents[0][2][0], 'here');
168
178
  };
169
- // every module export — `default` and named becomes a top-level path segment
179
+ // only the `proof` export is used; other module properties are ignored
170
180
  export const namedExports = () => {
171
181
  const [events, exit] = run({
172
- 'e.test.f.ts': () => ({ default: ok0, helper: ok0 }),
182
+ 'e.proof.f.ts': () => ({ proof: { a: ok0, b: ok0 }, other: ok0 }),
173
183
  });
174
184
  assertEq(exit, 0);
175
185
  const passEvents = events.filter(e => e[0] === 'result');
176
- assertEq(passEvents.length, 2);
177
- assertEq(passEvents[0][2][0], 'default');
178
- assertEq(passEvents[1][2][0], 'helper');
186
+ assertEq(passEvents.length, 2); // `other` is ignored
187
+ assertEq(passEvents[0][2][0], 'a');
188
+ assertEq(passEvents[1][2][0], 'b');
179
189
  };
180
190
  // the default (non-GitHub) reporter formats module/pass/summary lines on stdout
181
191
  export const defaultReporterOutput = () => {
182
192
  const [stdout, stderr, exit] = runMain({
183
- 'a.test.f.ts': () => ({ x: ok0 }),
193
+ 'a.proof.f.ts': () => ({ proof: { x: ok0 } }),
184
194
  });
185
195
  assertEq(exit, 0);
186
196
  assertEq(stderr, '');
187
- assertEq(stdout, 'import("./a.test.f.ts").x(): ok, 0.0000 ms\n'
197
+ assertEq(stdout, 'import("./a.proof.f.ts").proof.x(): ok, 0.0000 ms\n'
188
198
  + 'Number of tests: pass: 1, fail: 0, total: 1\n'
189
199
  + 'Time: 0.0000 ms\n');
190
200
  };
191
201
  // a failure on the non-GitHub reporter writes the error to stderr, not stdout
192
202
  export const defaultReporterFailOutput = () => {
193
203
  const [, stderr, exit] = runMain({
194
- 'a.test.f.ts': () => ({ bad: fail0 }),
204
+ 'a.proof.f.ts': () => ({ proof: { bad: fail0 } }),
195
205
  });
196
206
  assertEq(exit, 1);
197
- assertEq(stderr, 'import("./a.test.f.ts").bad(): error, 0.0000 ms\noops\n');
207
+ assertEq(stderr, 'import("./a.proof.f.ts").proof.bad(): error, 0.0000 ms\noops\n');
198
208
  };
199
209
  // the GitHub reporter emits an `::error` annotation with a percent-encoded
200
210
  // title (the JSON path) and message
201
211
  export const githubReporterOutput = () => {
202
212
  const [, stderr, exit] = runMain({
203
- 's.test.f.ts': () => ({ 'a:b,c%d': fail0 }),
213
+ 's.proof.f.ts': () => ({ proof: { 'a:b,c%d': fail0 } }),
204
214
  }, true);
205
215
  assertEq(exit, 1);
206
- assertEq(stderr, '::error file=./s.test.f.ts,line=1,title=import("./s.test.f.ts")["a%3Ab%2Cc%25d"]()::oops\n');
216
+ assertEq(stderr, '::error file=./s.proof.f.ts,line=1,title=import("./s.proof.f.ts").proof["a%3Ab%2Cc%25d"]()::oops\n');
207
217
  };
208
218
  // direct unit tests for the pure path-format helpers
209
219
  export const helpers = {
@@ -224,12 +234,32 @@ export const helpers = {
224
234
  assert(!isIdentifier('1a'));
225
235
  assert(!isIdentifier('a-b'));
226
236
  },
237
+ shouldLoad: () => {
238
+ // all .f.ts / .f.js — FS modules are safe to bulk-load
239
+ assert(shouldLoad('module.f.ts'));
240
+ assert(shouldLoad('module.f.js'));
241
+ assert(shouldLoad('a.proof.f.ts'));
242
+ assert(shouldLoad('dir/module.f.ts'));
243
+ // vanilla opt-in by filename
244
+ assert(shouldLoad('proof.ts'));
245
+ assert(shouldLoad('proof.js'));
246
+ assert(shouldLoad('proof.mts'));
247
+ assert(shouldLoad('proof.mjs'));
248
+ assert(shouldLoad('math.proof.ts'));
249
+ assert(shouldLoad('math.proof.js'));
250
+ assert(shouldLoad('math.proof.mts'));
251
+ assert(shouldLoad('dir/math.proof.ts'));
252
+ // non-FS, non-proof vanilla files are not loaded
253
+ assert(!shouldLoad('helper.ts'));
254
+ assert(!shouldLoad('module.ts'));
255
+ assert(!shouldLoad('proof.tsx'));
256
+ },
227
257
  fmtImport: () => {
228
- assertEq(fmtImport('./a.test.f.ts', []), 'import("./a.test.f.ts")()');
229
- assertEq(fmtImport('./a.test.f.ts', ['math', 'add']), 'import("./a.test.f.ts").math.add()');
230
- assertEq(fmtImport('./a.test.f.ts', ['users', '3']), 'import("./a.test.f.ts").users[3]()');
231
- assertEq(fmtImport('./a.test.f.ts', ['x', 'hello world']), 'import("./a.test.f.ts").x["hello world"]()');
232
- assertEq(fmtImport('./a.test.f.ts', ['outer', null, 'inner']), 'import("./a.test.f.ts").outer().inner()');
258
+ assertEq(fmtImport('./a.proof.f.ts', []), 'import("./a.proof.f.ts").proof()');
259
+ assertEq(fmtImport('./a.proof.f.ts', ['math', 'add']), 'import("./a.proof.f.ts").proof.math.add()');
260
+ assertEq(fmtImport('./a.proof.f.ts', ['users', '3']), 'import("./a.proof.f.ts").proof.users[3]()');
261
+ assertEq(fmtImport('./a.proof.f.ts', ['x', 'hello world']), 'import("./a.proof.f.ts").proof.x["hello world"]()');
262
+ assertEq(fmtImport('./a.proof.f.ts', ['outer', null, 'inner']), 'import("./a.proof.f.ts").proof.outer().inner()');
233
263
  },
234
264
  fmtPath: () => {
235
265
  assertEq(fmtPath([]), '');
@@ -252,3 +282,20 @@ export const helpers = {
252
282
  assertEq(ghEscape('a%b:c,d'), 'a%25b%3Ac%2Cd');
253
283
  },
254
284
  };
285
+ export const proof = {
286
+ flat,
287
+ nested,
288
+ throwKey,
289
+ throwKeyFail,
290
+ mixedPassFail,
291
+ returnValueSubTree,
292
+ arrayKeys,
293
+ nonTestFilesSkipped,
294
+ multipleFiles,
295
+ throwByFunctionName,
296
+ namedExports,
297
+ defaultReporterOutput,
298
+ defaultReporterFailOutput,
299
+ githubReporterOutput,
300
+ helpers
301
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { run } from "../module.js";
2
+ // we need `await` for Playwright.
3
+ await run();
@@ -0,0 +1,4 @@
1
+ export declare const withSubtests: () => Promise<{
2
+ sub1: () => void;
3
+ sub2: () => never;
4
+ }>;
@@ -0,0 +1,7 @@
1
+ export const withSubtests = async () => {
2
+ await new Promise(resolve => setTimeout(resolve, 10));
3
+ return {
4
+ sub1: () => { },
5
+ sub2: () => { throw 'sub-test failure'; },
6
+ };
7
+ };
@@ -0,0 +1,4 @@
1
+ export declare const withSubtests: () => Promise<{
2
+ sub1: () => void;
3
+ sub2: () => void;
4
+ }>;
@@ -0,0 +1,7 @@
1
+ export const withSubtests = async () => {
2
+ await new Promise(resolve => setTimeout(resolve, 10));
3
+ return {
4
+ sub1: () => { },
5
+ sub2: () => { },
6
+ };
7
+ };
@@ -0,0 +1 @@
1
+ export declare const sleep_fail: () => Promise<never>;
@@ -0,0 +1,4 @@
1
+ export const sleep_fail = async () => {
2
+ await new Promise(resolve => setTimeout(resolve, 10));
3
+ throw 'async failure';
4
+ };
@@ -0,0 +1 @@
1
+ export declare const sleep: () => Promise<void>;
@@ -0,0 +1,3 @@
1
+ export const sleep = async () => {
2
+ await new Promise(resolve => setTimeout(resolve, 10));
3
+ };
@@ -0,0 +1 @@
1
+ export declare const failing: () => never;
@@ -0,0 +1 @@
1
+ export const failing = () => { throw 'intentional failure'; };
@@ -0,0 +1 @@
1
+ export declare const outer: () => unknown;
@@ -0,0 +1,2 @@
1
+ const inner = () => { };
2
+ export const outer = () => ({ inner });
@@ -0,0 +1,3 @@
1
+ export declare const thenableResolves: () => {
2
+ then(resolve: (v: undefined) => void): void;
3
+ };
@@ -0,0 +1,9 @@
1
+ // A test that returns a thenable (Promise-like object, not a real Promise).
2
+ // Per FunctionalScript convention, thenables are treated as plain values —
3
+ // not awaited. Both sandbox (fjs) and registerModule (node/bun/deno/playwright)
4
+ // must exit 0: the thenable object is walked as a sub-tree whose only key
5
+ // `then` is a function with parameters, so no leaf tests are found and the
6
+ // test trivially passes.
7
+ export const thenableResolves = () => ({
8
+ then(resolve) { resolve(undefined); }
9
+ });
@@ -0,0 +1,3 @@
1
+ export declare const shouldPass: () => {
2
+ then: () => string;
3
+ };
@@ -0,0 +1,3 @@
1
+ export const shouldPass = () => ({
2
+ then: () => 'ok'
3
+ });
@@ -1,4 +1,6 @@
1
1
  declare const _default: {
2
- test: () => void;
2
+ throw: {
3
+ a: () => never;
4
+ };
3
5
  };
4
6
  export default _default;
@@ -0,0 +1,3 @@
1
+ export default {
2
+ throw: { a: () => { throw 'expected'; } }
3
+ };
@@ -0,0 +1,3 @@
1
+ export declare const proof: {
2
+ new: () => void;
3
+ };
@@ -77,7 +77,7 @@ const e = '{\n' +
77
77
  ' "typescript": "^4.7.4"\n' +
78
78
  ' }\n' +
79
79
  '}';
80
- export default {
80
+ export const proof = {
81
81
  new: () => {
82
82
  const w = (name) => {
83
83
  const fn = `${name}.json`;
@@ -1,8 +1,7 @@
1
- declare const _default: {
1
+ export declare const proof: {
2
2
  test: () => void;
3
3
  testCref: () => void;
4
4
  testAref: () => void;
5
5
  testArray: () => void;
6
6
  testObj: () => void;
7
7
  };
8
- export default _default;
@@ -1,7 +1,7 @@
1
1
  import { sort } from "../../types/object/module.f.js";
2
2
  import { run } from "./module.f.js";
3
3
  import { stringifyAsTree } from "../serializer/module.f.js";
4
- export default {
4
+ export const proof = {
5
5
  test: () => {
6
6
  const djs = run([1])([]);
7
7
  const result = stringifyAsTree(sort)(djs);
@@ -1,4 +1,4 @@
1
- declare const _default: {
1
+ export declare const proof: {
2
2
  valid: (() => void)[];
3
3
  invalid: (() => void)[];
4
4
  errorMetadata: (() => void)[];
@@ -11,4 +11,3 @@ declare const _default: {
11
11
  invalidWithArgs: (() => void)[];
12
12
  comments: (() => void)[];
13
13
  };
14
- export default _default;
@@ -7,7 +7,7 @@ import { stringifyAsTree } from "../serializer/module.f.js";
7
7
  import { stringify } from "../../json/module.f.js";
8
8
  const tokenizeString = s => toArray(tokenize(stringToList(s))(''));
9
9
  const stringifyDjsModule = stringifyAsTree(sort);
10
- export default {
10
+ export const proof = {
11
11
  valid: [
12
12
  () => {
13
13
  const tokenList = tokenizeString('export default null');
@@ -1,4 +1,4 @@
1
- declare const _default: {
1
+ export declare const proof: {
2
2
  tooFewArgs: {
3
3
  noArgs: () => void;
4
4
  oneArg: () => void;
@@ -8,4 +8,3 @@ declare const _default: {
8
8
  fileNotFound: () => void;
9
9
  parseError: () => void;
10
10
  };
11
- export default _default;
@@ -9,7 +9,7 @@ const readOutput = (root, path) => {
9
9
  }
10
10
  return utf8ToString(file);
11
11
  };
12
- export default {
12
+ export const proof = {
13
13
  tooFewArgs: {
14
14
  noArgs: () => {
15
15
  const [state, code] = virtual(emptyState)(compile([]));
@@ -7,11 +7,11 @@ import type { Unknown } from '../module.f.ts';
7
7
  import type { Entry as ObjectEntry } from '../../types/object/module.f.ts';
8
8
  import { type List } from '../../types/list/module.f.ts';
9
9
  export declare const undefinedSerialize: string[];
10
- type RefCounter = [number, number, boolean];
10
+ type RefCounter = readonly [number, number];
11
11
  type Entry = ObjectEntry<Unknown>;
12
12
  type Entries = List<Entry>;
13
13
  type MapEntries = (entries: Entries) => Entries;
14
- type Refs = Map<Unknown, RefCounter>;
14
+ type Refs = ReadonlyMap<Unknown, RefCounter>;
15
15
  export declare const serializeWithoutConst: (mapEntries: MapEntries) => (value: Unknown) => List<string>;
16
16
  export declare const stringify: (sort: MapEntries) => (djs: Unknown) => string;
17
17
  export declare const stringifyAsTree: (mapEntries: MapEntries) => (value: Unknown) => string;
@@ -7,88 +7,47 @@ import { serialize as bigintSerialize } from "../../types/bigint/module.f.js";
7
7
  import { objectWrap, arrayWrap, stringSerialize, numberSerialize, nullSerialize, boolSerialize } from "../../json/serializer/module.f.js";
8
8
  const colon = [':'];
9
9
  export const undefinedSerialize = ['undefined'];
10
- const getConstantsOp = djs => state => {
11
- switch (typeof djs) {
12
- case 'boolean': {
13
- return state;
14
- }
15
- case 'number':
16
- case 'string':
17
- case 'bigint': {
18
- return getConstantSelf(djs)(state);
19
- }
20
- default: {
21
- if (djs === null) {
22
- return state;
23
- }
24
- if (djs === undefined) {
25
- return state;
26
- }
27
- if (djs instanceof Array) {
28
- return getConstantSelf(djs)(fold(getConstantsOp)(state)(djs));
29
- }
30
- return getConstantSelf(djs)(fold(getConstantsOp)(state)(map(entryValue)(entries(djs))));
10
+ const getConstants = refs => {
11
+ const checkSelf = djs => state => {
12
+ const refCounter = refs.get(djs);
13
+ if (refCounter !== undefined && refCounter[1] > 1 && !state.added.has(djs)) {
14
+ return {
15
+ added: new Set([...state.added, djs]),
16
+ consts: { head: state.consts, tail: [djs] }
17
+ };
31
18
  }
32
- }
33
- };
34
- const getConstantSelf = djs => state => {
35
- const refs = state.refs;
36
- const refCounter = refs.get(djs);
37
- if (refCounter !== undefined && refCounter[1] > 1 && !refCounter[2]) {
38
- refCounter[2] = true;
39
- refs.set(djs, refCounter);
40
- return { refs, consts: { head: state.consts, tail: [djs] } };
41
- }
42
- return state;
43
- };
44
- const getConstants = djs => refs => {
45
- return getConstantsOp(djs)(refs);
46
- };
47
- const entryValue = kv => kv[1];
48
- export const serializeWithoutConst = sort => {
49
- const propertySerialize = ([k, v]) => flat([
50
- stringSerialize(k),
51
- colon,
52
- f(v)
53
- ]);
54
- const mapPropertySerialize = map(propertySerialize);
55
- const objectSerialize = fn(entries)
56
- .map(sort)
57
- .map(mapPropertySerialize)
58
- .map(objectWrap)
59
- .result;
60
- const f = value => {
61
- switch (typeof value) {
19
+ return state;
20
+ };
21
+ const op = djs => state => {
22
+ switch (typeof djs) {
62
23
  case 'boolean': {
63
- return boolSerialize(value);
64
- }
65
- case 'number': {
66
- return numberSerialize(value);
67
- }
68
- case 'string': {
69
- return stringSerialize(value);
24
+ return state;
70
25
  }
26
+ case 'number':
27
+ case 'string':
71
28
  case 'bigint': {
72
- return [bigintSerialize(value)];
29
+ return checkSelf(djs)(state);
73
30
  }
74
31
  default: {
75
- if (value === null) {
76
- return nullSerialize;
32
+ if (djs === null) {
33
+ return state;
77
34
  }
78
- if (value === undefined) {
79
- return undefinedSerialize;
35
+ if (djs === undefined) {
36
+ return state;
80
37
  }
81
- if (value instanceof Array) {
82
- return arraySerialize(value);
38
+ if (djs instanceof Array) {
39
+ return checkSelf(djs)(fold(op)(state)(djs));
83
40
  }
84
- return objectSerialize(value);
41
+ return checkSelf(djs)(fold(op)(state)(map(entryValue)(entries(djs))));
85
42
  }
86
43
  }
87
44
  };
88
- const arraySerialize = compose(map(f))(arrayWrap);
89
- return f;
45
+ const init = { added: new Set(), consts: [] };
46
+ return djs => op(djs)(init).consts;
90
47
  };
91
- const serializeWithConst = sort => refs => root => {
48
+ const entryValue = kv => kv[1];
49
+ const noRef = () => null;
50
+ const buildSerialize = refLookup => sort => {
92
51
  const propertySerialize = ([k, v]) => flat([
93
52
  stringSerialize(k),
94
53
  colon,
@@ -101,11 +60,9 @@ const serializeWithConst = sort => refs => root => {
101
60
  .map(objectWrap)
102
61
  .result;
103
62
  const f = value => {
104
- if (value !== root) {
105
- const refCounter = refs.get(value);
106
- if (refCounter !== undefined && refCounter[1] > 1) {
107
- return [`c${refCounter[0]}`];
108
- }
63
+ const ref = refLookup(value);
64
+ if (ref !== null) {
65
+ return ref;
109
66
  }
110
67
  switch (typeof value) {
111
68
  case 'boolean': {
@@ -137,6 +94,17 @@ const serializeWithConst = sort => refs => root => {
137
94
  const arraySerialize = compose(map(f))(arrayWrap);
138
95
  return f;
139
96
  };
97
+ export const serializeWithoutConst = buildSerialize(noRef);
98
+ const serializeWithConst = sort => refs => root => buildSerialize(value => {
99
+ if (value === root) {
100
+ return null;
101
+ }
102
+ const refCounter = refs.get(value);
103
+ if (refCounter !== undefined && refCounter[1] > 1) {
104
+ return [`c${refCounter[0]}`];
105
+ }
106
+ return null;
107
+ })(sort);
140
108
  const countRefsOp = djs => refs => {
141
109
  switch (typeof djs) {
142
110
  case 'boolean':
@@ -167,14 +135,14 @@ const countRefsOp = djs => refs => {
167
135
  };
168
136
  const addRef = djs => refs => {
169
137
  const refCounter = refs.get(djs);
170
- if (refCounter === undefined) {
171
- return refs.set(djs, [refs.size, 1, false]);
172
- }
173
- return refs.set(djs, [refCounter[0], refCounter[1] + 1, false]);
138
+ const newCounter = refCounter === undefined
139
+ ? [refs.size, 1]
140
+ : [refCounter[0], refCounter[1] + 1];
141
+ return new Map([...refs, [djs, newCounter]]);
174
142
  };
175
143
  export const stringify = sort => djs => {
176
144
  const refs = countRefs(djs);
177
- const consts = getConstants(djs)({ refs, consts: [] }).consts;
145
+ const consts = getConstants(refs)(djs);
178
146
  const constSerialize = entry => {
179
147
  const refCounter = refs.get(entry);
180
148
  if (refCounter === undefined) {