functionalscript 0.18.0 → 0.19.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/asn.1/module.f.js +7 -8
- package/fs/asn.1/test.f.js +11 -12
- package/fs/ci/config/module.f.d.ts +4 -4
- package/fs/ci/config/module.f.js +4 -4
- package/fs/crypto/sign/module.f.js +3 -3
- package/fs/dev/tf/module.d.ts +1 -2
- package/fs/dev/tf/module.f.d.ts +10 -5
- package/fs/dev/tf/module.f.js +53 -22
- package/fs/dev/tf/module.js +4 -103
- package/fs/dev/tf/scenarios/all.d.ts +1 -0
- package/fs/dev/tf/scenarios/all.js +3 -0
- package/fs/dev/tf/scenarios/fail.fail.f.d.ts +1 -0
- package/fs/dev/tf/scenarios/fail.fail.f.js +1 -0
- package/fs/dev/tf/scenarios/return-value.pass.f.d.ts +1 -0
- package/fs/dev/tf/scenarios/return-value.pass.f.js +2 -0
- package/fs/dev/tf/scenarios/throw.pass.f.d.ts +6 -0
- package/fs/dev/tf/scenarios/throw.pass.f.js +3 -0
- package/fs/dev/tf/test.f.js +9 -4
- package/fs/io/module.f.d.ts +5 -1
- package/fs/io/module.f.js +8 -9
- package/fs/io/module.js +30 -0
- package/fs/sul/id/module.f.js +3 -4
- package/fs/sul/level/literal/module.f.js +3 -3
- package/fs/text/sgr/test.f.js +16 -1
- package/fs/types/bit_vec/module.f.d.ts +9 -4
- package/fs/types/bit_vec/module.f.js +7 -9
- package/fs/types/btree/remove/module.f.d.ts +1 -1
- package/fs/types/btree/remove/module.f.js +7 -2
- package/fs/types/btree/set/module.f.d.ts +1 -1
- package/fs/types/btree/set/module.f.js +7 -2
- package/fs/types/btree/types/module.f.d.ts +1 -0
- package/fs/types/btree/types/module.f.js +1 -1
- package/fs/types/effects/node/module.f.d.ts +14 -1
- package/fs/types/effects/node/module.f.js +1 -0
- package/fs/types/effects/node/virtual/module.f.d.ts +2 -1
- package/fs/types/effects/node/virtual/module.f.js +1 -0
- package/fs/types/nullable/test.f.js +10 -1
- package/package.json +4 -4
package/fs/asn.1/module.f.js
CHANGED
|
@@ -5,19 +5,18 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
import { bitLength, max } from "../types/bigint/module.f.js";
|
|
8
|
-
import { empty, isVec, length,
|
|
8
|
+
import { empty, isVec, length, msb, uint, unpack, vec, vec8 } from "../types/bit_vec/module.f.js";
|
|
9
9
|
import { identity } from "../types/function/module.f.js";
|
|
10
10
|
import { encode as b128encode, decode as b128decode } from "../base128/module.f.js";
|
|
11
|
-
const pop = msb
|
|
11
|
+
const { popFront: pop, listToVec } = msb;
|
|
12
12
|
const pop8 = pop(8n);
|
|
13
|
-
const concat = listToVec(msb);
|
|
14
13
|
const classPcMask = 224n;
|
|
15
14
|
const tagNumberMask = 31n;
|
|
16
15
|
const parsedTagEncode = ([classPc, number]) => {
|
|
17
16
|
const [firstByteNumber, rest] = number < tagNumberMask
|
|
18
17
|
? [number, empty]
|
|
19
18
|
: [tagNumberMask, b128encode(number)];
|
|
20
|
-
return
|
|
19
|
+
return listToVec([vec8(classPc | firstByteNumber), rest]);
|
|
21
20
|
};
|
|
22
21
|
const parsedTagDecode = (v) => {
|
|
23
22
|
const [firstByte, rest] = pop8(v);
|
|
@@ -86,7 +85,7 @@ const lenEncode = (uint) => {
|
|
|
86
85
|
return vec8(uint);
|
|
87
86
|
}
|
|
88
87
|
const { byteLen, v } = round8({ length: bitLength(uint), uint });
|
|
89
|
-
return
|
|
88
|
+
return listToVec([vec8(0x80n | byteLen), v]);
|
|
90
89
|
};
|
|
91
90
|
/**
|
|
92
91
|
* Decodes the length field of an ASN.1 TLV and returns the length in bits and the remaining input.
|
|
@@ -104,7 +103,7 @@ const lenDecode = (v) => {
|
|
|
104
103
|
export const encodeRaw = ([tag, value]) => {
|
|
105
104
|
const tagVec = tagEncode(tag);
|
|
106
105
|
const { byteLen, v } = round8(unpack(value));
|
|
107
|
-
return
|
|
106
|
+
return listToVec([tagVec, lenEncode(byteLen), v]);
|
|
108
107
|
};
|
|
109
108
|
/** Decodes a raw ASN.1 TLV tuple and returns the remaining input. */
|
|
110
109
|
export const decodeRaw = (v) => {
|
|
@@ -139,7 +138,7 @@ export const decodeOctetString = (v) => v;
|
|
|
139
138
|
export const encodeObjectIdentifier = (oid) => {
|
|
140
139
|
const [first, second, ...rest] = oid;
|
|
141
140
|
const firstByte = first * 40n + second;
|
|
142
|
-
return
|
|
141
|
+
return listToVec([vec8(firstByte), ...rest.map(b128encode)]);
|
|
143
142
|
};
|
|
144
143
|
/** Decodes an OBJECT IDENTIFIER value. */
|
|
145
144
|
export const decodeObjectIdentifier = (v) => {
|
|
@@ -155,7 +154,7 @@ export const decodeObjectIdentifier = (v) => {
|
|
|
155
154
|
}
|
|
156
155
|
return result;
|
|
157
156
|
};
|
|
158
|
-
const genericEncodeSequence = (map) => (...records) =>
|
|
157
|
+
const genericEncodeSequence = (map) => (...records) => listToVec(map(records.map(encode)));
|
|
159
158
|
/** Encodes a SEQUENCE payload from ordered records. */
|
|
160
159
|
export const encodeSequence = genericEncodeSequence(identity);
|
|
161
160
|
/** Decodes a SEQUENCE payload into records. */
|
package/fs/asn.1/test.f.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { empty, length,
|
|
1
|
+
import { empty, length, msb, uint, unpack, vec, vec8 } from "../types/bit_vec/module.f.js";
|
|
2
2
|
import { asBase } from "../types/nominal/module.f.js";
|
|
3
3
|
import { decodeRaw, decodeInteger, encodeRaw, encodeInteger, integer, encode, decode, constructedSequence, octetString, boolean, constructedSet } from "./module.f.js";
|
|
4
|
-
const { concat, popFront: pop } = msb;
|
|
5
|
-
const cat = listToVec(msb);
|
|
4
|
+
const { concat, popFront: pop, listToVec } = msb;
|
|
6
5
|
const pop8 = pop(8n);
|
|
7
6
|
const check = (tag, v, rest) => {
|
|
8
7
|
const s = encodeRaw([tag, v]);
|
|
@@ -244,19 +243,19 @@ export default {
|
|
|
244
243
|
},
|
|
245
244
|
encodeDecode: {
|
|
246
245
|
integer: () => {
|
|
247
|
-
ch([integer, 0n],
|
|
248
|
-
ch([integer, 1n],
|
|
246
|
+
ch([integer, 0n], listToVec([vec8(BigInt(integer)), vec8(1n), vec8(0n)]));
|
|
247
|
+
ch([integer, 1n], listToVec([vec8(BigInt(integer)), vec8(1n), vec8(1n)]));
|
|
249
248
|
},
|
|
250
249
|
sequence: () => {
|
|
251
|
-
ch([constructedSequence, []],
|
|
252
|
-
ch([constructedSequence, [[integer, 0n]]],
|
|
253
|
-
ch([constructedSequence, [[integer, 1n], [integer, 2n]]],
|
|
250
|
+
ch([constructedSequence, []], listToVec([vec8(BigInt(constructedSequence)), vec8(0n)]));
|
|
251
|
+
ch([constructedSequence, [[integer, 0n]]], listToVec([vec8(BigInt(constructedSequence)), vec8(3n), encode([integer, 0n])]));
|
|
252
|
+
ch([constructedSequence, [[integer, 1n], [integer, 2n]]], listToVec([
|
|
254
253
|
vec8(BigInt(constructedSequence)),
|
|
255
254
|
vec8(6n),
|
|
256
255
|
encode([integer, 1n]),
|
|
257
256
|
encode([integer, 2n])
|
|
258
257
|
]));
|
|
259
|
-
ch([constructedSequence, [[octetString, vec8(0x23n)], [boolean, true], [boolean, false]]],
|
|
258
|
+
ch([constructedSequence, [[octetString, vec8(0x23n)], [boolean, true], [boolean, false]]], listToVec([
|
|
260
259
|
vec8(BigInt(constructedSequence)),
|
|
261
260
|
vec8(9n),
|
|
262
261
|
encode([octetString, vec8(0x23n)]),
|
|
@@ -265,7 +264,7 @@ export default {
|
|
|
265
264
|
]));
|
|
266
265
|
},
|
|
267
266
|
set: () => {
|
|
268
|
-
ch([constructedSet, [[integer, 2n], [integer, 1n]]],
|
|
267
|
+
ch([constructedSet, [[integer, 2n], [integer, 1n]]], listToVec([
|
|
269
268
|
vec8(BigInt(constructedSet)),
|
|
270
269
|
vec8(6n),
|
|
271
270
|
encode([integer, 1n]),
|
|
@@ -276,7 +275,7 @@ export default {
|
|
|
276
275
|
raw: [
|
|
277
276
|
() => {
|
|
278
277
|
const e = encodeRaw([0x00n, vec8(0x23n)]);
|
|
279
|
-
if (e !==
|
|
278
|
+
if (e !== listToVec([vec8(0x00n), vec8(1n), vec8(0x23n)])) {
|
|
280
279
|
throw `encode: ${length(e)}: ${uint(e).toString(2)}`;
|
|
281
280
|
}
|
|
282
281
|
const [[tag, value], rest] = decodeRaw(e);
|
|
@@ -292,7 +291,7 @@ export default {
|
|
|
292
291
|
},
|
|
293
292
|
() => {
|
|
294
293
|
const e = encodeRaw([0x1f20n, vec(16n)(0x1234n)]);
|
|
295
|
-
if (e !==
|
|
294
|
+
if (e !== listToVec([vec8(0x1fn), vec8(0x20n), vec8(2n), vec(16n)(0x1234n)])) {
|
|
296
295
|
const l = length(e);
|
|
297
296
|
const u = uint(e);
|
|
298
297
|
throw `encode: ${l}: ${u.toString(16)}`;
|
|
@@ -20,13 +20,13 @@ export declare const images: {
|
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
22
|
export declare const bun = "1.3.14";
|
|
23
|
-
export declare const deno = "2.8.
|
|
23
|
+
export declare const deno = "2.8.1";
|
|
24
24
|
export declare const playwright = "1.60.0";
|
|
25
25
|
export declare const rust = "1.95.0";
|
|
26
26
|
export declare const node: {
|
|
27
27
|
readonly default: "26.2.0";
|
|
28
|
-
readonly others: readonly ["
|
|
28
|
+
readonly others: readonly ["24.16.0"];
|
|
29
29
|
};
|
|
30
|
-
export declare const wasmtime = "
|
|
30
|
+
export declare const wasmtime = "45.0.0";
|
|
31
31
|
export declare const wasmer = "7.1.0";
|
|
32
|
-
export declare const tsgo = "7.0.0-dev.
|
|
32
|
+
export declare const tsgo = "7.0.0-dev.20260527.1";
|
package/fs/ci/config/module.f.js
CHANGED
|
@@ -23,7 +23,7 @@ export const images = {
|
|
|
23
23
|
// https://bun.sh/
|
|
24
24
|
export const bun = '1.3.14';
|
|
25
25
|
// https://deno.com/
|
|
26
|
-
export const deno = '2.8.
|
|
26
|
+
export const deno = '2.8.1';
|
|
27
27
|
// https://www.npmjs.com/package/playwright
|
|
28
28
|
export const playwright = '1.60.0';
|
|
29
29
|
// https://rust-lang.org/
|
|
@@ -31,11 +31,11 @@ export const rust = '1.95.0';
|
|
|
31
31
|
// https://nodejs.org/en/download
|
|
32
32
|
export const node = {
|
|
33
33
|
default: '26.2.0',
|
|
34
|
-
others: ['
|
|
34
|
+
others: ['24.16.0'],
|
|
35
35
|
};
|
|
36
36
|
// https://github.com/bytecodealliance/wasmtime/releases
|
|
37
|
-
export const wasmtime = '
|
|
37
|
+
export const wasmtime = '45.0.0';
|
|
38
38
|
// https://github.com/wasmerio/wasmer/releases
|
|
39
39
|
export const wasmer = '7.1.0';
|
|
40
40
|
// https://www.npmjs.com/package/@typescript/native-preview?activeTab=versions
|
|
41
|
-
export const tsgo = '7.0.0-dev.
|
|
41
|
+
export const tsgo = '7.0.0-dev.20260527.1';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { bitLength, divUp, roundUp } from "../../types/bigint/module.f.js";
|
|
2
|
-
import { empty, length,
|
|
2
|
+
import { empty, length, msb, repeat, unpack, vec, vec8 } from "../../types/bit_vec/module.f.js";
|
|
3
3
|
import { hmac } from "../hmac/module.f.js";
|
|
4
4
|
import { computeSync } from "../sha2/module.f.js";
|
|
5
5
|
// qlen to rlen
|
|
@@ -34,8 +34,8 @@ export const all = (q) => {
|
|
|
34
34
|
export const fromCurve = (c) => all(c.nf.p);
|
|
35
35
|
const x01 = vec8(0x01n);
|
|
36
36
|
const x00 = vec8(0x00n);
|
|
37
|
-
const
|
|
38
|
-
export const concat = (...x) =>
|
|
37
|
+
const { listToVec } = msb;
|
|
38
|
+
export const concat = (...x) => listToVec(x);
|
|
39
39
|
/**
|
|
40
40
|
* Computes deterministic ECDSA nonce `k` as described by RFC6979.
|
|
41
41
|
*/
|
package/fs/dev/tf/module.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare const run: () => Promise<void>;
|
|
1
|
+
export declare const run: () => Promise<number>;
|
package/fs/dev/tf/module.f.d.ts
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import { type All, type NodeProgram, type NodeProgramOptions, type Program, type Sandbox, type SandboxResult, type Write } from '../../types/effects/node/module.f.ts';
|
|
1
|
+
import { type All, type NodeProgram, type NodeProgramOptions, type Program, type Sandbox, type SandboxResult, type Test, type TestContext, type Write } from '../../types/effects/node/module.f.ts';
|
|
2
2
|
import { type Effect, type Operation } from '../../types/effects/module.f.ts';
|
|
3
3
|
import { type LoadModuleOperations, type ModuleMap } from '../module.f.ts';
|
|
4
4
|
export declare const isTest: (s: string) => boolean;
|
|
5
|
-
export type
|
|
5
|
+
export type TestFn = () => unknown;
|
|
6
6
|
export type TestEntry = {
|
|
7
|
-
readonly fn:
|
|
7
|
+
readonly fn: TestFn;
|
|
8
8
|
readonly throws: boolean;
|
|
9
9
|
};
|
|
10
10
|
export type TestSet = TestEntry | readonly (readonly [string, unknown])[];
|
|
11
11
|
export declare const parseTestSet: (throws: boolean, x: unknown) => TestSet;
|
|
12
|
+
type TestAndPath = readonly [Path, TestEntry];
|
|
12
13
|
/**
|
|
13
14
|
* Recursively collects all leaf tests reachable from `v` as `[path, entry]`
|
|
14
15
|
* pairs, without running anything. Return-value sub-trees are not walked
|
|
15
16
|
* (that requires execution); only the static object/array/function structure
|
|
16
17
|
* is traversed.
|
|
17
18
|
*/
|
|
18
|
-
export declare const collectTests: (path: Path, throws: boolean, v: unknown) => readonly
|
|
19
|
+
export declare const collectTests: (path: Path, throws: boolean, v: unknown) => readonly TestAndPath[];
|
|
19
20
|
/**
|
|
20
21
|
* Receives semantic test-run events. Each method is the runner's notification
|
|
21
22
|
* of an event; the reporter decides how to render it (terminal, GitHub
|
|
@@ -29,8 +30,10 @@ export type Reporter<O extends Operation> = {
|
|
|
29
30
|
readonly summary: (pass: number, fail: number, time: number) => Effect<O, void>;
|
|
30
31
|
readonly test: (file: string, path: Path, set: TestEntry) => Effect<O, SandboxResult<unknown>>;
|
|
31
32
|
};
|
|
33
|
+
export declare const registerModule: (ctx: TestContext, k: string, v: unknown) => Effect<Test | All, void>;
|
|
32
34
|
export declare const runModuleMap: <O extends Operation>(reporter: Reporter<O>) => (moduleMap: ModuleMap) => Effect<O | All, number>;
|
|
33
|
-
export declare const
|
|
35
|
+
export declare const testAll: <O extends Operation>(reporter: Reporter<O>) => Program<O | All | LoadModuleOperations>;
|
|
36
|
+
export declare const registerModuleMap: (ctx: TestContext, moduleMap: ModuleMap) => Effect<Test | All, void>;
|
|
34
37
|
export type Path = readonly (string | null)[];
|
|
35
38
|
export declare const isInteger: (s: string) => boolean;
|
|
36
39
|
export declare const isIdentifier: (s: string) => boolean;
|
|
@@ -70,3 +73,5 @@ export declare const defaultTest: (file: string, path: Path, { fn, throws }: Tes
|
|
|
70
73
|
*/
|
|
71
74
|
export declare const defaultReporter: (options: NodeProgramOptions) => Reporter<Write | Sandbox>;
|
|
72
75
|
export declare const main: NodeProgram;
|
|
76
|
+
export declare const register: NodeProgram;
|
|
77
|
+
export {};
|
package/fs/dev/tf/module.f.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { reset, fgGreen, fgRed, bold, csiWrite } from "../../text/sgr/module.f.js";
|
|
7
|
-
import { all, sandbox } from "../../types/effects/node/module.f.js";
|
|
8
|
-
import { pure } from "../../types/effects/module.f.js";
|
|
7
|
+
import { all, sandbox, test } from "../../types/effects/node/module.f.js";
|
|
8
|
+
import { pure, do_ } from "../../types/effects/module.f.js";
|
|
9
9
|
import { loadModuleMap } from "../module.f.js";
|
|
10
10
|
import { invert } from "../../types/result/module.f.js";
|
|
11
11
|
export const isTest = (s) => s.endsWith('test.f.js') || s.endsWith('test.f.ts');
|
|
@@ -51,28 +51,47 @@ export const collectTests = (path, throws, v) => {
|
|
|
51
51
|
}
|
|
52
52
|
return [[path, set]];
|
|
53
53
|
};
|
|
54
|
+
export const registerModule = (ctx, k, v) => {
|
|
55
|
+
const registerOne = (ctx, [path, { fn, throws }]) => test(ctx, fmtImport(k, path), throws, (t) => {
|
|
56
|
+
if (throws) {
|
|
57
|
+
fn();
|
|
58
|
+
return pure(undefined);
|
|
59
|
+
}
|
|
60
|
+
const r = fn();
|
|
61
|
+
const sub = collectTests([...path, null], false, r);
|
|
62
|
+
if (sub.length === 0) {
|
|
63
|
+
return pure(undefined);
|
|
64
|
+
}
|
|
65
|
+
return all(...sub.map(e => registerOne(t, e))).step(() => pure(undefined));
|
|
66
|
+
});
|
|
67
|
+
const tests = collectTests([], false, v);
|
|
68
|
+
if (tests.length === 0) {
|
|
69
|
+
return pure(undefined);
|
|
70
|
+
}
|
|
71
|
+
return all(...tests.map(e => registerOne(ctx, e))).step(() => pure(undefined));
|
|
72
|
+
};
|
|
54
73
|
const mergeState = (a, b) => ({ time: a.time + b.time, pass: a.pass + b.pass, fail: a.fail + b.fail });
|
|
55
74
|
const zero = { time: 0, pass: 0, fail: 0 };
|
|
56
75
|
const runModule = ({ result, test }) => (k, v) => (ts) => {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
.
|
|
64
|
-
|
|
65
|
-
if (set.throws) {
|
|
66
|
-
return pure(addPass(duration)(zero));
|
|
67
|
-
}
|
|
68
|
-
// Walk return-value sub-tree; null marks the call boundary so
|
|
69
|
-
// paths render as e.g. `outer().inner`. throws resets to false.
|
|
70
|
-
return walk([...testPath, null], false, r)
|
|
71
|
-
.step(sub => pure(mergeState(addPass(duration)(zero), sub)));
|
|
76
|
+
const one = ([testPath, set]) => test(k, testPath, set)
|
|
77
|
+
.step(sr => {
|
|
78
|
+
const { result: [s, r], duration } = sr;
|
|
79
|
+
return result(k, testPath, sr)
|
|
80
|
+
.step(() => {
|
|
81
|
+
if (s === 'ok') {
|
|
82
|
+
if (set.throws) {
|
|
83
|
+
return pure(addPass(duration)(zero));
|
|
72
84
|
}
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
// Walk return-value sub-tree; null marks the call boundary so
|
|
86
|
+
// paths render as e.g. `outer().inner`. throws resets to false.
|
|
87
|
+
return walk([...testPath, null], false, r)
|
|
88
|
+
.step(sub => pure(mergeState(addPass(duration)(zero), sub)));
|
|
89
|
+
}
|
|
90
|
+
return pure(addFail(duration)(zero));
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
const walk = (path, throws, v) => {
|
|
94
|
+
const effects = collectTests(path, throws, v).map(one);
|
|
76
95
|
return all(...effects)
|
|
77
96
|
.step(states => pure(states.reduce(mergeState, zero)));
|
|
78
97
|
};
|
|
@@ -87,7 +106,14 @@ export const runModuleMap = (reporter) => (moduleMap) => {
|
|
|
87
106
|
.step(ts => summary(ts.pass, ts.fail, ts.time)
|
|
88
107
|
.step(() => pure(ts.fail !== 0 ? 1 : 0)));
|
|
89
108
|
};
|
|
90
|
-
export const
|
|
109
|
+
export const testAll = (reporter) => options => loadModuleMap(options.env).step(runModuleMap(reporter));
|
|
110
|
+
export const registerModuleMap = (ctx, moduleMap) => {
|
|
111
|
+
const modules = entries(moduleMap).filter(([k]) => isTest(k));
|
|
112
|
+
if (modules.length === 0) {
|
|
113
|
+
return pure(undefined);
|
|
114
|
+
}
|
|
115
|
+
return all(...modules.map(([k, v]) => registerModule(ctx, k, v))).step(() => pure(undefined));
|
|
116
|
+
};
|
|
91
117
|
const isAlpha = (c) => (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c === '_' || c === '$';
|
|
92
118
|
const isDigit = (c) => c >= '0' && c <= '9';
|
|
93
119
|
export const isInteger = (s) => s.length > 0 && [...s].every(isDigit) && (s === '0' || s[0] !== '0');
|
|
@@ -169,4 +195,9 @@ export const defaultReporter = (options) => {
|
|
|
169
195
|
test: defaultTest,
|
|
170
196
|
};
|
|
171
197
|
};
|
|
172
|
-
export const main = options =>
|
|
198
|
+
export const main = options => testAll(defaultReporter(options))(options);
|
|
199
|
+
export const register = o => loadModuleMap(o.env)
|
|
200
|
+
.step(m => registerModuleMap(o.engine === 'bun' ? o.bunTestContext :
|
|
201
|
+
o.engine === 'playwright' ? o.playwrightTestContext :
|
|
202
|
+
o.testContext, m))
|
|
203
|
+
.step(() => pure(0));
|
package/fs/dev/tf/module.js
CHANGED
|
@@ -1,103 +1,4 @@
|
|
|
1
|
-
import { io
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import { asyncImport } from "../../io/module.js";
|
|
6
|
-
import { fromIo } from "../../io/module.f.js";
|
|
7
|
-
import { pure } from "../../types/effects/module.f.js";
|
|
8
|
-
import { asyncRun } from "../../types/effects/module.js";
|
|
9
|
-
import {} from "../../types/effects/node/module.f.js";
|
|
10
|
-
const isBun = typeof Bun !== 'undefined';
|
|
11
|
-
const isPlaywright = typeof process !== 'undefined' && process?.env?.PLAYWRIGHT_TEST !== undefined;
|
|
12
|
-
const createFramework = (fw) => (prefix, f) => fw.test(prefix, t => f((name, v) => t.test(name, v)));
|
|
13
|
-
// Bun doesn't support nested tests yet.
|
|
14
|
-
const createBunFramework = (fw) => (prefix, f) => f((name, v) => fw.test(`${prefix}: ${name}`, v));
|
|
15
|
-
const createPlaywrightFramework = async () => {
|
|
16
|
-
const pwTest = (await asyncImport('@playwright/test')).test;
|
|
17
|
-
return (prefix, f) => f((name, v) => pwTest(`${prefix}: ${name}`, v));
|
|
18
|
-
};
|
|
19
|
-
const framework = isPlaywright ? await createPlaywrightFramework() :
|
|
20
|
-
isBun ? createBunFramework(nodeTest) :
|
|
21
|
-
createFramework(nodeTest);
|
|
22
|
-
const scanModule = (x) => async (subTestRunner) => {
|
|
23
|
-
let subTests = [x];
|
|
24
|
-
while (true) {
|
|
25
|
-
const [first, ...rest] = subTests;
|
|
26
|
-
if (first === undefined) {
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
subTests = rest;
|
|
30
|
-
//
|
|
31
|
-
const [name, value, throws] = first;
|
|
32
|
-
const set = parseTestSet(throws, value);
|
|
33
|
-
if (set instanceof Array) {
|
|
34
|
-
for (const [j, y] of set) {
|
|
35
|
-
const pr = `${name}/${j}`;
|
|
36
|
-
subTests = [...subTests, [pr, y, throws || j === 'throw']];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
await subTestRunner(name, () => {
|
|
41
|
-
if (set.throws) {
|
|
42
|
-
let threw = false;
|
|
43
|
-
try {
|
|
44
|
-
set.fn();
|
|
45
|
-
}
|
|
46
|
-
catch (_) {
|
|
47
|
-
threw = true;
|
|
48
|
-
}
|
|
49
|
-
if (!threw) {
|
|
50
|
-
throw new Error(`${name}() expected to throw`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
const r = set.fn();
|
|
55
|
-
// The result of a function is walked as a fresh sub-tree;
|
|
56
|
-
// the parent's `throws` flag does not propagate into it.
|
|
57
|
-
subTests = [...subTests, [`${name}()`, r, false]];
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
const noOp = () => pure(undefined);
|
|
64
|
-
const reporter = {
|
|
65
|
-
result: noOp,
|
|
66
|
-
summary: noOp,
|
|
67
|
-
test: (file, path, { throws, fn }) => {
|
|
68
|
-
nodeTest.test(fmtImport(file, path), async (_t) => {
|
|
69
|
-
const [s, r] = tryCatch(fn);
|
|
70
|
-
if (throws === (s === 'ok')) {
|
|
71
|
-
throw r;
|
|
72
|
-
}
|
|
73
|
-
if (!throws) {
|
|
74
|
-
// TODO: add subtests
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
return pure({
|
|
78
|
-
result: ['ok', undefined],
|
|
79
|
-
duration: 0,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
const map = {
|
|
84
|
-
// TODO: we use the same algorithm twice. Refactor by creating a `createAll(map)`
|
|
85
|
-
// helper that takes a `map` and returns an `all` function that runs effects
|
|
86
|
-
// according to the map. There could be a problem with circular dependencies,
|
|
87
|
-
// but we can use a lazy function `() => ToAsyncOperationMap<All>` instead od `map`.
|
|
88
|
-
all: async (...effects) => Promise.all(effects.map(asyncRun(map))),
|
|
89
|
-
};
|
|
90
|
-
export const run3 = async () => {
|
|
91
|
-
const fio = fromIo(io);
|
|
92
|
-
const moduleMap = await fio(loadModuleMap(io.process.env));
|
|
93
|
-
const runner = runModuleMap(reporter)(moduleMap);
|
|
94
|
-
await asyncRun(map)(runner);
|
|
95
|
-
};
|
|
96
|
-
export const run = async () => {
|
|
97
|
-
const moduleMap = await fromIo(io)(loadModuleMap(io.process.env));
|
|
98
|
-
for (const [i, v] of Object.entries(moduleMap)) {
|
|
99
|
-
if (isTest(i)) {
|
|
100
|
-
framework(i, scanModule(['', v, false]));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
1
|
+
import { io } from "../../io/module.js";
|
|
2
|
+
import { register } from "./module.f.js";
|
|
3
|
+
import { runProgram } from "../../io/module.f.js";
|
|
4
|
+
export const run = () => runProgram(io)([])(register);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const failing: () => never;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const failing = () => { throw 'intentional failure'; };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const outer: () => unknown;
|
package/fs/dev/tf/test.f.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { pure } from "../../types/effects/module.f.js";
|
|
2
2
|
import { emptyState } from "../../types/effects/node/virtual/module.f.js";
|
|
3
3
|
import { virtual } from "../../types/effects/node/virtual/module.f.js";
|
|
4
|
-
import { assert, assertEq } from "../module.f.js";
|
|
5
|
-
import {
|
|
4
|
+
import { assert, assertEq, todo } from "../module.f.js";
|
|
5
|
+
import { testAll, defaultReporter, fmtPath, fmtTerm, fmtImport, ghEscape, isInteger, isIdentifier, defaultTest, } from "./module.f.js";
|
|
6
6
|
const makeReporter = () => {
|
|
7
7
|
const events = [];
|
|
8
8
|
const reporter = {
|
|
@@ -12,10 +12,15 @@ const makeReporter = () => {
|
|
|
12
12
|
};
|
|
13
13
|
return [reporter, () => events];
|
|
14
14
|
};
|
|
15
|
+
const noopTestContext = { test: todo };
|
|
15
16
|
const options = (initCwd, github = false) => ({
|
|
16
17
|
args: [],
|
|
17
18
|
env: { INIT_CWD: initCwd, ...(github ? { GITHUB_ACTION: 'true' } : {}) },
|
|
18
19
|
std: { stdout: { isTTY: false }, stderr: { isTTY: false } },
|
|
20
|
+
testContext: noopTestContext,
|
|
21
|
+
bunTestContext: noopTestContext,
|
|
22
|
+
playwrightTestContext: noopTestContext,
|
|
23
|
+
engine: 'node',
|
|
19
24
|
});
|
|
20
25
|
const ok0 = () => ({ result: ['ok', undefined], duration: 0 });
|
|
21
26
|
const fail0 = () => ({ result: ['error', 'oops'], duration: 0 });
|
|
@@ -23,7 +28,7 @@ const ok1 = () => ({ result: ['ok', undefined], duration: 1 });
|
|
|
23
28
|
const run = (dir, initCwd = '.') => {
|
|
24
29
|
const [reporter, getEvents] = makeReporter();
|
|
25
30
|
const state = { ...emptyState, root: dir };
|
|
26
|
-
const [, exitCode] = virtual(state)(
|
|
31
|
+
const [, exitCode] = virtual(state)(testAll(reporter)(options(initCwd)));
|
|
27
32
|
return [getEvents(), exitCode];
|
|
28
33
|
};
|
|
29
34
|
// Runs the real `defaultReporter` and returns its captured stdout/stderr so the
|
|
@@ -31,7 +36,7 @@ const run = (dir, initCwd = '.') => {
|
|
|
31
36
|
const runMain = (dir, github = false) => {
|
|
32
37
|
const state = { ...emptyState, root: dir };
|
|
33
38
|
const opts = options('.', github);
|
|
34
|
-
const [finalState, exitCode] = virtual(state)(
|
|
39
|
+
const [finalState, exitCode] = virtual(state)(testAll(defaultReporter(opts))(opts));
|
|
35
40
|
return [finalState.stdout, finalState.stderr, exitCode];
|
|
36
41
|
};
|
|
37
42
|
// flat object: two passing tests
|
package/fs/io/module.f.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Effect } from '../types/effects/module.f.ts';
|
|
2
|
-
import type
|
|
2
|
+
import { type Headers, type Module, type NodeOp, type Env, type SandboxResult, type NodeProgram, type WriteConsoles, type TestContext, type Engine } from '../types/effects/node/module.f.ts';
|
|
3
3
|
import type { Vec } from '../types/bit_vec/module.f.ts';
|
|
4
4
|
import { type Result } from '../types/result/module.f.ts';
|
|
5
5
|
/**
|
|
@@ -116,6 +116,10 @@ export type Io = {
|
|
|
116
116
|
readonly now: () => number;
|
|
117
117
|
readonly sandbox: Sandbox;
|
|
118
118
|
readonly write: (stream: WriteConsoles, data: Vec) => Promise<void>;
|
|
119
|
+
readonly testContext: TestContext;
|
|
120
|
+
readonly bunTestContext: TestContext;
|
|
121
|
+
readonly playwrightTestContext: TestContext;
|
|
122
|
+
readonly engine: Engine;
|
|
119
123
|
};
|
|
120
124
|
export type App = (io: Io) => Promise<number>;
|
|
121
125
|
export type Run = (f: App) => Promise<never>;
|
package/fs/io/module.f.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { normalize } from "../path/module.f.js";
|
|
11
11
|
import {} from "../types/effects/module.f.js";
|
|
12
12
|
import { asyncRun } from "../types/effects/module.js";
|
|
13
|
+
import {} from "../types/effects/node/module.f.js";
|
|
13
14
|
import { asBase, asNominal } from "../types/nominal/module.f.js";
|
|
14
15
|
import { error, ok } from "../types/result/module.f.js";
|
|
15
16
|
import { fromVec, listToVec, toVec } from "../types/uint8array/module.f.js";
|
|
@@ -96,18 +97,16 @@ export const fromIo = ({ fs: { promises: { mkdir, readFile, readdir, writeFile,
|
|
|
96
97
|
now: async () => ioNow(),
|
|
97
98
|
sandbox: async (f) => ioSandbox(f),
|
|
98
99
|
write: ioWrite,
|
|
100
|
+
test: async (ctx, name, expectFailure, test) => ctx.test(name, { expectFailure }, async (t) => result(test(t))),
|
|
99
101
|
});
|
|
100
102
|
return result;
|
|
101
103
|
};
|
|
102
104
|
export const runProgram = (io) => {
|
|
103
|
-
const { process: { env, stdout, stderr } } = io;
|
|
105
|
+
const { process: { env, stdout, stderr }, testContext, bunTestContext, playwrightTestContext, engine } = io;
|
|
106
|
+
const std = { stdout, stderr };
|
|
104
107
|
const f = fromIo(io);
|
|
105
|
-
return
|
|
106
|
-
args,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
stdout: { isTTY: stdout.isTTY },
|
|
110
|
-
stderr: { isTTY: stderr.isTTY },
|
|
111
|
-
},
|
|
112
|
-
}));
|
|
108
|
+
return args => {
|
|
109
|
+
const options = { args, env, std, testContext, bunTestContext, playwrightTestContext, engine };
|
|
110
|
+
return program => f(program(options));
|
|
111
|
+
};
|
|
113
112
|
};
|
package/fs/io/module.js
CHANGED
|
@@ -15,6 +15,32 @@ import { once } from 'node:events';
|
|
|
15
15
|
import { fromIo, runProgram } from "./module.f.js";
|
|
16
16
|
import { error, ok } from "../types/result/module.f.js";
|
|
17
17
|
import { fromVec } from "../types/uint8array/module.f.js";
|
|
18
|
+
import * as testContext from 'node:test';
|
|
19
|
+
const isPlaywright = 'PLAYWRIGHT_TEST' in (process?.env ?? {});
|
|
20
|
+
const pwTest = isPlaywright
|
|
21
|
+
? (await import('@playwright/test')).test
|
|
22
|
+
: undefined;
|
|
23
|
+
const inlineTest = async (name, { expectFailure }, fn) => {
|
|
24
|
+
if (expectFailure) {
|
|
25
|
+
try {
|
|
26
|
+
await fn(inlineContext);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`expected to throw: ${name}`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
await fn(inlineContext);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const inlineContext = { test: inlineTest };
|
|
38
|
+
const bunTestContext = {
|
|
39
|
+
test: (name, opts, fn) => testContext.test(name, () => inlineTest(name, opts, fn))
|
|
40
|
+
};
|
|
41
|
+
const playwrightTestContext = {
|
|
42
|
+
test: (name, opts, fn) => pwTest(name, () => inlineTest(name, opts, fn))
|
|
43
|
+
};
|
|
18
44
|
const prefix = 'file:///';
|
|
19
45
|
const { now } = Date;
|
|
20
46
|
/** Maps `WriteConsoles` names to the corresponding Node.js writable streams. */
|
|
@@ -89,6 +115,10 @@ export const io = {
|
|
|
89
115
|
return { result, duration: after - before };
|
|
90
116
|
},
|
|
91
117
|
write: (stream, data) => writeAll(streams[stream], fromVec(data)),
|
|
118
|
+
testContext,
|
|
119
|
+
bunTestContext,
|
|
120
|
+
playwrightTestContext,
|
|
121
|
+
engine: isPlaywright ? 'playwright' : 'Bun' in globalThis ? 'bun' : 'node',
|
|
92
122
|
};
|
|
93
123
|
const effectRun = runProgram(io)(io.process.argv.slice(2));
|
|
94
124
|
export default effectRun;
|
package/fs/sul/id/module.f.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
8
|
import { toArray } from "../../types/list/module.f.js";
|
|
9
|
-
import { length,
|
|
9
|
+
import { length, msb, uint, uintChunkList, unpack, vec } from "../../types/bit_vec/module.f.js";
|
|
10
10
|
import { assertEq } from "../../dev/module.f.js";
|
|
11
11
|
import { utf8 } from "../../text/module.f.js";
|
|
12
12
|
import { secp256r1 } from "../../crypto/secp/module.f.js";
|
|
@@ -77,9 +77,8 @@ export const isHash = (v) => asBase(v) >> hashPrefixOffset === 1n;
|
|
|
77
77
|
export const hashId = (hash) => asNominal(hashPrefix | hash);
|
|
78
78
|
const hash2 = base32.compress(iv);
|
|
79
79
|
const vecX20 = vec(0x20n);
|
|
80
|
-
const
|
|
81
|
-
const hashMerge = (a, b) => hashId(uint(
|
|
82
|
-
const { concat } = msb;
|
|
80
|
+
const { concat, listToVec } = msb;
|
|
81
|
+
const hashMerge = (a, b) => hashId(uint(listToVec(hash2((asBase(a) << 0x100n) | asBase(b)).map(vecX20))));
|
|
83
82
|
export const compress = (a, b) => {
|
|
84
83
|
if (isHash(a) || isHash(b)) {
|
|
85
84
|
return hashMerge(a, b);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
import { log2 } from "../../../types/bigint/module.f.js";
|
|
8
|
-
import {
|
|
8
|
+
import { msb, vec } from "../../../types/bit_vec/module.f.js";
|
|
9
9
|
import { strictEqual } from "../../../types/function/operator/module.f.js";
|
|
10
10
|
import { equal, map } from "../../../types/list/module.f.js";
|
|
11
11
|
import { join } from "../../../types/string/module.f.js";
|
|
@@ -73,12 +73,12 @@ export const pipelineStep = (bit, [l1s, l2s, l3s]) => {
|
|
|
73
73
|
const [l3Out, newL3s] = l3.encode(l2Out, l3s);
|
|
74
74
|
return [l3Out, [newL1s, newL2s, newL3s]];
|
|
75
75
|
};
|
|
76
|
-
const concat = listToVec(msb);
|
|
77
76
|
const vec1 = vec(1n);
|
|
77
|
+
const { listToVec } = msb;
|
|
78
78
|
const literalToVec = (prior, e) => {
|
|
79
79
|
const m = map(prior);
|
|
80
80
|
const { decode } = level(e);
|
|
81
|
-
return literal =>
|
|
81
|
+
return literal => listToVec(m(decode(literal)));
|
|
82
82
|
};
|
|
83
83
|
/** Decodes a level-1 symbol to its canonical MSB bit vector. */
|
|
84
84
|
export const literal1ToVec = literalToVec(vec1, 0n);
|
package/fs/text/sgr/test.f.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
import { fgRed } from "./module.f.js";
|
|
1
|
+
import { fgRed, createConsoleText, backspace } from "./module.f.js";
|
|
2
2
|
export default [
|
|
3
3
|
() => {
|
|
4
4
|
if (fgRed !== '\x1b[31m') {
|
|
5
5
|
throw new Error('Test failed: sgr(0)');
|
|
6
6
|
}
|
|
7
|
+
},
|
|
8
|
+
() => {
|
|
9
|
+
const output = [];
|
|
10
|
+
const stdout = { write: (s) => { output.push(s); } };
|
|
11
|
+
const writer1 = createConsoleText(stdout);
|
|
12
|
+
const writer2 = writer1('hello');
|
|
13
|
+
if (output[0] !== 'hello') {
|
|
14
|
+
throw output[0];
|
|
15
|
+
}
|
|
16
|
+
// replacing 'hello' (len=5) with 'hi' (len=2): suffixLength=3
|
|
17
|
+
writer2('hi');
|
|
18
|
+
const expected = backspace.repeat(5) + 'hi' + ' '.repeat(3) + backspace.repeat(3);
|
|
19
|
+
if (output[1] !== expected) {
|
|
20
|
+
throw output[1];
|
|
21
|
+
}
|
|
7
22
|
}
|
|
8
23
|
];
|
|
@@ -146,6 +146,15 @@ export type BitOrder = {
|
|
|
146
146
|
* ```
|
|
147
147
|
*/
|
|
148
148
|
readonly concat: Reduce;
|
|
149
|
+
/**
|
|
150
|
+
* Folds a list of vectors into a single vector in this bit order.
|
|
151
|
+
*
|
|
152
|
+
* Unlike `concat`, which joins exactly two vectors, this joins a whole list.
|
|
153
|
+
*
|
|
154
|
+
* @returns The concatenation of every vector in the list, or `empty` when the
|
|
155
|
+
* list is empty.
|
|
156
|
+
*/
|
|
157
|
+
readonly listToVec: (list: List<Vec>) => Vec;
|
|
149
158
|
/**
|
|
150
159
|
* Computes the bitwise exclusive OR of two vectors after normalizing their lengths.
|
|
151
160
|
*
|
|
@@ -219,10 +228,6 @@ export declare const chunkList: (bo: BitOrder) => (n: bigint) => (v: Vec) => Thu
|
|
|
219
228
|
* @returns A thunk that produces a list of unsigned 8-bit integers.
|
|
220
229
|
*/
|
|
221
230
|
export declare const u8List: (bo: BitOrder) => (v: Vec) => Thunk<number>;
|
|
222
|
-
/**
|
|
223
|
-
* Concatenates a list of vectors using the provided bit order.
|
|
224
|
-
*/
|
|
225
|
-
export declare const listToVec: ({ concat }: BitOrder) => (list: List<Vec>) => Vec;
|
|
226
231
|
/**
|
|
227
232
|
* Repeats a vector to create a padded block of the desired length.
|
|
228
233
|
*/
|
|
@@ -127,14 +127,16 @@ const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }
|
|
|
127
127
|
return [uint, pack(u)];
|
|
128
128
|
};
|
|
129
129
|
};
|
|
130
|
+
const concat = a => b => {
|
|
131
|
+
const au = unpack(a);
|
|
132
|
+
const bu = unpack(b);
|
|
133
|
+
return pack(unpackConcat(au)(bu));
|
|
134
|
+
};
|
|
130
135
|
return {
|
|
131
136
|
front,
|
|
132
137
|
removeFront,
|
|
133
|
-
concat
|
|
134
|
-
|
|
135
|
-
const bu = unpack(b);
|
|
136
|
-
return pack(unpackConcat(au)(bu));
|
|
137
|
-
},
|
|
138
|
+
concat,
|
|
139
|
+
listToVec: fold(flip(concat))(empty),
|
|
138
140
|
xor: op(norm)(xor),
|
|
139
141
|
unpackPopFront,
|
|
140
142
|
popFront,
|
|
@@ -301,10 +303,6 @@ const vecToU8 = ({ unpackSplit }) => {
|
|
|
301
303
|
* @returns A thunk that produces a list of unsigned 8-bit integers.
|
|
302
304
|
*/
|
|
303
305
|
export const u8List = (bo) => (v) => map(vecToU8(bo))(chunkList(bo)(8n)(v));
|
|
304
|
-
/**
|
|
305
|
-
* Concatenates a list of vectors using the provided bit order.
|
|
306
|
-
*/
|
|
307
|
-
export const listToVec = ({ concat }) => fold(flip(concat))(empty);
|
|
308
306
|
/**
|
|
309
307
|
* Repeats a vector to create a padded block of the desired length.
|
|
310
308
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import type
|
|
6
|
+
import { type TNode, type Tree } from '../types/module.f.ts';
|
|
7
7
|
import type { Compare } from '../../function/compare/module.f.ts';
|
|
8
8
|
export declare const nodeRemove: <T>(c: Compare<T>) => (node: TNode<T>) => Tree<T>;
|
|
9
9
|
export declare const remove: <T>(c: Compare<T>) => (tree: Tree<T>) => Tree<T>;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removal operations for persistent B-tree structures.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { collapseRoot } from "../types/module.f.js";
|
|
1
7
|
import { find } from "../find/module.f.js";
|
|
2
8
|
import { fold, concat, next } from "../../list/module.f.js";
|
|
3
9
|
import { map } from "../../nullable/module.f.js";
|
|
@@ -163,7 +169,6 @@ export const nodeRemove = (c) => (node) => {
|
|
|
163
169
|
return first;
|
|
164
170
|
}
|
|
165
171
|
const { first: tf, tail: tt } = tailR;
|
|
166
|
-
|
|
167
|
-
return result.length === 1 ? result[0] : result;
|
|
172
|
+
return collapseRoot(reduce(initReduce(tf)(first))(tt));
|
|
168
173
|
};
|
|
169
174
|
export const remove = c => map(nodeRemove(c));
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import type
|
|
6
|
+
import { type TNode, type Tree } from '../types/module.f.ts';
|
|
7
7
|
import type { Compare } from '../../function/compare/module.f.ts';
|
|
8
8
|
export declare const set: <T>(c: Compare<T>) => (f: (value: T | null) => T) => (tree: Tree<T>) => TNode<T>;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Insertion and update operations for persistent B-tree structures.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { collapseRoot } from "../types/module.f.js";
|
|
1
7
|
import { find } from "../find/module.f.js";
|
|
2
8
|
import { fold } from "../../list/module.f.js";
|
|
3
9
|
const b57 = b => b.length === 5 ? [b] : [[b[0], b[1], b[2]], b[3], [b[4], b[5], b[6]]];
|
|
@@ -114,7 +120,6 @@ const nodeSet = (c) => (g) => (node) => {
|
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
122
|
};
|
|
117
|
-
|
|
118
|
-
return r.length === 1 ? r[0] : r;
|
|
123
|
+
return collapseRoot(reduceBranch(f())(tail));
|
|
119
124
|
};
|
|
120
125
|
export const set = c => f => tree => tree === null ? [f(null)] : nodeSet(c)(f)(tree);
|
|
@@ -12,3 +12,4 @@ export type TNode<T> = Leaf1<T> | Leaf2<T> | Branch3<T> | Branch5<T>;
|
|
|
12
12
|
export type Tree<T> = TNode<T> | null;
|
|
13
13
|
export type Branch1<T> = readonly [TNode<T>];
|
|
14
14
|
export type Branch7<T> = readonly [...Branch5<T>, T, TNode<T>];
|
|
15
|
+
export declare const collapseRoot: <T>(b: Branch1<T> | Branch3<T> | Branch5<T>) => TNode<T>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const collapseRoot = b => b.length === 1 ? b[0] : b;
|
|
@@ -116,7 +116,15 @@ export type Sandbox = readonly ['sandbox', <T>(f: () => T) => SandboxResult<T>];
|
|
|
116
116
|
* @see {@link SandboxResult}
|
|
117
117
|
*/
|
|
118
118
|
export declare const sandbox: Func<Sandbox>;
|
|
119
|
-
export type
|
|
119
|
+
export type TestFn = (name: string, options: {
|
|
120
|
+
readonly expectFailure: boolean;
|
|
121
|
+
}, fn: (t: TestContext) => Promise<void>) => Promise<void>;
|
|
122
|
+
export type TestContext = {
|
|
123
|
+
readonly test: TestFn;
|
|
124
|
+
};
|
|
125
|
+
export type Test = readonly ['test', (ctx: TestContext, name: string, expectFailure: boolean, test: (t: TestContext) => Effect<Test | All, void>) => void];
|
|
126
|
+
export declare const test: Func<Test>;
|
|
127
|
+
export type NodeOp = All | Fetch | Fs | Http | Forever | Import | Now | Sandbox | Write | Test;
|
|
120
128
|
export type NodeEffect<T> = Effect<NodeOp, T>;
|
|
121
129
|
export type NodeOperationMap = ToAsyncOperationMap<NodeOp>;
|
|
122
130
|
/**
|
|
@@ -125,6 +133,7 @@ export type NodeOperationMap = ToAsyncOperationMap<NodeOp>;
|
|
|
125
133
|
export type Env = {
|
|
126
134
|
readonly [k: string]: string | undefined;
|
|
127
135
|
};
|
|
136
|
+
export type Engine = 'node' | 'bun' | 'playwright';
|
|
128
137
|
export type NodeProgramOptions = {
|
|
129
138
|
readonly args: readonly string[];
|
|
130
139
|
readonly env: Env;
|
|
@@ -133,6 +142,10 @@ export type NodeProgramOptions = {
|
|
|
133
142
|
readonly isTTY: boolean;
|
|
134
143
|
};
|
|
135
144
|
};
|
|
145
|
+
readonly testContext: TestContext;
|
|
146
|
+
readonly bunTestContext: TestContext;
|
|
147
|
+
readonly playwrightTestContext: TestContext;
|
|
148
|
+
readonly engine: Engine;
|
|
136
149
|
};
|
|
137
150
|
export type Program<O extends Operation> = (options: NodeProgramOptions) => Effect<O, number>;
|
|
138
151
|
export type NodeProgram = Program<NodeOp>;
|
|
@@ -10,8 +10,9 @@ import type { Module, NodeOp } from '../module.f.ts';
|
|
|
10
10
|
* each import for closures/state.
|
|
11
11
|
*/
|
|
12
12
|
export type JsModule = () => Module;
|
|
13
|
+
export type Entity = Vec | Dir | JsModule;
|
|
13
14
|
export type Dir = {
|
|
14
|
-
readonly [name in string]?:
|
|
15
|
+
readonly [name in string]?: Entity;
|
|
15
16
|
};
|
|
16
17
|
export type State = {
|
|
17
18
|
stdout: string;
|
|
@@ -167,6 +167,7 @@ const map = {
|
|
|
167
167
|
// exception in a fixture propagates loudly as a bug in the fixture.
|
|
168
168
|
// See: issues/156-tf-virtual-tests.md
|
|
169
169
|
sandbox: (state, f) => [state, f()],
|
|
170
|
+
test: todo,
|
|
170
171
|
write: (state, stream, data) => {
|
|
171
172
|
const s = utf8ToString(data);
|
|
172
173
|
return [{ ...state, [stream]: `${state[stream]}${s}` }, undefined];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { map, toOption } from "./module.f.js";
|
|
1
|
+
import { map, match, toOption } from "./module.f.js";
|
|
2
2
|
export default [
|
|
3
3
|
() => {
|
|
4
4
|
const optionSq = map((v) => v * v);
|
|
@@ -20,5 +20,14 @@ export default [
|
|
|
20
20
|
if (opt2.length !== 0) {
|
|
21
21
|
throw opt2;
|
|
22
22
|
}
|
|
23
|
+
},
|
|
24
|
+
() => {
|
|
25
|
+
const double = match((v) => v * 2)(() => -1);
|
|
26
|
+
if (double(3) !== 6) {
|
|
27
|
+
throw double(3);
|
|
28
|
+
}
|
|
29
|
+
if (double(null) !== -1) {
|
|
30
|
+
throw double(null);
|
|
31
|
+
}
|
|
23
32
|
}
|
|
24
33
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functionalscript",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"**/*.js",
|
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
"description": "FunctionalScript is a purely functional subset of JavaScript",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"prepack": "tsc --NoEmit false",
|
|
12
|
-
"test": "tsc && node --test --experimental-
|
|
12
|
+
"test": "tsc && node --test --experimental-test-coverage --test-coverage-include=**/module.f.ts",
|
|
13
13
|
"index": "node ./fs/fjs/module.ts r ./fs/dev/index/module.f.ts",
|
|
14
14
|
"fst": "node ./fs/fjs/module.ts t",
|
|
15
15
|
"start": "node ./fs/fjs/module.ts",
|
|
16
16
|
"ci-update": "node ./fs/fjs/module.ts r ./fs/ci/module.f.ts",
|
|
17
17
|
"update": "npm install && npm run index && npm run ci-update",
|
|
18
|
-
"index-html": "node
|
|
18
|
+
"index-html": "node ./fs/fjs/module.ts r ./fs/website/module.f.ts",
|
|
19
19
|
"website": "npm run prepack &&npm run index-html"
|
|
20
20
|
},
|
|
21
21
|
"engines": {
|
|
22
|
-
"node": ">=
|
|
22
|
+
"node": ">=24"
|
|
23
23
|
},
|
|
24
24
|
"bin": {
|
|
25
25
|
"fjs": "fs/fjs/module.js"
|