functionalscript 0.20.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 (83) hide show
  1. package/fs/asserts/module.f.d.ts +4 -0
  2. package/fs/asserts/module.f.js +6 -0
  3. package/fs/cas/module.f.js +7 -11
  4. package/fs/cas/proof.f.d.ts +4 -1
  5. package/fs/cas/proof.f.js +37 -1
  6. package/fs/ci/bun/module.f.js +2 -7
  7. package/fs/ci/common/module.f.d.ts +2 -1
  8. package/fs/ci/common/module.f.js +7 -9
  9. package/fs/ci/config/module.f.d.ts +12 -3
  10. package/fs/ci/config/module.f.js +24 -4
  11. package/fs/ci/deno/module.f.js +2 -5
  12. package/fs/ci/node/module.f.js +2 -2
  13. package/fs/ci/playwright/module.f.js +5 -8
  14. package/fs/ci/proof.f.js +1 -1
  15. package/fs/ci/rust/module.f.js +3 -9
  16. package/fs/dev/module.f.d.ts +0 -3
  17. package/fs/dev/module.f.js +0 -6
  18. package/fs/dev/proof.f.js +2 -1
  19. package/fs/djs/tokenizer-new/module.f.js +1 -1
  20. package/fs/{dev/tf → emergent-testing}/module.f.d.ts +5 -5
  21. package/fs/{dev/tf → emergent-testing}/module.f.js +34 -24
  22. package/fs/{dev/tf → emergent-testing}/module.js +2 -2
  23. package/fs/{dev/tf → emergent-testing}/proof.f.d.ts +2 -0
  24. package/fs/{dev/tf → emergent-testing}/proof.f.js +44 -7
  25. package/fs/emergent-testing/scenarios/async-subtests.fail.d.ts +6 -0
  26. package/fs/emergent-testing/scenarios/async-subtests.fail.js +9 -0
  27. package/fs/emergent-testing/scenarios/async-subtests.pass.d.ts +6 -0
  28. package/fs/emergent-testing/scenarios/async-subtests.pass.js +9 -0
  29. package/fs/emergent-testing/scenarios/async.fail.d.ts +3 -0
  30. package/fs/emergent-testing/scenarios/async.fail.js +6 -0
  31. package/fs/emergent-testing/scenarios/async.pass.d.ts +3 -0
  32. package/fs/emergent-testing/scenarios/async.pass.js +5 -0
  33. package/fs/emergent-testing/scenarios/fail.fail.d.ts +3 -0
  34. package/fs/emergent-testing/scenarios/fail.fail.js +3 -0
  35. package/fs/emergent-testing/scenarios/return-value.pass.d.ts +3 -0
  36. package/fs/emergent-testing/scenarios/return-value.pass.js +4 -0
  37. package/fs/emergent-testing/scenarios/thenable.pass.d.ts +5 -0
  38. package/fs/{dev/tf → emergent-testing}/scenarios/thenable.pass.js +5 -3
  39. package/fs/emergent-testing/scenarios/thenable2.pass.d.ts +5 -0
  40. package/fs/emergent-testing/scenarios/thenable2.pass.js +3 -0
  41. package/fs/emergent-testing/scenarios/throw.pass.d.ts +5 -0
  42. package/fs/{dev/tf/scenarios/throw.pass.f.js → emergent-testing/scenarios/throw.pass.js} +1 -1
  43. package/fs/fjs/module.f.js +4 -5
  44. package/fs/sul/id/module.f.js +1 -1
  45. package/fs/sul/id/proof.f.js +1 -1
  46. package/fs/sul/level/hash/proof.f.js +1 -1
  47. package/fs/sul/proof.f.js +1 -1
  48. package/fs/types/array/module.f.js +2 -5
  49. package/fs/types/effects/node/module.f.d.ts +6 -0
  50. package/fs/types/effects/node/module.f.js +9 -1
  51. package/fs/types/effects/node/virtual/module.f.js +1 -1
  52. package/fs/types/list/proof.f.d.ts +1 -0
  53. package/fs/types/list/proof.f.js +14 -0
  54. package/fs/types/nullable/module.f.d.ts +7 -0
  55. package/fs/types/nullable/module.f.js +7 -0
  56. package/fs/types/nullable/proof.f.js +19 -2
  57. package/fs/types/object/module.f.js +2 -4
  58. package/fs/types/patricia_trie/proof.f.js +1 -1
  59. package/fs/types/ts/module.f.d.ts +0 -1
  60. package/fs/website/proof.f.d.ts +3 -0
  61. package/fs/website/proof.f.js +9 -0
  62. package/package.json +1 -1
  63. package/fs/dev/tf/scenarios/async-subtests.fail.d.ts +0 -4
  64. package/fs/dev/tf/scenarios/async-subtests.fail.js +0 -7
  65. package/fs/dev/tf/scenarios/async-subtests.pass.d.ts +0 -4
  66. package/fs/dev/tf/scenarios/async-subtests.pass.js +0 -7
  67. package/fs/dev/tf/scenarios/async.fail.d.ts +0 -1
  68. package/fs/dev/tf/scenarios/async.fail.js +0 -4
  69. package/fs/dev/tf/scenarios/async.pass.d.ts +0 -1
  70. package/fs/dev/tf/scenarios/async.pass.js +0 -3
  71. package/fs/dev/tf/scenarios/fail.fail.f.d.ts +0 -1
  72. package/fs/dev/tf/scenarios/fail.fail.f.js +0 -1
  73. package/fs/dev/tf/scenarios/return-value.pass.f.d.ts +0 -1
  74. package/fs/dev/tf/scenarios/return-value.pass.f.js +0 -2
  75. package/fs/dev/tf/scenarios/thenable.pass.d.ts +0 -3
  76. package/fs/dev/tf/scenarios/thenable2.pass.f.d.ts +0 -3
  77. package/fs/dev/tf/scenarios/thenable2.pass.f.js +0 -3
  78. package/fs/dev/tf/scenarios/throw.pass.f.d.ts +0 -6
  79. /package/fs/{dev/tf → emergent-testing}/all.test.d.ts +0 -0
  80. /package/fs/{dev/tf → emergent-testing}/all.test.js +0 -0
  81. /package/fs/{dev/tf → emergent-testing}/module.d.ts +0 -0
  82. /package/fs/{dev/tf → emergent-testing}/scenarios/all.d.ts +0 -0
  83. /package/fs/{dev/tf → emergent-testing}/scenarios/all.js +0 -0
@@ -0,0 +1,4 @@
1
+ export declare const todo: () => never;
2
+ export declare const assert: (v: boolean, msg?: unknown) => asserts v;
3
+ export declare const assertEq: <T>(a: T, b: T) => void;
4
+ export type Assert<T extends true> = T;
@@ -0,0 +1,6 @@
1
+ export const todo = () => { throw 'not implemented'; };
2
+ export const assert = (v, msg = 'assertion failed') => {
3
+ if (!v)
4
+ throw msg;
5
+ };
6
+ export const assertEq = (a, b) => assert(a === b, [a, b]);
@@ -7,7 +7,7 @@ import { computeSync, sha256 } from "../crypto/sha2/module.f.js";
7
7
  import { parse } from "../path/module.f.js";
8
8
  import { cBase32ToVec, vecToCBase32 } from "../cbase32/module.f.js";
9
9
  import { begin, forEachStep, pure } from "../types/effects/module.f.js";
10
- import { error, log, mkdir, readdir, readFile, writeFile } from "../types/effects/node/module.f.js";
10
+ import { errorExit, log, mkdir, readdir, readFile, writeFile } from "../types/effects/node/module.f.js";
11
11
  import { toOption } from "../types/nullable/module.f.js";
12
12
  import { unwrap } from "../types/result/module.f.js";
13
13
  const o = { withFileTypes: true };
@@ -61,10 +61,6 @@ export const cas = (sha2) => {
61
61
  list,
62
62
  });
63
63
  };
64
- /** Prints an error message and returns exit code `1`. */
65
- const e = (s) => begin
66
- .step(() => error(s))
67
- .step(() => pure(1));
68
64
  /**
69
65
  * Runs the CAS CLI.
70
66
  *
@@ -79,7 +75,7 @@ export const main = (args) => {
79
75
  switch (cmd) {
80
76
  case 'add': {
81
77
  if (options.length !== 1) {
82
- return e("'cas add' expects one parameter");
78
+ return errorExit("'cas add' expects one parameter");
83
79
  }
84
80
  const [path] = options;
85
81
  return begin
@@ -90,18 +86,18 @@ export const main = (args) => {
90
86
  }
91
87
  case 'get': {
92
88
  if (options.length !== 2) {
93
- return e("'cas get' expects two parameters");
89
+ return errorExit("'cas get' expects two parameters");
94
90
  }
95
91
  const [hashCBase32, path] = options;
96
92
  const hash = cBase32ToVec(hashCBase32);
97
93
  if (hash === null) {
98
- return e(`invalid hash format: ${hashCBase32}`);
94
+ return errorExit(`invalid hash format: ${hashCBase32}`);
99
95
  }
100
96
  return begin
101
97
  .step(() => c.read(hash))
102
98
  .step(v => {
103
99
  const result = v === undefined
104
- ? e(`no such hash: ${hashCBase32}`)
100
+ ? errorExit(`no such hash: ${hashCBase32}`)
105
101
  : begin
106
102
  .step(() => writeFile(path, v))
107
103
  .step(() => pure(0));
@@ -115,9 +111,9 @@ export const main = (args) => {
115
111
  .step(() => pure(0));
116
112
  }
117
113
  case undefined: {
118
- return e('Error: CAS command requires subcommand');
114
+ return errorExit('Error: CAS command requires subcommand');
119
115
  }
120
116
  default:
121
- return e(`Error: Unknown CAS subcommand "${args[0]}"`);
117
+ return errorExit(`Error: Unknown CAS subcommand "${args[0]}"`);
122
118
  }
123
119
  };
@@ -1 +1,4 @@
1
- export declare const proof: () => void;
1
+ export declare const proof: {
2
+ casWrite: () => void;
3
+ casReadPassthrough: () => void;
4
+ };
package/fs/cas/proof.f.js CHANGED
@@ -1 +1,37 @@
1
- export const proof = () => { };
1
+ import { cas } from "./module.f.js";
2
+ import { sha256 } from "../crypto/sha2/module.f.js";
3
+ import { empty, length } from "../types/bit_vec/module.f.js";
4
+ import { pure } from "../types/effects/module.f.js";
5
+ import { run } from "../types/effects/mock/module.f.js";
6
+ export const proof = {
7
+ casWrite: () => {
8
+ const store = {
9
+ read: (_key) => pure(undefined),
10
+ write: (_key, _value) => pure(undefined),
11
+ list: () => pure([]),
12
+ };
13
+ const c = cas(sha256)(store);
14
+ const [, hash] = run({})(undefined)(c.write(empty));
15
+ // sha256 of empty input produces a 256-bit hash
16
+ if (length(hash) !== 256n) {
17
+ throw ['expected 256-bit hash', length(hash)];
18
+ }
19
+ },
20
+ casReadPassthrough: () => {
21
+ const stored = empty;
22
+ const store = {
23
+ read: (_key) => pure(stored),
24
+ write: (_key, _value) => pure(undefined),
25
+ list: () => pure([stored]),
26
+ };
27
+ const c = cas(sha256)(store);
28
+ const [, readResult] = run({})(undefined)(c.read(empty));
29
+ if (readResult !== stored) {
30
+ throw ['read should pass through', readResult];
31
+ }
32
+ const [, listResult] = run({})(undefined)(c.list());
33
+ if (listResult.length !== 1) {
34
+ throw ['list should pass through', listResult];
35
+ }
36
+ },
37
+ };
@@ -5,17 +5,12 @@
5
5
  * @module
6
6
  */
7
7
  import { bun } from "../config/module.f.js";
8
- import { clean, install, test } from "../common/module.f.js";
8
+ import { clean, install, test, uses } from "../common/module.f.js";
9
9
  const installOnWindowsArm = ({ def, name, path }) => (v) => (a) => install(v === 'windows' && a === 'arm'
10
10
  ? { run: `irm ${path}/install.ps1 | iex; "$env:USERPROFILE\\.${name}\\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append` }
11
11
  : def);
12
12
  const installBun = installOnWindowsArm({
13
- def: {
14
- uses: 'oven-sh/setup-bun@v2',
15
- with: {
16
- 'bun-version': bun
17
- },
18
- },
13
+ def: uses('oven-sh/setup-bun', { 'bun-version': bun }),
19
14
  name: 'bun',
20
15
  path: 'bun.sh',
21
16
  });
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @module
7
7
  */
8
- import { images } from '../config/module.f.ts';
8
+ import { actions, images } from '../config/module.f.ts';
9
9
  import { type Ts } from '../../types/rtti/ts/module.f.ts';
10
10
  export declare const os: readonly ["ubuntu", "macos", "windows"];
11
11
  export type Os = typeof os[number];
@@ -76,6 +76,7 @@ export type MetaStep = {
76
76
  readonly type: 'apt-get';
77
77
  readonly package: string;
78
78
  };
79
+ export declare const uses: (name: keyof typeof actions, w?: Record<string, string>) => Step;
79
80
  export declare const install: (step: Step) => MetaStep;
80
81
  export declare const test: (step: Step) => MetaStep;
81
82
  export declare const clean: (steps: readonly MetaStep[]) => readonly MetaStep[];
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @module
7
7
  */
8
- import { images, rust } from "../config/module.f.js";
8
+ import { actions, images } from "../config/module.f.js";
9
9
  import { option, array, record, string } from "../../types/rtti/module.f.js";
10
10
  import {} from "../../types/rtti/ts/module.f.js";
11
11
  import { parse as rttiParse } from "../../types/rtti/parse/module.f.js";
@@ -27,6 +27,10 @@ export const gitHubActionSchema = {
27
27
  jobs: jobsSchema
28
28
  };
29
29
  export const parseGitHubAction = rttiParse(gitHubActionSchema);
30
+ export const uses = (name, w) => ({
31
+ uses: `${name}@${actions[name]}`,
32
+ ...(w === undefined ? {} : { with: w })
33
+ });
30
34
  export const install = (step) => ({ type: 'install', step });
31
35
  export const test = (step) => ({ type: 'test', step });
32
36
  export const clean = (steps) => [
@@ -40,15 +44,9 @@ export const toSteps = (m) => {
40
44
  const targets = m.flatMap(v => v.type === 'rust' && v.target !== undefined ? [v.target] : []).join(',');
41
45
  return [
42
46
  ...(aptGet !== '' ? [{ run: `sudo apt-get update && sudo apt-get install -y ${aptGet}` }] : []),
43
- ...(needRust ? [{
44
- uses: `dtolnay/rust-toolchain@${rust}`,
45
- with: {
46
- components: 'rustfmt,clippy',
47
- targets
48
- }
49
- }] : []),
47
+ ...(needRust ? [uses('dtolnay/rust-toolchain', { components: 'rustfmt,clippy', targets })] : []),
50
48
  ...filter('install'),
51
- { uses: 'actions/checkout@v5' },
49
+ uses('actions/checkout'),
52
50
  ...filter('test'),
53
51
  ];
54
52
  };
@@ -22,11 +22,20 @@ export declare const images: {
22
22
  export declare const bun = "1.3.14";
23
23
  export declare const deno = "2.8.1";
24
24
  export declare const playwright = "1.60.0";
25
- export declare const rust = "1.95.0";
26
25
  export declare const node: {
27
- readonly default: "26.2.0";
26
+ readonly default: "26.3.0";
28
27
  readonly others: readonly ["24.16.0"];
29
28
  };
30
29
  export declare const wasmtime = "45.0.0";
31
30
  export declare const wasmer = "7.1.0";
32
- export declare const tsgo = "7.0.0-dev.20260527.1";
31
+ export declare const tsgo = "7.0.0-dev.20260601.1";
32
+ export declare const actions: {
33
+ readonly 'actions/checkout': "v6";
34
+ readonly 'actions/setup-node': "v6";
35
+ readonly 'actions/cache': "v5";
36
+ readonly 'denoland/setup-deno': "v2";
37
+ readonly 'oven-sh/setup-bun': "v2";
38
+ readonly 'bytecodealliance/actions/wasmtime/setup': "v1";
39
+ readonly 'wasmerio/setup-wasmer': "v3.1";
40
+ readonly 'dtolnay/rust-toolchain': "1.95.0";
41
+ };
@@ -26,11 +26,9 @@ export const bun = '1.3.14';
26
26
  export const deno = '2.8.1';
27
27
  // https://www.npmjs.com/package/playwright
28
28
  export const playwright = '1.60.0';
29
- // https://rust-lang.org/
30
- export const rust = '1.95.0';
31
29
  // https://nodejs.org/en/download
32
30
  export const node = {
33
- default: '26.2.0',
31
+ default: '26.3.0',
34
32
  others: ['24.16.0'],
35
33
  };
36
34
  // https://github.com/bytecodealliance/wasmtime/releases
@@ -38,4 +36,26 @@ export const wasmtime = '45.0.0';
38
36
  // https://github.com/wasmerio/wasmer/releases
39
37
  export const wasmer = '7.1.0';
40
38
  // https://www.npmjs.com/package/@typescript/native-preview?activeTab=versions
41
- export const tsgo = '7.0.0-dev.20260527.1';
39
+ export const tsgo = '7.0.0-dev.20260601.1';
40
+ // GitHub Action versions used by CI step builders. The key is the action
41
+ // `owner/name`; call sites compose the full ref as
42
+ // `` `${name}@${actions[name]}` ``.
43
+ // Note: dtolnay/rust-toolchain value is a Rust version, not an action version.
44
+ export const actions = {
45
+ // https://github.com/marketplace/actions/checkout
46
+ 'actions/checkout': 'v6',
47
+ // https://github.com/marketplace/actions/setup-node-js-environment
48
+ 'actions/setup-node': 'v6',
49
+ // https://github.com/marketplace/actions/cache
50
+ 'actions/cache': 'v5',
51
+ // https://github.com/marketplace/actions/setup-deno
52
+ 'denoland/setup-deno': 'v2',
53
+ // https://github.com/marketplace/actions/setup-bun
54
+ 'oven-sh/setup-bun': 'v2',
55
+ // https://github.com/bytecodealliance/actions
56
+ 'bytecodealliance/actions/wasmtime/setup': 'v1',
57
+ // https://github.com/wasmerio/setup-wasmer
58
+ 'wasmerio/setup-wasmer': 'v3.1',
59
+ // https://rust-lang.org/ - value is Rust version, not action version
60
+ 'dtolnay/rust-toolchain': '1.95.0',
61
+ };
@@ -5,12 +5,9 @@
5
5
  * @module
6
6
  */
7
7
  import { deno } from "../config/module.f.js";
8
- import { clean, install, test } from "../common/module.f.js";
8
+ import { clean, install, test, uses } from "../common/module.f.js";
9
9
  export const denoSteps = (extra) => clean([
10
- install({
11
- uses: 'denoland/setup-deno@v2',
12
- with: { 'deno-version': deno },
13
- }),
10
+ install(uses('denoland/setup-deno', { 'deno-version': deno })),
14
11
  test({ run: 'deno install' }),
15
12
  test({ run: 'deno task test' }),
16
13
  ...extra,
@@ -5,9 +5,9 @@
5
5
  * @module
6
6
  */
7
7
  import { node, tsgo } from "../config/module.f.js";
8
- import { clean, findTgz, install, test, ubuntu } from "../common/module.f.js";
8
+ import { clean, findTgz, install, test, ubuntu, uses } from "../common/module.f.js";
9
9
  export const major = (v) => v.split('.')[0];
10
- const installNode = (version) => ({ uses: 'actions/setup-node@v6', with: { 'node-version': version } });
10
+ const installNode = (version) => uses('actions/setup-node', { 'node-version': version });
11
11
  const nodeInstall = (v) => [
12
12
  install(installNode(v)),
13
13
  test({ run: 'npm ci' }),
@@ -5,19 +5,16 @@
5
5
  * @module
6
6
  */
7
7
  import { images, node, playwright } from "../config/module.f.js";
8
- import { install, test, toSteps } from "../common/module.f.js";
8
+ import { install, test, toSteps, uses } from "../common/module.f.js";
9
9
  import { basicNode } from "../node/module.f.js";
10
10
  const playwrightImage = images.ubuntu.intel;
11
11
  export const playwrightJob = {
12
12
  'runs-on': playwrightImage,
13
13
  steps: toSteps(basicNode(node.default)([
14
- install({
15
- uses: 'actions/cache@v4',
16
- with: {
17
- path: '~/.cache/ms-playwright',
18
- key: `${playwrightImage}-playwright-${playwright}`,
19
- },
20
- }),
14
+ install(uses('actions/cache', {
15
+ path: '~/.cache/ms-playwright',
16
+ key: `${playwrightImage}-playwright-${playwright}`,
17
+ })),
21
18
  install({ run: `npm install -g playwright@${playwright}` }),
22
19
  install({ run: 'playwright install-deps' }),
23
20
  install({ run: 'playwright install' }),
package/fs/ci/proof.f.js CHANGED
@@ -2,7 +2,7 @@ import { ci } from "./module.f.js";
2
2
  import { utf8ToString } from "../text/module.f.js";
3
3
  import { isVec } from "../types/bit_vec/module.f.js";
4
4
  import { test, parseGitHubAction } from "./common/module.f.js";
5
- import { assert } from "../dev/module.f.js";
5
+ import { assert } from "../asserts/module.f.js";
6
6
  import { emptyState, virtual } from "../types/effects/node/virtual/module.f.js";
7
7
  import { parse as jsonParse } from "../json/module.f.js";
8
8
  import { unwrap } from "../types/result/module.f.js";
@@ -6,7 +6,7 @@
6
6
  * @module
7
7
  */
8
8
  import { wasmer, wasmtime } from "../config/module.f.js";
9
- import { install, test } from "../common/module.f.js";
9
+ import { install, test, uses } from "../common/module.f.js";
10
10
  const cargoTest = (target, config) => {
11
11
  const to = target ? ` --target ${target}` : '';
12
12
  const co = config ? ` --config ${config}` : '';
@@ -40,14 +40,8 @@ export const rustSteps = (v, a) => [
40
40
  test({ run: 'cargo fmt -- --check' }),
41
41
  test({ run: 'cargo clippy -- -D warnings' }),
42
42
  ...cargoTest(),
43
- install({
44
- uses: 'bytecodealliance/actions/wasmtime/setup@v1',
45
- with: { version: wasmtime }
46
- }),
47
- install({
48
- uses: 'wasmerio/setup-wasmer@v3.1',
49
- with: { version: `v${wasmer}` },
50
- }),
43
+ install(uses('bytecodealliance/actions/wasmtime/setup', { version: wasmtime })),
44
+ install(uses('wasmerio/setup-wasmer', { version: `v${wasmer}` })),
51
45
  ...wasmTarget('wasm32-wasip1'),
52
46
  ...wasmTarget('wasm32-wasip2'),
53
47
  ...wasmTarget('wasm32-unknown-unknown'),
@@ -6,9 +6,6 @@
6
6
  import type { Io } from '../io/module.f.ts';
7
7
  import { type Access, type All, type Env, type Import, type NodeProgram, type Readdir } from '../types/effects/node/module.f.ts';
8
8
  import { type Effect } from '../types/effects/module.f.ts';
9
- export declare const todo: () => never;
10
- export declare const assert: (v: boolean, msg?: unknown) => asserts v;
11
- export declare const assertEq: <T>(a: T, b: T) => void;
12
9
  export type Module = {
13
10
  readonly proof?: unknown;
14
11
  readonly [k: string]: unknown;
@@ -8,12 +8,6 @@ import { parse as jsonParse } from "../json/module.f.js";
8
8
  import { record, unknown as rttiUnknown } from "../types/rtti/module.f.js";
9
9
  import { parse as rttiParse } from "../types/rtti/parse/module.f.js";
10
10
  import { relativize } from "../path/module.f.js";
11
- export const todo = () => { throw 'not implemented'; };
12
- export const assert = (v, msg = 'assertion failed') => {
13
- if (!v)
14
- throw msg;
15
- };
16
- export const assertEq = (a, b) => assert(a === b, [a, b]);
17
11
  export const env = ({ process: { env } }) => a => {
18
12
  const r = Object.getOwnPropertyDescriptor(env, a);
19
13
  return r === undefined ? undefined :
package/fs/dev/proof.f.js CHANGED
@@ -1,4 +1,5 @@
1
- import { todo, env } from "./module.f.js";
1
+ import { todo } from "../asserts/module.f.js";
2
+ import { env } from "./module.f.js";
2
3
  export const proof = {
3
4
  shouldPass: () => ({
4
5
  then: () => undefined
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { descentParser } from "../../bnf/data/module.f.js";
7
7
  import { eof, join0Plus, max, none, not, notSet, oneEncode, option, range, remove, repeat, repeat0Plus, repeat1Plus, set, unicodeRange } from "../../bnf/module.f.js";
8
- import { todo } from "../../dev/module.f.js";
8
+ import { todo } from "../../asserts/module.f.js";
9
9
  export const parse = (input) => {
10
10
  const m = descentParser(jsGrammar());
11
11
  return todo();
@@ -1,6 +1,6 @@
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 '../module.f.ts';
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
4
  /** A zero-argument test function whose return value may contain sub-tests. */
5
5
  export type TestFn = () => unknown;
6
6
  /**
@@ -46,7 +46,7 @@ export declare const collectTests: (path: Path, throws: boolean, v: unknown) =>
46
46
  * contained `inner`.
47
47
  */
48
48
  export type Reporter<O extends Operation> = {
49
- 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>;
50
50
  readonly summary: (pass: number, fail: number, time: number) => Effect<O, void>;
51
51
  readonly test: (file: string, path: Path, set: TestEntry) => Effect<O, SandboxResult<unknown>>;
52
52
  };
@@ -60,7 +60,7 @@ export type Reporter<O extends Operation> = {
60
60
  * This is the correct model for Node `--test`, Bun, and Playwright, where tests
61
61
  * must be declared upfront and the framework drives execution.
62
62
  */
63
- export declare const registerModule: (ctx: TestContext, k: string, v: unknown) => Effect<Test | All | Await, void>;
63
+ export declare const registerModule: (ctx: TestContext, k: string, v: unknown, star: string) => Effect<Test | All | Await, void>;
64
64
  /**
65
65
  * Runs all test modules in `moduleMap` whose names pass `isTest`, accumulates
66
66
  * pass/fail/time via `reporter`, and returns an exit code (0 = all passed,
@@ -10,11 +10,11 @@
10
10
  *
11
11
  * @module
12
12
  */
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 "../module.f.js";
17
- import { invert } from "../../types/result/module.f.js";
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";
18
18
  const addPass = (delta) => (ts) => ({ ...ts, time: ts.time + delta, pass: ts.pass + 1 });
19
19
  const addFail = (delta) => (ts) => ({ ...ts, time: ts.time + delta, fail: ts.fail + 1 });
20
20
  const timeFormat = (a) => {
@@ -75,18 +75,26 @@ export const collectTests = (path, throws, v) => {
75
75
  * This is the correct model for Node `--test`, Bun, and Playwright, where tests
76
76
  * must be declared upfront and the framework drives execution.
77
77
  */
78
- export const registerModule = (ctx, k, v) => {
79
- const registerOne = (ctx, [path, { fn, throws }]) => test(ctx, fmtImport(k, path), throws, (t) => awaitIfPromise(fn())
80
- .step(resolved => {
81
- if (throws) {
82
- return pure(undefined);
83
- }
84
- const sub = collectTests([...path, null], false, resolved);
85
- if (sub.length === 0) {
86
- return pure(undefined);
87
- }
88
- return all(...sub.map(e => registerOne(t, e))).step(() => pure(undefined));
89
- }));
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
+ };
90
98
  const tests = collectTests([], false, v);
91
99
  if (tests.length === 0) {
92
100
  return pure(undefined);
@@ -99,7 +107,7 @@ const runModule = ({ result, test }) => (k, v) => (ts) => {
99
107
  const one = ([testPath, set]) => test(k, testPath, set)
100
108
  .step(sr => {
101
109
  const { result: [s, r], duration } = sr;
102
- return result(k, testPath, sr)
110
+ return result(k, testPath, sr, set.throws)
103
111
  .step(() => {
104
112
  if (s === 'ok') {
105
113
  if (set.throws) {
@@ -146,13 +154,13 @@ export const testAll = (reporter) => options => loadModuleMap(options.env).step(
146
154
  * Registers all modules in `moduleMap` that export a `proof` property with
147
155
  * `ctx`. Delegates to `registerModule` for each matching entry.
148
156
  */
149
- const registerModuleMap = (ctx) => (moduleMap) => {
157
+ const registerModuleMap = (ctx, star) => (moduleMap) => {
150
158
  const modules = entries(moduleMap)
151
159
  .flatMap(([k, v]) => v.proof !== undefined ? [[k, v.proof]] : []);
152
160
  if (modules.length === 0) {
153
161
  return pure(undefined);
154
162
  }
155
- 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));
156
164
  };
157
165
  const isAlpha = (c) => (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c === '_' || c === '$';
158
166
  const isDigit = (c) => c >= '0' && c <= '9';
@@ -227,8 +235,8 @@ export const defaultReporter = (options) => {
227
235
  const isGitHub = options.env['GITHUB_ACTION'] !== undefined;
228
236
  return {
229
237
  // https://github.com/OndraM/ci-detector/blob/main/src/Ci/GitHubActions.php
230
- result: (file, path, { result: [s, v], duration }) => s === 'ok'
231
- ? 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' : ''))
232
240
  : isGitHub
233
241
  ? csiError(`::error file=${file},line=1,title=${ghEscape(fmtImport(file, path))}::${ghEscape(String(v))}`)
234
242
  : csiError(fmtResultLine(file, path, fgRed, 'error', duration))
@@ -251,9 +259,11 @@ export const main = options => testAll(defaultReporter(options))(options);
251
259
  * based on the detected `engine`.
252
260
  */
253
261
  export const register = o => {
254
- const r = registerModuleMap(o.engine === 'bun' ? o.bunTestContext :
262
+ const star = o.engine === 'bun' || o.engine === 'playwright' ? ' ...' : '';
263
+ const ctx = o.engine === 'bun' ? o.bunTestContext :
255
264
  o.engine === 'playwright' ? o.playwrightTestContext :
256
- o.testContext);
265
+ o.testContext;
266
+ const r = registerModuleMap(ctx, star);
257
267
  return loadModuleMap(o.env)
258
268
  .step(r)
259
269
  .step(() => pure(0));
@@ -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,6 +12,7 @@ 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;
@@ -36,6 +37,7 @@ export declare const proof: {
36
37
  defaultReporterOutput: () => void;
37
38
  defaultReporterFailOutput: () => void;
38
39
  githubReporterOutput: () => void;
40
+ registerSuffixes: () => void;
39
41
  helpers: {
40
42
  isInteger: () => void;
41
43
  isIdentifier: () => void;
@@ -1,13 +1,14 @@
1
- import { pure } from "../../types/effects/module.f.js";
2
- import { emptyState } from "../../types/effects/node/virtual/module.f.js";
3
- import { virtual } from "../../types/effects/node/virtual/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";
1
+ import { pure } from "../types/effects/module.f.js";
2
+ import { emptyState } from "../types/effects/node/virtual/module.f.js";
3
+ import { virtual } from "../types/effects/node/virtual/module.f.js";
4
+ import { assert, assertEq, todo } from "../asserts/module.f.js";
5
+ import { testAll, defaultReporter, fmtPath, fmtTerm, fmtImport, ghEscape, isInteger, isIdentifier, registerModule, defaultTest, } from "./module.f.js";
6
+ import { run as mockRun } from "../types/effects/mock/module.f.js";
7
+ import { shouldLoad } from "../dev/module.f.js";
7
8
  const makeReporter = () => {
8
9
  const events = [];
9
10
  const reporter = {
10
- result: (file, path, r) => { events.push(['result', file, [...path], r]); return pure(undefined); },
11
+ result: (file, path, r, _throws) => { events.push(['result', file, [...path], r]); return pure(undefined); },
11
12
  summary: (pass, fail, time) => { events.push(['summary', pass, fail, time]); return pure(undefined); },
12
13
  test: defaultTest,
13
14
  };
@@ -215,6 +216,41 @@ export const githubReporterOutput = () => {
215
216
  assertEq(exit, 1);
216
217
  assertEq(stderr, '::error file=./s.proof.f.ts,line=1,title=import("./s.proof.f.ts").proof["a%3Ab%2Cc%25d"]()::oops\n');
217
218
  };
219
+ // registerModule appends ' ...' for inline runners (Bun/Playwright).
220
+ // Uses a minimal synchronous mock for the Test/All/Await effect operations.
221
+ export const registerSuffixes = () => {
222
+ let runner;
223
+ const noopCtx = { test: (_n, _o, _f) => Promise.resolve() };
224
+ const makeRunner = () => mockRun({
225
+ test: (s, _ctx, name, _xf, _fn) => [[...s, name], undefined],
226
+ all: (s, ...effects) => {
227
+ let st = s;
228
+ const rs = [];
229
+ for (const e of effects) {
230
+ const [ns, r] = runner(st)(e);
231
+ st = ns;
232
+ rs.push(r);
233
+ }
234
+ return [st, rs];
235
+ },
236
+ await: (s, p) => [s, [p]],
237
+ });
238
+ runner = makeRunner();
239
+ const proof = {
240
+ ok: () => { },
241
+ throw: { a: () => { throw 'expected'; } },
242
+ };
243
+ // Node (star = ''): no suffixes
244
+ const [nodeNames] = runner([])(registerModule(noopCtx, './a.f.ts', proof, ''));
245
+ assertEq(nodeNames.length, 2);
246
+ assertEq(nodeNames[0], 'import("./a.f.ts").proof.ok()');
247
+ assertEq(nodeNames[1], 'import("./a.f.ts").proof.throw.a()');
248
+ // Bun/Playwright (star = ' ...'): ... on normal tests, path shows throw for throw-tests
249
+ const [inlineNames] = runner([])(registerModule(noopCtx, './a.f.ts', proof, ' ...'));
250
+ assertEq(inlineNames.length, 2);
251
+ assertEq(inlineNames[0], 'import("./a.f.ts").proof.ok() ...');
252
+ assertEq(inlineNames[1], 'import("./a.f.ts").proof.throw.a()');
253
+ };
218
254
  // direct unit tests for the pure path-format helpers
219
255
  export const helpers = {
220
256
  isInteger: () => {
@@ -297,5 +333,6 @@ export const proof = {
297
333
  defaultReporterOutput,
298
334
  defaultReporterFailOutput,
299
335
  githubReporterOutput,
336
+ registerSuffixes,
300
337
  helpers
301
338
  };
@@ -0,0 +1,6 @@
1
+ export declare const proof: {
2
+ withSubtests: () => Promise<{
3
+ sub1: () => void;
4
+ sub2: () => never;
5
+ }>;
6
+ };
@@ -0,0 +1,9 @@
1
+ export const proof = {
2
+ withSubtests: async () => {
3
+ await new Promise(resolve => setTimeout(resolve, 10));
4
+ return {
5
+ sub1: () => { },
6
+ sub2: () => { throw 'sub-test failure'; },
7
+ };
8
+ }
9
+ };
@@ -0,0 +1,6 @@
1
+ export declare const proof: {
2
+ withSubtests: () => Promise<{
3
+ sub1: () => void;
4
+ sub2: () => void;
5
+ }>;
6
+ };
@@ -0,0 +1,9 @@
1
+ export const proof = {
2
+ withSubtests: async () => {
3
+ await new Promise(resolve => setTimeout(resolve, 10));
4
+ return {
5
+ sub1: () => { },
6
+ sub2: () => { },
7
+ };
8
+ }
9
+ };
@@ -0,0 +1,3 @@
1
+ export declare const proof: {
2
+ sleep_fail: () => Promise<never>;
3
+ };
@@ -0,0 +1,6 @@
1
+ export const proof = {
2
+ sleep_fail: async () => {
3
+ await new Promise(resolve => setTimeout(resolve, 10));
4
+ throw 'async failure';
5
+ }
6
+ };
@@ -0,0 +1,3 @@
1
+ export declare const proof: {
2
+ sleep: () => Promise<void>;
3
+ };
@@ -0,0 +1,5 @@
1
+ export const proof = {
2
+ sleep: async () => {
3
+ await new Promise(resolve => setTimeout(resolve, 10));
4
+ }
5
+ };
@@ -0,0 +1,3 @@
1
+ export declare const proof: {
2
+ failing: () => never;
3
+ };
@@ -0,0 +1,3 @@
1
+ export const proof = {
2
+ failing: () => { throw 'intentional failure'; }
3
+ };
@@ -0,0 +1,3 @@
1
+ export declare const proof: {
2
+ outer: () => unknown;
3
+ };
@@ -0,0 +1,4 @@
1
+ const inner = () => { };
2
+ export const proof = {
3
+ outer: () => ({ inner })
4
+ };
@@ -0,0 +1,5 @@
1
+ export declare const proof: {
2
+ thenableResolves: () => {
3
+ then(resolve: (v: undefined) => void): void;
4
+ };
5
+ };
@@ -4,6 +4,8 @@
4
4
  // must exit 0: the thenable object is walked as a sub-tree whose only key
5
5
  // `then` is a function with parameters, so no leaf tests are found and the
6
6
  // test trivially passes.
7
- export const thenableResolves = () => ({
8
- then(resolve) { resolve(undefined); }
9
- });
7
+ export const proof = {
8
+ thenableResolves: () => ({
9
+ then(resolve) { resolve(undefined); }
10
+ })
11
+ };
@@ -0,0 +1,5 @@
1
+ export declare const proof: {
2
+ shouldPass: () => {
3
+ then: () => string;
4
+ };
5
+ };
@@ -0,0 +1,3 @@
1
+ export const proof = {
2
+ shouldPass: () => ({ then: () => 'ok' })
3
+ };
@@ -0,0 +1,5 @@
1
+ export declare const proof: {
2
+ throw: {
3
+ a: () => never;
4
+ };
5
+ };
@@ -1,3 +1,3 @@
1
- export default {
1
+ export const proof = {
2
2
  throw: { a: () => { throw 'expected'; } }
3
3
  };
@@ -4,10 +4,9 @@
4
4
  * @module
5
5
  */
6
6
  import { compile } from "../djs/module.f.js";
7
- import { main as testMain } from "../dev/tf/module.f.js";
7
+ import { main as testMain } from "../emergent-testing/module.f.js";
8
8
  import { main as casMain } from "../cas/module.f.js";
9
- import { import_, error } from "../types/effects/node/module.f.js";
10
- import { pure } from "../types/effects/module.f.js";
9
+ import { import_, errorExit } from "../types/effects/node/module.f.js";
11
10
  export const main = options => {
12
11
  const [command, ...rest] = options.args;
13
12
  switch (command) {
@@ -31,8 +30,8 @@ export const main = options => {
31
30
  });
32
31
  }
33
32
  case undefined:
34
- return error('Error: command is required').step(() => pure(1));
33
+ return errorExit('Error: command is required');
35
34
  default:
36
- return error(`Error: Unknown command "${command}"`).step(() => pure(1));
35
+ return errorExit(`Error: Unknown command "${command}"`);
37
36
  }
38
37
  };
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { toArray } from "../../types/list/module.f.js";
9
9
  import { length, msb, uint, uintChunkList, unpack, vec } from "../../types/bit_vec/module.f.js";
10
- import { assertEq } from "../../dev/module.f.js";
10
+ import { assertEq } from "../../asserts/module.f.js";
11
11
  import { utf8 } from "../../text/module.f.js";
12
12
  import { secp256r1 } from "../../crypto/secp/module.f.js";
13
13
  import { base32 } from "../../crypto/sha2/module.f.js";
@@ -1,4 +1,4 @@
1
- import { assert, assertEq } from "../../dev/module.f.js";
1
+ import { assert, assertEq } from "../../asserts/module.f.js";
2
2
  import { mask } from "../../types/bigint/module.f.js";
3
3
  import { vec } from "../../types/bit_vec/module.f.js";
4
4
  import { compress, hashId, isHash, isRaw, level3Id, rawId } from "./module.f.js";
@@ -1,4 +1,4 @@
1
- import { assert, assertEq } from "../../../dev/module.f.js";
1
+ import { assert, assertEq } from "../../../asserts/module.f.js";
2
2
  import { compress, level3Id } from "../../id/module.f.js";
3
3
  import { emptyEncodeState, encode } from "./module.f.js";
4
4
  const add = (l, r, m, isSymbol, s) => [...s, [l, r, m, isSymbol]];
package/fs/sul/proof.f.js CHANGED
@@ -1,4 +1,4 @@
1
- import { assert, assertEq } from "../dev/module.f.js";
1
+ import { assert, assertEq } from "../asserts/module.f.js";
2
2
  import { compress } from "./id/module.f.js";
3
3
  import { encode, emptyEncodeState } from "./module.f.js";
4
4
  const run = (bits) => {
@@ -3,15 +3,12 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { map } from "../nullable/module.f.js";
6
+ import { fromUndefined, map } from "../nullable/module.f.js";
7
7
  export const isArray = (value) => value instanceof Array;
8
8
  export const isArray2 = (a) => a.length === 2;
9
9
  const uncheckTail = (a) => a.slice(1);
10
10
  const uncheckHead = (a) => a.slice(0, -1);
11
- export const at = (i) => (a) => {
12
- const r = a[i];
13
- return r === undefined ? null : r;
14
- };
11
+ export const at = (i) => (a) => fromUndefined(a[i]);
15
12
  export const first = at(0);
16
13
  export const last = (a) => at(a.length - 1)(a);
17
14
  export const tail = (a) => a.length === 0 ? null : uncheckTail(a);
@@ -161,6 +161,12 @@ export type Test = readonly ['test', (ctx: TestContext, name: string, expectFail
161
161
  export declare const test: Func<Test>;
162
162
  export type NodeOp = All | Await | Fetch | Fs | Http | Forever | Import | Now | Sandbox | Write | Test;
163
163
  export type NodeEffect<T> = Effect<NodeOp, T>;
164
+ /**
165
+ * Writes an error line to `stderr` and yields exit code `1`. The canonical
166
+ * "fail with a message" program for a `NodeProgram`. For non-`1` exit codes,
167
+ * compose `error(s).step(() => pure(n))` directly.
168
+ */
169
+ export declare const errorExit: (s: string) => Effect<NodeOp, number>;
164
170
  export type NodeOperationMap = ToAsyncOperationMap<NodeOp>;
165
171
  /**
166
172
  * The environment variables.
@@ -8,7 +8,7 @@
8
8
  * @module
9
9
  */
10
10
  import { utf8 } from "../../../text/module.f.js";
11
- import { do_, pure } from "../module.f.js";
11
+ import { begin, do_, pure } from "../module.f.js";
12
12
  const doAll = do_('all');
13
13
  /**
14
14
  * To run the operation `O` should be known by the runner/engine.
@@ -65,3 +65,11 @@ export const sandbox = do_('sandbox');
65
65
  const awaitPromise = do_('await');
66
66
  export const awaitIfPromise = (p) => awaitPromise(p).step(([x]) => pure(x));
67
67
  export const test = do_('test');
68
+ /**
69
+ * Writes an error line to `stderr` and yields exit code `1`. The canonical
70
+ * "fail with a message" program for a `NodeProgram`. For non-`1` exit codes,
71
+ * compose `error(s).step(() => pure(n))` directly.
72
+ */
73
+ export const errorExit = (s) => begin
74
+ .step(() => error(s))
75
+ .step(() => pure(1));
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { assert, todo } from "../../../../dev/module.f.js";
6
+ import { assert, todo } from "../../../../asserts/module.f.js";
7
7
  import { parse } from "../../../../path/module.f.js";
8
8
  import { utf8ToString } from "../../../../text/module.f.js";
9
9
  import { isVec } from "../../../bit_vec/module.f.js";
@@ -18,4 +18,5 @@ export declare const proof: {
18
18
  strictEqual: (() => void)[];
19
19
  isEmpty: (() => void)[];
20
20
  length: () => void;
21
+ filterMap: (() => void)[];
21
22
  };
@@ -389,5 +389,19 @@ export const proof = {
389
389
  throw 6;
390
390
  }
391
391
  },
392
+ filterMap: [
393
+ () => {
394
+ const result = str(filterMap((x) => x % 2 === 0 ? x * 10 : null)([1, 2, 3, 4, 5]));
395
+ if (result !== '[20,40]') {
396
+ throw result;
397
+ }
398
+ },
399
+ () => {
400
+ const result = str(filterMap((x) => x > 3 ? x : null)([1, 2, 3]));
401
+ if (result !== '[]') {
402
+ throw result;
403
+ }
404
+ },
405
+ ],
392
406
  // stress
393
407
  };
@@ -8,3 +8,10 @@ export type Nullable<T> = T | null;
8
8
  export declare const map: <T, R>(f: (value: T) => R) => (value: Nullable<T>) => Nullable<R>;
9
9
  export declare const match: <T, R>(f: (_: T) => R) => (none: () => R) => (_: Nullable<T>) => Nullable<R>;
10
10
  export declare const toOption: <T>(value: Nullable<T>) => Option<T>;
11
+ /**
12
+ * Normalizes a possibly-`undefined` value into the codebase's `null` convention.
13
+ *
14
+ * The boundary rule between JavaScript hosts (which return `undefined` from
15
+ * property/index lookups) and FunctionalScript (which uses `null` for absence).
16
+ */
17
+ export declare const fromUndefined: <T>(value: T | undefined) => Nullable<T>;
@@ -1,3 +1,10 @@
1
1
  export const map = f => value => value === null ? null : f(value);
2
2
  export const match = f => none => value => value === null ? none() : f(value);
3
3
  export const toOption = (value) => value === null ? [] : [value];
4
+ /**
5
+ * Normalizes a possibly-`undefined` value into the codebase's `null` convention.
6
+ *
7
+ * The boundary rule between JavaScript hosts (which return `undefined` from
8
+ * property/index lookups) and FunctionalScript (which uses `null` for absence).
9
+ */
10
+ export const fromUndefined = (value) => value === undefined ? null : value;
@@ -1,4 +1,4 @@
1
- import { map, match, toOption } from "./module.f.js";
1
+ import { fromUndefined, map, match, toOption } from "./module.f.js";
2
2
  export const proof = [
3
3
  () => {
4
4
  const optionSq = map((v) => v * v);
@@ -29,5 +29,22 @@ export const proof = [
29
29
  if (double(null) !== -1) {
30
30
  throw double(null);
31
31
  }
32
- }
32
+ },
33
+ () => {
34
+ if (fromUndefined(undefined) !== null) {
35
+ throw 0;
36
+ }
37
+ if (fromUndefined(5) !== 5) {
38
+ throw 1;
39
+ }
40
+ if (fromUndefined(0) !== 0) {
41
+ throw 2;
42
+ }
43
+ if (fromUndefined(null) !== null) {
44
+ throw 3;
45
+ }
46
+ if (fromUndefined('') !== '') {
47
+ throw 4;
48
+ }
49
+ },
33
50
  ];
@@ -7,12 +7,10 @@
7
7
  */
8
8
  import { isArray } from "../array/module.f.js";
9
9
  import { iterable } from "../list/module.f.js";
10
+ import { fromUndefined, map } from "../nullable/module.f.js";
10
11
  import { entries as mapEntries, fromEntries as mapFromEntries } from "../ordered_map/module.f.js";
11
12
  const { getOwnPropertyDescriptor, fromEntries: objectFromEntries } = Object;
12
- export const at = name => object => {
13
- const r = getOwnPropertyDescriptor(object, name);
14
- return r === undefined ? null : r.value;
15
- };
13
+ export const at = name => object => map((d) => d.value)(fromUndefined(getOwnPropertyDescriptor(object, name)));
16
14
  export const sort = e => mapEntries(mapFromEntries(e));
17
15
  export const fromEntries = e => objectFromEntries(iterable(e));
18
16
  export const fromMap = m => fromEntries(mapEntries(m));
@@ -1,4 +1,4 @@
1
- import { assert } from "../../dev/module.f.js";
1
+ import { assert } from "../../asserts/module.f.js";
2
2
  import { emptyState, patriciaTrie } from "./module.f.js";
3
3
  const combine = (a, b) => a * 1000n + b;
4
4
  const create = (a, b, s) => {
@@ -6,7 +6,6 @@
6
6
  * @module
7
7
  */
8
8
  export type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false;
9
- export type Assert<T extends true> = T;
10
9
  /** Functions for emitting TypeScript type expression strings. */
11
10
  export type Printer = {
12
11
  /** Emits a tuple type: `readonly[A, B]` or `[A, B]` when mutable. */
@@ -0,0 +1,3 @@
1
+ export declare const proof: {
2
+ default: () => void;
3
+ };
@@ -0,0 +1,9 @@
1
+ import defaultExport from "./module.f.js";
2
+ export const proof = {
3
+ default: () => {
4
+ const program = defaultExport();
5
+ if (program === undefined) {
6
+ throw 'expected a program effect';
7
+ }
8
+ }
9
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",
@@ -1,4 +0,0 @@
1
- export declare const withSubtests: () => Promise<{
2
- sub1: () => void;
3
- sub2: () => never;
4
- }>;
@@ -1,7 +0,0 @@
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
- };
@@ -1,4 +0,0 @@
1
- export declare const withSubtests: () => Promise<{
2
- sub1: () => void;
3
- sub2: () => void;
4
- }>;
@@ -1,7 +0,0 @@
1
- export const withSubtests = async () => {
2
- await new Promise(resolve => setTimeout(resolve, 10));
3
- return {
4
- sub1: () => { },
5
- sub2: () => { },
6
- };
7
- };
@@ -1 +0,0 @@
1
- export declare const sleep_fail: () => Promise<never>;
@@ -1,4 +0,0 @@
1
- export const sleep_fail = async () => {
2
- await new Promise(resolve => setTimeout(resolve, 10));
3
- throw 'async failure';
4
- };
@@ -1 +0,0 @@
1
- export declare const sleep: () => Promise<void>;
@@ -1,3 +0,0 @@
1
- export const sleep = async () => {
2
- await new Promise(resolve => setTimeout(resolve, 10));
3
- };
@@ -1 +0,0 @@
1
- export declare const failing: () => never;
@@ -1 +0,0 @@
1
- export const failing = () => { throw 'intentional failure'; };
@@ -1 +0,0 @@
1
- export declare const outer: () => unknown;
@@ -1,2 +0,0 @@
1
- const inner = () => { };
2
- export const outer = () => ({ inner });
@@ -1,3 +0,0 @@
1
- export declare const thenableResolves: () => {
2
- then(resolve: (v: undefined) => void): void;
3
- };
@@ -1,3 +0,0 @@
1
- export declare const shouldPass: () => {
2
- then: () => string;
3
- };
@@ -1,3 +0,0 @@
1
- export const shouldPass = () => ({
2
- then: () => 'ok'
3
- });
@@ -1,6 +0,0 @@
1
- declare const _default: {
2
- throw: {
3
- a: () => never;
4
- };
5
- };
6
- export default _default;
File without changes
File without changes
File without changes