functionalscript 0.16.1 → 0.17.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 (53) hide show
  1. package/fs/asn.1/module.f.js +6 -0
  2. package/fs/ci/bun/module.f.js +6 -0
  3. package/fs/ci/common/module.f.d.ts +7 -0
  4. package/fs/ci/common/module.f.js +7 -0
  5. package/fs/ci/config/module.f.d.ts +7 -0
  6. package/fs/ci/config/module.f.js +7 -0
  7. package/fs/ci/deno/module.f.js +6 -0
  8. package/fs/ci/node/module.f.js +6 -0
  9. package/fs/ci/playwright/module.f.js +6 -0
  10. package/fs/ci/rust/module.f.js +7 -0
  11. package/fs/crypto/secp/module.f.d.ts +7 -0
  12. package/fs/dev/index/module.f.d.ts +6 -0
  13. package/fs/dev/index/module.f.js +6 -0
  14. package/fs/dev/module.f.d.ts +2 -1
  15. package/fs/dev/module.f.js +3 -4
  16. package/fs/dev/tf/module.f.d.ts +3 -19
  17. package/fs/dev/tf/module.f.js +25 -36
  18. package/fs/dev/tf/module.js +4 -3
  19. package/fs/djs/module.f.d.ts +5 -2
  20. package/fs/djs/module.f.js +13 -21
  21. package/fs/djs/test.f.d.ts +11 -0
  22. package/fs/djs/test.f.js +75 -0
  23. package/fs/djs/transpiler/module.f.d.ts +3 -3
  24. package/fs/djs/transpiler/module.f.js +34 -29
  25. package/fs/djs/transpiler/test.f.js +20 -26
  26. package/fs/fjs/module.f.js +10 -4
  27. package/fs/html/module.f.d.ts +6 -0
  28. package/fs/html/module.f.js +6 -0
  29. package/fs/io/module.f.d.ts +5 -2
  30. package/fs/io/module.f.js +13 -3
  31. package/fs/io/module.js +19 -1
  32. package/fs/js/tokenizer/module.f.js +7 -0
  33. package/fs/json/module.f.d.ts +7 -0
  34. package/fs/json/module.f.js +7 -0
  35. package/fs/text/module.f.d.ts +7 -0
  36. package/fs/text/module.f.js +7 -0
  37. package/fs/text/utf16/module.f.d.ts +7 -0
  38. package/fs/text/utf16/module.f.js +7 -0
  39. package/fs/types/effects/node/module.f.d.ts +45 -3
  40. package/fs/types/effects/node/module.f.js +19 -0
  41. package/fs/types/effects/node/test.f.d.ts +4 -0
  42. package/fs/types/effects/node/test.f.js +27 -3
  43. package/fs/types/effects/node/virtual/module.f.d.ts +1 -1
  44. package/fs/types/effects/node/virtual/module.f.js +11 -1
  45. package/fs/types/monoid/module.f.d.ts +7 -0
  46. package/fs/types/number/module.f.d.ts +6 -0
  47. package/fs/types/number/module.f.js +6 -0
  48. package/fs/types/object/module.f.js +7 -0
  49. package/fs/types/prime_field/module.f.d.ts +8 -0
  50. package/fs/types/ts/module.f.d.ts +7 -0
  51. package/package.json +3 -2
  52. package/fs/io/virtual/module.f.d.ts +0 -8
  53. package/fs/io/virtual/module.f.js +0 -43
@@ -1,3 +1,9 @@
1
+ /**
2
+ * ASN.1 BER/DER encoding and decoding over bit vectors. Includes tag/class
3
+ * helpers, length-prefixed payloads, and OID conversion via Base-128.
4
+ *
5
+ * @module
6
+ */
1
7
  import { bitLength, max } from "../types/bigint/module.f.js";
2
8
  import { empty, isVec, length, listToVec, msb, uint, unpack, vec, vec8 } from "../types/bit_vec/module.f.js";
3
9
  import { identity } from "../types/function/module.f.js";
@@ -1,3 +1,9 @@
1
+ /**
2
+ * CI step builder for Bun: installs the pinned Bun version (with a PowerShell
3
+ * fallback for Windows ARM) and runs `bun install` and `bun test`.
4
+ *
5
+ * @module
6
+ */
1
7
  import { bun } from "../config/module.f.js";
2
8
  import { clean, install, test } from "../common/module.f.js";
3
9
  const installOnWindowsArm = ({ def, name, path }) => (v) => (a) => install(v === 'windows' && a === 'arm'
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Shared CI types and helpers: GitHub Actions step/job RTTI schemas, the
3
+ * `MetaStep` representation used by tool-specific modules, and assemblers like
4
+ * `install`, `test`, `clean`, `ubuntu`, and `toSteps`.
5
+ *
6
+ * @module
7
+ */
1
8
  import { images } from '../config/module.f.ts';
2
9
  import { type Ts } from '../../types/rtti/ts/module.f.ts';
3
10
  export declare const os: readonly ["ubuntu", "macos", "windows"];
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Shared CI types and helpers: GitHub Actions step/job RTTI schemas, the
3
+ * `MetaStep` representation used by tool-specific modules, and assemblers like
4
+ * `install`, `test`, `clean`, `ubuntu`, and `toSteps`.
5
+ *
6
+ * @module
7
+ */
1
8
  import { images, rust } from "../config/module.f.js";
2
9
  import { option, array, record, string } from "../../types/rtti/module.f.js";
3
10
  import {} from "../../types/rtti/ts/module.f.js";
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Centralized version pins and OS images used by the CI generator: runner
3
+ * images, tool versions (Bun, Deno, Playwright, Rust, Node, Wasmtime, Wasmer,
4
+ * TSGO).
5
+ *
6
+ * @module
7
+ */
1
8
  export declare const images: {
2
9
  readonly ubuntu: {
3
10
  readonly intel: "ubuntu-24.04";
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Centralized version pins and OS images used by the CI generator: runner
3
+ * images, tool versions (Bun, Deno, Playwright, Rust, Node, Wasmtime, Wasmer,
4
+ * TSGO).
5
+ *
6
+ * @module
7
+ */
1
8
  // https://docs.github.com/en/actions/reference/runners/github-hosted-runners#standard-github-hosted-runners-for-public-repositories
2
9
  export const images = {
3
10
  ubuntu: {
@@ -1,3 +1,9 @@
1
+ /**
2
+ * CI step builder for Deno: installs the pinned Deno version and runs
3
+ * `deno install` followed by `deno task test`.
4
+ *
5
+ * @module
6
+ */
1
7
  import { deno } from "../config/module.f.js";
2
8
  import { clean, install, test } from "../common/module.f.js";
3
9
  export const denoSteps = (extra) => clean([
@@ -1,3 +1,9 @@
1
+ /**
2
+ * CI step builders for Node.js: setup-node installation, common npm
3
+ * install/test sequences, per-version job matrices, and the main TSGO step.
4
+ *
5
+ * @module
6
+ */
1
7
  import { node, tsgo } from "../config/module.f.js";
2
8
  import { clean, findTgz, install, test, ubuntu } from "../common/module.f.js";
3
9
  export const major = (v) => v.split('.')[0];
@@ -1,3 +1,9 @@
1
+ /**
2
+ * CI job that installs Playwright (with a browser-cache step) and runs the
3
+ * test suite against Chromium, Firefox, and WebKit.
4
+ *
5
+ * @module
6
+ */
1
7
  import { images, node, playwright } from "../config/module.f.js";
2
8
  import { install, test, toSteps } from "../common/module.f.js";
3
9
  import { basicNode } from "../node/module.f.js";
@@ -1,3 +1,10 @@
1
+ /**
2
+ * CI step builder for the Rust crate: `cargo fmt`, `cargo clippy`, native and
3
+ * `--release` test runs, plus matrix entries for WASM targets (Wasmtime,
4
+ * Wasmer) and the 32-bit `i686` targets.
5
+ *
6
+ * @module
7
+ */
1
8
  import { wasmer, wasmtime } from "../config/module.f.js";
2
9
  import { install, test } from "../common/module.f.js";
3
10
  const cargoTest = (target, config) => {
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Short Weierstrass elliptic-curve arithmetic over a prime field: `curve`
3
+ * builds point negation, addition, and scalar multiplication for any
4
+ * `secp`-family curve from its `(p, a, g, n)` parameters.
5
+ *
6
+ * @module
7
+ */
1
8
  import type { Equal, Fold, Reduce } from '../../types/function/operator/module.f.ts';
2
9
  import { type PrimeField } from '../../types/prime_field/module.f.ts';
3
10
  /**
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Entry point for the `index` developer program: re-exports `index4` from the
3
+ * parent `dev` module as the default `NodeProgram`.
4
+ *
5
+ * @module
6
+ */
1
7
  import { index4 } from '../module.f.ts';
2
8
  export default index4;
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Entry point for the `index` developer program: re-exports `index4` from the
3
+ * parent `dev` module as the default `NodeProgram`.
4
+ *
5
+ * @module
6
+ */
1
7
  import { index4 } from "../module.f.js";
2
8
  export default index4;
@@ -4,7 +4,7 @@
4
4
  * @module
5
5
  */
6
6
  import { type Io } from '../io/module.f.ts';
7
- import { type All, type NodeProgram, type Readdir } from '../types/effects/node/module.f.ts';
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
9
  export declare const todo: () => never;
10
10
  export declare const assert: (v: boolean, msg?: unknown) => asserts v;
@@ -18,5 +18,6 @@ export type ModuleMap = {
18
18
  };
19
19
  export declare const env: (io: Io) => (v: string) => string | undefined;
20
20
  export declare const allFiles: (s: string) => Effect<Readdir | All, readonly string[]>;
21
+ export declare const loadModuleMap2: (env: Env) => Effect<Access | Import | All | Readdir, ModuleMap>;
21
22
  export declare const loadModuleMap: (io: Io) => Promise<ModuleMap>;
22
23
  export declare const index4: NodeProgram;
@@ -63,15 +63,14 @@ const loadFile = (f) => {
63
63
  }
64
64
  return pure([]);
65
65
  };
66
- export const loadModuleMap = async (io) => {
67
- const { process: { env } } = io;
66
+ export const loadModuleMap2 = (env) => {
68
67
  const initCwd = env['INIT_CWD'];
69
68
  const s = initCwd === undefined ? '.' : `${initCwd.replaceAll('\\', '/')}`;
70
- const effect = allFiles(s)
69
+ return allFiles(s)
71
70
  .step(files => all(...files.map(loadFile)))
72
71
  .step(entries => pure(Object.fromEntries(entries.flat().toSorted(cmp))));
73
- return fromIo(io)(effect);
74
72
  };
73
+ export const loadModuleMap = async (io) => fromIo(io)(loadModuleMap2(io.process.env));
75
74
  const denoJson = './deno.json';
76
75
  const parseDenoJson = rttiParse(record(rttiUnknown));
77
76
  const index2 = begin
@@ -1,24 +1,8 @@
1
- import { type CsiConsole } from '../../text/sgr/module.f.ts';
2
- import type * as Result from '../../types/result/module.f.ts';
3
- import type { Io, Performance, TryCatch } from '../../io/module.f.ts';
4
- import { type ModuleMap } from '../module.f.ts';
5
- type Log<T> = CsiConsole;
6
- type Measure<T> = <R>(f: () => R) => (state: T) => readonly [R, number, T];
7
- type Input<T> = {
8
- readonly moduleMap: ModuleMap;
9
- readonly log: Log<T>;
10
- readonly error: Log<T>;
11
- readonly measure: Measure<T>;
12
- readonly state: T;
13
- readonly tryCatch: <R>(f: () => R) => Result.Result<R, unknown>;
14
- readonly env: (n: string) => string | undefined;
15
- };
1
+ import type { Io } from '../../io/module.f.ts';
2
+ import type { SandboxResult } from '../../types/effects/node/module.f.ts';
16
3
  export declare const isTest: (s: string) => boolean;
17
4
  export type Test = () => unknown;
18
5
  export type TestSet = Test | readonly (readonly [string, unknown])[];
19
- export declare const parseTestSet: (t: TryCatch) => (throws: boolean) => (x: unknown) => TestSet;
20
- export declare const test: <T>(input: Input<T>) => readonly [number, T];
6
+ export declare const parseTestSet: (sandbox: <R>(f: () => R) => SandboxResult<R>) => (throws: boolean) => (x: unknown) => TestSet;
21
7
  export declare const anyLog: (f: (s: string) => void) => (s: string) => <T>(state: T) => T;
22
- export declare const measure: (p: Performance) => <R>(f: () => R) => <T>(state: T) => readonly [R, number, T];
23
8
  export declare const main: (io: Io) => Promise<number>;
24
- export {};
@@ -18,7 +18,7 @@ const timeFormat = (a) => {
18
18
  const e = x.substring(s);
19
19
  return `${b}.${e} ms`;
20
20
  };
21
- export const parseTestSet = (t) => (throws) => (x) => {
21
+ export const parseTestSet = (sandbox) => (throws) => (x) => {
22
22
  switch (typeof x) {
23
23
  case 'function': {
24
24
  if (x.length === 0) {
@@ -30,7 +30,7 @@ export const parseTestSet = (t) => (throws) => (x) => {
30
30
  // enclosing tree node is named 'throw' (so any function reference
31
31
  // works, not only inline ones whose inferred name is 'throw').
32
32
  return () => {
33
- const [tag, value] = t(xt);
33
+ const { result: [tag, value] } = sandbox(xt);
34
34
  if (tag === 'ok') {
35
35
  throw value;
36
36
  }
@@ -48,17 +48,20 @@ export const parseTestSet = (t) => (throws) => (x) => {
48
48
  }
49
49
  return [];
50
50
  };
51
- export const test = (input) => {
52
- let { moduleMap, log, error, measure, tryCatch, env, state } = input;
53
- const isGitHub = env('GITHUB_ACTION') !== undefined;
54
- const parse = parseTestSet(tryCatch);
51
+ const test = async (io) => {
52
+ const moduleMap = await loadModuleMap(io);
53
+ const log = stdio(io);
54
+ const error = stderr(io);
55
+ const { sandbox } = io;
56
+ const env_ = env(io);
57
+ const isGitHub = env_('GITHUB_ACTION') !== undefined;
58
+ const parse = parseTestSet(sandbox);
55
59
  const f = ([k, v]) => {
56
- const test = i => throws => v => ([ts, state]) => {
60
+ const test = i => throws => v => ts => {
57
61
  const next = test(`${i}| `);
58
62
  const set = parse(throws)(v);
59
63
  if (typeof set === 'function') {
60
- const [[s, r], delta, state0] = measure(() => tryCatch(set))(state);
61
- state = state0;
64
+ const { result: [s, r], duration: delta } = sandbox(set);
62
65
  if (s !== 'ok') {
63
66
  ts = addFail(delta)(ts);
64
67
  if (isGitHub) {
@@ -76,23 +79,23 @@ export const test = (input) => {
76
79
  log(`${i}() ${fgGreen}ok${reset}, ${timeFormat(delta)}`);
77
80
  // The result of a function is walked as a fresh sub-tree;
78
81
  // the parent's `throws` flag does not propagate into it.
79
- [ts, state] = next(false)(r)([ts, state]);
82
+ ts = next(false)(r)(ts);
80
83
  }
81
84
  }
82
85
  else {
83
- const f = ([k, v]) => ([time, state]) => {
86
+ const f = ([k, v]) => ts => {
84
87
  log(`${i}${k}:`);
85
- [time, state] = next(throws || k === 'throw')(v)([time, state]);
86
- return [time, state];
88
+ ts = next(throws || k === 'throw')(v)(ts);
89
+ return ts;
87
90
  };
88
- [ts, state] = fold(f)([ts, state])(set);
91
+ ts = fold(f)(ts)(set);
89
92
  }
90
- return [ts, state];
93
+ return ts;
91
94
  };
92
- return ([ts, state]) => {
95
+ return ts => {
93
96
  if (isTest(k)) {
94
97
  log(`testing ${k}`);
95
- [ts, state] = test('| ')(false)(v.default)([ts, state]);
98
+ ts = test('| ')(false)(v.default)(ts);
96
99
  // Non-default exports are walked as a sibling test group so
97
100
  // a test file can spread its tests across multiple named
98
101
  // exports (see issue 27 in `issues/README.md`). Skip exports
@@ -101,35 +104,21 @@ export const test = (input) => {
101
104
  const others = Object.fromEntries(Object.entries(v).filter(([key, val]) => key !== 'default' && ((typeof val === 'function' && val.length === 0) ||
102
105
  (typeof val === 'object' && val !== null))));
103
106
  if (Object.keys(others).length !== 0) {
104
- [ts, state] = test('| ')(false)(others)([ts, state]);
107
+ ts = test('| ')(false)(others)(ts);
105
108
  }
106
109
  }
107
- return [ts, state];
110
+ return ts;
108
111
  };
109
112
  };
110
113
  let ts = { time: 0, pass: 0, fail: 0 };
111
- [ts, state] = fold(f)([ts, state])(Object.entries(moduleMap));
114
+ ts = fold(f)(ts)(Object.entries(moduleMap));
112
115
  const fgFail = ts.fail === 0 ? fgGreen : fgRed;
113
116
  log(`${bold}Number of tests: pass: ${fgGreen}${ts.pass}${reset}${bold}, fail: ${fgFail}${ts.fail}${reset}${bold}, total: ${ts.pass + ts.fail}${reset}`);
114
117
  log(`${bold}Time: ${timeFormat(ts.time)}${reset}`);
115
- return [ts.fail !== 0 ? 1 : 0, state];
118
+ return ts.fail !== 0 ? 1 : 0;
116
119
  };
117
120
  export const anyLog = (f) => (s) => (state) => {
118
121
  f(s);
119
122
  return state;
120
123
  };
121
- export const measure = (p) => (f) => (state) => {
122
- const b = p.now();
123
- const r = f();
124
- const e = p.now();
125
- return [r, e - b, state];
126
- };
127
- export const main = async (io) => test({
128
- moduleMap: await loadModuleMap(io),
129
- log: stdio(io), // anyLog(io.console.log),
130
- error: stderr(io), // anyLog(io.console.error),
131
- measure: measure(io.performance),
132
- tryCatch: io.tryCatch,
133
- env: env(io),
134
- state: undefined,
135
- })[0];
124
+ export const main = test;
@@ -1,8 +1,9 @@
1
1
  import { io } from "../../io/module.js";
2
- import { loadModuleMap } from "../module.f.js";
2
+ import { loadModuleMap2 } from "../module.f.js";
3
3
  import { isTest, parseTestSet } from "./module.f.js";
4
4
  import * as nodeTest from 'node:test';
5
5
  import { asyncImport } from "../../io/module.js";
6
+ import { fromIo } from "../../io/module.f.js";
6
7
  const isBun = typeof Bun !== 'undefined';
7
8
  const isPlaywright = typeof process !== 'undefined' && process?.env?.PLAYWRIGHT_TEST !== undefined;
8
9
  const createFramework = (fw) => (prefix, f) => fw.test(prefix, t => f((name, v) => t.test(name, v)));
@@ -15,7 +16,7 @@ const createPlaywrightFramework = async () => {
15
16
  const framework = isPlaywright ? await createPlaywrightFramework() :
16
17
  isBun ? createBunFramework(nodeTest) :
17
18
  createFramework(nodeTest);
18
- const parse = parseTestSet(io.tryCatch);
19
+ const parse = parseTestSet(io.sandbox);
19
20
  const scanModule = (x) => async (subTestRunner) => {
20
21
  let subTests = [x];
21
22
  while (true) {
@@ -44,7 +45,7 @@ const scanModule = (x) => async (subTestRunner) => {
44
45
  }
45
46
  };
46
47
  export const run = async () => {
47
- const x = await loadModuleMap(io);
48
+ const x = await fromIo(io)(loadModuleMap2(io.process.env));
48
49
  for (const [i, v] of Object.entries(x)) {
49
50
  if (isTest(i)) {
50
51
  framework(i, scanModule(['', v.default, false]));
@@ -3,12 +3,15 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import type { Io } from '../io/module.f.ts';
7
6
  import type { Primitive as JsonPrimitive } from '../json/module.f.ts';
7
+ import { type Effect } from '../types/effects/module.f.ts';
8
+ import { type Error, type WriteFile, type ReadFile } from '../types/effects/node/module.f.ts';
8
9
  export type Object = {
9
10
  readonly [k in string]: Unknown;
10
11
  };
11
12
  export type Array = readonly Unknown[];
12
13
  export type Primitive = JsonPrimitive | bigint | undefined;
13
14
  export type Unknown = Primitive | Object | Array;
14
- export declare const compile: ({ console: { error }, fs }: Io) => (args: readonly string[]) => Promise<number>;
15
+ type CompileOp = ReadFile | WriteFile | Error;
16
+ export declare const compile: (args: readonly string[]) => Effect<CompileOp, number>;
17
+ export {};
@@ -1,31 +1,23 @@
1
1
  import { transpile } from "./transpiler/module.f.js";
2
2
  import { stringify, stringifyAsTree } from "./serializer/module.f.js";
3
3
  import { sort } from "../types/object/module.f.js";
4
- import { encodeUtf8 } from "../types/uint8array/module.f.js";
5
- export const compile = ({ console: { error }, fs }) => (args) => {
4
+ import { encodeUtf8, toVec } from "../types/uint8array/module.f.js";
5
+ import { pure } from "../types/effects/module.f.js";
6
+ import { error, writeFile, } from "../types/effects/node/module.f.js";
7
+ export const compile = args => {
6
8
  if (args.length < 2) {
7
- error('Error: Requires 2 or more arguments');
8
- return Promise.resolve(1);
9
+ return error('Error: Requires 2 or more arguments').step(() => pure(1));
9
10
  }
10
11
  const inputFileName = args[0];
11
12
  const outputFileName = args[1];
12
- const result = transpile(fs)(inputFileName);
13
- switch (result[0]) {
14
- case 'ok': {
15
- if (outputFileName.endsWith('.json')) {
16
- const output = stringifyAsTree(sort)(result[1]);
17
- fs.writeFileSync(outputFileName, encodeUtf8(output));
18
- break;
19
- }
20
- const output = stringify(sort)(result[1]);
21
- fs.writeFileSync(outputFileName, encodeUtf8(output));
22
- break;
23
- }
24
- case 'error': {
13
+ return transpile(inputFileName).step((result) => {
14
+ if (result[0] === 'error') {
25
15
  const metadata = result[1].metadata;
26
- error(`${metadata?.path}:${metadata?.line}:${metadata?.column} - error: ${result[1].message}`);
27
- break;
16
+ return error(`${metadata?.path}:${metadata?.line}:${metadata?.column} - error: ${result[1].message}`).step(() => pure(0));
28
17
  }
29
- }
30
- return Promise.resolve(0);
18
+ const content = outputFileName.endsWith('.json')
19
+ ? stringifyAsTree(sort)(result[1])
20
+ : stringify(sort)(result[1]);
21
+ return writeFile(outputFileName, toVec(encodeUtf8(content))).step(() => pure(0));
22
+ });
31
23
  };
@@ -0,0 +1,11 @@
1
+ declare const _default: {
2
+ tooFewArgs: {
3
+ noArgs: () => void;
4
+ oneArg: () => void;
5
+ };
6
+ success: () => void;
7
+ jsonOutput: () => void;
8
+ fileNotFound: () => void;
9
+ parseError: () => void;
10
+ };
11
+ export default _default;
@@ -0,0 +1,75 @@
1
+ import { compile } from "./module.f.js";
2
+ import { virtual, emptyState } from "../types/effects/node/virtual/module.f.js";
3
+ import { encodeUtf8, toVec, fromVec, decodeUtf8 } from "../types/uint8array/module.f.js";
4
+ import { isVec } from "../types/bit_vec/module.f.js";
5
+ const fileVec = (s) => toVec(encodeUtf8(s));
6
+ const readOutput = (root, path) => {
7
+ const file = root[path];
8
+ if (!isVec(file)) {
9
+ throw `${path} is not a file`;
10
+ }
11
+ return decodeUtf8(fromVec(file));
12
+ };
13
+ export default {
14
+ tooFewArgs: {
15
+ noArgs: () => {
16
+ const [state, code] = virtual(emptyState)(compile([]));
17
+ if (code !== 1) {
18
+ throw code;
19
+ }
20
+ if (!state.stderr.includes('Requires 2 or more arguments')) {
21
+ throw state.stderr;
22
+ }
23
+ },
24
+ oneArg: () => {
25
+ const [state, code] = virtual(emptyState)(compile(['input.f.js']));
26
+ if (code !== 1) {
27
+ throw code;
28
+ }
29
+ if (!state.stderr.includes('Requires 2 or more arguments')) {
30
+ throw state.stderr;
31
+ }
32
+ },
33
+ },
34
+ success: () => {
35
+ const root = { 'input.f.js': fileVec('export default 42') };
36
+ const [state, code] = virtual({ ...emptyState, root })(compile(['input.f.js', 'output.f.js']));
37
+ if (code !== 0) {
38
+ throw code;
39
+ }
40
+ const content = readOutput(state.root, 'output.f.js');
41
+ if (content !== 'export default 42') {
42
+ throw content;
43
+ }
44
+ },
45
+ jsonOutput: () => {
46
+ const root = { 'input.f.js': fileVec('export default 42') };
47
+ const [state, code] = virtual({ ...emptyState, root })(compile(['input.f.js', 'output.json']));
48
+ if (code !== 0) {
49
+ throw code;
50
+ }
51
+ const content = readOutput(state.root, 'output.json');
52
+ if (content !== '42') {
53
+ throw content;
54
+ }
55
+ },
56
+ fileNotFound: () => {
57
+ const [state, code] = virtual(emptyState)(compile(['missing.f.js', 'output.f.js']));
58
+ if (code !== 0) {
59
+ throw code;
60
+ }
61
+ if (!state.stderr.includes('file not found')) {
62
+ throw state.stderr;
63
+ }
64
+ },
65
+ parseError: () => {
66
+ const root = { 'bad.f.js': fileVec('export default @') };
67
+ const [state, code] = virtual({ ...emptyState, root })(compile(['bad.f.js', 'output.f.js']));
68
+ if (code !== 0) {
69
+ throw code;
70
+ }
71
+ if (state.stderr === '') {
72
+ throw 'expected error output';
73
+ }
74
+ },
75
+ };
@@ -7,10 +7,10 @@ import { type Unknown } from '../module.f.ts';
7
7
  import { type Result } from '../../types/result/module.f.ts';
8
8
  import { type List } from '../../types/list/module.f.ts';
9
9
  import { type OrderedMap } from '../../types/ordered_map/module.f.ts';
10
- import type { Fs } from '../../io/module.f.ts';
11
10
  import { type ParseError } from '../parser/module.f.ts';
11
+ import { type Effect } from '../../types/effects/module.f.ts';
12
+ import { type ReadFile } from '../../types/effects/node/module.f.ts';
12
13
  export type ParseContext = {
13
- readonly fs: Fs;
14
14
  readonly complete: OrderedMap<djsResult>;
15
15
  readonly stack: List<string>;
16
16
  readonly error: ParseError | null;
@@ -18,4 +18,4 @@ export type ParseContext = {
18
18
  export type djsResult = {
19
19
  djs: Unknown;
20
20
  };
21
- export declare const transpile: (fs: Fs) => (path: string) => Result<Unknown, ParseError>;
21
+ export declare const transpile: (path: string) => Effect<ReadFile, Result<Unknown, ParseError>>;
@@ -5,15 +5,16 @@
5
5
  */
6
6
  import {} from "../module.f.js";
7
7
  import { error, ok } from "../../types/result/module.f.js";
8
- import { fold, drop, map as listMap, toArray, includes } from "../../types/list/module.f.js";
9
- import {} from "../../types/function/operator/module.f.js";
8
+ import { drop, map as listMap, toArray, includes } from "../../types/list/module.f.js";
10
9
  import { tokenize } from "../tokenizer/module.f.js";
11
10
  import { setReplace, at } from "../../types/ordered_map/module.f.js";
12
11
  import { stringToList } from "../../text/utf16/module.f.js";
13
12
  import { concat as pathConcat } from "../../path/module.f.js";
14
13
  import { parseFromTokens } from "../parser/module.f.js";
15
14
  import { run } from "../ast/module.f.js";
16
- import { decodeUtf8 } from "../../types/uint8array/module.f.js";
15
+ import { decodeUtf8, fromVec } from "../../types/uint8array/module.f.js";
16
+ import { pure } from "../../types/effects/module.f.js";
17
+ import { readFile } from "../../types/effects/node/module.f.js";
17
18
  const mapDjs = context => path => {
18
19
  const res = at(path)(context.complete);
19
20
  if (res === null) {
@@ -21,46 +22,50 @@ const mapDjs = context => path => {
21
22
  }
22
23
  return res.djs;
23
24
  };
25
+ const parseModule = path => readFile(path).step(result => {
26
+ if (result[0] === 'error') {
27
+ return pure(error({ message: 'file not found', metadata: null }));
28
+ }
29
+ const tokens = tokenize(stringToList(decodeUtf8(fromVec(result[1]))))(path);
30
+ return pure(parseFromTokens(tokens));
31
+ });
24
32
  const transpileWithImports = path => parseModuleResult => context => {
25
33
  if (parseModuleResult[0] === 'ok') {
26
34
  const dir = pathConcat(path)('..');
27
35
  const pathsCombine = listMap(pathConcat(dir))(parseModuleResult[1][0]);
28
- const contextWithImports = fold(foldNextModuleOp)({ ...context, stack: { first: path, tail: context.stack } })(pathsCombine);
29
- if (contextWithImports.error !== null) {
30
- return contextWithImports;
31
- }
32
- const args = toArray(listMap(mapDjs(contextWithImports))(pathsCombine));
33
- const djs = { djs: run(parseModuleResult[1][1])(args) };
34
- return { ...contextWithImports, stack: drop(1)(contextWithImports.stack), complete: setReplace(path)(djs)(contextWithImports.complete) };
35
- }
36
- return { ...context, error: parseModuleResult[1] };
37
- };
38
- const parseModule = path => context => {
39
- const content = context.fs.readFileSync(path);
40
- if (content === null) {
41
- return error({ message: 'file not found', metadata: null });
36
+ const pathsArray = toArray(pathsCombine);
37
+ const contextWithStack = { ...context, stack: { first: path, tail: context.stack } };
38
+ return pathsArray.reduce((acc, p) => acc.step(ctx => foldNextModuleOp(p)(ctx)), pure(contextWithStack)).step(contextWithImports => {
39
+ if (contextWithImports.error !== null) {
40
+ return pure(contextWithImports);
41
+ }
42
+ const args = toArray(listMap(mapDjs(contextWithImports))(pathsCombine));
43
+ const djs = { djs: run(parseModuleResult[1][1])(args) };
44
+ return pure({
45
+ ...contextWithImports,
46
+ stack: drop(1)(contextWithImports.stack),
47
+ complete: setReplace(path)(djs)(contextWithImports.complete),
48
+ });
49
+ });
42
50
  }
43
- const tokens = tokenize(stringToList(decodeUtf8(content)))(path);
44
- return parseFromTokens(tokens);
51
+ return pure({ ...context, error: parseModuleResult[1] });
45
52
  };
46
53
  const foldNextModuleOp = path => context => {
47
54
  if (context.error !== null) {
48
- return context;
55
+ return pure(context);
49
56
  }
50
57
  if (includes(path)(context.stack)) {
51
- return { ...context, error: { message: 'circular dependency', metadata: null } };
58
+ return pure({ ...context, error: { message: 'circular dependency', metadata: null } });
52
59
  }
53
60
  if (at(path)(context.complete) !== null) {
54
- return context;
61
+ return pure(context);
55
62
  }
56
- const parseModuleResult = parseModule(path)(context);
57
- return transpileWithImports(path)(parseModuleResult)(context);
63
+ return parseModule(path).step(parseModuleResult => transpileWithImports(path)(parseModuleResult)(context));
58
64
  };
59
- export const transpile = fs => path => {
60
- const context = foldNextModuleOp(path)({ fs, stack: null, complete: null, error: null });
65
+ export const transpile = path => foldNextModuleOp(path)({ stack: null, complete: null, error: null }).step((context) => {
61
66
  if (context.error !== null) {
62
- return error(context.error);
67
+ return pure(error(context.error));
63
68
  }
64
69
  const result = at(path)(context.complete)?.djs;
65
- return ok(result);
66
- };
70
+ return pure(ok(result));
71
+ });