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.
- package/fs/bnf/data/proof.f.d.ts +3 -0
- package/fs/bnf/data/proof.f.js +7 -0
- package/fs/cas/mcp/module.f.js +3 -1
- package/fs/cas/mcp/proof.f.js +16 -11
- package/fs/cas/module.f.d.ts +5 -19
- package/fs/cas/module.f.js +42 -54
- package/fs/cas/proof.f.js +15 -24
- package/fs/ci/config/module.f.d.ts +6 -6
- package/fs/ci/config/module.f.js +6 -6
- package/fs/ci/proof.f.js +8 -6
- package/fs/cli/proof.f.js +2 -12
- package/fs/djs/parser/module.f.d.ts +0 -5
- package/fs/djs/proof.f.js +5 -6
- package/fs/djs/serializer/module.f.d.ts +1 -3
- package/fs/djs/tokenizer-new/module.f.js +2 -1
- package/fs/djs/tokenizer-new/proof.f.d.ts +1 -0
- package/fs/djs/tokenizer-new/proof.f.js +101 -67
- package/fs/djs/transpiler/proof.f.js +10 -10
- package/fs/effects/node/proof.f.js +36 -36
- package/fs/effects/node/virtual/module.f.d.ts +16 -2
- package/fs/effects/node/virtual/module.f.js +72 -20
- package/fs/effects/node/virtual/proof.f.d.ts +4 -0
- package/fs/effects/node/virtual/proof.f.js +47 -14
- package/fs/emergent_testing/proof.f.js +3 -11
- package/fs/fjs/module.f.js +3 -3
- package/fs/fjs/proof.f.js +2 -11
- package/fs/js/tokenizer/module.f.d.ts +5 -0
- package/fs/js/tokenizer/module.f.js +13 -0
- package/fs/json/serializer/module.f.js +0 -1
- package/fs/text/sgr/proof.f.js +2 -11
- package/fs/types/bigint/module.f.d.ts +8 -15
- package/fs/types/bigint/module.f.js +9 -22
- package/fs/types/bigint/proof.f.d.ts +0 -2
- package/fs/types/bigint/proof.f.js +6 -16
- package/package.json +1 -1
package/fs/bnf/data/proof.f.d.ts
CHANGED
package/fs/bnf/data/proof.f.js
CHANGED
|
@@ -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
|
};
|
package/fs/cas/mcp/module.f.js
CHANGED
|
@@ -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(
|
|
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);
|
package/fs/cas/mcp/proof.f.js
CHANGED
|
@@ -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 {
|
|
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
|
|
37
|
+
const memCas = (mapKey) => ({
|
|
38
38
|
read: (key) => read(mapKey).step(m => pure(m[vecToCBase32(key)]?.[1])),
|
|
39
|
-
write: (
|
|
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 =
|
|
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 =
|
|
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' }),
|
package/fs/cas/module.f.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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<
|
|
48
|
-
export declare const main: (options: NodeProgramOptions) => Effect<All | Read | Write |
|
|
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>;
|
package/fs/cas/module.f.js
CHANGED
|
@@ -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
|
|
28
|
+
export const fileCas = (sha2) => {
|
|
68
29
|
const compute = computeSync(sha2);
|
|
69
|
-
return (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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 =
|
|
131
|
+
const c = fileCas(sha256)(home);
|
|
145
132
|
return readFile(path)
|
|
146
133
|
.step(v => c.write(unwrap(v)))
|
|
147
|
-
.step(hash =>
|
|
148
|
-
|
|
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 =
|
|
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 =
|
|
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 {
|
|
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
|
-
|
|
9
|
-
|
|
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
|
|
132
|
+
const c = {
|
|
141
133
|
read: (_key) => pure(undefined),
|
|
142
|
-
write: (
|
|
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
|
|
146
|
+
const c = {
|
|
155
147
|
read: (_key) => pure(stored),
|
|
156
|
-
write: (
|
|
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.
|
|
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.
|
|
28
|
-
readonly node22: "22.
|
|
29
|
-
readonly node24: "24.
|
|
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.
|
|
33
|
+
export declare const tsgo = "7.0.0-dev.20260620.1";
|
|
34
34
|
export declare const actions: {
|
|
35
|
-
readonly 'actions/checkout': "
|
|
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";
|
package/fs/ci/config/module.f.js
CHANGED
|
@@ -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.
|
|
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.
|
|
37
|
-
node22: '22.
|
|
38
|
-
node24: '24.
|
|
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.
|
|
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': '
|
|
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
|
-
|
|
28
|
-
|
|
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 (!
|
|
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
|
|
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;
|