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
|
@@ -9,7 +9,7 @@ const run = (root) => (path) => {
|
|
|
9
9
|
};
|
|
10
10
|
export const proof = {
|
|
11
11
|
parse: () => {
|
|
12
|
-
const result = run({ a: utf8('export default 1') })('a');
|
|
12
|
+
const result = run({ a: [utf8('export default 1')] })('a');
|
|
13
13
|
if (result[0] === 'error') {
|
|
14
14
|
throw result[1];
|
|
15
15
|
}
|
|
@@ -19,7 +19,7 @@ export const proof = {
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
parseWithSubModule: () => {
|
|
22
|
-
const result = run({ a: { b: utf8('import c from "c"\nexport default c'), c: utf8('export default 2') } })('a/b');
|
|
22
|
+
const result = run({ a: { b: [utf8('import c from "c"\nexport default c')], c: [utf8('export default 2')] } })('a/b');
|
|
23
23
|
if (result[0] === 'error') {
|
|
24
24
|
throw result[1];
|
|
25
25
|
}
|
|
@@ -30,10 +30,10 @@ export const proof = {
|
|
|
30
30
|
},
|
|
31
31
|
parseWithSubModules: () => {
|
|
32
32
|
const result = run({
|
|
33
|
-
a: utf8('import b from "b"\nimport c from "c"\nexport default [b,c,b]'),
|
|
34
|
-
b: utf8('import d from "d"\nexport default [0,d]'),
|
|
35
|
-
c: utf8('import d from "d"\nexport default [1,d]'),
|
|
36
|
-
d: utf8('export default 2'),
|
|
33
|
+
a: [utf8('import b from "b"\nimport c from "c"\nexport default [b,c,b]')],
|
|
34
|
+
b: [utf8('import d from "d"\nexport default [0,d]')],
|
|
35
|
+
c: [utf8('import d from "d"\nexport default [1,d]')],
|
|
36
|
+
d: [utf8('export default 2')],
|
|
37
37
|
})('a');
|
|
38
38
|
if (result[0] === 'error') {
|
|
39
39
|
throw result[1];
|
|
@@ -44,7 +44,7 @@ export const proof = {
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
parseWithFileNotFoundError: () => {
|
|
47
|
-
const result = run({ a: utf8('import b from "b"\nexport default b') })('a');
|
|
47
|
+
const result = run({ a: [utf8('import b from "b"\nexport default b')] })('a');
|
|
48
48
|
if (result[0] !== 'error') {
|
|
49
49
|
throw result;
|
|
50
50
|
}
|
|
@@ -54,9 +54,9 @@ export const proof = {
|
|
|
54
54
|
},
|
|
55
55
|
parseWithCycleError: () => {
|
|
56
56
|
const result = run({
|
|
57
|
-
a: utf8('import b from "b"\nimport c from "c"\nexport default [b,c,b]'),
|
|
58
|
-
b: utf8('import c from "c"\nexport default c'),
|
|
59
|
-
c: utf8('import b from "b"\nexport default b'),
|
|
57
|
+
a: [utf8('import b from "b"\nimport c from "c"\nexport default [b,c,b]')],
|
|
58
|
+
b: [utf8('import c from "c"\nexport default c')],
|
|
59
|
+
c: [utf8('import b from "b"\nexport default b')],
|
|
60
60
|
})('a');
|
|
61
61
|
if (result[0] !== 'error') {
|
|
62
62
|
throw result;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Effect, type Operation } from "../module.f.ts";
|
|
2
|
+
export type NonEmpty<O extends Operation, T> = {
|
|
3
|
+
readonly first: T;
|
|
4
|
+
readonly tail: List<O, T>;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* The payload of a `List` effect: the next cons cell, or `undefined` at
|
|
8
|
+
* end-of-stream.
|
|
9
|
+
*
|
|
10
|
+
* Since `Pure<T>` is now itself a lazy thunk (`() => T`), the effect alone is the
|
|
11
|
+
* suspension point and the cell needs no extra wrapping thunk. `Effect<O, Next<O, T>>`
|
|
12
|
+
* is used directly in places where `List<O, T>` cannot be written as a return type
|
|
13
|
+
* (see {@link empty}).
|
|
14
|
+
*/
|
|
15
|
+
export type Next<O extends Operation, T> = NonEmpty<O, T> | undefined;
|
|
16
|
+
export type List<O extends Operation, T> = Effect<O, Next<O, T>>;
|
|
17
|
+
/**
|
|
18
|
+
* The empty `List`: a pure end-of-stream marker (`undefined`).
|
|
19
|
+
*
|
|
20
|
+
* The explicit `Effect<O, Next<O, T>>` return type lets the contextual type drive the
|
|
21
|
+
* check, so the recursive payload type-checks without a cast. Construct streams through
|
|
22
|
+
* these two combinators.
|
|
23
|
+
*
|
|
24
|
+
* Note: we use `Effect<O, Next<O, T>>` because TypeScript can't convert `pure(...)` to
|
|
25
|
+
* `List<O, T>`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const empty: <O extends Operation, T>() => Effect<O, Next<O, T>>;
|
|
28
|
+
/**
|
|
29
|
+
* Prepends `head` to a `ListEffect` `tail`, as a pure cons cell. See {@link empty}.
|
|
30
|
+
*/
|
|
31
|
+
export declare const nonEmpty: <O extends Operation, T>(first: T, tail: List<O, T>) => Effect<O, Next<O, T>>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { pure } from "../module.f.js";
|
|
2
|
+
/**
|
|
3
|
+
* The empty `List`: a pure end-of-stream marker (`undefined`).
|
|
4
|
+
*
|
|
5
|
+
* The explicit `Effect<O, Next<O, T>>` return type lets the contextual type drive the
|
|
6
|
+
* check, so the recursive payload type-checks without a cast. Construct streams through
|
|
7
|
+
* these two combinators.
|
|
8
|
+
*
|
|
9
|
+
* Note: we use `Effect<O, Next<O, T>>` because TypeScript can't convert `pure(...)` to
|
|
10
|
+
* `List<O, T>`.
|
|
11
|
+
*/
|
|
12
|
+
export const empty = () => pure(undefined);
|
|
13
|
+
/**
|
|
14
|
+
* Prepends `head` to a `ListEffect` `tail`, as a pure cons cell. See {@link empty}.
|
|
15
|
+
*/
|
|
16
|
+
export const nonEmpty = (first, tail) => pure({ first, tail });
|
package/fs/effects/module.f.d.ts
CHANGED
|
@@ -10,12 +10,19 @@ export type Effect<O extends Operation, T> = {
|
|
|
10
10
|
step: <Q extends Operation, R>(f: (p: T) => Effect<Q, R>) => Effect<O | Q, R>;
|
|
11
11
|
};
|
|
12
12
|
export type Value<O extends Operation, T> = Pure<T> | Do<O, T>;
|
|
13
|
-
export type Pure<T> =
|
|
13
|
+
export type Pure<T> = () => T;
|
|
14
14
|
export type DoKPR<O extends Operation, T, K extends string, PR extends readonly [unknown, unknown]> = readonly [K, PR[0], (_: PR[1]) => Effect<O, T>];
|
|
15
15
|
export type Pr<O extends Operation, K extends O[0]> = O extends readonly [K, (...args: infer P) => infer R] ? readonly [P, R] : never;
|
|
16
16
|
export type DoK<O extends Operation, T, K extends O[0]> = DoKPR<O, T, K, Pr<O, K>>;
|
|
17
17
|
export type Do<O extends Operation, T> = DoK<O, T, O[0]>;
|
|
18
18
|
export declare const pure: <T>(v: T) => Effect<never, T>;
|
|
19
|
+
/**
|
|
20
|
+
* A lazy pure effect. Like {@link pure}, but takes a thunk and evaluates it on
|
|
21
|
+
* demand instead of holding an already-computed value. Use this to produce the
|
|
22
|
+
* next item of a list without instantiating the rest — the thunk runs only when
|
|
23
|
+
* the effect is decoded (or stepped into).
|
|
24
|
+
*/
|
|
25
|
+
export declare const lazy: <T>(t: () => T) => Effect<never, T>;
|
|
19
26
|
export declare const doFull: <O extends Operation, T, K extends O[0]>(cmd: K, param: Pr<O, K>[0], cont: (input: Pr<O, K>[1]) => Effect<O, T>) => Effect<O, T>;
|
|
20
27
|
export type Param<O extends Operation> = F<O>[0];
|
|
21
28
|
export type Return<O extends Operation> = F<O>[1];
|
|
@@ -53,10 +60,10 @@ export type Decoded<O extends Operation, T> = {
|
|
|
53
60
|
/**
|
|
54
61
|
* Decodes an effect's next step: a pure result, or a command to perform.
|
|
55
62
|
*
|
|
56
|
-
* This is the only function that knows how `Value` is laid out (a
|
|
57
|
-
*
|
|
63
|
+
* This is the only function that knows how `Value` is laid out (a thunk
|
|
64
|
+
* `() => T` for `Pure`, a `[command, payload, continuation]` tuple for `Do`).
|
|
58
65
|
* Interpreters and proofs must go through `decode` (or `match`) instead of
|
|
59
|
-
* inspecting the
|
|
66
|
+
* inspecting the value, so the representation can change without touching
|
|
60
67
|
* them.
|
|
61
68
|
*/
|
|
62
69
|
export declare const decode: <O extends Operation, T>({ value }: Effect<O, T>) => Decoded<O, T>;
|
|
@@ -82,4 +89,3 @@ export type ToAsyncOperationMap<O extends Operation> = {
|
|
|
82
89
|
};
|
|
83
90
|
export type F<O extends Operation> = Pr<O, O[0]>;
|
|
84
91
|
export type Func<O extends Operation> = (..._: Param<O>) => Effect<O, Return<O>>;
|
|
85
|
-
export type ListEffect<O extends Operation, T> = Effect<O, readonly [T, ListEffect<O, T>] | undefined>;
|
package/fs/effects/module.f.js
CHANGED
|
@@ -5,9 +5,19 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { fold } from "../types/list/module.f.js";
|
|
7
7
|
export const pure = (v) => ({
|
|
8
|
-
value:
|
|
8
|
+
value: () => v,
|
|
9
9
|
step: f => f(v)
|
|
10
10
|
});
|
|
11
|
+
/**
|
|
12
|
+
* A lazy pure effect. Like {@link pure}, but takes a thunk and evaluates it on
|
|
13
|
+
* demand instead of holding an already-computed value. Use this to produce the
|
|
14
|
+
* next item of a list without instantiating the rest — the thunk runs only when
|
|
15
|
+
* the effect is decoded (or stepped into).
|
|
16
|
+
*/
|
|
17
|
+
export const lazy = (t) => ({
|
|
18
|
+
value: t,
|
|
19
|
+
step: f => f(t())
|
|
20
|
+
});
|
|
11
21
|
export const doFull = (cmd, param, cont) => ({
|
|
12
22
|
value: [cmd, param, cont],
|
|
13
23
|
step: (f) => doFull(cmd, param, x => cont(x).step(f)),
|
|
@@ -32,14 +42,14 @@ export const forEachStep = (f) => (items) => foldStep((item) => () => f(item))(u
|
|
|
32
42
|
/**
|
|
33
43
|
* Decodes an effect's next step: a pure result, or a command to perform.
|
|
34
44
|
*
|
|
35
|
-
* This is the only function that knows how `Value` is laid out (a
|
|
36
|
-
*
|
|
45
|
+
* This is the only function that knows how `Value` is laid out (a thunk
|
|
46
|
+
* `() => T` for `Pure`, a `[command, payload, continuation]` tuple for `Do`).
|
|
37
47
|
* Interpreters and proofs must go through `decode` (or `match`) instead of
|
|
38
|
-
* inspecting the
|
|
48
|
+
* inspecting the value, so the representation can change without touching
|
|
39
49
|
* them.
|
|
40
50
|
*/
|
|
41
|
-
export const decode = ({ value }) => value
|
|
42
|
-
? { done: true, result: value
|
|
51
|
+
export const decode = ({ value }) => typeof value === 'function'
|
|
52
|
+
? { done: true, result: value() }
|
|
43
53
|
: { done: false, command: value[0], payload: value[1], continuation: value[2] };
|
|
44
54
|
/**
|
|
45
55
|
* Decodes an effect's next step and dispatches its command to `map`,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Vec } from '../../types/bit_vec/module.f.ts';
|
|
2
2
|
import type { MemOp } from '../memory/module.f.ts';
|
|
3
3
|
import type { Nominal } from '../../types/nominal/module.f.ts';
|
|
4
4
|
import { type Result } from '../../types/result/module.f.ts';
|
|
5
5
|
import type { StringMap } from '../../types/object/module.f.ts';
|
|
6
6
|
import { type Effect, type Func, type Operation, type ToAsyncOperationMap } from '../module.f.ts';
|
|
7
|
+
import type { List } from '../list/module.f.ts';
|
|
7
8
|
export type IoResult<T> = Result<T, unknown>;
|
|
8
9
|
/**
|
|
9
10
|
* True if `e` is a "file or directory does not exist" (`ENOENT`) error.
|
|
@@ -81,7 +82,32 @@ export type Exec = readonly ['exec', (command: string, stdin?: string) => IoResu
|
|
|
81
82
|
export declare const exec: Func<Exec>;
|
|
82
83
|
export type Access = readonly ['access', (path: string) => IoResult<void>];
|
|
83
84
|
export declare const access: Func<Access>;
|
|
84
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Creates `path` as an empty file with `O_CREAT|O_EXCL` — fails if it already
|
|
87
|
+
* exists. This is the exclusive create that claims a staging name in the
|
|
88
|
+
* lock-free upload ([staging-lease.md](../../../issues/cas/staging-lease.md));
|
|
89
|
+
* with 256 random bits in the name `EEXIST` never happens in practice, so it
|
|
90
|
+
* is just a sanity guard.
|
|
91
|
+
*/
|
|
92
|
+
export type CreateExclusive = readonly ['createExclusive', (path: string) => IoResult<void>];
|
|
93
|
+
export declare const createExclusive: Func<CreateExclusive>;
|
|
94
|
+
/**
|
|
95
|
+
* Writes the **entire** `data` vector to an **existing** `path` at byte `offset`
|
|
96
|
+
* (positional write). The mirror of {@link readBytes}: it never creates the file
|
|
97
|
+
* (a missing path is `ENOENT`), and it writes every byte or returns an error —
|
|
98
|
+
* the runner loops over short writes — so a later size check can never pass over
|
|
99
|
+
* a hole. Bounded to ≤128 KiB per call, like `readBytes`.
|
|
100
|
+
*/
|
|
101
|
+
export type WriteBytes = readonly ['writeBytes', (path: string, offset: number, data: Vec) => IoResult<void>];
|
|
102
|
+
export declare const writeBytes: Func<WriteBytes>;
|
|
103
|
+
export declare const writeFromStream: <O extends Operation>(path: string, e: List<O, IoResult<Vec>>) => Effect<O | WriteBytes | CreateExclusive, IoResult<void>>;
|
|
104
|
+
/** File metadata returned by {@link stat}. Only `size` (in bytes) for now. */
|
|
105
|
+
export type FileStat = {
|
|
106
|
+
readonly size: number;
|
|
107
|
+
};
|
|
108
|
+
export type Stat = readonly ['stat', (path: string) => IoResult<FileStat>];
|
|
109
|
+
export declare const stat: Func<Stat>;
|
|
110
|
+
export type Fs = Mkdir | ReadFile | ReadBytes | Readdir | WriteFile | Rm | Rename | Exec | Access | CreateExclusive | WriteBytes | Stat;
|
|
85
111
|
export type Server = Nominal<'server', `160855c4f69310fece3273c1853ac32de43dee1eb41bf59d821917f8eebe9272`, unknown>;
|
|
86
112
|
export type Headers = StringMap<string, string>;
|
|
87
113
|
export type IncomingMessage = {
|
|
@@ -216,7 +242,7 @@ export type TestContext = {
|
|
|
216
242
|
/** Effect operation that registers a named test with the active `TestContext`. */
|
|
217
243
|
export type Test = readonly ['test', (ctx: TestContext, name: string, expectFailure: boolean, test: (t: TestContext) => Effect<Test | All | Await, void>) => void];
|
|
218
244
|
export declare const test: Func<Test>;
|
|
219
|
-
export type NodeOp = All | Await | Fetch | Fs | Http | Forever | Import | MemOp | Now | RandomInt | Read | Sandbox | Write | Test;
|
|
245
|
+
export type NodeOp = Access | All | Await | Fetch | Fs | Http | Forever | Import | MemOp | Now | RandomInt | Read | Sandbox | Write | Test;
|
|
220
246
|
export type NodeEffect<T> = Effect<NodeOp, T>;
|
|
221
247
|
/**
|
|
222
248
|
* Writes an error line to `stderr` and yields exit code `1`. The canonical
|
|
@@ -12,7 +12,8 @@ import { utf8, utf8ToString } from "../../text/module.f.js";
|
|
|
12
12
|
import { toCodePointList } from "../../text/utf8/module.f.js";
|
|
13
13
|
import { codePointListToString } from "../../text/utf16/module.f.js";
|
|
14
14
|
import { reverse } from "../../types/list/module.f.js";
|
|
15
|
-
import {
|
|
15
|
+
import { length } from "../../types/bit_vec/module.f.js";
|
|
16
|
+
import { ok, error as resultError } from "../../types/result/module.f.js";
|
|
16
17
|
import { do_, pure } from "../module.f.js";
|
|
17
18
|
/**
|
|
18
19
|
* True if `e` is a "file or directory does not exist" (`ENOENT`) error.
|
|
@@ -53,6 +54,39 @@ export const readBytes = do_('readBytes');
|
|
|
53
54
|
export const randomInt = do_('randomInt');
|
|
54
55
|
export const exec = do_('exec');
|
|
55
56
|
export const access = do_('access');
|
|
57
|
+
export const createExclusive = do_('createExclusive');
|
|
58
|
+
export const writeBytes = do_('writeBytes');
|
|
59
|
+
const writeLoop = (path) => {
|
|
60
|
+
const f = (offset, e) => e.step(r => {
|
|
61
|
+
if (r === undefined) {
|
|
62
|
+
return pure(ok(undefined));
|
|
63
|
+
}
|
|
64
|
+
const { first: [t, v], tail } = r;
|
|
65
|
+
if (t === 'error') {
|
|
66
|
+
return pure(resultError(v));
|
|
67
|
+
}
|
|
68
|
+
const lenV = length(v);
|
|
69
|
+
if ((lenV & 7n) !== 0n) {
|
|
70
|
+
return pure(resultError('invalid buffer size'));
|
|
71
|
+
}
|
|
72
|
+
return writeBytes(path, offset, v)
|
|
73
|
+
.step((r) => {
|
|
74
|
+
if (r[0] === 'error') {
|
|
75
|
+
return pure(r);
|
|
76
|
+
}
|
|
77
|
+
return f(offset + Number(lenV >> 3n), tail);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
return f;
|
|
81
|
+
};
|
|
82
|
+
export const writeFromStream = (path, e) => createExclusive(path)
|
|
83
|
+
.step(([r, v]) => {
|
|
84
|
+
if (r === 'error') {
|
|
85
|
+
return pure(resultError(v));
|
|
86
|
+
}
|
|
87
|
+
return writeLoop(path)(0, e);
|
|
88
|
+
});
|
|
89
|
+
export const stat = do_('stat');
|
|
56
90
|
export const createServer = do_('createServer');
|
|
57
91
|
export const listen = do_('listen');
|
|
58
92
|
export const forever = do_('forever');
|
|
@@ -35,6 +35,7 @@ import { memoryOperationMap } from "./memory/module.js";
|
|
|
35
35
|
import {} from "./module.f.js";
|
|
36
36
|
import { asBase, asNominal } from "../../types/nominal/module.f.js";
|
|
37
37
|
import { error, ok } from "../../types/result/module.f.js";
|
|
38
|
+
import { asyncTryCatch } from "../../types/result/module.js";
|
|
38
39
|
import { fromVec, listToVec, toVec } from "../../types/uint8array/module.f.js";
|
|
39
40
|
import { maxLengthBytes } from "../../types/bit_vec/module.f.js";
|
|
40
41
|
/**
|
|
@@ -44,14 +45,6 @@ import { maxLengthBytes } from "../../types/bit_vec/module.f.js";
|
|
|
44
45
|
* requires them present; this local view keeps the narrowing in one place.
|
|
45
46
|
*/
|
|
46
47
|
const createServer = http.createServer;
|
|
47
|
-
const tc = async (f) => {
|
|
48
|
-
try {
|
|
49
|
-
return ok(await f());
|
|
50
|
-
}
|
|
51
|
-
catch (e) {
|
|
52
|
-
return error(e);
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
48
|
const collect = async (v) => {
|
|
56
49
|
let result = [];
|
|
57
50
|
for await (const a of v) {
|
|
@@ -156,15 +149,15 @@ const readStdinByte = async () => {
|
|
|
156
149
|
const runNodeEffect = asyncRun({
|
|
157
150
|
...memoryOperationMap(),
|
|
158
151
|
all: async (...effects) => await Promise.all(effects.map(runNodeEffect)),
|
|
159
|
-
fetch: async (url) =>
|
|
152
|
+
fetch: async (url) => asyncTryCatch(async () => {
|
|
160
153
|
const response = await fetch(url);
|
|
161
154
|
if (!response.ok) {
|
|
162
155
|
throw new Error(`Fetch error: ${response.status} ${response.statusText}`);
|
|
163
156
|
}
|
|
164
157
|
return toVec(new Uint8Array(await response.arrayBuffer()));
|
|
165
158
|
}),
|
|
166
|
-
mkdir: (...p) =>
|
|
167
|
-
readFile: path =>
|
|
159
|
+
mkdir: (...p) => asyncTryCatch(async () => { await mkdir(...p); }),
|
|
160
|
+
readFile: path => asyncTryCatch(async () => {
|
|
168
161
|
const fileStats = await stat(path);
|
|
169
162
|
// if the file is too big, toVec should fail anyway but in this case we don't want to load the file.
|
|
170
163
|
if (fileStats.size > maxFileSizeBytes) {
|
|
@@ -172,16 +165,16 @@ const runNodeEffect = asyncRun({
|
|
|
172
165
|
}
|
|
173
166
|
return toVec(await readFile(path));
|
|
174
167
|
}),
|
|
175
|
-
readdir: (path, r) =>
|
|
168
|
+
readdir: (path, r) => asyncTryCatch(async () => (await readdir(path, { ...r, withFileTypes: true }))
|
|
176
169
|
.map(v => ({
|
|
177
170
|
name: v.name,
|
|
178
171
|
parentPath: normalize(v.parentPath),
|
|
179
172
|
isFile: v.isFile()
|
|
180
173
|
}))),
|
|
181
|
-
writeFile: (path, data) =>
|
|
182
|
-
rm: path =>
|
|
183
|
-
rename: (src, dst) =>
|
|
184
|
-
readBytes: (path, offset, size) =>
|
|
174
|
+
writeFile: (path, data) => asyncTryCatch(() => writeFile(path, fromVec(data))),
|
|
175
|
+
rm: path => asyncTryCatch(() => rm(path)),
|
|
176
|
+
rename: (src, dst) => asyncTryCatch(() => rename(src, dst)),
|
|
177
|
+
readBytes: (path, offset, size) => asyncTryCatch(async () => {
|
|
185
178
|
if (offset < 0) {
|
|
186
179
|
throw new Error(`Offset ${offset} is negative`);
|
|
187
180
|
}
|
|
@@ -199,8 +192,29 @@ const runNodeEffect = asyncRun({
|
|
|
199
192
|
}
|
|
200
193
|
}),
|
|
201
194
|
randomInt: async () => crypto.randomInt(2 ** 32),
|
|
202
|
-
access: path =>
|
|
203
|
-
|
|
195
|
+
access: path => asyncTryCatch(() => access(path)),
|
|
196
|
+
createExclusive: path => asyncTryCatch(async () => {
|
|
197
|
+
const fh = await open(path, 'wx');
|
|
198
|
+
await fh.close();
|
|
199
|
+
}),
|
|
200
|
+
writeBytes: (path, offset, data) => asyncTryCatch(async () => {
|
|
201
|
+
const fh = await open(path, 'r+');
|
|
202
|
+
try {
|
|
203
|
+
const buffer = fromVec(data);
|
|
204
|
+
// Loop over short writes so the whole Vec lands — a partial pwrite would
|
|
205
|
+
// leave a hole the publish-time size check could pass over.
|
|
206
|
+
let written = 0;
|
|
207
|
+
while (written < buffer.length) {
|
|
208
|
+
const { bytesWritten } = await fh.write(buffer, written, buffer.length - written, offset + written);
|
|
209
|
+
written += bytesWritten;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
await fh.close();
|
|
214
|
+
}
|
|
215
|
+
}),
|
|
216
|
+
stat: path => asyncTryCatch(async () => ({ size: (await stat(path)).size })),
|
|
217
|
+
import: path => asyncTryCatch(() => asyncImport(path)),
|
|
204
218
|
exec: (command, stdin) => new Promise(resolve => {
|
|
205
219
|
const child = exec(command, (e, stdout, stderr) => resolve(e !== null ? ['error', e] : ok({ stdout, stderr })));
|
|
206
220
|
child.stdin?.end(stdin);
|
|
@@ -51,7 +51,7 @@ export const proof = {
|
|
|
51
51
|
throw result;
|
|
52
52
|
}
|
|
53
53
|
const a = state.root.a;
|
|
54
|
-
if (a === undefined ||
|
|
54
|
+
if (a === undefined || Array.isArray(a)) {
|
|
55
55
|
throw a;
|
|
56
56
|
}
|
|
57
57
|
},
|
|
@@ -61,11 +61,11 @@ export const proof = {
|
|
|
61
61
|
throw result;
|
|
62
62
|
}
|
|
63
63
|
const tmp = state.root.tmp;
|
|
64
|
-
if (typeof tmp !== 'object') {
|
|
64
|
+
if (typeof tmp !== 'object' || Array.isArray(tmp)) {
|
|
65
65
|
throw state.root;
|
|
66
66
|
}
|
|
67
67
|
const cache = tmp.cache;
|
|
68
|
-
if (typeof cache !== 'object') {
|
|
68
|
+
if (typeof cache !== 'object' || Array.isArray(cache)) {
|
|
69
69
|
throw tmp;
|
|
70
70
|
}
|
|
71
71
|
},
|
|
@@ -84,7 +84,7 @@ export const proof = {
|
|
|
84
84
|
const initial = {
|
|
85
85
|
...emptyState,
|
|
86
86
|
root: {
|
|
87
|
-
hello: vec8(0x2an),
|
|
87
|
+
hello: [vec8(0x2an)],
|
|
88
88
|
},
|
|
89
89
|
};
|
|
90
90
|
const [state, [t, result]] = virtual(initial)(readFile('hello'));
|
|
@@ -104,7 +104,7 @@ export const proof = {
|
|
|
104
104
|
nested: () => {
|
|
105
105
|
const [_, [tag, result]] = virtual({
|
|
106
106
|
...emptyState,
|
|
107
|
-
root: { tmp: { cache: vec8(0x15n) } }
|
|
107
|
+
root: { tmp: { cache: [vec8(0x15n)] } }
|
|
108
108
|
})(readFile('tmp/cache'));
|
|
109
109
|
if (tag === 'error') {
|
|
110
110
|
throw result;
|
|
@@ -133,7 +133,7 @@ export const proof = {
|
|
|
133
133
|
const initial = {
|
|
134
134
|
...emptyState,
|
|
135
135
|
root: {
|
|
136
|
-
smallFile: vec8(0x2an),
|
|
136
|
+
smallFile: [vec8(0x2an)],
|
|
137
137
|
},
|
|
138
138
|
};
|
|
139
139
|
const [_, [t, result]] = virtual(initial)(readFile('smallFile'));
|
|
@@ -149,7 +149,7 @@ export const proof = {
|
|
|
149
149
|
ok: () => {
|
|
150
150
|
const [_, [t, result]] = virtual({
|
|
151
151
|
...emptyState,
|
|
152
|
-
root: { hello: utf8('Hello, world!') },
|
|
152
|
+
root: { hello: [utf8('Hello, world!')] },
|
|
153
153
|
})(readUtf8File('hello'));
|
|
154
154
|
if (t !== 'ok') {
|
|
155
155
|
throw result;
|
|
@@ -170,9 +170,9 @@ export const proof = {
|
|
|
170
170
|
const [_, [t, result]] = virtual({
|
|
171
171
|
...emptyState,
|
|
172
172
|
root: {
|
|
173
|
-
file: vec8(0x2an),
|
|
173
|
+
file: [vec8(0x2an)],
|
|
174
174
|
dir: {
|
|
175
|
-
a: empty
|
|
175
|
+
a: [empty]
|
|
176
176
|
},
|
|
177
177
|
},
|
|
178
178
|
})(readdir('', { recursive: true }));
|
|
@@ -192,9 +192,9 @@ export const proof = {
|
|
|
192
192
|
const [_, [t, result]] = virtual({
|
|
193
193
|
...emptyState,
|
|
194
194
|
root: {
|
|
195
|
-
file: vec8(0x2an),
|
|
195
|
+
file: [vec8(0x2an)],
|
|
196
196
|
dir: {
|
|
197
|
-
a: empty
|
|
197
|
+
a: [empty]
|
|
198
198
|
},
|
|
199
199
|
},
|
|
200
200
|
})(readdir('', {}));
|
|
@@ -216,7 +216,7 @@ export const proof = {
|
|
|
216
216
|
nested: () => {
|
|
217
217
|
const [_, [t, result]] = virtual({
|
|
218
218
|
...emptyState,
|
|
219
|
-
root: { tmp: { cache: vec8(0x15n) } }
|
|
219
|
+
root: { tmp: { cache: [vec8(0x15n)] } }
|
|
220
220
|
})(readdir('tmp', { recursive: true }));
|
|
221
221
|
if (t !== 'ok') {
|
|
222
222
|
throw result;
|
|
@@ -249,10 +249,10 @@ export const proof = {
|
|
|
249
249
|
throw result;
|
|
250
250
|
}
|
|
251
251
|
const file = state.root.hello;
|
|
252
|
-
if (!
|
|
252
|
+
if (!Array.isArray(file)) {
|
|
253
253
|
throw file;
|
|
254
254
|
}
|
|
255
|
-
if (uint(file) !== 0x2an) {
|
|
255
|
+
if (uint(file[0]) !== 0x2an) {
|
|
256
256
|
throw file;
|
|
257
257
|
}
|
|
258
258
|
},
|
|
@@ -260,17 +260,17 @@ export const proof = {
|
|
|
260
260
|
const [state, [t, result]] = virtual({
|
|
261
261
|
...emptyState,
|
|
262
262
|
root: {
|
|
263
|
-
hello: vec8(0x15n),
|
|
263
|
+
hello: [vec8(0x15n)],
|
|
264
264
|
},
|
|
265
265
|
})(writeFile('hello', vec8(0x2an)));
|
|
266
266
|
if (t !== 'ok') {
|
|
267
267
|
throw result;
|
|
268
268
|
}
|
|
269
269
|
const file = state.root.hello;
|
|
270
|
-
if (!
|
|
270
|
+
if (!Array.isArray(file)) {
|
|
271
271
|
throw file;
|
|
272
272
|
}
|
|
273
|
-
if (uint(file) !== 0x2an) {
|
|
273
|
+
if (uint(file[0]) !== 0x2an) {
|
|
274
274
|
throw file;
|
|
275
275
|
}
|
|
276
276
|
},
|
|
@@ -300,7 +300,7 @@ export const proof = {
|
|
|
300
300
|
throw result;
|
|
301
301
|
}
|
|
302
302
|
const tmp = state.root.tmp;
|
|
303
|
-
if (tmp === undefined ||
|
|
303
|
+
if (tmp === undefined || Array.isArray(tmp)) {
|
|
304
304
|
throw tmp;
|
|
305
305
|
}
|
|
306
306
|
},
|
|
@@ -311,10 +311,10 @@ export const proof = {
|
|
|
311
311
|
throw result;
|
|
312
312
|
}
|
|
313
313
|
const file = state.root.hello;
|
|
314
|
-
if (!
|
|
314
|
+
if (!Array.isArray(file)) {
|
|
315
315
|
throw file;
|
|
316
316
|
}
|
|
317
|
-
if (utf8ToString(file) !== 'Hello, world!') {
|
|
317
|
+
if (utf8ToString(file[0]) !== 'Hello, world!') {
|
|
318
318
|
throw file;
|
|
319
319
|
}
|
|
320
320
|
},
|
|
@@ -322,7 +322,7 @@ export const proof = {
|
|
|
322
322
|
one: () => {
|
|
323
323
|
const [state, [t, result]] = virtual({
|
|
324
324
|
...emptyState,
|
|
325
|
-
root: { hello: vec8(0x2an) },
|
|
325
|
+
root: { hello: [vec8(0x2an)] },
|
|
326
326
|
})(rm('hello'));
|
|
327
327
|
if (t !== 'ok') {
|
|
328
328
|
throw result;
|
|
@@ -334,13 +334,13 @@ export const proof = {
|
|
|
334
334
|
nested: () => {
|
|
335
335
|
const [state, [t, result]] = virtual({
|
|
336
336
|
...emptyState,
|
|
337
|
-
root: { tmp: { cache: vec8(0x15n) } },
|
|
337
|
+
root: { tmp: { cache: [vec8(0x15n)] } },
|
|
338
338
|
})(rm('tmp/cache'));
|
|
339
339
|
if (t !== 'ok') {
|
|
340
340
|
throw result;
|
|
341
341
|
}
|
|
342
342
|
const tmp = state.root.tmp;
|
|
343
|
-
if (typeof tmp !== 'object') {
|
|
343
|
+
if (typeof tmp !== 'object' || Array.isArray(tmp)) {
|
|
344
344
|
throw state.root;
|
|
345
345
|
}
|
|
346
346
|
if (tmp.cache !== undefined) {
|
|
@@ -376,8 +376,8 @@ export const proof = {
|
|
|
376
376
|
const [_, results] = virtual({
|
|
377
377
|
...emptyState,
|
|
378
378
|
root: {
|
|
379
|
-
a: vec8(0x2an),
|
|
380
|
-
b: vec8(0x15n),
|
|
379
|
+
a: [vec8(0x2an)],
|
|
380
|
+
b: [vec8(0x15n)],
|
|
381
381
|
},
|
|
382
382
|
})(both(readFile('a'))(readFile('b')));
|
|
383
383
|
if (results[0][0] !== 'ok') {
|
|
@@ -446,7 +446,7 @@ export const proof = {
|
|
|
446
446
|
fileOverFile: () => {
|
|
447
447
|
const [state, [t, result]] = virtual({
|
|
448
448
|
...emptyState,
|
|
449
|
-
root: { src: vec8(0x2an), dst: vec8(0x15n) },
|
|
449
|
+
root: { src: [vec8(0x2an)], dst: [vec8(0x15n)] },
|
|
450
450
|
})(rename('src', 'dst'));
|
|
451
451
|
if (t !== 'ok') {
|
|
452
452
|
throw result;
|
|
@@ -454,23 +454,23 @@ export const proof = {
|
|
|
454
454
|
if (state.root.src !== undefined) {
|
|
455
455
|
throw state.root;
|
|
456
456
|
}
|
|
457
|
-
if (!
|
|
457
|
+
if (!Array.isArray(state.root.dst)) {
|
|
458
458
|
throw state.root;
|
|
459
459
|
}
|
|
460
|
-
if (uint(state.root.dst) !== 0x2an) {
|
|
460
|
+
if (uint(state.root.dst[0]) !== 0x2an) {
|
|
461
461
|
throw state.root;
|
|
462
462
|
}
|
|
463
463
|
},
|
|
464
464
|
nestedRename: () => {
|
|
465
465
|
const [state, [t, result]] = virtual({
|
|
466
466
|
...emptyState,
|
|
467
|
-
root: { tmp: { src: vec8(0x2an) } },
|
|
467
|
+
root: { tmp: { src: [vec8(0x2an)] } },
|
|
468
468
|
})(rename('tmp/src', 'tmp/dst'));
|
|
469
469
|
if (t !== 'ok') {
|
|
470
470
|
throw result;
|
|
471
471
|
}
|
|
472
472
|
const tmp = state.root.tmp;
|
|
473
|
-
if (typeof tmp !== 'object') {
|
|
473
|
+
if (typeof tmp !== 'object' || Array.isArray(tmp)) {
|
|
474
474
|
throw state.root;
|
|
475
475
|
}
|
|
476
476
|
if (tmp.src !== undefined) {
|
|
@@ -480,12 +480,12 @@ export const proof = {
|
|
|
480
480
|
dirOverFile: () => {
|
|
481
481
|
const [state, [t, result]] = virtual({
|
|
482
482
|
...emptyState,
|
|
483
|
-
root: { src: {}, dst: vec8(0x15n) },
|
|
483
|
+
root: { src: {}, dst: [vec8(0x15n)] },
|
|
484
484
|
})(rename('src', 'dst'));
|
|
485
485
|
if (t !== 'error') {
|
|
486
486
|
throw result;
|
|
487
487
|
}
|
|
488
|
-
if (!
|
|
488
|
+
if (!Array.isArray(state.root.dst)) {
|
|
489
489
|
throw state.root;
|
|
490
490
|
}
|
|
491
491
|
},
|
|
@@ -500,8 +500,8 @@ export const proof = {
|
|
|
500
500
|
simple: () => {
|
|
501
501
|
const [_, [t, result]] = virtual({
|
|
502
502
|
...emptyState,
|
|
503
|
-
root: { file: vec8(
|
|
504
|
-
})(readBytes('file', 0,
|
|
503
|
+
root: { file: [vec8(0xabn)] },
|
|
504
|
+
})(readBytes('file', 0, 1));
|
|
505
505
|
if (t !== 'ok') {
|
|
506
506
|
throw result;
|
|
507
507
|
}
|
|
@@ -512,7 +512,7 @@ export const proof = {
|
|
|
512
512
|
withOffset: () => {
|
|
513
513
|
const [_, [t, result]] = virtual({
|
|
514
514
|
...emptyState,
|
|
515
|
-
root: { file: vec8(
|
|
515
|
+
root: { file: [vec8(0xabn), vec8(0xcdn)] },
|
|
516
516
|
})(readBytes('file', 1, 1));
|
|
517
517
|
if (t !== 'ok') {
|
|
518
518
|
throw result;
|
|
@@ -524,7 +524,7 @@ export const proof = {
|
|
|
524
524
|
oversizeChunk: () => {
|
|
525
525
|
const [_, [t, result]] = virtual({
|
|
526
526
|
...emptyState,
|
|
527
|
-
root: { file: vec8(0x2an) },
|
|
527
|
+
root: { file: [vec8(0x2an)] },
|
|
528
528
|
})(readBytes('file', 0, Number(2n ** 32n)));
|
|
529
529
|
if (t !== 'error') {
|
|
530
530
|
throw result;
|