functionalscript 0.12.9 → 0.13.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/ci/module.f.js +1 -1
- package/package.json +1 -1
- package/types/bit_vec/module.f.d.ts +1 -0
- package/types/bit_vec/module.f.js +12 -7
- package/types/bit_vec/test.f.d.ts +5 -0
- package/types/bit_vec/test.f.js +23 -0
- package/types/rtti/ts/module.f.d.ts +10 -4
- package/types/rtti/ts/module.f.js +29 -20
- package/types/rtti/ts/test.f.d.ts +7 -0
- package/types/rtti/ts/test.f.js +18 -3
- package/types/string/module.f.d.ts +1 -0
- package/types/string/module.f.js +1 -0
- package/types/string/test.f.d.ts +7 -0
- package/types/string/test.f.js +48 -1
- package/types/ts/module.f.d.ts +13 -4
- package/types/ts/module.f.js +10 -4
package/ci/module.f.js
CHANGED
|
@@ -69,7 +69,7 @@ const node = (version) => (extra) => basicNode(version)([
|
|
|
69
69
|
const findTgz = (v) => v === 'windows' ? '(Get-ChildItem *.tgz).FullName' : './*.tgz';
|
|
70
70
|
const playwrightVersion = '1.58.2';
|
|
71
71
|
const playwrightAndVersion = `playwright@${playwrightVersion}`;
|
|
72
|
-
const rustToolchain = '1.
|
|
72
|
+
const rustToolchain = '1.95.0';
|
|
73
73
|
const toSteps = (m) => {
|
|
74
74
|
const filter = (st) => m.flatMap((mt) => mt.type === st ? [mt.step] : []);
|
|
75
75
|
const aptGet = m.flatMap(v => v.type === 'apt-get' ? [v.package] : []).join(' ');
|
package/package.json
CHANGED
|
@@ -163,6 +163,7 @@ export type BitOrder = {
|
|
|
163
163
|
readonly cmp: (a: Vec) => (b: Vec) => Sign;
|
|
164
164
|
readonly unpackSplit: (len: bigint) => (u: Unpacked) => readonly [bigint, bigint];
|
|
165
165
|
readonly unpackConcat: (a: Unpacked) => (b: Unpacked) => Unpacked;
|
|
166
|
+
readonly startsWith: (prefix: Vec) => (v: Vec) => boolean;
|
|
166
167
|
};
|
|
167
168
|
/**
|
|
168
169
|
* Implements operations for handling vectors in a least-significant-bit (LSb) first order.
|
|
@@ -119,6 +119,13 @@ const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }
|
|
|
119
119
|
const unpackConcat = (a) => (b) => ({
|
|
120
120
|
length: a.length + b.length, uint: unpackConcatUint(a)(b)
|
|
121
121
|
});
|
|
122
|
+
const popFront = len => {
|
|
123
|
+
const f = unpackPopFront(len);
|
|
124
|
+
return v => {
|
|
125
|
+
const [uint, u] = f(unpack(v));
|
|
126
|
+
return [uint, pack(u)];
|
|
127
|
+
};
|
|
128
|
+
};
|
|
122
129
|
return {
|
|
123
130
|
front,
|
|
124
131
|
removeFront,
|
|
@@ -129,13 +136,7 @@ const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }
|
|
|
129
136
|
},
|
|
130
137
|
xor: op(norm)(xor),
|
|
131
138
|
unpackPopFront,
|
|
132
|
-
popFront
|
|
133
|
-
const f = unpackPopFront(len);
|
|
134
|
-
return v => {
|
|
135
|
-
const [uint, u] = f(unpack(v));
|
|
136
|
-
return [uint, pack(u)];
|
|
137
|
-
};
|
|
138
|
-
},
|
|
139
|
+
popFront,
|
|
139
140
|
norm,
|
|
140
141
|
cmp: a => b => {
|
|
141
142
|
const au = unpack(a);
|
|
@@ -148,6 +149,10 @@ const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }
|
|
|
148
149
|
},
|
|
149
150
|
unpackSplit,
|
|
150
151
|
unpackConcat,
|
|
152
|
+
startsWith: prefix => {
|
|
153
|
+
const { length: n, uint: u } = unpack(prefix);
|
|
154
|
+
return v => length(v) < n ? false : popFront(n)(v)[0] === u;
|
|
155
|
+
}
|
|
151
156
|
};
|
|
152
157
|
};
|
|
153
158
|
const lsbUnpackConcatUint = ({ uint: a, length }) => ({ uint: b }) => (b << length) | a;
|
|
@@ -39,6 +39,11 @@ declare const _default: {
|
|
|
39
39
|
repeat: () => void;
|
|
40
40
|
lsbCmp: () => void;
|
|
41
41
|
msbCmp: () => void;
|
|
42
|
+
startsWith: {
|
|
43
|
+
lsb: () => void;
|
|
44
|
+
msb: () => void;
|
|
45
|
+
emptyVec: () => void;
|
|
46
|
+
};
|
|
42
47
|
u8ListToVec: () => () => void;
|
|
43
48
|
u8ListUnaligned: () => void;
|
|
44
49
|
chunkList: {
|
package/types/bit_vec/test.f.js
CHANGED
|
@@ -458,6 +458,29 @@ export default {
|
|
|
458
458
|
c(vec(5n)(0x5n))(vec(4n)(0x5n))(-1); // 0b00101 < 0b0101_
|
|
459
459
|
c(vec(4n)(0x5n))(vec(5n)(0xan))(-1); // 0b0101_ < 0b01010
|
|
460
460
|
},
|
|
461
|
+
startsWith: {
|
|
462
|
+
// vector 0xF5 = 0b1111_0101 (8 bits)
|
|
463
|
+
// LSB reads from the low end: bits 0-3 = 0101 = 0x5, bits 4-7 = 1111 = 0xF
|
|
464
|
+
lsb: () => {
|
|
465
|
+
const v = vec(8n)(0xf5n);
|
|
466
|
+
assertEq(lsb.startsWith(vec(4n)(0x5n))(v), true); // low nibble matches
|
|
467
|
+
assertEq(lsb.startsWith(vec(4n)(0xfn))(v), false); // low nibble doesn't match
|
|
468
|
+
assertEq(lsb.startsWith(v)(vec(4n)(0x5n)), false); // prefix longer than vector
|
|
469
|
+
assertEq(lsb.startsWith(empty)(v), true); // empty prefix always matches
|
|
470
|
+
},
|
|
471
|
+
// MSB reads from the high end: bits 0-3 = 1111 = 0xF, bits 4-7 = 0101 = 0x5
|
|
472
|
+
msb: () => {
|
|
473
|
+
const v = vec(8n)(0xf5n);
|
|
474
|
+
assertEq(msb.startsWith(vec(4n)(0xfn))(v), true); // high nibble matches
|
|
475
|
+
assertEq(msb.startsWith(vec(4n)(0x5n))(v), false); // high nibble doesn't match
|
|
476
|
+
assertEq(msb.startsWith(v)(vec(4n)(0xfn)), false); // prefix longer than vector
|
|
477
|
+
assertEq(msb.startsWith(empty)(v), true); // empty prefix always matches
|
|
478
|
+
},
|
|
479
|
+
emptyVec: () => {
|
|
480
|
+
assertEq(lsb.startsWith(empty)(empty), true);
|
|
481
|
+
assertEq(msb.startsWith(empty)(empty), true);
|
|
482
|
+
},
|
|
483
|
+
},
|
|
461
484
|
u8ListToVec: () => {
|
|
462
485
|
// 131_072 is too much for Bun
|
|
463
486
|
const x = u8ListToVec(msb)(listRepeat(0x12)(131_071));
|
|
@@ -41,9 +41,10 @@ export type Ts<T extends Type> = T extends () => infer I ? (I extends readonly [
|
|
|
41
41
|
readonly [K in string]: Ts<E>;
|
|
42
42
|
} : I extends readonly ['or', ...infer A extends readonly Type[]] ? Ts<A[number]> : never) : ConstTs<T>;
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* Creates a printer that converts an RTTI schema `Type` to its TypeScript type expression as a string.
|
|
45
45
|
*
|
|
46
46
|
* Mirrors the compile-time `Ts<T>` mapped type at runtime.
|
|
47
|
+
* Pass `true` to emit mutable (non-`readonly`) types.
|
|
47
48
|
*
|
|
48
49
|
* **Note:** recursive schemas (e.g. `const list = () => ['array', list] as const`)
|
|
49
50
|
* will cause infinite recursion. Only acyclic schemas are supported.
|
|
@@ -53,14 +54,19 @@ export type Ts<T extends Type> = T extends () => infer I ? (I extends readonly [
|
|
|
53
54
|
*
|
|
54
55
|
* @example
|
|
55
56
|
* ```ts
|
|
57
|
+
* const toTs = printer()
|
|
56
58
|
* toTs(boolean) // 'boolean'
|
|
57
59
|
* toTs(array(number)) // 'readonly(number)[]'
|
|
58
|
-
* toTs(record(string)) // '{readonly[k
|
|
60
|
+
* toTs(record(string)) // '{readonly[k:string]:string}'
|
|
59
61
|
* toTs(or(string, number)) // 'string|number'
|
|
60
62
|
* toTs(42) // '42'
|
|
61
63
|
* toTs('hello') // '"hello"'
|
|
62
64
|
* toTs([boolean, number]) // 'readonly[boolean,number]'
|
|
63
|
-
* toTs({ x: string }) // '{readonly
|
|
65
|
+
* toTs({ x: string }) // '{readonly"x":string}'
|
|
66
|
+
*
|
|
67
|
+
* const toTsMut = printer(true)
|
|
68
|
+
* toTsMut(array(number)) // '(number)[]'
|
|
69
|
+
* toTsMut(record(string)) // '{[k:string]:string}'
|
|
64
70
|
* ```
|
|
65
71
|
*/
|
|
66
|
-
export declare const
|
|
72
|
+
export declare const printer: (mut?: true) => (rtti: Type) => string;
|
|
@@ -7,15 +7,12 @@
|
|
|
7
7
|
* The runtime `toTs` function mirrors `Ts<T>` at value level, returning a TypeScript
|
|
8
8
|
* type expression string for a given RTTI schema.
|
|
9
9
|
*/
|
|
10
|
-
import { primitive,
|
|
11
|
-
/** Serialises a `Const` schema to its TypeScript type expression. */
|
|
12
|
-
const constToTs = (rtti) => typeof rtti !== 'object' || rtti === null ? primitive(rtti) :
|
|
13
|
-
rtti instanceof Array ? tuple(rtti.map(toTs)) :
|
|
14
|
-
struct(Object.entries(rtti).map(([k, v]) => [k, toTs(v)]));
|
|
10
|
+
import { primitive, union, printer as tsPrinter } from "../../ts/module.f.js";
|
|
15
11
|
/**
|
|
16
|
-
*
|
|
12
|
+
* Creates a printer that converts an RTTI schema `Type` to its TypeScript type expression as a string.
|
|
17
13
|
*
|
|
18
14
|
* Mirrors the compile-time `Ts<T>` mapped type at runtime.
|
|
15
|
+
* Pass `true` to emit mutable (non-`readonly`) types.
|
|
19
16
|
*
|
|
20
17
|
* **Note:** recursive schemas (e.g. `const list = () => ['array', list] as const`)
|
|
21
18
|
* will cause infinite recursion. Only acyclic schemas are supported.
|
|
@@ -25,26 +22,38 @@ const constToTs = (rtti) => typeof rtti !== 'object' || rtti === null ? primitiv
|
|
|
25
22
|
*
|
|
26
23
|
* @example
|
|
27
24
|
* ```ts
|
|
25
|
+
* const toTs = printer()
|
|
28
26
|
* toTs(boolean) // 'boolean'
|
|
29
27
|
* toTs(array(number)) // 'readonly(number)[]'
|
|
30
|
-
* toTs(record(string)) // '{readonly[k
|
|
28
|
+
* toTs(record(string)) // '{readonly[k:string]:string}'
|
|
31
29
|
* toTs(or(string, number)) // 'string|number'
|
|
32
30
|
* toTs(42) // '42'
|
|
33
31
|
* toTs('hello') // '"hello"'
|
|
34
32
|
* toTs([boolean, number]) // 'readonly[boolean,number]'
|
|
35
|
-
* toTs({ x: string }) // '{readonly
|
|
33
|
+
* toTs({ x: string }) // '{readonly"x":string}'
|
|
34
|
+
*
|
|
35
|
+
* const toTsMut = printer(true)
|
|
36
|
+
* toTsMut(array(number)) // '(number)[]'
|
|
37
|
+
* toTsMut(record(string)) // '{[k:string]:string}'
|
|
36
38
|
* ```
|
|
37
39
|
*/
|
|
38
|
-
export const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
export const printer = (mut) => {
|
|
41
|
+
const { tuple, struct, array, record } = tsPrinter(mut);
|
|
42
|
+
const constToTs = (rtti) => typeof rtti !== 'object' || rtti === null ? primitive(rtti) :
|
|
43
|
+
rtti instanceof Array ? tuple(rtti.map(toTs)) :
|
|
44
|
+
struct(Object.entries(rtti).map(([k, v]) => [k, toTs(v)]));
|
|
45
|
+
const toTs = (rtti) => {
|
|
46
|
+
if (typeof rtti !== 'function') {
|
|
47
|
+
return constToTs(rtti);
|
|
48
|
+
}
|
|
49
|
+
const [tag, ...rest] = rtti();
|
|
50
|
+
switch (tag) {
|
|
51
|
+
case 'const': return constToTs(rest[0]);
|
|
52
|
+
case 'array': return array(toTs(rest[0]));
|
|
53
|
+
case 'record': return record(toTs(rest[0]));
|
|
54
|
+
case 'or': return union(rest.map(toTs));
|
|
55
|
+
default: return tag; // tag0: 'boolean' | 'number' | 'string' | 'bigint' | 'unknown'
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return toTs;
|
|
50
59
|
};
|
package/types/rtti/ts/test.f.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { printer } from "./module.f.js";
|
|
2
2
|
import { boolean, number, string, bigint, unknown, array, record, or, option, never } from "../module.f.js";
|
|
3
|
+
const toTs = printer();
|
|
4
|
+
const toTsMut = printer(true);
|
|
5
|
+
const eqMut = (rtti, expected) => {
|
|
6
|
+
const result = toTsMut(rtti);
|
|
7
|
+
if (result !== expected) {
|
|
8
|
+
throw `expected ${JSON.stringify(expected)}, got ${JSON.stringify(result)}`;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
3
11
|
const eq = (rtti, expected) => {
|
|
4
12
|
const result = toTs(rtti);
|
|
5
13
|
if (result !== expected) {
|
|
@@ -21,8 +29,8 @@ export default {
|
|
|
21
29
|
union: () => eq(array(or(number, string)), 'readonly(number|string)[]'),
|
|
22
30
|
},
|
|
23
31
|
record: {
|
|
24
|
-
primitive: () => eq(record(string), '{readonly[k
|
|
25
|
-
nested: () => eq(record(record(number)), '{readonly[k
|
|
32
|
+
primitive: () => eq(record(string), '{readonly[k:string]:string}'),
|
|
33
|
+
nested: () => eq(record(record(number)), '{readonly[k:string]:{readonly[k:string]:number}}'),
|
|
26
34
|
},
|
|
27
35
|
},
|
|
28
36
|
const: {
|
|
@@ -57,4 +65,11 @@ export default {
|
|
|
57
65
|
},
|
|
58
66
|
never: () => eq(never, 'never'),
|
|
59
67
|
option: () => eq(option(number), 'number|undefined'),
|
|
68
|
+
mut: {
|
|
69
|
+
array: () => eqMut(array(number), '(number)[]'),
|
|
70
|
+
nestedArray: () => eqMut(array(array(boolean)), '((boolean)[])[]'),
|
|
71
|
+
record: () => eqMut(record(string), '{[k:string]:string}'),
|
|
72
|
+
tuple: () => eqMut([12, true], '[12,true]'),
|
|
73
|
+
struct: () => eqMut({ a: number, b: string }, '{"a":number,"b":string}'),
|
|
74
|
+
},
|
|
60
75
|
};
|
|
@@ -21,3 +21,4 @@ export declare const join: (_: string) => (input: List<string>) => string;
|
|
|
21
21
|
export declare const concat: (input: List<string>) => string;
|
|
22
22
|
export declare const repeat: (n: string) => (v: number) => string;
|
|
23
23
|
export declare const cmp: (a: string) => (b: string) => Sign;
|
|
24
|
+
export declare const splitAt: (p: number) => (v: string) => readonly [string, string];
|
package/types/string/module.f.js
CHANGED
package/types/string/test.f.d.ts
CHANGED
package/types/string/test.f.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join, concat, repeat, cmp } from "./module.f.js";
|
|
1
|
+
import { join, concat, repeat, cmp, splitAt } from "./module.f.js";
|
|
2
2
|
import { repeat as repeatList } from "../list/module.f.js";
|
|
3
3
|
export default {
|
|
4
4
|
example: () => {
|
|
@@ -67,5 +67,52 @@ export default {
|
|
|
67
67
|
if (result !== -1) {
|
|
68
68
|
throw result;
|
|
69
69
|
}
|
|
70
|
+
},
|
|
71
|
+
splitAt: {
|
|
72
|
+
middle: () => {
|
|
73
|
+
const [a, b] = splitAt(3)('hello');
|
|
74
|
+
if (a !== 'hel') {
|
|
75
|
+
throw a;
|
|
76
|
+
}
|
|
77
|
+
if (b !== 'lo') {
|
|
78
|
+
throw b;
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
zero: () => {
|
|
82
|
+
const [a, b] = splitAt(0)('hello');
|
|
83
|
+
if (a !== '') {
|
|
84
|
+
throw a;
|
|
85
|
+
}
|
|
86
|
+
if (b !== 'hello') {
|
|
87
|
+
throw b;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
full: () => {
|
|
91
|
+
const [a, b] = splitAt(5)('hello');
|
|
92
|
+
if (a !== 'hello') {
|
|
93
|
+
throw a;
|
|
94
|
+
}
|
|
95
|
+
if (b !== '') {
|
|
96
|
+
throw b;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
beyond: () => {
|
|
100
|
+
const [a, b] = splitAt(10)('hello');
|
|
101
|
+
if (a !== 'hello') {
|
|
102
|
+
throw a;
|
|
103
|
+
}
|
|
104
|
+
if (b !== '') {
|
|
105
|
+
throw b;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
empty: () => {
|
|
109
|
+
const [a, b] = splitAt(0)('');
|
|
110
|
+
if (a !== '') {
|
|
111
|
+
throw a;
|
|
112
|
+
}
|
|
113
|
+
if (b !== '') {
|
|
114
|
+
throw b;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
70
117
|
}
|
|
71
118
|
};
|
package/types/ts/module.f.d.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
export type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false;
|
|
2
2
|
export type Assert<T extends true> = T;
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/** Functions for emitting TypeScript type expression strings. */
|
|
4
|
+
export type Printer = {
|
|
5
|
+
/** Emits a tuple type: `readonly[A, B]` or `[A, B]` when mutable. */
|
|
6
|
+
readonly tuple: (types: readonly string[]) => string;
|
|
7
|
+
/** Emits an object type with named fields: `{readonly"k":T}` or `{"k":T}` when mutable. */
|
|
8
|
+
readonly struct: (fields: readonly (readonly [string, string])[]) => string;
|
|
9
|
+
/** Emits an array type: `readonly(T)[]` or `(T)[]` when mutable. */
|
|
10
|
+
readonly array: (type: string) => string;
|
|
11
|
+
/** Emits an index-signature record type: `{readonly[k:string]:T}` or `{[k:string]:T}` when mutable. */
|
|
12
|
+
readonly record: (type: string) => string;
|
|
13
|
+
};
|
|
14
|
+
/** Creates a `Printer`. Pass `true` to emit mutable (non-`readonly`) types. */
|
|
15
|
+
export declare const printer: (mut?: true) => Printer;
|
|
7
16
|
export declare const primitive: (c: bigint | string | undefined | boolean | number | null) => string;
|
|
8
17
|
export declare const union: (types: readonly string[]) => string;
|
package/types/ts/module.f.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
const complex = (open, close) => (i) => `${open}${i.join(',')}${close}`;
|
|
2
|
-
export const tuple = complex('readonly[', ']');
|
|
3
2
|
const structX = complex('{', '}');
|
|
4
|
-
|
|
5
|
-
export const
|
|
6
|
-
|
|
3
|
+
/** Creates a `Printer`. Pass `true` to emit mutable (non-`readonly`) types. */
|
|
4
|
+
export const printer = (mut) => {
|
|
5
|
+
const ro = mut ? '' : 'readonly';
|
|
6
|
+
return {
|
|
7
|
+
tuple: (mut ? complex('[', ']') : complex('readonly[', ']')),
|
|
8
|
+
struct: (fields) => structX(fields.map(([k, v]) => `${ro}${JSON.stringify(k)}:${v}`)),
|
|
9
|
+
array: (type) => `${ro}(${type})[]`,
|
|
10
|
+
record: (type) => structX([`${ro}[k:string]:${type}`]),
|
|
11
|
+
};
|
|
12
|
+
};
|
|
7
13
|
export const primitive = (c) => {
|
|
8
14
|
if (c === null) {
|
|
9
15
|
return 'null';
|