functionalscript 0.32.4 → 0.34.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/README.md +1 -1
- package/fs/asserts/module.f.d.ts +1 -1
- package/fs/asserts/module.f.js +4 -1
- package/fs/bnf/data/module.f.d.ts +0 -103
- package/fs/bnf/data/module.f.js +10 -246
- package/fs/bnf/data/proof.f.d.ts +0 -7
- package/fs/bnf/data/proof.f.js +3 -636
- package/fs/bnf/descent/module.f.d.ts +56 -0
- package/fs/bnf/descent/module.f.js +119 -0
- package/fs/bnf/descent/proof.f.d.ts +5 -0
- package/fs/bnf/descent/proof.f.js +296 -0
- package/fs/bnf/ll1/module.f.d.ts +72 -0
- package/fs/bnf/ll1/module.f.js +153 -0
- package/fs/bnf/ll1/proof.f.d.ts +9 -0
- package/fs/bnf/ll1/proof.f.js +358 -0
- package/fs/cas/cli/module.f.d.ts +6 -0
- package/fs/cas/cli/module.f.js +56 -0
- package/fs/cas/cli/proof.f.d.ts +14 -0
- package/fs/cas/cli/proof.f.js +149 -0
- package/fs/cas/mcp/module.f.d.ts +9 -17
- package/fs/cas/mcp/module.f.js +117 -83
- package/fs/cas/mcp/proof.f.d.ts +5 -1
- package/fs/cas/mcp/proof.f.js +79 -32
- package/fs/cas/module.f.d.ts +33 -27
- package/fs/cas/module.f.js +151 -126
- package/fs/cas/proof.f.d.ts +16 -13
- package/fs/cas/proof.f.js +255 -130
- package/fs/ci/config/module.f.d.ts +15 -15
- package/fs/ci/config/module.f.js +15 -15
- 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 +3 -2
- package/fs/djs/tokenizer-new/proof.f.d.ts +3 -0
- package/fs/djs/tokenizer-new/proof.f.js +458 -296
- package/fs/djs/transpiler/proof.f.js +10 -10
- package/fs/effects/list/module.f.d.ts +31 -0
- package/fs/effects/list/module.f.js +16 -0
- package/fs/effects/module.f.d.ts +11 -5
- package/fs/effects/module.f.js +16 -6
- package/fs/effects/node/module.f.d.ts +29 -3
- package/fs/effects/node/module.f.js +35 -1
- package/fs/effects/node/module.js +32 -18
- 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 +129 -20
- package/fs/effects/node/virtual/proof.f.d.ts +4 -0
- package/fs/effects/node/virtual/proof.f.js +47 -14
- package/fs/effects/proof.f.d.ts +4 -0
- package/fs/effects/proof.f.js +18 -1
- package/fs/emergent_testing/proof.f.js +3 -11
- package/fs/fjs/module.f.js +2 -8
- package/fs/fjs/proof.f.js +2 -11
- package/fs/html/module.f.js +6 -8
- 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/mcp/module.f.d.ts +1 -1
- package/fs/mime/module.f.d.ts +101 -5
- package/fs/mime/module.f.js +159 -6
- package/fs/mime/proof.f.d.ts +25 -0
- package/fs/mime/proof.f.js +193 -3
- package/fs/text/code_point/module.f.d.ts +39 -2
- package/fs/text/code_point/module.f.js +74 -2
- package/fs/text/code_point/proof.f.d.ts +8 -0
- package/fs/text/code_point/proof.f.js +72 -0
- package/fs/text/sgr/proof.f.js +2 -11
- package/fs/text/utf16/module.f.js +4 -26
- package/fs/text/utf8/module.f.d.ts +27 -0
- package/fs/text/utf8/module.f.js +8 -14
- 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/fs/types/function/operator/module.f.d.ts +26 -0
- package/fs/types/list/module.f.d.ts +3 -0
- package/fs/types/result/module.d.ts +2 -4
- package/fs/types/result/module.js +9 -3
- package/package.json +3 -3
- package/issues/demo/data/data.f.js +0 -12
- package/issues/demo/data/shared.f.js +0 -3
- package/issues/demo/fs/app.js +0 -4
- package/issues/demo/fs/math.f.js +0 -4
- package/issues/demo/sample/proof.f.js +0 -13
- /package/{issues → todo}/031-json.f.d.ts +0 -0
- /package/{issues → todo}/031-json.f.js +0 -0
- /package/{issues → todo}/proof.f.d.ts +0 -0
- /package/{issues → todo}/proof.f.js +0 -0
package/fs/cas/proof.f.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
import { type FileCasOperation } from './module.f.ts';
|
|
2
|
+
import { type Effect } from '../effects/module.f.ts';
|
|
3
|
+
import { type ReadFile, type WriteFile, type Rm, type Mkdir } from '../effects/node/module.f.ts';
|
|
4
|
+
type TestOp = FileCasOperation | WriteFile | ReadFile | Rm | Mkdir;
|
|
1
5
|
export declare const proof: {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
casWrite: () => void;
|
|
14
|
-
casReadPassthrough: () => void;
|
|
6
|
+
addBigFile: () => Effect<TestOp, void>;
|
|
7
|
+
addAndGetBigFile: () => Effect<TestOp, void>;
|
|
8
|
+
casWriteRead: () => void;
|
|
9
|
+
casReadMissingShard: () => void;
|
|
10
|
+
casWriteMultiChunk: () => void;
|
|
11
|
+
casWriteDedup: () => void;
|
|
12
|
+
casWriteErrorItemAborts: () => void;
|
|
13
|
+
casWriteReadExceedsMaxLength: () => void;
|
|
14
|
+
casWriteGcReclaimsExpired: () => void;
|
|
15
|
+
casUploadSuccess: () => void;
|
|
16
|
+
casUploadFailureKeepsSource: () => void;
|
|
15
17
|
};
|
|
18
|
+
export {};
|
package/fs/cas/proof.f.js
CHANGED
|
@@ -1,169 +1,294 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { length, maxLength, msb, vec, vec8 } from "../types/bit_vec/module.f.js";
|
|
2
|
+
import { cBase32ToVec, vecToCBase32 } from "../cbase32/module.f.js";
|
|
3
|
+
import { computeSync, sha256 } from "../crypto/sha2/module.f.js";
|
|
4
|
+
import { fileCas, casAddFile, casUpload } from "./module.f.js";
|
|
4
5
|
import { pure } from "../effects/module.f.js";
|
|
5
|
-
import {
|
|
6
|
+
import { mkdir, writeFile, rm, readFile, access } from "../effects/node/module.f.js";
|
|
7
|
+
import { error, ok } from "../types/result/module.f.js";
|
|
6
8
|
import { emptyState, virtual } from "../effects/node/virtual/module.f.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
import { join } from "../path/module.f.js";
|
|
10
|
+
import { nonEmpty, empty } from "../effects/list/module.f.js";
|
|
11
|
+
const testDir = './test-cas-cli';
|
|
12
|
+
// Create a 128 KiB big file content (at the max Vec size limit)
|
|
13
|
+
// This tests the boundary where files are at the chunk size limit
|
|
14
|
+
const createBigFileContent = () => {
|
|
15
|
+
const byteCount = 128n * 1024n; // 128 KiB
|
|
16
|
+
// Create a repeating pattern: 0x42 repeated across the file
|
|
17
|
+
return vec(byteCount * 8n)(0x42424242n);
|
|
18
|
+
};
|
|
19
|
+
// Test adding a big file and verifying the hash
|
|
20
|
+
const testAddBigFile = () => mkdir(testDir, { recursive: true }).step(() => {
|
|
21
|
+
const bigContent = createBigFileContent();
|
|
22
|
+
const bigFilePath = `${testDir}/big-file.bin`;
|
|
23
|
+
return writeFile(bigFilePath, bigContent).step(writeRes => {
|
|
24
|
+
if (writeRes[0] === 'error') {
|
|
25
|
+
throw new Error(`Failed to write test file: ${writeRes[1]}`);
|
|
26
|
+
}
|
|
27
|
+
const cas = fileCas(sha256)(testDir);
|
|
28
|
+
return casAddFile(cas)(bigFilePath).step(addRes => {
|
|
29
|
+
if (addRes[0] === 'error') {
|
|
30
|
+
throw new Error(`Failed to add file to CAS: ${addRes[1]}`);
|
|
31
|
+
}
|
|
32
|
+
const hash = addRes[1];
|
|
33
|
+
// Verify hash is 256 bits (SHA-256)
|
|
34
|
+
if (length(hash) !== 256n) {
|
|
35
|
+
throw new Error(`Expected hash length 256 bits, got ${length(hash)}`);
|
|
36
|
+
}
|
|
37
|
+
// Verify hash can be encoded/decoded
|
|
38
|
+
const hashCBase32 = vecToCBase32(hash);
|
|
39
|
+
const decodedHash = cBase32ToVec(hashCBase32);
|
|
40
|
+
if (decodedHash === null) {
|
|
41
|
+
throw new Error('Failed to decode hash from base32');
|
|
42
|
+
}
|
|
43
|
+
return rm(testDir).step(() => pure(undefined));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
// Test adding and retrieving a big file
|
|
48
|
+
const testAddAndGetBigFile = () => mkdir(testDir, { recursive: true })
|
|
49
|
+
.step(() => {
|
|
50
|
+
const bigContent = createBigFileContent();
|
|
51
|
+
const bigFilePath = `${testDir}/big-file.bin`;
|
|
52
|
+
return writeFile(bigFilePath, bigContent)
|
|
53
|
+
.step(writeRes => {
|
|
54
|
+
if (writeRes[0] === 'error') {
|
|
55
|
+
throw new Error(`Failed to write test file: ${writeRes[1]}`);
|
|
56
|
+
}
|
|
57
|
+
const cas = fileCas(sha256)(testDir);
|
|
58
|
+
return casAddFile(cas)(bigFilePath)
|
|
59
|
+
.step(addRes => {
|
|
60
|
+
if (addRes[0] === 'error') {
|
|
61
|
+
throw new Error(`Failed to add file to CAS: ${addRes[1]}`);
|
|
62
|
+
}
|
|
63
|
+
const hash = addRes[1];
|
|
64
|
+
const storedPath = cas.url(hash);
|
|
65
|
+
// Verify file is stored at the expected location
|
|
66
|
+
return readFile(storedPath).step(readRes => {
|
|
67
|
+
if (readRes[0] === 'error') {
|
|
68
|
+
throw new Error(`Failed to read stored file: ${readRes[1]}`);
|
|
69
|
+
}
|
|
70
|
+
const storedContent = readRes[1];
|
|
71
|
+
// Verify content is the same size as original
|
|
72
|
+
const storedLen = length(storedContent);
|
|
73
|
+
const originalLen = length(bigContent);
|
|
74
|
+
if (storedLen !== originalLen) {
|
|
75
|
+
throw new Error(`Content size mismatch: stored ${storedLen} bits, expected ${originalLen} bits`);
|
|
76
|
+
}
|
|
77
|
+
return rm(testDir).step(() => pure(undefined));
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
17
81
|
});
|
|
18
|
-
const main = dispatch(commands);
|
|
19
82
|
export const proof = {
|
|
20
|
-
|
|
83
|
+
addBigFile: testAddBigFile,
|
|
84
|
+
addAndGetBigFile: testAddAndGetBigFile,
|
|
85
|
+
//
|
|
86
|
+
casWriteRead: () => {
|
|
87
|
+
// Round-trip a single-chunk payload through the real streaming CAS: `write` returns
|
|
88
|
+
// the content hash, and `read` streams the same bytes back as `ok` chunk items.
|
|
21
89
|
const content = vec8(0x2an);
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (finalState.stdout.length === 0) {
|
|
28
|
-
throw 'expected hash in stdout';
|
|
90
|
+
const c = fileCas(sha256)('.');
|
|
91
|
+
const payload = nonEmpty(ok(content), empty());
|
|
92
|
+
const [state1, writeResult] = virtual(emptyState)(c.write(payload));
|
|
93
|
+
if (writeResult[0] !== 'ok') {
|
|
94
|
+
throw ['expected write ok', writeResult];
|
|
29
95
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (exitCode !== 1) {
|
|
34
|
-
throw ['expected exit 1', exitCode];
|
|
96
|
+
const hash = writeResult[1];
|
|
97
|
+
if (length(hash) !== 256n) {
|
|
98
|
+
throw ['expected 256-bit hash', length(hash)];
|
|
35
99
|
}
|
|
36
|
-
if (
|
|
37
|
-
throw '
|
|
100
|
+
if (msb.cmp(hash)(computeSync(sha256)([content])) !== 0) {
|
|
101
|
+
throw 'write hash mismatch';
|
|
38
102
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
103
|
+
const drain = (acc) => (stream) => stream.step((node) => {
|
|
104
|
+
if (node === undefined) {
|
|
105
|
+
return pure(ok(acc));
|
|
106
|
+
}
|
|
107
|
+
const { first, tail } = node;
|
|
108
|
+
if (first[0] === 'error') {
|
|
109
|
+
return pure(first);
|
|
110
|
+
}
|
|
111
|
+
return drain([...acc, first[1]])(tail);
|
|
112
|
+
});
|
|
113
|
+
const [, readResult] = virtual(state1)(drain([])(c.read(hash)));
|
|
114
|
+
if (readResult[0] !== 'ok') {
|
|
115
|
+
throw ['expected read ok', readResult];
|
|
46
116
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (exitCode2 !== 0) {
|
|
50
|
-
throw ['expected get exit 0', exitCode2];
|
|
117
|
+
if (msb.cmp(msb.listToVec(readResult[1]))(content) !== 0) {
|
|
118
|
+
throw 'read content mismatch';
|
|
51
119
|
}
|
|
52
120
|
},
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const [finalState, exitCode] = virtual(emptyState)(main(makeOptions(['get', hashStr, 'output'])));
|
|
61
|
-
if (exitCode !== 1) {
|
|
62
|
-
throw ['expected exit 1', exitCode];
|
|
121
|
+
casReadMissingShard: () => {
|
|
122
|
+
// A missing shard surfaces as an explicit error *item*, never as end-of-stream.
|
|
123
|
+
const c = fileCas(sha256)('.');
|
|
124
|
+
const hash = computeSync(sha256)([vec8(0x2an)]);
|
|
125
|
+
const node = virtual(emptyState)(c.read(hash))[1];
|
|
126
|
+
if (node === undefined) {
|
|
127
|
+
throw 'missing shard must not be EOF';
|
|
63
128
|
}
|
|
64
|
-
if (
|
|
65
|
-
throw 'expected error
|
|
129
|
+
if (node.first[0] !== 'error') {
|
|
130
|
+
throw ['expected error item', node.tail];
|
|
66
131
|
}
|
|
67
132
|
},
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
133
|
+
casWriteMultiChunk: () => {
|
|
134
|
+
// A multi-chunk payload streams through `writeBytes` chunk-by-chunk (the lease is
|
|
135
|
+
// renewed between chunks); the hash equals the SHA-256 of the concatenated bytes,
|
|
136
|
+
// and read streams the same content back.
|
|
137
|
+
const chunks = [vec8(0x11n), vec8(0x22n), vec8(0x33n)];
|
|
138
|
+
const c = fileCas(sha256)('.');
|
|
139
|
+
const payload = chunks.reduceRight((tail, chunk) => nonEmpty(ok(chunk), tail), empty());
|
|
140
|
+
const [state1, writeResult] = virtual(emptyState)(c.write(payload));
|
|
141
|
+
if (writeResult[0] !== 'ok') {
|
|
142
|
+
throw ['expected write ok', writeResult];
|
|
72
143
|
}
|
|
73
|
-
|
|
74
|
-
|
|
144
|
+
const hash = writeResult[1];
|
|
145
|
+
if (msb.cmp(hash)(computeSync(sha256)(chunks)) !== 0) {
|
|
146
|
+
throw 'multi-chunk write hash mismatch';
|
|
75
147
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
148
|
+
const drain = (acc) => (stream) => stream.step((node) => {
|
|
149
|
+
if (node === undefined) {
|
|
150
|
+
return pure(ok(acc));
|
|
151
|
+
}
|
|
152
|
+
const { first, tail } = node;
|
|
153
|
+
if (first[0] === 'error') {
|
|
154
|
+
return pure(first);
|
|
155
|
+
}
|
|
156
|
+
return drain([...acc, first[1]])(tail);
|
|
157
|
+
});
|
|
158
|
+
const [, readResult] = virtual(state1)(drain([])(c.read(hash)));
|
|
159
|
+
if (readResult[0] !== 'ok') {
|
|
160
|
+
throw ['expected read ok', readResult];
|
|
81
161
|
}
|
|
82
|
-
|
|
83
|
-
|
|
162
|
+
const expected = msb.concat(msb.concat(chunks[0])(chunks[1]))(chunks[2]);
|
|
163
|
+
if (msb.cmp(msb.listToVec(readResult[1]))(expected) !== 0) {
|
|
164
|
+
throw 'multi-chunk read content mismatch';
|
|
84
165
|
}
|
|
85
166
|
},
|
|
86
|
-
|
|
167
|
+
casWriteDedup: () => {
|
|
168
|
+
// Same content ⇒ same hash; the second upload's replace-`rename` publishes over the
|
|
169
|
+
// first, leaving exactly one shard in the store.
|
|
87
170
|
const content = vec8(0x2an);
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const [,
|
|
91
|
-
|
|
92
|
-
|
|
171
|
+
const c = fileCas(sha256)('.');
|
|
172
|
+
const payload = () => nonEmpty(ok(content), empty());
|
|
173
|
+
const [state1, w1] = virtual(emptyState)(c.write(payload()));
|
|
174
|
+
const [state2, w2] = virtual(state1)(c.write(payload()));
|
|
175
|
+
if (w1[0] !== 'ok' || w2[0] !== 'ok') {
|
|
176
|
+
throw ['expected both writes ok', w1, w2];
|
|
177
|
+
}
|
|
178
|
+
if (msb.cmp(w1[1])(w2[1]) !== 0) {
|
|
179
|
+
throw 'dedup hash mismatch';
|
|
180
|
+
}
|
|
181
|
+
const [, hashes] = virtual(state2)(c.list());
|
|
182
|
+
if (hashes.length !== 1) {
|
|
183
|
+
throw ['expected one shard after dedup', hashes.length];
|
|
93
184
|
}
|
|
94
185
|
},
|
|
95
|
-
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
186
|
+
casWriteErrorItemAborts: () => {
|
|
187
|
+
// An error item mid-stream deletes the partial staging file and fails; nothing is
|
|
188
|
+
// published, so the store stays empty.
|
|
189
|
+
const c = fileCas(sha256)('.');
|
|
190
|
+
const okItem = ok(vec8(0x11n));
|
|
191
|
+
const errItem = error({ code: 'BOOM' });
|
|
192
|
+
const payload = nonEmpty(okItem, nonEmpty(errItem, empty()));
|
|
193
|
+
const [state1, result] = virtual(emptyState)(c.write(payload));
|
|
194
|
+
if (result[0] !== 'error') {
|
|
195
|
+
throw ['expected write error', result];
|
|
101
196
|
}
|
|
102
|
-
|
|
103
|
-
|
|
197
|
+
const [, hashes] = virtual(state1)(c.list());
|
|
198
|
+
if (hashes.length !== 0) {
|
|
199
|
+
throw ['expected nothing published on abort', hashes];
|
|
104
200
|
}
|
|
105
201
|
},
|
|
106
|
-
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
202
|
+
casWriteReadExceedsMaxLength: () => {
|
|
203
|
+
// The point of streaming: a payload larger than a single `Vec`'s `maxLength`
|
|
204
|
+
// (128 KiB) must round-trip. `write` lands the chunks on disk without ever
|
|
205
|
+
// holding them as one `Vec`, and `read` streams them back the same way — so the
|
|
206
|
+
// round-trip is verified by hashing the read stream incrementally rather than
|
|
207
|
+
// concatenating it (which would itself overflow `maxLength`).
|
|
208
|
+
const big = vec(maxLength)(0xabn); // one full-size chunk: exactly maxLength bits
|
|
209
|
+
const tail = vec8(0x2an); // one more byte ⇒ total > maxLength
|
|
210
|
+
const chunks = [big, tail];
|
|
211
|
+
const c = fileCas(sha256)('.');
|
|
212
|
+
const payload = chunks.reduceRight((tl, chunk) => nonEmpty(ok(chunk), tl), empty());
|
|
213
|
+
const [state1, w] = virtual(emptyState)(c.write(payload));
|
|
214
|
+
if (w[0] !== 'ok') {
|
|
215
|
+
throw ['expected write ok', w];
|
|
216
|
+
}
|
|
217
|
+
const hash = w[1];
|
|
218
|
+
if (msb.cmp(hash)(computeSync(sha256)(chunks)) !== 0) {
|
|
219
|
+
throw 'oversized write hash mismatch';
|
|
113
220
|
}
|
|
114
|
-
|
|
115
|
-
|
|
221
|
+
// Fold the read stream straight into a fresh SHA-2 state — never one `Vec`.
|
|
222
|
+
const rehash = (state) => (stream) => stream.step((node) => {
|
|
223
|
+
if (node === undefined) {
|
|
224
|
+
return pure(ok(sha256.end(state)));
|
|
225
|
+
}
|
|
226
|
+
const { first, tail } = node;
|
|
227
|
+
if (first[0] === 'error') {
|
|
228
|
+
return pure(first);
|
|
229
|
+
}
|
|
230
|
+
return rehash(sha256.append(first[1])(state))(tail);
|
|
231
|
+
});
|
|
232
|
+
const [, readBack] = virtual(state1)(rehash(sha256.init)(c.read(hash)));
|
|
233
|
+
if (readBack[0] !== 'ok') {
|
|
234
|
+
throw ['expected read ok', readBack];
|
|
116
235
|
}
|
|
117
|
-
if (
|
|
118
|
-
throw '
|
|
236
|
+
if (msb.cmp(readBack[1])(hash) !== 0) {
|
|
237
|
+
throw 'oversized read-back hash mismatch';
|
|
119
238
|
}
|
|
120
239
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
240
|
+
casWriteGcReclaimsExpired: () => {
|
|
241
|
+
// A staging file whose deadline is in the past is reclaimed by the GC that `write`
|
|
242
|
+
// runs before staging its own file.
|
|
243
|
+
const stalePath = join('.', '.cas', '_stage', '0000000000000000000-stale');
|
|
244
|
+
const state0 = {
|
|
245
|
+
...emptyState,
|
|
246
|
+
epochNs: 1_000_000,
|
|
247
|
+
root: { '.cas': { '_stage': { '0000000000000000000-stale': [vec8(0x99n)] } } },
|
|
248
|
+
};
|
|
249
|
+
const content = vec8(0x2an);
|
|
250
|
+
const c = fileCas(sha256)('.');
|
|
251
|
+
const x = c.write(nonEmpty(ok(content), empty()));
|
|
252
|
+
const [state1, w] = virtual(state0)(x);
|
|
253
|
+
if (w[0] !== 'ok') {
|
|
254
|
+
throw ['expected write ok', w];
|
|
125
255
|
}
|
|
126
|
-
|
|
127
|
-
|
|
256
|
+
const [, present] = virtual(state1)(access(stalePath));
|
|
257
|
+
if (present[0] !== 'error') {
|
|
258
|
+
throw 'expected GC to reclaim the expired staging file';
|
|
128
259
|
}
|
|
129
260
|
},
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
261
|
+
casUploadSuccess: () => {
|
|
262
|
+
// A successful upload returns the hash and deletes the source file from cas_upload/.
|
|
263
|
+
const content = vec8(0x2an);
|
|
264
|
+
const state0 = { ...emptyState, root: { 'cas_upload': { 'myfile': [content] } } };
|
|
265
|
+
const [state1, result] = virtual(state0)(casUpload('.')('myfile'));
|
|
266
|
+
if (result[0] !== 'ok') {
|
|
267
|
+
throw ['expected casUpload ok', result];
|
|
134
268
|
}
|
|
135
|
-
if (
|
|
136
|
-
throw 'expected
|
|
269
|
+
if (length(result[1]) !== 256n) {
|
|
270
|
+
throw ['expected 256-bit hash', length(result[1])];
|
|
137
271
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
list: () => pure([]),
|
|
144
|
-
};
|
|
145
|
-
const c = cas(sha256)(store);
|
|
146
|
-
const [, hash] = run({})(undefined)(c.write(empty));
|
|
147
|
-
// sha256 of empty input produces a 256-bit hash
|
|
148
|
-
if (length(hash) !== 256n) {
|
|
149
|
-
throw ['expected 256-bit hash', length(hash)];
|
|
272
|
+
// Source must be deleted after successful publish.
|
|
273
|
+
const srcPath = join('.', 'cas_upload', 'myfile');
|
|
274
|
+
const [, srcAccess] = virtual(state1)(access(srcPath));
|
|
275
|
+
if (srcAccess[0] !== 'error') {
|
|
276
|
+
throw 'expected source to be deleted after successful upload';
|
|
150
277
|
}
|
|
151
278
|
},
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (listResult.length !== 1) {
|
|
166
|
-
throw ['list should pass through', listResult];
|
|
279
|
+
casUploadFailureKeepsSource: () => {
|
|
280
|
+
// A missing source file causes write to fail; casUpload returns error and the
|
|
281
|
+
// source is left in place (trivially: it was never there, but the upload is not published).
|
|
282
|
+
const state0 = { ...emptyState, root: {} };
|
|
283
|
+
const [state1, result] = virtual(state0)(casUpload('.')('nonexistent'));
|
|
284
|
+
if (result[0] !== 'error') {
|
|
285
|
+
throw ['expected casUpload to fail on missing source', result];
|
|
286
|
+
}
|
|
287
|
+
// Nothing published to the store.
|
|
288
|
+
const c = fileCas(sha256)('.');
|
|
289
|
+
const [, hashes] = virtual(state1)(c.list());
|
|
290
|
+
if (hashes.length !== 0) {
|
|
291
|
+
throw ['expected nothing published on failed upload', hashes];
|
|
167
292
|
}
|
|
168
293
|
},
|
|
169
294
|
};
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export declare const images: {
|
|
9
9
|
readonly ubuntu: {
|
|
10
|
-
readonly intel: "ubuntu-
|
|
11
|
-
readonly arm: "ubuntu-
|
|
10
|
+
readonly intel: "ubuntu-26.04";
|
|
11
|
+
readonly arm: "ubuntu-26.04-arm";
|
|
12
12
|
};
|
|
13
13
|
readonly macos: {
|
|
14
14
|
readonly intel: "macos-26-intel";
|
|
@@ -19,24 +19,24 @@ 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.33.0";
|
|
23
23
|
export declare const bun = "1.3.14";
|
|
24
|
-
export declare const deno = "2.
|
|
25
|
-
export declare const playwright = "1.61.
|
|
24
|
+
export declare const deno = "2.9.0";
|
|
25
|
+
export declare const playwright = "1.61.1";
|
|
26
26
|
export declare const node: {
|
|
27
|
-
readonly default: "26.
|
|
28
|
-
readonly node22: "22.
|
|
29
|
-
readonly node24: "24.
|
|
27
|
+
readonly default: "26.4.0";
|
|
28
|
+
readonly node22: "22.23.1";
|
|
29
|
+
readonly node24: "24.18.0";
|
|
30
30
|
};
|
|
31
|
-
export declare const wasmtime = "
|
|
31
|
+
export declare const wasmtime = "46.0.1";
|
|
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.20260627.2";
|
|
34
34
|
export declare const actions: {
|
|
35
|
-
readonly 'actions/checkout': "
|
|
36
|
-
readonly 'actions/setup-node': "v6";
|
|
37
|
-
readonly 'actions/cache': "
|
|
38
|
-
readonly 'denoland/setup-deno': "v2";
|
|
39
|
-
readonly 'oven-sh/setup-bun': "v2";
|
|
35
|
+
readonly 'actions/checkout': "v7";
|
|
36
|
+
readonly 'actions/setup-node': "v6.4.0";
|
|
37
|
+
readonly 'actions/cache': "v6.1.0";
|
|
38
|
+
readonly 'denoland/setup-deno': "v2.0.4";
|
|
39
|
+
readonly 'oven-sh/setup-bun': "v2.2.0";
|
|
40
40
|
readonly 'bytecodealliance/actions/wasmtime/setup': "v1";
|
|
41
41
|
readonly 'wasmerio/setup-wasmer': "v3.1";
|
|
42
42
|
readonly 'dtolnay/rust-toolchain': "1.96.0";
|
package/fs/ci/config/module.f.js
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
// https://docs.github.com/en/actions/reference/runners/github-hosted-runners#standard-github-hosted-runners-for-public-repositories
|
|
9
9
|
export const images = {
|
|
10
10
|
ubuntu: {
|
|
11
|
-
intel: 'ubuntu-
|
|
12
|
-
arm: 'ubuntu-
|
|
11
|
+
intel: 'ubuntu-26.04',
|
|
12
|
+
arm: 'ubuntu-26.04-arm'
|
|
13
13
|
},
|
|
14
14
|
macos: {
|
|
15
15
|
intel: 'macos-26-intel',
|
|
@@ -24,40 +24,40 @@ 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.33.0';
|
|
28
28
|
// https://bun.sh/
|
|
29
29
|
export const bun = '1.3.14';
|
|
30
30
|
// https://deno.com/
|
|
31
|
-
export const deno = '2.
|
|
31
|
+
export const deno = '2.9.0';
|
|
32
32
|
// https://www.npmjs.com/package/playwright
|
|
33
|
-
export const playwright = '1.61.
|
|
33
|
+
export const playwright = '1.61.1';
|
|
34
34
|
// https://nodejs.org/en/download
|
|
35
35
|
export const node = {
|
|
36
|
-
default: '26.
|
|
37
|
-
node22: '22.
|
|
38
|
-
node24: '24.
|
|
36
|
+
default: '26.4.0',
|
|
37
|
+
node22: '22.23.1',
|
|
38
|
+
node24: '24.18.0',
|
|
39
39
|
};
|
|
40
40
|
// https://github.com/bytecodealliance/wasmtime/releases
|
|
41
|
-
export const wasmtime = '
|
|
41
|
+
export const wasmtime = '46.0.1';
|
|
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.20260627.2';
|
|
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
|
-
'actions/setup-node': 'v6',
|
|
54
|
+
'actions/setup-node': 'v6.4.0',
|
|
55
55
|
// https://github.com/marketplace/actions/cache
|
|
56
|
-
'actions/cache': '
|
|
56
|
+
'actions/cache': 'v6.1.0',
|
|
57
57
|
// https://github.com/marketplace/actions/setup-deno
|
|
58
|
-
'denoland/setup-deno': 'v2',
|
|
58
|
+
'denoland/setup-deno': 'v2.0.4',
|
|
59
59
|
// https://github.com/marketplace/actions/setup-bun
|
|
60
|
-
'oven-sh/setup-bun': 'v2',
|
|
60
|
+
'oven-sh/setup-bun': 'v2.2.0',
|
|
61
61
|
// https://github.com/bytecodealliance/actions
|
|
62
62
|
'bytecodealliance/actions/wasmtime/setup': 'v1',
|
|
63
63
|
// https://github.com/wasmerio/setup-wasmer
|
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'],
|