functionalscript 0.16.1 → 0.18.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 (88) 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 +11 -5
  4. package/fs/ci/common/module.f.js +11 -4
  5. package/fs/ci/config/module.f.d.ts +11 -4
  6. package/fs/ci/config/module.f.js +11 -4
  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/ci/test.f.js +2 -4
  12. package/fs/crypto/secp/module.f.d.ts +7 -0
  13. package/fs/dev/index/module.f.d.ts +6 -0
  14. package/fs/dev/index/module.f.js +6 -0
  15. package/fs/dev/module.f.d.ts +4 -3
  16. package/fs/dev/module.f.js +13 -11
  17. package/fs/dev/tf/module.d.ts +1 -0
  18. package/fs/dev/tf/module.f.d.ts +70 -22
  19. package/fs/dev/tf/module.f.js +134 -97
  20. package/fs/dev/tf/module.js +66 -16
  21. package/fs/dev/tf/test.f.d.ts +21 -20
  22. package/fs/dev/tf/test.f.js +249 -31
  23. package/fs/djs/module.f.d.ts +5 -2
  24. package/fs/djs/module.f.js +16 -21
  25. package/fs/djs/test.f.d.ts +11 -0
  26. package/fs/djs/test.f.js +74 -0
  27. package/fs/djs/tokenizer-new/test.f.js +126 -78
  28. package/fs/djs/transpiler/module.f.d.ts +3 -3
  29. package/fs/djs/transpiler/module.f.js +34 -29
  30. package/fs/djs/transpiler/test.f.js +19 -26
  31. package/fs/fjs/module.f.d.ts +2 -7
  32. package/fs/fjs/module.f.js +17 -17
  33. package/fs/fjs/module.js +2 -2
  34. package/fs/html/module.f.d.ts +6 -0
  35. package/fs/html/module.f.js +6 -0
  36. package/fs/io/module.d.ts +3 -3
  37. package/fs/io/module.f.d.ts +8 -2
  38. package/fs/io/module.f.js +25 -4
  39. package/fs/io/module.js +54 -15
  40. package/fs/js/tokenizer/module.f.js +7 -0
  41. package/fs/json/module.f.d.ts +7 -0
  42. package/fs/json/module.f.js +7 -0
  43. package/fs/path/module.f.d.ts +6 -0
  44. package/fs/path/module.f.js +6 -0
  45. package/fs/path/test.f.d.ts +3 -5
  46. package/fs/path/test.f.js +67 -49
  47. package/fs/text/module.f.d.ts +7 -0
  48. package/fs/text/module.f.js +7 -0
  49. package/fs/text/sgr/module.f.d.ts +9 -1
  50. package/fs/text/sgr/module.f.js +16 -5
  51. package/fs/text/utf16/module.f.d.ts +7 -0
  52. package/fs/text/utf16/module.f.js +7 -0
  53. package/fs/types/effects/node/module.f.d.ts +51 -9
  54. package/fs/types/effects/node/module.f.js +39 -2
  55. package/fs/types/effects/node/test.f.d.ts +4 -0
  56. package/fs/types/effects/node/test.f.js +33 -6
  57. package/fs/types/effects/node/virtual/module.f.d.ts +12 -3
  58. package/fs/types/effects/node/virtual/module.f.js +32 -9
  59. package/fs/types/function/compare/module.f.d.ts +12 -0
  60. package/fs/types/function/compare/module.f.js +33 -0
  61. package/fs/types/function/operator/test.f.d.ts +10 -0
  62. package/fs/types/function/operator/test.f.js +81 -0
  63. package/fs/types/monoid/module.f.d.ts +7 -0
  64. package/fs/types/number/module.f.d.ts +6 -0
  65. package/fs/types/number/module.f.js +6 -0
  66. package/fs/types/object/module.f.js +7 -0
  67. package/fs/types/prime_field/module.f.d.ts +8 -0
  68. package/fs/types/range_map/module.f.js +3 -18
  69. package/fs/types/result/module.f.d.ts +4 -0
  70. package/fs/types/result/module.f.js +4 -0
  71. package/fs/types/result/test.f.d.ts +2 -4
  72. package/fs/types/result/test.f.js +24 -16
  73. package/fs/types/rtti/common/module.f.d.ts +10 -1
  74. package/fs/types/rtti/common/module.f.js +7 -2
  75. package/fs/types/rtti/parse/module.f.js +35 -46
  76. package/fs/types/rtti/validate/module.f.js +9 -12
  77. package/fs/types/sorted_list/module.f.d.ts +1 -2
  78. package/fs/types/sorted_list/module.f.js +8 -21
  79. package/fs/types/sorted_set/module.f.d.ts +1 -3
  80. package/fs/types/ts/module.f.d.ts +7 -0
  81. package/fs/types/ts/test.f.d.ts +18 -0
  82. package/fs/types/ts/test.f.js +111 -0
  83. package/fs/types/uint8array/module.f.js +7 -1
  84. package/fs/types/uint8array/test.f.d.ts +1 -0
  85. package/fs/types/uint8array/test.f.js +5 -1
  86. package/package.json +3 -2
  87. package/fs/io/virtual/module.f.d.ts +0 -8
  88. package/fs/io/virtual/module.f.js +0 -43
@@ -1,3 +1,9 @@
1
+ /**
2
+ * HTML serialization helpers: `Element`/`Attributes` builders, void-tag
3
+ * handling, attribute/text escaping, and a UTF-8 emitter for full documents.
4
+ *
5
+ * @module
6
+ */
1
7
  import { map, flatMap, flat, concat as listConcat } from "../types/list/module.f.js";
2
8
  import { concat, concat as stringConcat } from "../types/string/module.f.js";
3
9
  import { compose } from "../types/function/module.f.js";
package/fs/io/module.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { type Io, type Run } from './module.f.ts';
1
+ import { type Io } from './module.f.ts';
2
2
  import type { Module, NodeProgram } from '../types/effects/node/module.f.ts';
3
+ import { type Result } from '../types/result/module.f.ts';
3
4
  export declare const asyncImport: (v: string) => Promise<Module>;
5
+ export declare const tryCatch: <T>(f: () => T) => Result<T, unknown>;
4
6
  export declare const io: Io;
5
- export declare const legacyRun: Run;
6
7
  export type NodeRun = (p: NodeProgram) => Promise<number>;
7
- export declare const ioRun: (io: Io) => NodeRun;
8
8
  declare const effectRun: NodeRun;
9
9
  export default effectRun;
@@ -1,5 +1,6 @@
1
1
  import { type Effect } from '../types/effects/module.f.ts';
2
- import type { Headers, Module, NodeOp, Env } from '../types/effects/node/module.f.ts';
2
+ import type { Headers, Module, NodeOp, Env, SandboxResult, NodeProgram, WriteConsoles } from '../types/effects/node/module.f.ts';
3
+ import type { Vec } from '../types/bit_vec/module.f.ts';
3
4
  import { type Result } from '../types/result/module.f.ts';
4
5
  /**
5
6
  * Represents a directory entry (file or directory) in the filesystem
@@ -73,6 +74,7 @@ export type Process = {
73
74
  readonly stderr: Writable;
74
75
  };
75
76
  export type TryCatch = <T>(f: () => T) => Result<T, unknown>;
77
+ export type Sandbox = <T>(f: () => T) => SandboxResult<T>;
76
78
  export type Server = {
77
79
  readonly listen: (port: number) => void;
78
80
  };
@@ -111,6 +113,9 @@ export type Io = {
111
113
  readonly asyncTryCatch: <T>(f: () => Promise<T>) => Promise<Result<T, unknown>>;
112
114
  readonly http: Http;
113
115
  readonly childProcess: ChildProcess;
116
+ readonly now: () => number;
117
+ readonly sandbox: Sandbox;
118
+ readonly write: (stream: WriteConsoles, data: Vec) => Promise<void>;
114
119
  };
115
120
  export type App = (io: Io) => Promise<number>;
116
121
  export type Run = (f: App) => Promise<never>;
@@ -120,4 +125,5 @@ export type Run = (f: App) => Promise<never>;
120
125
  */
121
126
  export declare const run: (io: Io) => Run;
122
127
  export type EffectToPromise = <T>(effect: Effect<NodeOp, T>) => Promise<T>;
123
- export declare const fromIo: ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile, rm, access } }, fetch, http: { createServer }, childProcess, asyncImport, }: Io) => EffectToPromise;
128
+ export declare const fromIo: ({ fs: { promises: { mkdir, readFile, readdir, writeFile, rm, access } }, fetch, http: { createServer }, childProcess, asyncImport, now: ioNow, sandbox: ioSandbox, write: ioWrite, }: Io) => EffectToPromise;
129
+ export declare const runProgram: (io: Io) => (args: readonly string[]) => (program: NodeProgram) => Promise<number>;
package/fs/io/module.f.js CHANGED
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Legacy `Io` interface and adapter (`fromIo`) bridging the old IO API to the
3
+ * effect runner.
4
+ *
5
+ * @deprecated Use `fs/types/effects/node` (see issue
6
+ * [i139](../../issues/README.md)).
7
+ *
8
+ * @module
9
+ */
1
10
  import { normalize } from "../path/module.f.js";
2
11
  import {} from "../types/effects/module.f.js";
3
12
  import { asyncRun } from "../types/effects/module.js";
@@ -35,11 +44,9 @@ const collect = async (v) => {
35
44
  }
36
45
  return result;
37
46
  };
38
- export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile, rm, access } }, fetch, http: { createServer }, childProcess, asyncImport, }) => {
47
+ export const fromIo = ({ fs: { promises: { mkdir, readFile, readdir, writeFile, rm, access } }, fetch, http: { createServer }, childProcess, asyncImport, now: ioNow, sandbox: ioSandbox, write: ioWrite, }) => {
39
48
  const result = asyncRun({
40
49
  all: async (...effects) => await Promise.all(effects.map(result)),
41
- error: async (message) => error(message),
42
- log: async (message) => log(message),
43
50
  fetch: async (url) => tc(async () => {
44
51
  const response = await fetch(url);
45
52
  if (!response.ok) {
@@ -86,7 +93,21 @@ export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readF
86
93
  s.listen(port);
87
94
  },
88
95
  forever: () => new Promise(() => { }),
89
- now: async () => BigInt(Date.now()) * 1000000n,
96
+ now: async () => ioNow(),
97
+ sandbox: async (f) => ioSandbox(f),
98
+ write: ioWrite,
90
99
  });
91
100
  return result;
92
101
  };
102
+ export const runProgram = (io) => {
103
+ const { process: { env, stdout, stderr } } = io;
104
+ const f = fromIo(io);
105
+ return (args) => (program) => f(program({
106
+ args,
107
+ env,
108
+ std: {
109
+ stdout: { isTTY: stdout.isTTY },
110
+ stderr: { isTTY: stderr.isTTY },
111
+ },
112
+ }));
113
+ };
package/fs/io/module.js CHANGED
@@ -10,11 +10,46 @@ import http from 'node:http';
10
10
  import childProcess from 'node:child_process';
11
11
  import fs from 'node:fs';
12
12
  import process from 'node:process';
13
- import { fromIo, run } from "./module.f.js";
14
13
  import { concat } from "../path/module.f.js";
14
+ import { once } from 'node:events';
15
+ import { fromIo, runProgram } from "./module.f.js";
15
16
  import { error, ok } from "../types/result/module.f.js";
17
+ import { fromVec } from "../types/uint8array/module.f.js";
16
18
  const prefix = 'file:///';
19
+ const { now } = Date;
20
+ /** Maps `WriteConsoles` names to the corresponding Node.js writable streams. */
21
+ const streams = {
22
+ stdout: process.stdout,
23
+ stderr: process.stderr,
24
+ };
25
+ /**
26
+ * Writes `data` to `stream` respecting Node.js backpressure.
27
+ *
28
+ * `stream.write()` returns `false` when the internal buffer is full; the data
29
+ * is already buffered at that point (no retry needed) but the caller must not
30
+ * issue more writes until the `'drain'` event fires. Waiting here throttles the
31
+ * producer to the speed of the OS consumer, preventing unbounded memory growth
32
+ * when many large messages arrive faster than they can be flushed.
33
+ *
34
+ * When the buffer is not full `write()` returns `true` and we return
35
+ * immediately, so large computations with occasional prints never stall.
36
+ *
37
+ * @see {@link https://nodejs.org/api/stream.html#writablewritechunk}
38
+ */
39
+ const writeAll = async (stream, data) => {
40
+ if (!stream.write(data)) {
41
+ await once(stream, 'drain');
42
+ }
43
+ };
17
44
  export const asyncImport = (v) => import(__rewriteRelativeImportExtension(v));
45
+ export const tryCatch = f => {
46
+ try {
47
+ return ok(f());
48
+ }
49
+ catch (e) {
50
+ return error(e);
51
+ }
52
+ };
18
53
  export const io = {
19
54
  console,
20
55
  fs,
@@ -26,30 +61,34 @@ export const io = {
26
61
  },
27
62
  performance,
28
63
  fetch,
29
- tryCatch: f => {
64
+ tryCatch,
65
+ asyncTryCatch: async (f) => {
30
66
  try {
31
- return ok(f());
67
+ return ok(await f());
32
68
  }
33
69
  catch (e) {
34
70
  return error(e);
35
71
  }
36
72
  },
37
- asyncTryCatch: async (f) => {
73
+ http,
74
+ childProcess,
75
+ now,
76
+ sandbox: (f) => {
77
+ let result;
78
+ let after;
79
+ const before = performance.now();
38
80
  try {
39
- return ok(await f());
81
+ const value = f();
82
+ after = performance.now();
83
+ result = ok(value);
40
84
  }
41
85
  catch (e) {
42
- return error(e);
86
+ after = performance.now();
87
+ result = error(e);
43
88
  }
89
+ return { result, duration: after - before };
44
90
  },
45
- http,
46
- childProcess,
47
- };
48
- export const legacyRun = run(io);
49
- export const ioRun = (io) => {
50
- const r = fromIo(io);
51
- const { argv, env } = io.process;
52
- return p => r(p(argv, env));
91
+ write: (stream, data) => writeAll(streams[stream], fromVec(data)),
53
92
  };
54
- const effectRun = ioRun(io);
93
+ const effectRun = runProgram(io)(io.process.argv.slice(2));
55
94
  export default effectRun;
@@ -1,3 +1,10 @@
1
+ /**
2
+ * JavaScript tokenizer built as a range-map state machine over code points,
3
+ * producing tokens for keywords, identifiers, punctuators, comments, strings,
4
+ * and numeric literals (including `BigFloat`).
5
+ *
6
+ * @module
7
+ */
1
8
  import { strictEqual } from "../../types/function/operator/module.f.js";
2
9
  import { merge, fromRange, get } from "../../types/range_map/module.f.js";
3
10
  import { empty, stateScan, flat, toArray, reduce as listReduce, scan, map as listMap } from "../../types/list/module.f.js";
@@ -1,3 +1,10 @@
1
+ /**
2
+ * JSON tree types and utilities: `Primitive`/`Unknown` value shapes,
3
+ * `setProperty` for immutable nested updates, and a `stringify` built from the
4
+ * serializer wraps.
5
+ *
6
+ * @module
7
+ */
1
8
  import { type List } from '../types/list/module.f.ts';
2
9
  import { type Entry as ObjectEntry } from '../types/object/module.f.ts';
3
10
  type Object = {
@@ -1,3 +1,10 @@
1
+ /**
2
+ * JSON tree types and utilities: `Primitive`/`Unknown` value shapes,
3
+ * `setProperty` for immutable nested updates, and a `stringify` built from the
4
+ * serializer wraps.
5
+ *
6
+ * @module
7
+ */
1
8
  import { next, flat, map } from "../types/list/module.f.js";
2
9
  import { concat } from "../types/string/module.f.js";
3
10
  import { at } from "../types/object/module.f.js";
@@ -20,3 +20,9 @@ export declare const normalize: Unary<string, string>;
20
20
  * Concatenates two path fragments and returns a normalized path.
21
21
  */
22
22
  export declare const concat: Reduce<string>;
23
+ /**
24
+ * Returns `path` relative to `base` with a `./` prefix, or `path` unchanged
25
+ * if it does not start with `base` or `base` is empty.
26
+ * E.g. `relativize('/repo', '/repo/fs/a.ts')` → `'./fs/a.ts'`.
27
+ */
28
+ export declare const relativize: (base: string, path: string) => string;
@@ -46,3 +46,9 @@ export const concat = a => b => {
46
46
  const s = stringConcat([a, '/', b]);
47
47
  return normalize(s);
48
48
  };
49
+ /**
50
+ * Returns `path` relative to `base` with a `./` prefix, or `path` unchanged
51
+ * if it does not start with `base` or `base` is empty.
52
+ * E.g. `relativize('/repo', '/repo/fs/a.ts')` → `'./fs/a.ts'`.
53
+ */
54
+ export const relativize = (base, path) => base !== '' && path.startsWith(base) ? `.${path.slice(base.length)}` : path;
@@ -1,5 +1,3 @@
1
- declare const _default: {
2
- normalize: (() => void)[];
3
- concat: (() => void)[];
4
- };
5
- export default _default;
1
+ export declare const normalizeTest: (() => void)[];
2
+ export declare const concatTest: (() => void)[];
3
+ export declare const relativizeTest: (() => void)[];
package/fs/path/test.f.js CHANGED
@@ -1,49 +1,67 @@
1
- import { concat, normalize } from "./module.f.js";
2
- export default {
3
- normalize: [
4
- () => {
5
- const norm = normalize("dir/file.json");
6
- if (norm !== "dir/file.json") {
7
- throw norm;
8
- }
9
- },
10
- () => {
11
- const norm = normalize("dir//file.json");
12
- if (norm !== "dir/file.json") {
13
- throw norm;
14
- }
15
- },
16
- () => {
17
- const norm = normalize("../../dir/file.json");
18
- if (norm !== "../../dir/file.json") {
19
- throw norm;
20
- }
21
- },
22
- () => {
23
- const norm = normalize("../../dir/../file.json");
24
- if (norm !== "../../file.json") {
25
- throw norm;
26
- }
27
- },
28
- ],
29
- concat: [
30
- () => {
31
- const c = concat("a")("b");
32
- if (c !== "a/b") {
33
- throw c;
34
- }
35
- },
36
- () => {
37
- const c = concat("a///b/")("c");
38
- if (c !== "a/b/c") {
39
- throw c;
40
- }
41
- },
42
- () => {
43
- const c = concat("a/../b/..")("c");
44
- if (c !== "c") {
45
- throw c;
46
- }
47
- },
48
- ]
49
- };
1
+ import { concat, normalize, relativize } from "./module.f.js";
2
+ export const normalizeTest = [
3
+ () => {
4
+ const norm = normalize("dir/file.json");
5
+ if (norm !== "dir/file.json") {
6
+ throw norm;
7
+ }
8
+ },
9
+ () => {
10
+ const norm = normalize("dir//file.json");
11
+ if (norm !== "dir/file.json") {
12
+ throw norm;
13
+ }
14
+ },
15
+ () => {
16
+ const norm = normalize("../../dir/file.json");
17
+ if (norm !== "../../dir/file.json") {
18
+ throw norm;
19
+ }
20
+ },
21
+ () => {
22
+ const norm = normalize("../../dir/../file.json");
23
+ if (norm !== "../../file.json") {
24
+ throw norm;
25
+ }
26
+ },
27
+ ];
28
+ export const concatTest = [
29
+ () => {
30
+ const c = concat("a")("b");
31
+ if (c !== "a/b") {
32
+ throw c;
33
+ }
34
+ },
35
+ () => {
36
+ const c = concat("a///b/")("c");
37
+ if (c !== "a/b/c") {
38
+ throw c;
39
+ }
40
+ },
41
+ () => {
42
+ const c = concat("a/../b/..")("c");
43
+ if (c !== "c") {
44
+ throw c;
45
+ }
46
+ },
47
+ ];
48
+ export const relativizeTest = [
49
+ () => {
50
+ const r = relativize('/repo', '/repo/fs/a.ts');
51
+ if (r !== './fs/a.ts') {
52
+ throw r;
53
+ }
54
+ },
55
+ () => {
56
+ const r = relativize('/repo', '/other/a.ts');
57
+ if (r !== '/other/a.ts') {
58
+ throw r;
59
+ }
60
+ },
61
+ () => {
62
+ const r = relativize('', './fs/a.ts');
63
+ if (r !== './fs/a.ts') {
64
+ throw r;
65
+ }
66
+ },
67
+ ];
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Indented text `Block` rendering and UTF-8 helpers: `flat` flattens a nested
3
+ * block into prefixed lines, while `utf8`/`utf8ToString` convert between
4
+ * strings and MSB-first UTF-8 bit vectors.
5
+ *
6
+ * @module
7
+ */
1
8
  import { type Vec } from '../types/bit_vec/module.f.ts';
2
9
  import { type List } from '../types/list/module.f.ts';
3
10
  export type Block = ItemThunk | ItemArray;
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Indented text `Block` rendering and UTF-8 helpers: `flat` flattens a nested
3
+ * block into prefixed lines, while `utf8`/`utf8ToString` convert between
4
+ * strings and MSB-first UTF-8 bit vectors.
5
+ *
6
+ * @module
7
+ */
1
8
  import { msb, u8List, u8ListToVec } from "../types/bit_vec/module.f.js";
2
9
  import { flatMap } from "../types/list/module.f.js";
3
10
  import { fromCodePointList, toCodePointList } from "./utf8/module.f.js";
@@ -5,6 +5,8 @@
5
5
  * @module
6
6
  */
7
7
  import type { Io, Writable } from "../../io/module.f.ts";
8
+ import { type Write, type WriteConsoles, type NodeProgramOptions } from '../../types/effects/node/module.f.ts';
9
+ import { type Effect } from '../../types/effects/module.f.ts';
8
10
  export declare const backspace: string;
9
11
  type End = 'm';
10
12
  type Csi = (code: number | string) => string;
@@ -44,7 +46,7 @@ export type WriteText = (text: string) => WriteText;
44
46
  export declare const createConsoleText: (stdout: Stdout) => WriteText;
45
47
  export type CsiConsole = (s: string) => void;
46
48
  /**
47
- * Creates a TTY-aware console function.
49
+ * Creates a TTY-aware console function. Appends `\n` to each string before writing.
48
50
  *
49
51
  * For TTY destinations, ANSI SGR sequences are preserved.
50
52
  * For non-TTY destinations, ANSI SGR sequences are stripped.
@@ -57,4 +59,10 @@ export declare const console: ({ fs: { writeSync } }: Io) => (w: Writable) => Cs
57
59
  export declare const stdio: (io: Io) => CsiConsole;
58
60
  /** Writes to process stderr using a TTY-aware CSI console. */
59
61
  export declare const stderr: (io: Io) => CsiConsole;
62
+ /**
63
+ * Effect-based TTY-aware write. Strips ANSI SGR sequences when the target
64
+ * stream is not a TTY, then encodes to UTF-8 and emits a `Write` effect.
65
+ * Does NOT append `\n` — callers are responsible for line termination.
66
+ */
67
+ export declare const csiWrite: ({ std }: NodeProgramOptions) => (stream: WriteConsoles) => (s: string) => Effect<Write, void>;
60
68
  export {};
@@ -4,6 +4,9 @@
4
4
  *
5
5
  * @module
6
6
  */
7
+ import { write } from "../../types/effects/node/module.f.js";
8
+ import {} from "../../types/effects/module.f.js";
9
+ import { utf8 } from "../module.f.js";
7
10
  export const backspace = '\x08';
8
11
  const begin = '\x1b[';
9
12
  /**
@@ -46,8 +49,9 @@ export const createConsoleText = (stdout) => {
46
49
  };
47
50
  return f('');
48
51
  };
52
+ const str = (isTTY) => (s) => isTTY ? s : s.replace(/\x1b\[[0-9;]*m/g, '');
49
53
  /**
50
- * Creates a TTY-aware console function.
54
+ * Creates a TTY-aware console function. Appends `\n` to each string before writing.
51
55
  *
52
56
  * For TTY destinations, ANSI SGR sequences are preserved.
53
57
  * For non-TTY destinations, ANSI SGR sequences are stripped.
@@ -56,12 +60,19 @@ export const createConsoleText = (stdout) => {
56
60
  * @returns A function that targets a writable stream.
57
61
  */
58
62
  export const console = ({ fs: { writeSync } }) => (w) => {
59
- const { isTTY } = w;
60
- return isTTY
61
- ? (s) => writeSync(w.fd, s + '\n')
62
- : (s) => writeSync(w.fd, s.replace(/\x1b\[[0-9;]*m/g, '') + '\n');
63
+ const toStr = str(w.isTTY);
64
+ return (s) => writeSync(w.fd, toStr(s) + '\n');
63
65
  };
64
66
  /** Writes to process stdout using a TTY-aware CSI console. */
65
67
  export const stdio = (io) => console(io)(io.process.stdout);
66
68
  /** Writes to process stderr using a TTY-aware CSI console. */
67
69
  export const stderr = (io) => console(io)(io.process.stderr);
70
+ /**
71
+ * Effect-based TTY-aware write. Strips ANSI SGR sequences when the target
72
+ * stream is not a TTY, then encodes to UTF-8 and emits a `Write` effect.
73
+ * Does NOT append `\n` — callers are responsible for line termination.
74
+ */
75
+ export const csiWrite = ({ std }) => (stream) => {
76
+ const toStr = str(std[stream].isTTY);
77
+ return (s) => write(stream, utf8(toStr(s)));
78
+ };
@@ -1,3 +1,10 @@
1
+ /**
2
+ * UTF-16 utilities: convert between strings, UTF-16 code units, and Unicode
3
+ * code points, including surrogate-pair encoding/decoding via streaming
4
+ * `stateScan` operations.
5
+ *
6
+ * @module
7
+ */
1
8
  import { type List, type Thunk } from '../../types/list/module.f.ts';
2
9
  /**
3
10
  * Represent an unsigned UTF16, used to store one word UTF-16 (code unit).
@@ -1,3 +1,10 @@
1
+ /**
2
+ * UTF-16 utilities: convert between strings, UTF-16 code units, and Unicode
3
+ * code points, including surrogate-pair encoding/decoding via streaming
4
+ * `stateScan` operations.
5
+ *
6
+ * @module
7
+ */
1
8
  import { map, flat, stateScan, reduce, flatMap, empty, } from "../../types/list/module.f.js";
2
9
  import { concat } from "../../types/function/operator/module.f.js";
3
10
  import { contains } from "../../types/range/module.f.js";
@@ -49,11 +49,6 @@ export declare const exec: Func<Exec>;
49
49
  export type Access = readonly ['access', (path: string) => IoResult<void>];
50
50
  export declare const access: Func<Access>;
51
51
  export type Fs = Mkdir | ReadFile | Readdir | WriteFile | Rm | Exec | Access;
52
- export type Error = ['error', (message: string) => void];
53
- export declare const error: Func<Error>;
54
- export type Log = ['log', (message: string) => void];
55
- export declare const log: Func<Log>;
56
- export type Console = Log | Error;
57
52
  export type Server = Nominal<'server', `160855c4f69310fece3273c1853ac32de43dee1eb41bf59d821917f8eebe9272`, unknown>;
58
53
  export type Headers = {
59
54
  readonly [k in string]: string;
@@ -78,13 +73,50 @@ export type Http = CreateServer | Listen;
78
73
  export type Forever = ['forever', () => never];
79
74
  export declare const forever: Func<Forever>;
80
75
  export type Module = {
81
- readonly default: unknown;
76
+ readonly [k in string]: unknown;
82
77
  };
83
78
  export type Import = ['import', (path: string) => IoResult<Module>];
84
79
  export declare const import_: Func<Import>;
85
- export type Now = readonly ['now', () => bigint];
80
+ export type WriteConsoles = 'stdout' | 'stderr';
81
+ export type Write = readonly ['write', (stream: WriteConsoles, data: Vec) => void];
82
+ export declare const write: Func<Write>;
83
+ export type Console = (s: string) => Effect<Write, void>;
84
+ /** Writes a line to `stdout`. Replaces the retired `Log` effect. */
85
+ export declare const log: Console;
86
+ /** Writes a line to `stderr`. Replaces the retired `Error` effect. */
87
+ export declare const error: Console;
88
+ export type Now = readonly ['now', () => number];
86
89
  export declare const now: Func<Now>;
87
- export type NodeOp = All | Fetch | Console | Fs | Http | Forever | Import | Now;
90
+ export type SandboxResult<T> = {
91
+ readonly result: Result<T, unknown>;
92
+ /**
93
+ * Measured milliseconds but it's not limited to that.
94
+ * Instead, they represent times as floating-point numbers
95
+ * with up to microsecond precision.
96
+ */
97
+ readonly duration: number;
98
+ };
99
+ export type Sandbox = readonly ['sandbox', <T>(f: () => T) => SandboxResult<T>];
100
+ /**
101
+ * Runs a plain synchronous function in an isolated, measured environment.
102
+ *
103
+ * Combines try/catch and high-resolution timing into a single atomic operation.
104
+ * Only plain synchronous functions are accepted — no effects, no promises.
105
+ *
106
+ * Using a single operation rather than separate `TryCatch` + `Perf` effects is
107
+ * necessary for correctness: effects execute as async tasks, so the scheduler
108
+ * can insert arbitrary work between two separate timing calls, making the
109
+ * measured delta inaccurate. Here the clock reads happen synchronously around
110
+ * the function call with nothing in between.
111
+ *
112
+ * Future parameters (time limit, memory limit) can be added to the payload
113
+ * without breaking the API. Worker-based implementations can enforce hard
114
+ * limits via worker termination.
115
+ *
116
+ * @see {@link SandboxResult}
117
+ */
118
+ export declare const sandbox: Func<Sandbox>;
119
+ export type NodeOp = All | Fetch | Fs | Http | Forever | Import | Now | Sandbox | Write;
88
120
  export type NodeEffect<T> = Effect<NodeOp, T>;
89
121
  export type NodeOperationMap = ToAsyncOperationMap<NodeOp>;
90
122
  /**
@@ -93,4 +125,14 @@ export type NodeOperationMap = ToAsyncOperationMap<NodeOp>;
93
125
  export type Env = {
94
126
  readonly [k: string]: string | undefined;
95
127
  };
96
- export type NodeProgram = (argv: readonly string[], env: Env) => Effect<NodeOp, number>;
128
+ export type NodeProgramOptions = {
129
+ readonly args: readonly string[];
130
+ readonly env: Env;
131
+ readonly std: {
132
+ readonly [k in WriteConsoles]: {
133
+ readonly isTTY: boolean;
134
+ };
135
+ };
136
+ };
137
+ export type Program<O extends Operation> = (options: NodeProgramOptions) => Effect<O, number>;
138
+ export type NodeProgram = Program<NodeOp>;
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Node.js effect operations: filesystem (`mkdir`, `readFile`, `readdir`,
3
+ * `writeFile`, `rm`, `access`), networking (`fetch`, `createServer`, `listen`),
4
+ * subprocess `exec`, `log`/`error` (wrappers over `write`), `import_`, `now`,
5
+ * `sandbox`, `forever`, and `all`/`both` parallelism; defines the
6
+ * `NodeOp`/`NodeProgram` types used by the Node runner.
7
+ *
8
+ * @module
9
+ */
10
+ import { utf8 } from "../../../text/module.f.js";
1
11
  import { do_ } from "../module.f.js";
2
12
  const doAll = do_('all');
3
13
  /**
@@ -17,10 +27,37 @@ export const writeFile = do_('writeFile');
17
27
  export const rm = do_('rm');
18
28
  export const exec = do_('exec');
19
29
  export const access = do_('access');
20
- export const error = do_('error');
21
- export const log = do_('log');
22
30
  export const createServer = do_('createServer');
23
31
  export const listen = do_('listen');
24
32
  export const forever = do_('forever');
25
33
  export const import_ = do_('import');
34
+ export const write = do_('write');
35
+ /**
36
+ * Encodes `s + '\n'` as UTF-8 and emits a `Write` effect to `stream`.
37
+ * Shared implementation for `log` and `error`.
38
+ */
39
+ const writeString = (stream) => (s) => write(stream, utf8(s + '\n'));
40
+ /** Writes a line to `stdout`. Replaces the retired `Log` effect. */
41
+ export const log = writeString('stdout');
42
+ /** Writes a line to `stderr`. Replaces the retired `Error` effect. */
43
+ export const error = writeString('stderr');
26
44
  export const now = do_('now');
45
+ /**
46
+ * Runs a plain synchronous function in an isolated, measured environment.
47
+ *
48
+ * Combines try/catch and high-resolution timing into a single atomic operation.
49
+ * Only plain synchronous functions are accepted — no effects, no promises.
50
+ *
51
+ * Using a single operation rather than separate `TryCatch` + `Perf` effects is
52
+ * necessary for correctness: effects execute as async tasks, so the scheduler
53
+ * can insert arbitrary work between two separate timing calls, making the
54
+ * measured delta inaccurate. Here the clock reads happen synchronously around
55
+ * the function call with nothing in between.
56
+ *
57
+ * Future parameters (time limit, memory limit) can be added to the payload
58
+ * without breaking the API. Worker-based implementations can enforce hard
59
+ * limits via worker termination.
60
+ *
61
+ * @see {@link SandboxResult}
62
+ */
63
+ export const sandbox = do_('sandbox');
@@ -31,5 +31,9 @@ declare const _default: {
31
31
  isDirectory: () => void;
32
32
  };
33
33
  now: () => void;
34
+ sandbox: {
35
+ ok: () => void;
36
+ error: () => void;
37
+ };
34
38
  };
35
39
  export default _default;