functionalscript 0.19.0 → 0.21.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 (256) hide show
  1. package/fs/asn.1/{test.f.d.ts → proof.f.d.ts} +1 -2
  2. package/fs/asn.1/{test.f.js → proof.f.js} +1 -1
  3. package/fs/asserts/module.f.d.ts +4 -0
  4. package/fs/asserts/module.f.js +6 -0
  5. package/fs/base128/proof.f.d.ts +1 -0
  6. package/fs/base128/{test.f.js → proof.f.js} +1 -1
  7. package/fs/bnf/data/{test.f.d.ts → proof.f.d.ts} +1 -2
  8. package/fs/bnf/data/{test.f.js → proof.f.js} +1 -1
  9. package/fs/bnf/proof.f.d.ts +3 -0
  10. package/fs/bnf/{test.f.js → proof.f.js} +1 -1
  11. package/fs/cas/module.f.js +9 -23
  12. package/fs/cas/proof.f.d.ts +4 -0
  13. package/fs/cas/proof.f.js +37 -0
  14. package/fs/cbase32/{test.f.d.ts → proof.f.d.ts} +1 -2
  15. package/fs/cbase32/{test.f.js → proof.f.js} +1 -1
  16. package/fs/ci/bun/module.f.js +2 -7
  17. package/fs/ci/common/module.f.d.ts +2 -1
  18. package/fs/ci/common/module.f.js +7 -9
  19. package/fs/ci/config/module.f.d.ts +12 -3
  20. package/fs/ci/config/module.f.js +24 -4
  21. package/fs/ci/deno/module.f.js +2 -5
  22. package/fs/ci/node/module.f.js +14 -9
  23. package/fs/ci/playwright/module.f.js +5 -8
  24. package/fs/ci/{test.f.d.ts → proof.f.d.ts} +1 -2
  25. package/fs/ci/{test.f.js → proof.f.js} +2 -2
  26. package/fs/ci/rust/module.f.js +3 -9
  27. package/fs/crypto/hmac/{test.f.d.ts → proof.f.d.ts} +1 -2
  28. package/fs/crypto/hmac/{test.f.js → proof.f.js} +1 -1
  29. package/fs/crypto/secp/{test.f.d.ts → proof.f.d.ts} +1 -2
  30. package/fs/crypto/secp/{test.f.js → proof.f.js} +1 -1
  31. package/fs/crypto/sha2/{test.f.d.ts → proof.f.d.ts} +1 -2
  32. package/fs/crypto/sha2/{test.f.js → proof.f.js} +1 -1
  33. package/fs/crypto/sign/{test.f.d.ts → proof.f.d.ts} +1 -2
  34. package/fs/crypto/sign/{test.f.js → proof.f.js} +1 -1
  35. package/fs/dev/module.f.d.ts +28 -5
  36. package/fs/dev/module.f.js +38 -28
  37. package/fs/dev/{test.f.d.ts → proof.f.d.ts} +5 -2
  38. package/fs/dev/{test.f.js → proof.f.js} +26 -2
  39. package/fs/dev/version/proof.f.d.ts +3 -0
  40. package/fs/dev/version/{test.f.js → proof.f.js} +1 -1
  41. package/fs/djs/ast/{test.f.d.ts → proof.f.d.ts} +1 -2
  42. package/fs/djs/ast/{test.f.js → proof.f.js} +1 -1
  43. package/fs/djs/parser/{test.f.d.ts → proof.f.d.ts} +1 -2
  44. package/fs/djs/parser/{test.f.js → proof.f.js} +1 -1
  45. package/fs/djs/{test.f.d.ts → proof.f.d.ts} +1 -2
  46. package/fs/djs/{test.f.js → proof.f.js} +1 -1
  47. package/fs/djs/serializer/module.f.d.ts +2 -2
  48. package/fs/djs/serializer/module.f.js +47 -79
  49. package/fs/djs/serializer/{test.f.d.ts → proof.f.d.ts} +1 -2
  50. package/fs/djs/serializer/{test.f.js → proof.f.js} +8 -8
  51. package/fs/djs/tokenizer/{test.f.d.ts → proof.f.d.ts} +1 -2
  52. package/fs/djs/tokenizer/{test.f.js → proof.f.js} +1 -1
  53. package/fs/djs/tokenizer-new/module.f.js +1 -1
  54. package/fs/djs/tokenizer-new/{test.f.d.ts → proof.f.d.ts} +1 -2
  55. package/fs/djs/tokenizer-new/{test.f.js → proof.f.js} +1 -1
  56. package/fs/djs/transpiler/module.f.d.ts +15 -0
  57. package/fs/djs/transpiler/module.f.js +10 -2
  58. package/fs/djs/transpiler/{test.f.d.ts → proof.f.d.ts} +1 -2
  59. package/fs/djs/transpiler/{test.f.js → proof.f.js} +1 -1
  60. package/fs/{dev/tf → emergent-testing}/module.f.d.ts +66 -8
  61. package/fs/{dev/tf → emergent-testing}/module.f.js +101 -34
  62. package/fs/{dev/tf → emergent-testing}/module.js +2 -2
  63. package/fs/{dev/tf/test.f.d.ts → emergent-testing/proof.f.d.ts} +28 -0
  64. package/fs/{dev/tf/test.f.js → emergent-testing/proof.f.js} +119 -40
  65. package/fs/emergent-testing/scenarios/async-subtests.fail.d.ts +6 -0
  66. package/fs/emergent-testing/scenarios/async-subtests.fail.js +9 -0
  67. package/fs/emergent-testing/scenarios/async-subtests.pass.d.ts +6 -0
  68. package/fs/emergent-testing/scenarios/async-subtests.pass.js +9 -0
  69. package/fs/emergent-testing/scenarios/async.fail.d.ts +3 -0
  70. package/fs/emergent-testing/scenarios/async.fail.js +6 -0
  71. package/fs/emergent-testing/scenarios/async.pass.d.ts +3 -0
  72. package/fs/emergent-testing/scenarios/async.pass.js +5 -0
  73. package/fs/emergent-testing/scenarios/fail.fail.d.ts +3 -0
  74. package/fs/emergent-testing/scenarios/fail.fail.js +3 -0
  75. package/fs/emergent-testing/scenarios/return-value.pass.d.ts +3 -0
  76. package/fs/emergent-testing/scenarios/return-value.pass.js +4 -0
  77. package/fs/emergent-testing/scenarios/thenable.pass.d.ts +5 -0
  78. package/fs/emergent-testing/scenarios/thenable.pass.js +11 -0
  79. package/fs/emergent-testing/scenarios/thenable2.pass.d.ts +5 -0
  80. package/fs/emergent-testing/scenarios/thenable2.pass.js +3 -0
  81. package/fs/emergent-testing/scenarios/throw.pass.d.ts +5 -0
  82. package/fs/{dev/tf/scenarios/throw.pass.f.js → emergent-testing/scenarios/throw.pass.js} +1 -1
  83. package/fs/fjs/module.f.js +4 -5
  84. package/fs/fsc/{test.f.d.ts → proof.f.d.ts} +1 -2
  85. package/fs/fsc/{test.f.js → proof.f.js} +1 -1
  86. package/fs/fsm/proof.f.d.ts +4 -0
  87. package/fs/fsm/{test.f.js → proof.f.js} +1 -1
  88. package/fs/html/{test.f.d.ts → proof.f.d.ts} +1 -2
  89. package/fs/html/{test.f.js → proof.f.js} +1 -1
  90. package/fs/io/module.d.ts +1 -1
  91. package/fs/io/module.f.d.ts +3 -2
  92. package/fs/io/module.f.js +4 -3
  93. package/fs/io/module.js +19 -11
  94. package/fs/js/tokenizer/{test.f.d.ts → proof.f.d.ts} +1 -2
  95. package/fs/js/tokenizer/{test.f.js → proof.f.js} +1 -1
  96. package/fs/json/parser/{test.f.d.ts → proof.f.d.ts} +1 -2
  97. package/fs/json/parser/{test.f.js → proof.f.js} +1 -1
  98. package/fs/json/{test.f.d.ts → proof.f.d.ts} +1 -2
  99. package/fs/json/{test.f.js → proof.f.js} +1 -1
  100. package/fs/json/serializer/{test.f.d.ts → proof.f.d.ts} +1 -2
  101. package/fs/json/serializer/{test.f.js → proof.f.js} +1 -1
  102. package/fs/json/tokenizer/{test.f.d.ts → proof.f.d.ts} +1 -2
  103. package/fs/json/tokenizer/{test.f.js → proof.f.js} +1 -1
  104. package/fs/path/proof.f.d.ts +5 -0
  105. package/fs/path/{test.f.js → proof.f.js} +4 -3
  106. package/fs/sul/id/module.f.js +1 -1
  107. package/fs/sul/id/{test.f.d.ts → proof.f.d.ts} +1 -2
  108. package/fs/sul/id/{test.f.js → proof.f.js} +2 -2
  109. package/fs/sul/level/hash/{test.f.d.ts → proof.f.d.ts} +1 -2
  110. package/fs/sul/level/hash/{test.f.js → proof.f.js} +2 -2
  111. package/fs/sul/level/literal/{test.f.d.ts → proof.f.d.ts} +1 -2
  112. package/fs/sul/level/literal/{test.f.js → proof.f.js} +1 -1
  113. package/fs/sul/{test.f.d.ts → proof.f.d.ts} +1 -2
  114. package/fs/sul/{test.f.js → proof.f.js} +2 -2
  115. package/fs/text/ascii/proof.f.d.ts +3 -0
  116. package/fs/text/ascii/{test.f.js → proof.f.js} +1 -1
  117. package/fs/text/code_point/module.f.d.ts +28 -0
  118. package/fs/text/code_point/module.f.js +31 -0
  119. package/fs/text/{test.f.d.ts → proof.f.d.ts} +1 -2
  120. package/fs/text/{test.f.js → proof.f.js} +1 -1
  121. package/fs/text/sgr/proof.f.d.ts +1 -0
  122. package/fs/text/sgr/{test.f.js → proof.f.js} +1 -1
  123. package/fs/text/utf16/module.f.js +3 -53
  124. package/fs/text/utf16/{test.f.d.ts → proof.f.d.ts} +1 -2
  125. package/fs/text/utf16/{test.f.js → proof.f.js} +1 -1
  126. package/fs/text/utf8/module.f.js +3 -25
  127. package/fs/text/utf8/{test.f.d.ts → proof.f.d.ts} +1 -2
  128. package/fs/text/utf8/{test.f.js → proof.f.js} +1 -1
  129. package/fs/types/array/module.f.js +2 -5
  130. package/fs/types/array/{test.f.d.ts → proof.f.d.ts} +1 -2
  131. package/fs/types/array/{test.f.js → proof.f.js} +1 -1
  132. package/fs/types/bigfloat/{test.f.d.ts → proof.f.d.ts} +1 -2
  133. package/fs/types/bigfloat/{test.f.js → proof.f.js} +1 -1
  134. package/fs/types/bigint/{test.f.d.ts → proof.f.d.ts} +1 -2
  135. package/fs/types/bigint/{test.f.js → proof.f.js} +1 -1
  136. package/fs/types/bit_vec/{test.f.d.ts → proof.f.d.ts} +1 -2
  137. package/fs/types/bit_vec/{test.f.js → proof.f.js} +1 -1
  138. package/fs/types/btree/find/proof.f.d.ts +1 -0
  139. package/fs/types/btree/find/{test.f.js → proof.f.js} +1 -1
  140. package/fs/types/btree/{test.f.d.ts → proof.f.d.ts} +1 -2
  141. package/fs/types/btree/{test.f.js → proof.f.js} +1 -1
  142. package/fs/types/btree/remove/proof.f.d.ts +4 -0
  143. package/fs/types/btree/remove/{test.f.js → proof.f.js} +1 -1
  144. package/fs/types/btree/set/proof.f.d.ts +1 -0
  145. package/fs/types/btree/set/{test.f.js → proof.f.js} +1 -1
  146. package/fs/types/btree/types/module.f.d.ts +8 -0
  147. package/fs/types/btree/types/module.f.js +8 -0
  148. package/fs/types/byte_set/{test.f.d.ts → proof.f.d.ts} +1 -2
  149. package/fs/types/byte_set/{test.f.js → proof.f.js} +1 -1
  150. package/fs/types/effects/module.f.d.ts +17 -0
  151. package/fs/types/effects/module.f.js +17 -0
  152. package/fs/types/effects/node/module.f.d.ts +60 -5
  153. package/fs/types/effects/node/module.f.js +12 -1
  154. package/fs/types/effects/node/{test.f.d.ts → proof.f.d.ts} +1 -2
  155. package/fs/types/effects/node/{test.f.js → proof.f.js} +1 -1
  156. package/fs/types/effects/node/virtual/module.f.js +2 -1
  157. package/fs/types/effects/proof.f.d.ts +11 -0
  158. package/fs/types/effects/proof.f.js +57 -0
  159. package/fs/types/function/compare/proof.f.d.ts +1 -0
  160. package/fs/types/function/compare/{test.f.js → proof.f.js} +1 -1
  161. package/fs/types/function/operator/proof.f.d.ts +12 -0
  162. package/fs/types/function/operator/{test.f.js → proof.f.js} +11 -10
  163. package/fs/types/function/proof.f.d.ts +1 -0
  164. package/fs/types/function/{test.f.js → proof.f.js} +1 -1
  165. package/fs/types/list/{test.f.d.ts → proof.f.d.ts} +2 -2
  166. package/fs/types/list/{test.f.js → proof.f.js} +15 -1
  167. package/fs/types/map/proof.f.d.ts +4 -0
  168. package/fs/types/map/{test.f.js → proof.f.js} +1 -1
  169. package/fs/types/monoid/{test.f.d.ts → proof.f.d.ts} +1 -2
  170. package/fs/types/monoid/{test.f.js → proof.f.js} +1 -1
  171. package/fs/types/nibble_set/{test.f.d.ts → proof.f.d.ts} +1 -2
  172. package/fs/types/nibble_set/{test.f.js → proof.f.js} +1 -1
  173. package/fs/types/nominal/proof.f.d.ts +4 -0
  174. package/fs/types/nominal/{test.f.js → proof.f.js} +1 -1
  175. package/fs/types/nullable/module.f.d.ts +7 -0
  176. package/fs/types/nullable/module.f.js +7 -0
  177. package/fs/types/nullable/proof.f.d.ts +1 -0
  178. package/fs/types/nullable/{test.f.js → proof.f.js} +20 -3
  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/module.f.js +2 -4
  182. package/fs/types/object/{test.f.d.ts → proof.f.d.ts} +1 -2
  183. package/fs/types/object/{test.f.js → proof.f.js} +1 -1
  184. package/fs/types/ordered_map/{test.f.d.ts → proof.f.d.ts} +1 -2
  185. package/fs/types/ordered_map/{test.f.js → proof.f.js} +1 -1
  186. package/fs/types/patricia_trie/{test.f.d.ts → proof.f.d.ts} +1 -2
  187. package/fs/types/patricia_trie/{test.f.js → proof.f.js} +2 -2
  188. package/fs/types/prime_field/{test.f.d.ts → proof.f.d.ts} +1 -2
  189. package/fs/types/prime_field/{test.f.js → proof.f.js} +1 -1
  190. package/fs/types/range/proof.f.d.ts +1 -0
  191. package/fs/types/range/{test.f.js → proof.f.js} +1 -1
  192. package/fs/types/range_map/{test.f.d.ts → proof.f.d.ts} +1 -2
  193. package/fs/types/range_map/{test.f.js → proof.f.js} +1 -1
  194. package/fs/types/result/proof.f.d.ts +5 -0
  195. package/fs/types/result/{test.f.js → proof.f.js} +18 -2
  196. package/fs/types/rtti/parse/{test.f.d.ts → proof.f.d.ts} +1 -2
  197. package/fs/types/rtti/parse/{test.f.js → proof.f.js} +1 -1
  198. package/fs/types/rtti/{test.f.d.ts → proof.f.d.ts} +1 -2
  199. package/fs/types/rtti/{test.f.js → proof.f.js} +1 -1
  200. package/fs/types/rtti/ts/{test.f.d.ts → proof.f.d.ts} +1 -2
  201. package/fs/types/rtti/ts/{test.f.js → proof.f.js} +1 -1
  202. package/fs/types/rtti/validate/{test.f.d.ts → proof.f.d.ts} +1 -2
  203. package/fs/types/rtti/validate/{test.f.js → proof.f.js} +1 -1
  204. package/fs/types/sorted_list/{test.f.d.ts → proof.f.d.ts} +1 -2
  205. package/fs/types/sorted_list/{test.f.js → proof.f.js} +1 -1
  206. package/fs/types/sorted_set/{test.f.d.ts → proof.f.d.ts} +1 -2
  207. package/fs/types/sorted_set/{test.f.js → proof.f.js} +1 -1
  208. package/fs/types/string/{test.f.d.ts → proof.f.d.ts} +1 -2
  209. package/fs/types/string/{test.f.js → proof.f.js} +1 -1
  210. package/fs/types/string_set/{test.f.d.ts → proof.f.d.ts} +1 -2
  211. package/fs/types/string_set/{test.f.js → proof.f.js} +1 -1
  212. package/fs/types/ts/module.f.d.ts +0 -1
  213. package/fs/types/ts/{test.f.d.ts → proof.f.d.ts} +20 -0
  214. package/fs/types/ts/{test.f.js → proof.f.js} +1 -0
  215. package/fs/types/uint8array/{test.f.d.ts → proof.f.d.ts} +1 -2
  216. package/fs/types/uint8array/{test.f.js → proof.f.js} +1 -1
  217. package/fs/website/proof.f.d.ts +3 -0
  218. package/fs/website/proof.f.js +9 -0
  219. package/issues/demo/sample/{test.f.js → proof.f.js} +1 -1
  220. package/issues/{test.f.d.ts → proof.f.d.ts} +1 -2
  221. package/issues/{test.f.js → proof.f.js} +1 -1
  222. package/nanvm-lib/tests/{test.f.d.ts → proof.f.d.ts} +1 -2
  223. package/nanvm-lib/tests/{test.f.js → proof.f.js} +1 -1
  224. package/nanvm-lib/tests/vm/{test.f.d.ts → proof.f.d.ts} +1 -2
  225. package/nanvm-lib/tests/vm/{test.f.js → proof.f.js} +1 -1
  226. package/package.json +2 -2
  227. package/fs/base128/test.f.d.ts +0 -2
  228. package/fs/bnf/test.f.d.ts +0 -4
  229. package/fs/cas/test.f.d.ts +0 -2
  230. package/fs/cas/test.f.js +0 -1
  231. package/fs/dev/tf/scenarios/fail.fail.f.d.ts +0 -1
  232. package/fs/dev/tf/scenarios/fail.fail.f.js +0 -1
  233. package/fs/dev/tf/scenarios/return-value.pass.f.d.ts +0 -1
  234. package/fs/dev/tf/scenarios/return-value.pass.f.js +0 -2
  235. package/fs/dev/tf/scenarios/throw.pass.f.d.ts +0 -6
  236. package/fs/dev/version/test.f.d.ts +0 -4
  237. package/fs/fsm/test.f.d.ts +0 -5
  238. package/fs/path/test.f.d.ts +0 -3
  239. package/fs/text/ascii/test.f.d.ts +0 -4
  240. package/fs/text/sgr/test.f.d.ts +0 -2
  241. package/fs/types/btree/find/test.f.d.ts +0 -2
  242. package/fs/types/btree/remove/test.f.d.ts +0 -5
  243. package/fs/types/btree/set/test.f.d.ts +0 -2
  244. package/fs/types/function/compare/test.f.d.ts +0 -2
  245. package/fs/types/function/operator/test.f.d.ts +0 -10
  246. package/fs/types/function/test.f.d.ts +0 -2
  247. package/fs/types/map/test.f.d.ts +0 -5
  248. package/fs/types/nominal/test.f.d.ts +0 -5
  249. package/fs/types/nullable/test.f.d.ts +0 -2
  250. package/fs/types/range/test.f.d.ts +0 -2
  251. package/fs/types/result/test.f.d.ts +0 -2
  252. /package/fs/{dev/tf → emergent-testing}/all.test.d.ts +0 -0
  253. /package/fs/{dev/tf → emergent-testing}/all.test.js +0 -0
  254. /package/fs/{dev/tf → emergent-testing}/module.d.ts +0 -0
  255. /package/fs/{dev/tf → emergent-testing}/scenarios/all.d.ts +0 -0
  256. /package/fs/{dev/tf → emergent-testing}/scenarios/all.js +0 -0
@@ -1,13 +1,33 @@
1
- import { type All, type NodeProgram, type NodeProgramOptions, type Program, type Sandbox, type SandboxResult, type Test, type TestContext, type Write } from '../../types/effects/node/module.f.ts';
2
- import { type Effect, type Operation } from '../../types/effects/module.f.ts';
3
- import { type LoadModuleOperations, type ModuleMap } from '../module.f.ts';
4
- export declare const isTest: (s: string) => boolean;
1
+ import { type All, type Await, type NodeProgram, type NodeProgramOptions, type Program, type Sandbox, type SandboxResult, type Test, type TestContext, type Write } from '../types/effects/node/module.f.ts';
2
+ import { type Effect, type Operation } from '../types/effects/module.f.ts';
3
+ import { type LoadModuleOperations, type ModuleMap } from '../dev/module.f.ts';
4
+ /** A zero-argument test function whose return value may contain sub-tests. */
5
5
  export type TestFn = () => unknown;
6
+ /**
7
+ * A leaf test bundled with its throw expectation.
8
+ *
9
+ * `throws: true` means the test is expected to throw; the runner inverts the
10
+ * `sandbox` result so a caught error becomes a pass and a clean return becomes
11
+ * a failure. Using a record instead of a wrapper function avoids a double
12
+ * `sandbox` call and gives accurate per-test timing.
13
+ */
6
14
  export type TestEntry = {
7
15
  readonly fn: TestFn;
8
16
  readonly throws: boolean;
9
17
  };
18
+ /**
19
+ * Either a leaf `TestEntry` (function + throw flag) or a named sub-tree of
20
+ * `[key, value]` pairs to recurse into. Discriminate with `Array.isArray`.
21
+ */
10
22
  export type TestSet = TestEntry | readonly (readonly [string, unknown])[];
23
+ /**
24
+ * Converts an arbitrary JS value into a `TestSet`.
25
+ *
26
+ * - Zero-argument functions become a `TestEntry`; the `throws` flag is set if
27
+ * `throws` is already `true` or the function's `.name === 'throw'`.
28
+ * - Non-null objects become an array of `[key, value]` pairs to recurse into.
29
+ * - All other values (including functions with parameters) produce an empty array.
30
+ */
11
31
  export declare const parseTestSet: (throws: boolean, x: unknown) => TestSet;
12
32
  type TestAndPath = readonly [Path, TestEntry];
13
33
  /**
@@ -26,16 +46,42 @@ export declare const collectTests: (path: Path, throws: boolean, v: unknown) =>
26
46
  * contained `inner`.
27
47
  */
28
48
  export type Reporter<O extends Operation> = {
29
- readonly result: (file: string, path: Path, r: SandboxResult<unknown>) => Effect<O, void>;
49
+ readonly result: (file: string, path: Path, r: SandboxResult<unknown>, throws: boolean) => Effect<O, void>;
30
50
  readonly summary: (pass: number, fail: number, time: number) => Effect<O, void>;
31
51
  readonly test: (file: string, path: Path, set: TestEntry) => Effect<O, SandboxResult<unknown>>;
32
52
  };
33
- export declare const registerModule: (ctx: TestContext, k: string, v: unknown) => Effect<Test | All, void>;
53
+ /**
54
+ * Registers all tests reachable from module export `v` (keyed by `k`) with
55
+ * the given `TestContext`.
56
+ *
57
+ * Unlike `runModule`, which sandboxes only the leaf function, `registerModule`
58
+ * lets the external framework own scheduling: each registered test callback
59
+ * calls `fn`, then recursively registers any sub-trees returned by the function.
60
+ * This is the correct model for Node `--test`, Bun, and Playwright, where tests
61
+ * must be declared upfront and the framework drives execution.
62
+ */
63
+ export declare const registerModule: (ctx: TestContext, k: string, v: unknown, star: string) => Effect<Test | All | Await, void>;
64
+ /**
65
+ * Runs all test modules in `moduleMap` whose names pass `isTest`, accumulates
66
+ * pass/fail/time via `reporter`, and returns an exit code (0 = all passed,
67
+ * 1 = at least one failure).
68
+ */
34
69
  export declare const runModuleMap: <O extends Operation>(reporter: Reporter<O>) => (moduleMap: ModuleMap) => Effect<O | All, number>;
70
+ /**
71
+ * Discovers all test modules via `loadModuleMap`, then runs them through
72
+ * `runModuleMap`. The composed effect is a `NodeProgram` entry point for the
73
+ * `fjs t` test runner.
74
+ */
35
75
  export declare const testAll: <O extends Operation>(reporter: Reporter<O>) => Program<O | All | LoadModuleOperations>;
36
- export declare const registerModuleMap: (ctx: TestContext, moduleMap: ModuleMap) => Effect<Test | All, void>;
76
+ /**
77
+ * A chain of property-access keys leading to a test location. String entries
78
+ * are object/array keys; `null` marks a function-call boundary (the return
79
+ * value was walked as a sub-tree).
80
+ */
37
81
  export type Path = readonly (string | null)[];
82
+ /** Returns `true` if `s` is a non-negative decimal integer without a leading zero. */
38
83
  export declare const isInteger: (s: string) => boolean;
84
+ /** Returns `true` if `s` is a valid JS identifier (ASCII subset: `[A-Za-z_$][A-Za-z0-9_$]*`). */
39
85
  export declare const isIdentifier: (s: string) => boolean;
40
86
  /**
41
87
  * Renders a key chain as a JS property-access expression: identifier keys use
@@ -46,7 +92,7 @@ export declare const isIdentifier: (s: string) => boolean;
46
92
  export declare const fmtPath: (path: Path) => string;
47
93
  /**
48
94
  * Formats a fully-qualified test identifier as a JS-like expression, e.g.
49
- * `import("./math.test.f.ts").add()` or `import("./a.test.f.ts").users[3].name()`.
95
+ * `import("./math.proof.f.ts").add()` or `import("./a.proof.f.ts").users[3].name()`.
50
96
  * Self-contained per line — suitable for parallel output and as a CLI filter argument.
51
97
  */
52
98
  export declare const fmtImport: (file: string, path: Path) => string;
@@ -63,6 +109,10 @@ export declare const fmtTerm: (path: Path) => string;
63
109
  * https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions
64
110
  */
65
111
  export declare const ghEscape: (s: string) => string;
112
+ /**
113
+ * Default `Reporter.test` implementation: sandboxes `fn` once and inverts the
114
+ * result when `throws` is `true` (caught error → pass, clean return → fail).
115
+ */
66
116
  export declare const defaultTest: (file: string, path: Path, { fn, throws }: TestEntry) => Effect<Sandbox, SandboxResult<unknown>>;
67
117
  /**
68
118
  * The terminal/GitHub reporter used by `fjs t`. Output goes through
@@ -72,6 +122,14 @@ export declare const defaultTest: (file: string, path: Path, { fn, throws }: Tes
72
122
  * GitHub format path can be exercised directly from tests.
73
123
  */
74
124
  export declare const defaultReporter: (options: NodeProgramOptions) => Reporter<Write | Sandbox>;
125
+ /** The `fjs t` entry point: runs all tests using `defaultReporter`. */
75
126
  export declare const main: NodeProgram;
127
+ /**
128
+ * Entry point for external test frameworks (Node `--test`, Bun, Playwright).
129
+ *
130
+ * Discovers test modules via `loadModuleMap`, then registers each with the
131
+ * framework-appropriate `TestContext` selected from `NodeProgramOptions`
132
+ * based on the detected `engine`.
133
+ */
76
134
  export declare const register: NodeProgram;
77
135
  export {};
@@ -1,14 +1,20 @@
1
1
  /**
2
2
  * Test-framework helpers for running and reporting FunctionalScript tests.
3
3
  *
4
+ * Two parallel execution paths:
5
+ * - `runModule` / `Reporter<O>` — self-hosted Effects runner used by `fjs t`;
6
+ * sandboxes each leaf call individually and accumulates `TestState`.
7
+ * - `registerModule` / `TestContext` — registers tests with an external
8
+ * framework (Node `--test`, Bun, Playwright) at import time; the framework
9
+ * owns scheduling and pass/fail counting.
10
+ *
4
11
  * @module
5
12
  */
6
- import { reset, fgGreen, fgRed, bold, csiWrite } from "../../text/sgr/module.f.js";
7
- import { all, sandbox, test } from "../../types/effects/node/module.f.js";
8
- import { pure, do_ } from "../../types/effects/module.f.js";
9
- import { loadModuleMap } from "../module.f.js";
10
- import { invert } from "../../types/result/module.f.js";
11
- export const isTest = (s) => s.endsWith('test.f.js') || s.endsWith('test.f.ts');
13
+ import { reset, fgGreen, fgRed, bold, csiWrite } from "../text/sgr/module.f.js";
14
+ import { all, awaitIfPromise, sandbox, test } from "../types/effects/node/module.f.js";
15
+ import { pure } from "../types/effects/module.f.js";
16
+ import { loadModuleMap, shouldLoad } from "../dev/module.f.js";
17
+ import { invert } from "../types/result/module.f.js";
12
18
  const addPass = (delta) => (ts) => ({ ...ts, time: ts.time + delta, pass: ts.pass + 1 });
13
19
  const addFail = (delta) => (ts) => ({ ...ts, time: ts.time + delta, fail: ts.fail + 1 });
14
20
  const timeFormat = (a) => {
@@ -20,6 +26,14 @@ const timeFormat = (a) => {
20
26
  const e = x.substring(s);
21
27
  return `${b}.${e} ms`;
22
28
  };
29
+ /**
30
+ * Converts an arbitrary JS value into a `TestSet`.
31
+ *
32
+ * - Zero-argument functions become a `TestEntry`; the `throws` flag is set if
33
+ * `throws` is already `true` or the function's `.name === 'throw'`.
34
+ * - Non-null objects become an array of `[key, value]` pairs to recurse into.
35
+ * - All other values (including functions with parameters) produce an empty array.
36
+ */
23
37
  export const parseTestSet = (throws, x) => {
24
38
  switch (typeof x) {
25
39
  case 'function': {
@@ -51,19 +65,36 @@ export const collectTests = (path, throws, v) => {
51
65
  }
52
66
  return [[path, set]];
53
67
  };
54
- export const registerModule = (ctx, k, v) => {
55
- const registerOne = (ctx, [path, { fn, throws }]) => test(ctx, fmtImport(k, path), throws, (t) => {
56
- if (throws) {
57
- fn();
58
- return pure(undefined);
59
- }
60
- const r = fn();
61
- const sub = collectTests([...path, null], false, r);
62
- if (sub.length === 0) {
63
- return pure(undefined);
64
- }
65
- return all(...sub.map(e => registerOne(t, e))).step(() => pure(undefined));
66
- });
68
+ /**
69
+ * Registers all tests reachable from module export `v` (keyed by `k`) with
70
+ * the given `TestContext`.
71
+ *
72
+ * Unlike `runModule`, which sandboxes only the leaf function, `registerModule`
73
+ * lets the external framework own scheduling: each registered test callback
74
+ * calls `fn`, then recursively registers any sub-trees returned by the function.
75
+ * This is the correct model for Node `--test`, Bun, and Playwright, where tests
76
+ * must be declared upfront and the framework drives execution.
77
+ */
78
+ export const registerModule = (ctx, k, v, star) => {
79
+ const registerOne = (ctx, [path, { fn, throws }]) => {
80
+ // ' *' (non-empty only for Bun/Playwright) signals that all sub-tests run
81
+ // inline inside this single registration. Not appended to throw-tests since
82
+ // those never produce sub-tests. The path already contains '.throw' when a
83
+ // test is expected to throw, so no extra suffix is needed.
84
+ const base = fmtImport(k, path);
85
+ const name = throws ? base : `${base}${star}`;
86
+ return test(ctx, name, throws, (t) => awaitIfPromise(fn())
87
+ .step(resolved => {
88
+ if (throws) {
89
+ return pure(undefined);
90
+ }
91
+ const sub = collectTests([...path, null], false, resolved);
92
+ if (sub.length === 0) {
93
+ return pure(undefined);
94
+ }
95
+ return all(...sub.map(e => registerOne(t, e))).step(() => pure(undefined));
96
+ }));
97
+ };
67
98
  const tests = collectTests([], false, v);
68
99
  if (tests.length === 0) {
69
100
  return pure(undefined);
@@ -76,7 +107,7 @@ const runModule = ({ result, test }) => (k, v) => (ts) => {
76
107
  const one = ([testPath, set]) => test(k, testPath, set)
77
108
  .step(sr => {
78
109
  const { result: [s, r], duration } = sr;
79
- return result(k, testPath, sr)
110
+ return result(k, testPath, sr, set.throws)
80
111
  .step(() => {
81
112
  if (s === 'ok') {
82
113
  if (set.throws) {
@@ -99,24 +130,43 @@ const runModule = ({ result, test }) => (k, v) => (ts) => {
99
130
  .step(delta => pure(mergeState(ts, delta)));
100
131
  };
101
132
  const { entries } = Object;
133
+ /**
134
+ * Runs all test modules in `moduleMap` whose names pass `isTest`, accumulates
135
+ * pass/fail/time via `reporter`, and returns an exit code (0 = all passed,
136
+ * 1 = at least one failure).
137
+ */
102
138
  export const runModuleMap = (reporter) => (moduleMap) => {
103
139
  const { summary } = reporter;
104
- const modules = entries(moduleMap).filter(([k]) => isTest(k));
105
- return modules.reduce((acc, [k, v]) => acc.step(runModule(reporter)(k, v)), pure({ time: 0, pass: 0, fail: 0 }))
140
+ const modules = entries(moduleMap)
141
+ .flatMap(([k, v]) => v.proof !== undefined ? [[k, v.proof]] : []);
142
+ return all(...modules.map(([k, v]) => runModule(reporter)(k, v)(zero)))
143
+ .step(m => pure(m.reduce(mergeState, zero)))
106
144
  .step(ts => summary(ts.pass, ts.fail, ts.time)
107
145
  .step(() => pure(ts.fail !== 0 ? 1 : 0)));
108
146
  };
147
+ /**
148
+ * Discovers all test modules via `loadModuleMap`, then runs them through
149
+ * `runModuleMap`. The composed effect is a `NodeProgram` entry point for the
150
+ * `fjs t` test runner.
151
+ */
109
152
  export const testAll = (reporter) => options => loadModuleMap(options.env).step(runModuleMap(reporter));
110
- export const registerModuleMap = (ctx, moduleMap) => {
111
- const modules = entries(moduleMap).filter(([k]) => isTest(k));
153
+ /**
154
+ * Registers all modules in `moduleMap` that export a `proof` property with
155
+ * `ctx`. Delegates to `registerModule` for each matching entry.
156
+ */
157
+ const registerModuleMap = (ctx, star) => (moduleMap) => {
158
+ const modules = entries(moduleMap)
159
+ .flatMap(([k, v]) => v.proof !== undefined ? [[k, v.proof]] : []);
112
160
  if (modules.length === 0) {
113
161
  return pure(undefined);
114
162
  }
115
- return all(...modules.map(([k, v]) => registerModule(ctx, k, v))).step(() => pure(undefined));
163
+ return all(...modules.map(([k, v]) => registerModule(ctx, k, v, star))).step(() => pure(undefined));
116
164
  };
117
165
  const isAlpha = (c) => (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c === '_' || c === '$';
118
166
  const isDigit = (c) => c >= '0' && c <= '9';
167
+ /** Returns `true` if `s` is a non-negative decimal integer without a leading zero. */
119
168
  export const isInteger = (s) => s.length > 0 && [...s].every(isDigit) && (s === '0' || s[0] !== '0');
169
+ /** Returns `true` if `s` is a valid JS identifier (ASCII subset: `[A-Za-z_$][A-Za-z0-9_$]*`). */
120
170
  export const isIdentifier = (s) => s.length > 0 && isAlpha(s[0]) && [...s.slice(1)].every(c => isAlpha(c) || isDigit(c));
121
171
  const fmtKey = (k) => k === null ? '()'
122
172
  : isInteger(k) ? `[${k}]`
@@ -131,10 +181,10 @@ const fmtKey = (k) => k === null ? '()'
131
181
  export const fmtPath = (path) => path.reduce((acc, k) => acc + fmtKey(k), '');
132
182
  /**
133
183
  * Formats a fully-qualified test identifier as a JS-like expression, e.g.
134
- * `import("./math.test.f.ts").add()` or `import("./a.test.f.ts").users[3].name()`.
184
+ * `import("./math.proof.f.ts").add()` or `import("./a.proof.f.ts").users[3].name()`.
135
185
  * Self-contained per line — suitable for parallel output and as a CLI filter argument.
136
186
  */
137
- export const fmtImport = (file, path) => `import(${JSON.stringify(file)})${fmtPath(path)}()`;
187
+ export const fmtImport = (file, path) => `import(${JSON.stringify(file)}).proof${fmtPath(path)}()`;
138
188
  /**
139
189
  * Renders a key chain for terminal output: `| ` per level of depth, followed
140
190
  * by the last segment formatted as a bare integer, a bare identifier, or a
@@ -160,6 +210,10 @@ export const ghEscape = (s) => s.replaceAll('%', '%25')
160
210
  .replaceAll(',', '%2C')
161
211
  .replaceAll('\r', '%0D')
162
212
  .replaceAll('\n', '%0A');
213
+ /**
214
+ * Default `Reporter.test` implementation: sandboxes `fn` once and inverts the
215
+ * result when `throws` is `true` (caught error → pass, clean return → fail).
216
+ */
163
217
  export const defaultTest = (file, path, { fn, throws }) => sandbox(fn)
164
218
  .step(r => pure(throws ? { ...r, result: invert(r.result) } : r));
165
219
  const fmtResultLine = (file, path, color, label, duration) => `${fmtImport(file, path)}: ${color}${label}${reset}, ${timeFormat(duration)}`;
@@ -181,8 +235,8 @@ export const defaultReporter = (options) => {
181
235
  const isGitHub = options.env['GITHUB_ACTION'] !== undefined;
182
236
  return {
183
237
  // https://github.com/OndraM/ci-detector/blob/main/src/Ci/GitHubActions.php
184
- result: (file, path, { result: [s, v], duration }) => s === 'ok'
185
- ? csiLog(fmtResultLine(file, path, fgGreen, 'ok', duration))
238
+ result: (file, path, { result: [s, v], duration }, throws) => s === 'ok'
239
+ ? csiLog(fmtResultLine(file, path, fgGreen, 'ok', duration) + (throws ? ' # EXPECTED TO THROW' : ''))
186
240
  : isGitHub
187
241
  ? csiError(`::error file=${file},line=1,title=${ghEscape(fmtImport(file, path))}::${ghEscape(String(v))}`)
188
242
  : csiError(fmtResultLine(file, path, fgRed, 'error', duration))
@@ -195,9 +249,22 @@ export const defaultReporter = (options) => {
195
249
  test: defaultTest,
196
250
  };
197
251
  };
252
+ /** The `fjs t` entry point: runs all tests using `defaultReporter`. */
198
253
  export const main = options => testAll(defaultReporter(options))(options);
199
- export const register = o => loadModuleMap(o.env)
200
- .step(m => registerModuleMap(o.engine === 'bun' ? o.bunTestContext :
201
- o.engine === 'playwright' ? o.playwrightTestContext :
202
- o.testContext, m))
203
- .step(() => pure(0));
254
+ /**
255
+ * Entry point for external test frameworks (Node `--test`, Bun, Playwright).
256
+ *
257
+ * Discovers test modules via `loadModuleMap`, then registers each with the
258
+ * framework-appropriate `TestContext` selected from `NodeProgramOptions`
259
+ * based on the detected `engine`.
260
+ */
261
+ export const register = o => {
262
+ const star = o.engine === 'bun' || o.engine === 'playwright' ? ' ...' : '';
263
+ const ctx = o.engine === 'bun' ? o.bunTestContext :
264
+ o.engine === 'playwright' ? o.playwrightTestContext :
265
+ o.testContext;
266
+ const r = registerModuleMap(ctx, star);
267
+ return loadModuleMap(o.env)
268
+ .step(r)
269
+ .step(() => pure(0));
270
+ };
@@ -1,4 +1,4 @@
1
- import { io } from "../../io/module.js";
1
+ import { io } from "../io/module.js";
2
2
  import { register } from "./module.f.js";
3
- import { runProgram } from "../../io/module.f.js";
3
+ import { runProgram } from "../io/module.f.js";
4
4
  export const run = () => runProgram(io)([])(register);
@@ -12,11 +12,39 @@ export declare const namedExports: () => void;
12
12
  export declare const defaultReporterOutput: () => void;
13
13
  export declare const defaultReporterFailOutput: () => void;
14
14
  export declare const githubReporterOutput: () => void;
15
+ export declare const registerSuffixes: () => void;
15
16
  export declare const helpers: {
16
17
  isInteger: () => void;
17
18
  isIdentifier: () => void;
19
+ shouldLoad: () => void;
18
20
  fmtImport: () => void;
19
21
  fmtPath: () => void;
20
22
  fmtTerm: () => void;
21
23
  ghEscape: () => void;
22
24
  };
25
+ export declare const proof: {
26
+ flat: () => void;
27
+ nested: () => void;
28
+ throwKey: () => void;
29
+ throwKeyFail: () => void;
30
+ mixedPassFail: () => void;
31
+ returnValueSubTree: () => void;
32
+ arrayKeys: () => void;
33
+ nonTestFilesSkipped: () => void;
34
+ multipleFiles: () => void;
35
+ throwByFunctionName: () => void;
36
+ namedExports: () => void;
37
+ defaultReporterOutput: () => void;
38
+ defaultReporterFailOutput: () => void;
39
+ githubReporterOutput: () => void;
40
+ registerSuffixes: () => void;
41
+ helpers: {
42
+ isInteger: () => void;
43
+ isIdentifier: () => void;
44
+ shouldLoad: () => void;
45
+ fmtImport: () => void;
46
+ fmtPath: () => void;
47
+ fmtTerm: () => void;
48
+ ghEscape: () => void;
49
+ };
50
+ };