functionalscript 0.32.4 → 0.33.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.
@@ -11,4 +11,7 @@ export declare const proof: {
11
11
  repeat: (() => void)[];
12
12
  repeatParser: (() => void)[];
13
13
  example: () => void;
14
+ throw: {
15
+ ambiguousVariantDispatch: () => void;
16
+ };
14
17
  };
@@ -763,5 +763,12 @@ export const proof = {
763
763
  'digit': 'digit',
764
764
  }
765
765
  };
766
+ },
767
+ throw: {
768
+ ambiguousVariantDispatch: () => {
769
+ // Two alternatives covering the same code point — dispatch merge throws.
770
+ const conflictRule = { 'a': range('AA'), 'b': range('AA') };
771
+ dispatchMap(toData(conflictRule)[0]);
772
+ }
766
773
  }
767
774
  };
@@ -112,7 +112,9 @@ const casToolRegistry = (c, home, toUrl) => {
112
112
  }
113
113
  return x.step(value => typeof value === 'string'
114
114
  ? pure(errorResult(value))
115
- : c.write(value).step(hash => pure(okResult(vecToCBase32(hash)))));
115
+ : c.write(value).step(hash => pure(hash === undefined
116
+ ? errorResult('write')
117
+ : okResult(vecToCBase32(hash)))));
116
118
  }),
117
119
  toolEntry('cas_get', 'Inspect a blob by hash. Always returns JSON {length,mime_type,type[,url]} where type is "text" or "base64". Pass content:true to also include the inline content string.', casGetArgs, r => {
118
120
  const key = cBase32ToVec(r.hash);
@@ -5,9 +5,9 @@ import { asBase, asNominal, create, read, write } from "../../effects/memory/mod
5
5
  import { msb, u8ListToVec, vec8 } from "../../types/bit_vec/module.f.js";
6
6
  import { vecToCBase32 } from "../../cbase32/module.f.js";
7
7
  import { encode as base64Encode } from "../../base64/module.f.js";
8
- import { sha256 } from "../../crypto/sha2/module.f.js";
8
+ import { computeSync, sha256 } from "../../crypto/sha2/module.f.js";
9
9
  import { utf8 } from "../../text/module.f.js";
10
- import { cas, fileKvStore } from "../module.f.js";
10
+ import { fileCas } from "../module.f.js";
11
11
  import { mcpStep, uninitializedState, } from "../../mcp/module.f.js";
12
12
  import {} from "../../effects/node/module.f.js";
13
13
  import { emptyState, virtual } from "../../effects/node/virtual/module.f.js";
@@ -34,9 +34,14 @@ const mock = {
34
34
  randomInt: () => _ => { throw new Error('randomInt not supported in memory mock'); },
35
35
  };
36
36
  const runMem = (effect) => run(mock)(initialTestState)(effect)[1];
37
- const memKvStore = (mapKey) => ({
37
+ const memCas = (mapKey) => ({
38
38
  read: (key) => read(mapKey).step(m => pure(m[vecToCBase32(key)]?.[1])),
39
- write: (key, value) => read(mapKey).step(m => write(mapKey, { ...m, [vecToCBase32(key)]: [key, value] })),
39
+ write: (value) => {
40
+ const key = computeSync(sha256)([value]);
41
+ return read(mapKey)
42
+ .step(m => write(mapKey, { ...m, [vecToCBase32(key)]: [key, value] }))
43
+ .step(() => pure(key));
44
+ },
40
45
  list: () => read(mapKey).step(m => pure(Object.values(m).map(([k]) => k))),
41
46
  });
42
47
  // ── Session driver ──────────────────────────────────────────────────────────────
@@ -49,7 +54,7 @@ const feed = (step) => (msgs) => {
49
54
  };
50
55
  // Runs a full session over a fresh in-memory CAS, returning all responses.
51
56
  const runSession = (msgs, home = '/home/user') => runMem(create({}).step(mapKey => create(uninitializedState).step(sessionKey => {
52
- const c = cas(sha256)(memKvStore(mapKey));
57
+ const c = memCas(mapKey);
53
58
  const step = mcpStep(casConfig)(casMcpHandlers(c, home))(sessionKey);
54
59
  return feed(step)(msgs);
55
60
  })));
@@ -58,7 +63,7 @@ const runSession = (msgs, home = '/home/user') => runMem(create({}).step(mapKey
58
63
  // the same filesystem-backed CAS.
59
64
  const runSessionVirtual = (root, home = '/home/user') => (msgs) => {
60
65
  const effect = create(uninitializedState).step(sessionKey => {
61
- const c = cas(sha256)(fileKvStore(home));
66
+ const c = fileCas(sha256)(home);
62
67
  const step = mcpStep(casConfig)(casMcpHandlers(c, home))(sessionKey);
63
68
  return feed(step)(msgs);
64
69
  });
@@ -218,7 +223,7 @@ export const proof = {
218
223
  // cas_add with type:'url' streams a file from /home/user/cas_upload/ into CAS.
219
224
  addUrlStoresFileAndReturnsHash: () => {
220
225
  const fileContent = utf8('hello from file');
221
- const root = { 'home': { 'user': { 'cas_upload': { 'hello.txt': fileContent } } } };
226
+ const root = { 'home': { 'user': { 'cas_upload': { 'hello.txt': [fileContent] } } } };
222
227
  const [addUrlResp] = runSessionVirtual(root)([
223
228
  init, initialized,
224
229
  call(2, 'cas_add', { content: '/home/user/cas_upload/hello.txt', type: 'url' }),
@@ -228,7 +233,7 @@ export const proof = {
228
233
  },
229
234
  addUrlRoundTrips: () => {
230
235
  const fileContent = utf8('round-trip content');
231
- const root = { 'home': { 'user': { 'cas_upload': { 'rt.txt': fileContent } } } };
236
+ const root = { 'home': { 'user': { 'cas_upload': { 'rt.txt': [fileContent] } } } };
232
237
  // First pass: add to get the hash (deterministic for same content).
233
238
  const [addResp] = runSessionVirtual(root)([
234
239
  init, initialized,
@@ -256,7 +261,7 @@ export const proof = {
256
261
  // cas_get without content:true returns only metadata.
257
262
  getMetaReturnsLengthAndMimeType: () => {
258
263
  const fileContent = utf8('text content');
259
- const root = { 'home': { 'user': { 'cas_upload': { 'f': fileContent } } } };
264
+ const root = { 'home': { 'user': { 'cas_upload': { 'f': [fileContent] } } } };
260
265
  // First pass: add to get the hash.
261
266
  const [addResp] = runSessionVirtual(root)([
262
267
  init, initialized,
@@ -321,7 +326,7 @@ export const proof = {
321
326
  // cas_add type:'url' with a subdirectory path flattens slashes to '-' in staging.
322
327
  addUrlFromSubdirectorySucceeds: () => {
323
328
  const fileContent = utf8('nested file content');
324
- const root = { 'home': { 'user': { 'cas_upload': { 'subdir': { 'file.txt': fileContent } } } } };
329
+ const root = { 'home': { 'user': { 'cas_upload': { 'subdir': { 'file.txt': [fileContent] } } } } };
325
330
  const [resp] = runSessionVirtual(root)([
326
331
  init, initialized,
327
332
  call(2, 'cas_add', { content: '/home/user/cas_upload/subdir/file.txt', type: 'url' }),
@@ -332,7 +337,7 @@ export const proof = {
332
337
  // cas_add with type:'url' accepts paths within /home/user/cas_upload/
333
338
  addUrlFromApprovedDirectorySucceeds: () => {
334
339
  const fileContent = utf8('approved file');
335
- const root = { 'home': { 'user': { 'cas_upload': { 'test.txt': fileContent } } } };
340
+ const root = { 'home': { 'user': { 'cas_upload': { 'test.txt': [fileContent] } } } };
336
341
  const [resp] = runSessionVirtual(root)([
337
342
  init, initialized,
338
343
  call(2, 'cas_add', { content: '/home/user/cas_upload/test.txt', type: 'url' }),
@@ -9,40 +9,26 @@ import { type Effect, type Operation } from '../effects/module.f.ts';
9
9
  import { type Access, type All, type IoResult, type Mkdir, type NodeProgramOptions, type RandomInt, type Read, type ReadBytes, type Readdir, type ReadFile, type Rename, type Write, type WriteFile } from '../effects/node/module.f.ts';
10
10
  import { type Commands } from '../cli/module.f.ts';
11
11
  import type { MemOp } from '../effects/memory/module.f.ts';
12
- export type KvStore<O extends Operation> = {
13
- /** Reads a value by key; returns `undefined` when the key does not exist. */
14
- readonly read: (key: Vec) => Effect<O, Vec | undefined>;
15
- /** Writes a key/value pair to the underlying storage. */
16
- readonly write: (key: Vec, value: Vec) => Effect<O, void>;
17
- /** Lists all keys available in the store. */
18
- readonly list: () => Effect<O, readonly Vec[]>;
19
- };
20
- /** A key/value tuple where index `0` is the key and index `1` is the value. */
21
- export type Kv = readonly [Vec, Vec];
22
12
  /** Converts a content key to its sharded relative CAS file path. */
23
13
  export declare const toPath: (key: Vec) => string;
24
- export type FileKvStoreOperation = ReadFile | Mkdir | WriteFile | Access | Readdir;
25
- /**
26
- * Creates a filesystem-backed key/value store under the provided root path.
27
- */
28
- export declare const fileKvStore: (path: string) => KvStore<FileKvStoreOperation>;
14
+ export type FileCasOperation = ReadFile | Mkdir | WriteFile | Access | Readdir;
29
15
  export type Cas<O extends Operation> = {
30
16
  /** Reads content by hash; returns `undefined` when not found. */
31
17
  readonly read: (key: Vec) => Effect<O, Vec | undefined>;
32
18
  /** Stores content and returns its computed hash. */
33
- readonly write: (value: Vec) => Effect<O, Vec>;
19
+ readonly write: (value: Vec) => Effect<O, Vec | undefined>;
34
20
  /** Lists all stored content hashes. */
35
21
  readonly list: () => Effect<O, readonly Vec[]>;
36
22
  };
37
23
  /**
38
24
  * Builds a content-addressable storage facade from a SHA-2 implementation.
39
25
  */
40
- export declare const cas: (sha2: Sha2) => <O extends Operation>(_: KvStore<O>) => Cas<O>;
26
+ export declare const fileCas: (sha2: Sha2) => (path: string) => Cas<FileCasOperation>;
41
27
  /**
42
28
  * Move-hash-move upload pipeline: moves `fileName` from `~/cas_upload/` to a
43
29
  * random staging path, stream-hashes it, then renames it to its final CAS shard
44
30
  * path. Returns `ok(hash)` on success or `error(reason)` on any I/O failure.
45
31
  */
46
32
  export declare const casUpload: (home: string) => (fileName: string) => Effect<Mkdir | Rename | RandomInt | ReadBytes, IoResult<Vec>>;
47
- export declare const commands: Commands<FileKvStoreOperation | Write | All | MemOp | Read>;
48
- export declare const main: (options: NodeProgramOptions) => Effect<All | Read | Write | FileKvStoreOperation | MemOp, number>;
33
+ export declare const commands: Commands<FileCasOperation | Write | All | MemOp | Read>;
34
+ export declare const main: (options: NodeProgramOptions) => Effect<All | Read | Write | FileCasOperation | MemOp, number>;
@@ -10,11 +10,9 @@ import { cBase32ToVec, vecToCBase32 } from "../cbase32/module.f.js";
10
10
  import { foldStep, forEachStep, pure } from "../effects/module.f.js";
11
11
  import { access, errorExit, isNotFound, log, mkdir, randomInt, readBytes, readdir, readFile, rename, writeFile } from "../effects/node/module.f.js";
12
12
  import { dispatch } from "../cli/module.f.js";
13
- import { casMcpServer } from "./mcp/module.f.js";
14
13
  import { toOption } from "../types/nullable/module.f.js";
15
14
  import { error, ok, unwrap } from "../types/result/module.f.js";
16
15
  import { splitAt } from "../types/string/module.f.js";
17
- const o = { withFileTypes: true };
18
16
  const split2 = splitAt(2);
19
17
  const prefix = '.cas';
20
18
  /** Converts a content key to its sharded relative CAS file path. */
@@ -24,57 +22,46 @@ export const toPath = (key) => {
24
22
  const [b, c] = split2(bc);
25
23
  return join(prefix, a, b, c);
26
24
  };
27
- /**
28
- * Creates a filesystem-backed key/value store under the provided root path.
29
- */
30
- export const fileKvStore = (path) => {
31
- const storePrefix = join(path, prefix);
32
- const normalizedStorePrefix = normalize(storePrefix);
33
- return {
34
- // TODO: extend the interface with `Result<Vec, unknown>` instead of `Vec|undefined`.
35
- read: (key) => readFile(join(path, toPath(key))).step(([t, v]) => pure(t === 'ok' ? v : undefined)),
36
- write: (key, value) => {
37
- const p = toPath(key);
38
- const parts = parse(p);
39
- const dir = join(path, ...parts.slice(0, -1));
40
- // TODO: error handling
41
- return mkdir(dir, { recursive: true })
42
- .step(() => writeFile(join(path, p), value))
43
- .step(() => pure(undefined));
44
- },
45
- list: () =>
46
- // A fresh store has no `.cas` directory yet. Treat *only* that case as an
47
- // empty store, mirroring how `read` maps a missing file to `undefined`.
48
- // A `.cas` that exists but cannot be read (permissions, corruption) is a
49
- // genuine storage error and is surfaced, not masked as "no hashes".
50
- access(storePrefix).step(a => {
51
- if (a[0] === 'error') {
52
- if (isNotFound(a[1])) {
53
- return pure([]);
54
- }
55
- throw a[1];
56
- }
57
- return readdir(storePrefix, { recursive: true })
58
- .step(r => pure(unwrap(r).flatMap(({ name, parentPath, isFile }) => toOption(isFile
59
- ? cBase32ToVec(normalize(parentPath).substring(normalizedStorePrefix.length).replaceAll('/', '') + name)
60
- : null))));
61
- }),
62
- };
63
- };
64
25
  /**
65
26
  * Builds a content-addressable storage facade from a SHA-2 implementation.
66
27
  */
67
- export const cas = (sha2) => {
28
+ export const fileCas = (sha2) => {
68
29
  const compute = computeSync(sha2);
69
- return ({ read, write, list }) => ({
70
- read,
71
- write: (value) => {
72
- const hash = compute([value]);
73
- return write(hash, value)
74
- .step(() => pure(hash));
75
- },
76
- list,
77
- });
30
+ return (path) => {
31
+ const storePrefix = join(path, prefix);
32
+ const normalizedStorePrefix = normalize(storePrefix);
33
+ return {
34
+ // TODO: extend the interface with `Result<Vec, unknown>` instead of `Vec|undefined`.
35
+ read: (key) => readFile(join(path, toPath(key))).step(([t, v]) => pure(t === 'ok' ? v : undefined)),
36
+ write: (value) => {
37
+ const hash = compute([value]);
38
+ const p = toPath(hash);
39
+ const parts = parse(p);
40
+ const dir = join(path, ...parts.slice(0, -1));
41
+ // TODO: error handling
42
+ return mkdir(dir, { recursive: true })
43
+ .step(() => writeFile(join(path, p), value))
44
+ .step(([t]) => pure(t === 'ok' ? hash : undefined));
45
+ },
46
+ list: () =>
47
+ // A fresh store has no `.cas` directory yet. Treat *only* that case as an
48
+ // empty store, mirroring how `read` maps a missing file to `undefined`.
49
+ // A `.cas` that exists but cannot be read (permissions, corruption) is a
50
+ // genuine storage error and is surfaced, not masked as "no hashes".
51
+ access(storePrefix).step(a => {
52
+ if (a[0] === 'error') {
53
+ if (isNotFound(a[1])) {
54
+ return pure([]);
55
+ }
56
+ throw a[1];
57
+ }
58
+ return readdir(storePrefix, { recursive: true })
59
+ .step(r => pure(unwrap(r).flatMap(({ name, parentPath, isFile }) => toOption(isFile
60
+ ? cBase32ToVec(normalize(parentPath).substring(normalizedStorePrefix.length).replaceAll('/', '') + name)
61
+ : null))));
62
+ }),
63
+ };
64
+ };
78
65
  };
79
66
  /** Maximum chunk size for streaming reads: the largest `Vec` the runtime allows. */
80
67
  const CHUNK_BYTES = Number(maxLengthBytes);
@@ -141,11 +128,12 @@ export const commands = [
141
128
  if (path === undefined || rest.length !== 0) {
142
129
  return errorExit("'cas add' expects one parameter");
143
130
  }
144
- const c = cas(sha256)(fileKvStore(home));
131
+ const c = fileCas(sha256)(home);
145
132
  return readFile(path)
146
133
  .step(v => c.write(unwrap(v)))
147
- .step(hash => log(vecToCBase32(hash)))
148
- .step(() => pure(0));
134
+ .step(hash => hash === undefined
135
+ ? pure(1)
136
+ : log(vecToCBase32(hash)).step(() => pure(0)));
149
137
  },
150
138
  },
151
139
  {
@@ -159,7 +147,7 @@ export const commands = [
159
147
  if (hash === null) {
160
148
  return errorExit(`invalid hash format: ${hashCBase32}`);
161
149
  }
162
- const c = cas(sha256)(fileKvStore(home));
150
+ const c = fileCas(sha256)(home);
163
151
  return c.read(hash)
164
152
  .step(v => {
165
153
  const result = v === undefined
@@ -174,7 +162,7 @@ export const commands = [
174
162
  names: ['list'],
175
163
  description: 'List all stored content hashes',
176
164
  handler: ({ home }) => {
177
- const c = cas(sha256)(fileKvStore(home));
165
+ const c = fileCas(sha256)(home);
178
166
  return c.list()
179
167
  .step(forEachStep(j => log(vecToCBase32(j))))
180
168
  .step(() => pure(0));
package/fs/cas/proof.f.js CHANGED
@@ -1,25 +1,17 @@
1
- import { cas, commands } from "./module.f.js";
2
- import { sha256 } from "../crypto/sha2/module.f.js";
1
+ import { commands } from "./module.f.js";
2
+ import { computeSync, sha256 } from "../crypto/sha2/module.f.js";
3
3
  import { empty, length, vec8 } from "../types/bit_vec/module.f.js";
4
4
  import { pure } from "../effects/module.f.js";
5
5
  import { run } from "../effects/mock/module.f.js";
6
- import { emptyState, virtual } from "../effects/node/virtual/module.f.js";
6
+ import { defaultNodeProgramOptions, emptyState, virtual } from "../effects/node/virtual/module.f.js";
7
7
  import { dispatch } from "../cli/module.f.js";
8
- const makeOptions = (args) => ({
9
- args,
10
- env: {},
11
- home: '.',
12
- std: { stdout: { isTTY: false }, stderr: { isTTY: false } },
13
- testContext: { test: async () => { } },
14
- bunTestContext: { test: async () => { } },
15
- playwrightTestContext: { test: async () => { } },
16
- engine: 'node',
17
- });
8
+ import { assert } from "../asserts/module.f.js";
9
+ const makeOptions = (args) => ({ ...defaultNodeProgramOptions, args });
18
10
  const main = dispatch(commands);
19
11
  export const proof = {
20
12
  mainAdd: () => {
21
13
  const content = vec8(0x2an);
22
- const state = { ...emptyState, root: { myfile: content } };
14
+ const state = { ...emptyState, root: { myfile: [content] } };
23
15
  const [finalState, exitCode] = virtual(state)(main(makeOptions(['add', 'myfile'])));
24
16
  if (exitCode !== 0) {
25
17
  throw ['expected exit 0', exitCode];
@@ -39,7 +31,7 @@ export const proof = {
39
31
  },
40
32
  mainGetFound: () => {
41
33
  const content = vec8(0x2an);
42
- const state = { ...emptyState, root: { myfile: content } };
34
+ const state = { ...emptyState, root: { myfile: [content] } };
43
35
  const [state1, exitCode1] = virtual(state)(main(makeOptions(['add', 'myfile'])));
44
36
  if (exitCode1 !== 0) {
45
37
  throw ['expected add exit 0', exitCode1];
@@ -53,7 +45,7 @@ export const proof = {
53
45
  mainGetNotFound: () => {
54
46
  // valid cBase32 hash that has not been stored
55
47
  const content = vec8(0x2an);
56
- const state = { ...emptyState, root: { myfile: content } };
48
+ const state = { ...emptyState, root: { myfile: [content] } };
57
49
  const [state1] = virtual(state)(main(makeOptions(['add', 'myfile'])));
58
50
  const hashStr = state1.stdout.trim();
59
51
  // use an empty store so the hash is not found
@@ -85,7 +77,7 @@ export const proof = {
85
77
  },
86
78
  mainList: () => {
87
79
  const content = vec8(0x2an);
88
- const state = { ...emptyState, root: { myfile: content } };
80
+ const state = { ...emptyState, root: { myfile: [content] } };
89
81
  const [state1] = virtual(state)(main(makeOptions(['add', 'myfile'])));
90
82
  const [, exitCode] = virtual(state1)(main(makeOptions(['list'])));
91
83
  if (exitCode !== 0) {
@@ -106,7 +98,7 @@ export const proof = {
106
98
  mainListCorruptStore: () => {
107
99
  // `.cas` exists but is a file, not a directory: a real storage error
108
100
  // that must surface, not be masked as an empty list.
109
- const state = { ...emptyState, root: { '.cas': vec8(0x2an) } };
101
+ const state = { ...emptyState, root: { '.cas': [vec8(0x2an)] } };
110
102
  let threw = false;
111
103
  try {
112
104
  virtual(state)(main(makeOptions(['list'])));
@@ -137,26 +129,25 @@ export const proof = {
137
129
  }
138
130
  },
139
131
  casWrite: () => {
140
- const store = {
132
+ const c = {
141
133
  read: (_key) => pure(undefined),
142
- write: (_key, _value) => pure(undefined),
134
+ write: (value) => pure(computeSync(sha256)([value])),
143
135
  list: () => pure([]),
144
136
  };
145
- const c = cas(sha256)(store);
146
137
  const [, hash] = run({})(undefined)(c.write(empty));
147
138
  // sha256 of empty input produces a 256-bit hash
139
+ assert(hash !== undefined);
148
140
  if (length(hash) !== 256n) {
149
141
  throw ['expected 256-bit hash', length(hash)];
150
142
  }
151
143
  },
152
144
  casReadPassthrough: () => {
153
145
  const stored = empty;
154
- const store = {
146
+ const c = {
155
147
  read: (_key) => pure(stored),
156
- write: (_key, _value) => pure(undefined),
148
+ write: (value) => pure(computeSync(sha256)([value])),
157
149
  list: () => pure([stored]),
158
150
  };
159
- const c = cas(sha256)(store);
160
151
  const [, readResult] = run({})(undefined)(c.read(empty));
161
152
  if (readResult !== stored) {
162
153
  throw ['read should pass through', readResult];
@@ -19,20 +19,20 @@ export declare const images: {
19
19
  readonly arm: "windows-11-arm";
20
20
  };
21
21
  };
22
- export declare const functionalscript: "0.30.0";
22
+ export declare const functionalscript: "0.32.4";
23
23
  export declare const bun = "1.3.14";
24
24
  export declare const deno = "2.8.3";
25
25
  export declare const playwright = "1.61.0";
26
26
  export declare const node: {
27
- readonly default: "26.3.0";
28
- readonly node22: "22.22.3";
29
- readonly node24: "24.16.0";
27
+ readonly default: "26.3.1";
28
+ readonly node22: "22.23.0";
29
+ readonly node24: "24.17.0";
30
30
  };
31
31
  export declare const wasmtime = "45.0.2";
32
32
  export declare const wasmer = "7.1.0";
33
- export declare const tsgo = "7.0.0-dev.20260615.1";
33
+ export declare const tsgo = "7.0.0-dev.20260620.1";
34
34
  export declare const actions: {
35
- readonly 'actions/checkout': "v6";
35
+ readonly 'actions/checkout': "v7";
36
36
  readonly 'actions/setup-node': "v6";
37
37
  readonly 'actions/cache': "v5";
38
38
  readonly 'denoland/setup-deno': "v2";
@@ -24,7 +24,7 @@ export const images = {
24
24
  // published FunctionalScript release; do not tie it to package.json's current
25
25
  // in-repo version. A separate maintenance job advances this pin.
26
26
  // https://www.npmjs.com/package/functionalscript
27
- export const functionalscript = '0.30.0';
27
+ export const functionalscript = '0.32.4';
28
28
  // https://bun.sh/
29
29
  export const bun = '1.3.14';
30
30
  // https://deno.com/
@@ -33,23 +33,23 @@ export const deno = '2.8.3';
33
33
  export const playwright = '1.61.0';
34
34
  // https://nodejs.org/en/download
35
35
  export const node = {
36
- default: '26.3.0',
37
- node22: '22.22.3',
38
- node24: '24.16.0',
36
+ default: '26.3.1',
37
+ node22: '22.23.0',
38
+ node24: '24.17.0',
39
39
  };
40
40
  // https://github.com/bytecodealliance/wasmtime/releases
41
41
  export const wasmtime = '45.0.2';
42
42
  // https://github.com/wasmerio/wasmer/releases
43
43
  export const wasmer = '7.1.0';
44
44
  // https://www.npmjs.com/package/@typescript/native-preview?activeTab=versions
45
- export const tsgo = '7.0.0-dev.20260615.1';
45
+ export const tsgo = '7.0.0-dev.20260620.1';
46
46
  // GitHub Action versions used by CI step builders. The key is the action
47
47
  // `owner/name`; call sites compose the full ref as
48
48
  // `` `${name}@${actions[name]}` ``.
49
49
  // Note: dtolnay/rust-toolchain value is a Rust version, not an action version.
50
50
  export const actions = {
51
51
  // https://github.com/marketplace/actions/checkout
52
- 'actions/checkout': 'v6',
52
+ 'actions/checkout': 'v7',
53
53
  // https://github.com/marketplace/actions/setup-node-js-environment
54
54
  'actions/setup-node': 'v6',
55
55
  // https://github.com/marketplace/actions/cache
package/fs/ci/proof.f.js CHANGED
@@ -14,18 +14,20 @@ const makeState = (rust, packageJson) => ({
14
14
  ...emptyState,
15
15
  root: {
16
16
  '.github': { workflows: {} },
17
- ...(packageJson !== undefined ? { 'package.json': utf8(packageJson) } : {}),
18
- ...(rust ? { 'Cargo.toml': emptyVec } : {}),
17
+ ...(packageJson !== undefined ? { 'package.json': [utf8(packageJson)] } : {}),
18
+ ...(rust ? { 'Cargo.toml': [emptyVec] } : {}),
19
19
  },
20
20
  });
21
21
  const workflow = (state) => {
22
22
  const dotGithub = state.root['.github'];
23
- assert(typeof dotGithub === 'object', dotGithub);
23
+ assert(typeof dotGithub === 'object' && !Array.isArray(dotGithub), dotGithub);
24
24
  const workflows = dotGithub['workflows'];
25
- assert(typeof workflows === 'object', workflows);
25
+ assert(typeof workflows === 'object' && !Array.isArray(workflows), workflows);
26
26
  const file = workflows['ci.yml'];
27
- assert(isVec(file), file);
28
- return unwrap(parseGitHubAction(jsonParse(utf8ToString(file))));
27
+ if (!Array.isArray(file) || file.length === 0) {
28
+ throw file;
29
+ }
30
+ return unwrap(parseGitHubAction(jsonParse(utf8ToString(file[0]))));
29
31
  };
30
32
  const run = (rust, nodeExtra = () => []) => {
31
33
  const [state, result] = virtual(makeState(rust))(ci({ nodeExtra }));
package/fs/cli/proof.f.js CHANGED
@@ -1,17 +1,7 @@
1
1
  import { pure } from "../effects/module.f.js";
2
- import {} from "../effects/node/module.f.js";
3
- import { emptyState, virtual } from "../effects/node/virtual/module.f.js";
2
+ import { defaultNodeProgramOptions, emptyState, virtual } from "../effects/node/virtual/module.f.js";
4
3
  import { dispatch } from "./module.f.js";
5
- const makeOptions = (args) => ({
6
- args,
7
- env: {},
8
- home: '.',
9
- std: { stdout: { isTTY: false }, stderr: { isTTY: false } },
10
- testContext: { test: async () => { } },
11
- bunTestContext: { test: async () => { } },
12
- playwrightTestContext: { test: async () => { } },
13
- engine: 'node',
14
- });
4
+ const makeOptions = (args) => ({ ...defaultNodeProgramOptions, args });
15
5
  const echoCommands = [
16
6
  {
17
7
  names: ['echo', 'e'],
@@ -6,13 +6,8 @@
6
6
  import { type Result } from '../../types/result/module.f.ts';
7
7
  import { type List } from '../../types/list/module.f.ts';
8
8
  import type { DjsTokenWithMetadata } from '../tokenizer/module.f.ts';
9
- import { type OrderedMap } from '../../types/ordered_map/module.f.ts';
10
9
  import type { AstModule } from '../ast/module.f.ts';
11
10
  import type { TokenMetadata } from '../../js/tokenizer/module.f.ts';
12
- export type ParseContext = {
13
- readonly complete: OrderedMap<Result<AstModule, string>>;
14
- readonly stack: List<string>;
15
- };
16
11
  export type ParseError = {
17
12
  readonly message: string;
18
13
  readonly metadata: TokenMetadata | null;
package/fs/djs/proof.f.js CHANGED
@@ -1,13 +1,12 @@
1
1
  import { compile } from "./module.f.js";
2
2
  import { virtual, emptyState } from "../effects/node/virtual/module.f.js";
3
- import { isVec } from "../types/bit_vec/module.f.js";
4
3
  import { utf8, utf8ToString } from "../text/module.f.js";
5
4
  const readOutput = (root, path) => {
6
5
  const file = root[path];
7
- if (!isVec(file)) {
6
+ if (!Array.isArray(file) || file.length === 0) {
8
7
  throw `${path} is not a file`;
9
8
  }
10
- return utf8ToString(file);
9
+ return utf8ToString(file[0]);
11
10
  };
12
11
  export const proof = {
13
12
  tooFewArgs: {
@@ -31,7 +30,7 @@ export const proof = {
31
30
  },
32
31
  },
33
32
  success: () => {
34
- const root = { 'input.f.js': utf8('export default 42') };
33
+ const root = { 'input.f.js': [utf8('export default 42')] };
35
34
  const [state, code] = virtual({ ...emptyState, root })(compile(['input.f.js', 'output.f.js']));
36
35
  if (code !== 0) {
37
36
  throw code;
@@ -42,7 +41,7 @@ export const proof = {
42
41
  }
43
42
  },
44
43
  jsonOutput: () => {
45
- const root = { 'input.f.js': utf8('export default 42') };
44
+ const root = { 'input.f.js': [utf8('export default 42')] };
46
45
  const [state, code] = virtual({ ...emptyState, root })(compile(['input.f.js', 'output.json']));
47
46
  if (code !== 0) {
48
47
  throw code;
@@ -62,7 +61,7 @@ export const proof = {
62
61
  }
63
62
  },
64
63
  parseError: () => {
65
- const root = { 'bad.f.js': utf8('export default @') };
64
+ const root = { 'bad.f.js': [utf8('export default @')] };
66
65
  const [state, code] = virtual({ ...emptyState, root })(compile(['bad.f.js', 'output.f.js']));
67
66
  if (code !== 0) {
68
67
  throw code;
@@ -8,9 +8,7 @@ import type { Entry as ObjectEntry } from '../../types/object/module.f.ts';
8
8
  import { type List } from '../../types/list/module.f.ts';
9
9
  export declare const undefinedSerialize: string[];
10
10
  type RefCounter = readonly [number, number];
11
- type Entry = ObjectEntry<Unknown>;
12
- type Entries = List<Entry>;
13
- type MapEntries = (entries: Entries) => Entries;
11
+ type MapEntries = (entries: List<ObjectEntry<Unknown>>) => List<ObjectEntry<Unknown>>;
14
12
  type Refs = ReadonlyMap<Unknown, RefCounter>;
15
13
  export declare const serializeWithoutConst: (mapEntries: MapEntries) => (value: Unknown) => List<string>;
16
14
  export declare const stringify: (sort: MapEntries) => (djs: Unknown) => string;
@@ -20,7 +20,8 @@ export const jsGrammar = () => {
20
20
  escape: [
21
21
  '\\',
22
22
  {
23
- ...set('"\\/bfnrt'),
23
+ ...set('"\\bfnrt'),
24
+ solidus: '/',
24
25
  u: [
25
26
  'u',
26
27
  ...repeat(4)({
@@ -3,6 +3,7 @@ export declare const proof: {
3
3
  tokenizer: (() => void)[];
4
4
  djs: (() => void)[];
5
5
  operators: (() => void)[];
6
+ ws: (() => void)[];
6
7
  throw: {
7
8
  parse: () => void;
8
9
  };