@evolu/common 8.7.0 → 8.8.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/dist/src/Array.d.ts +21 -21
- package/dist/src/Array.js +5 -5
- package/dist/src/Assert.d.ts +478 -141
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +644 -99
- package/dist/src/{Buffer.d.ts → Binary.d.ts} +85 -12
- package/dist/src/Binary.d.ts.map +1 -0
- package/dist/src/{Buffer.js → Binary.js} +206 -24
- package/dist/src/Console.d.ts +2 -10
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +6 -12
- package/dist/src/Crypto.d.ts +5 -5
- package/dist/src/Crypto.js +1 -1
- package/dist/src/Eq.d.ts +35 -40
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +51 -87
- package/dist/src/Function.d.ts +9 -14
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +7 -9
- package/dist/src/Http.d.ts +6 -7
- package/dist/src/Http.d.ts.map +1 -1
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +1 -0
- package/dist/src/LockManager.d.ts +1 -1
- package/dist/src/LockManager.js +1 -1
- package/dist/src/Lookup.d.ts +3 -2
- package/dist/src/Lookup.d.ts.map +1 -1
- package/dist/src/Lookup.js +4 -3
- package/dist/src/Number.d.ts +1 -1
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +1 -1
- package/dist/src/Object.d.ts +4 -4
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +5 -4
- package/dist/src/Option.d.ts +1 -2
- package/dist/src/Option.d.ts.map +1 -1
- package/dist/src/Option.js +1 -2
- package/dist/src/Platform.js +2 -0
- package/dist/src/Polyfills.js +4 -1
- package/dist/src/Random.d.ts +1 -1
- package/dist/src/Result.d.ts +49 -54
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +17 -22
- package/dist/src/Set.d.ts +6 -6
- package/dist/src/Set.js +2 -2
- package/dist/src/Sqlite.js +1 -1
- package/dist/src/String.js +3 -2
- package/dist/src/Task.d.ts +32 -32
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +20 -22
- package/dist/src/Test.d.ts +31 -3
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +46 -3
- package/dist/src/Time.d.ts +3 -11
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +10 -18
- package/dist/src/Type.d.ts +87 -85
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +70 -63
- package/dist/src/Types.d.ts +12 -12
- package/dist/src/Types.js +1 -1
- package/dist/src/WebSocket.d.ts +4 -7
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/Worker.d.ts +1 -1
- package/dist/src/Worker.js +7 -7
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/intl/_en.d.ts.map +1 -1
- package/dist/src/intl/_en.js +1 -1
- package/dist/src/intl/ar.d.ts.map +1 -1
- package/dist/src/intl/ar.js +1 -1
- package/dist/src/intl/bn.d.ts.map +1 -1
- package/dist/src/intl/bn.js +1 -1
- package/dist/src/intl/ca.d.ts.map +1 -1
- package/dist/src/intl/ca.js +1 -1
- package/dist/src/intl/cs.d.ts.map +1 -1
- package/dist/src/intl/cs.js +1 -1
- package/dist/src/intl/da.d.ts.map +1 -1
- package/dist/src/intl/da.js +1 -1
- package/dist/src/intl/de.d.ts.map +1 -1
- package/dist/src/intl/de.js +1 -1
- package/dist/src/intl/el.d.ts.map +1 -1
- package/dist/src/intl/el.js +1 -1
- package/dist/src/intl/es.d.ts.map +1 -1
- package/dist/src/intl/es.js +1 -1
- package/dist/src/intl/fa.d.ts.map +1 -1
- package/dist/src/intl/fa.js +1 -1
- package/dist/src/intl/fi.d.ts.map +1 -1
- package/dist/src/intl/fi.js +1 -1
- package/dist/src/intl/fil.d.ts.map +1 -1
- package/dist/src/intl/fil.js +1 -1
- package/dist/src/intl/fr.d.ts.map +1 -1
- package/dist/src/intl/fr.js +1 -1
- package/dist/src/intl/he.d.ts.map +1 -1
- package/dist/src/intl/he.js +1 -1
- package/dist/src/intl/hi.d.ts.map +1 -1
- package/dist/src/intl/hi.js +1 -1
- package/dist/src/intl/hr.d.ts.map +1 -1
- package/dist/src/intl/hr.js +1 -1
- package/dist/src/intl/hu.d.ts.map +1 -1
- package/dist/src/intl/hu.js +1 -1
- package/dist/src/intl/id.d.ts.map +1 -1
- package/dist/src/intl/id.js +1 -1
- package/dist/src/intl/it.d.ts.map +1 -1
- package/dist/src/intl/it.js +1 -1
- package/dist/src/intl/ja.d.ts.map +1 -1
- package/dist/src/intl/ja.js +1 -1
- package/dist/src/intl/ko.d.ts.map +1 -1
- package/dist/src/intl/ko.js +1 -1
- package/dist/src/intl/ml.d.ts.map +1 -1
- package/dist/src/intl/ml.js +1 -1
- package/dist/src/intl/mr.d.ts.map +1 -1
- package/dist/src/intl/mr.js +1 -1
- package/dist/src/intl/ms.d.ts.map +1 -1
- package/dist/src/intl/ms.js +1 -1
- package/dist/src/intl/nb.d.ts.map +1 -1
- package/dist/src/intl/nb.js +1 -1
- package/dist/src/intl/nl.d.ts.map +1 -1
- package/dist/src/intl/nl.js +1 -1
- package/dist/src/intl/pa.d.ts.map +1 -1
- package/dist/src/intl/pa.js +1 -1
- package/dist/src/intl/pl.d.ts.map +1 -1
- package/dist/src/intl/pl.js +1 -1
- package/dist/src/intl/pt-BR.d.ts.map +1 -1
- package/dist/src/intl/pt-BR.js +1 -1
- package/dist/src/intl/pt.d.ts.map +1 -1
- package/dist/src/intl/pt.js +1 -1
- package/dist/src/intl/ro.d.ts.map +1 -1
- package/dist/src/intl/ro.js +1 -1
- package/dist/src/intl/sk.d.ts.map +1 -1
- package/dist/src/intl/sk.js +1 -1
- package/dist/src/intl/sl.d.ts.map +1 -1
- package/dist/src/intl/sl.js +1 -1
- package/dist/src/intl/sv.d.ts.map +1 -1
- package/dist/src/intl/sv.js +1 -1
- package/dist/src/intl/sw.d.ts.map +1 -1
- package/dist/src/intl/sw.js +1 -1
- package/dist/src/intl/ta.d.ts.map +1 -1
- package/dist/src/intl/ta.js +1 -1
- package/dist/src/intl/te.d.ts.map +1 -1
- package/dist/src/intl/te.js +1 -1
- package/dist/src/intl/th.d.ts.map +1 -1
- package/dist/src/intl/th.js +1 -1
- package/dist/src/intl/tr.d.ts.map +1 -1
- package/dist/src/intl/tr.js +1 -1
- package/dist/src/intl/uk.d.ts.map +1 -1
- package/dist/src/intl/uk.js +1 -1
- package/dist/src/intl/ur.d.ts.map +1 -1
- package/dist/src/intl/ur.js +1 -1
- package/dist/src/intl/vi.d.ts.map +1 -1
- package/dist/src/intl/vi.js +1 -1
- package/dist/src/intl/zh-CN.d.ts.map +1 -1
- package/dist/src/intl/zh-CN.js +1 -1
- package/dist/src/intl/zh-TW.d.ts.map +1 -1
- package/dist/src/intl/zh-TW.js +1 -1
- package/dist/src/local-first/Evolu.d.ts +12 -12
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +2 -2
- package/dist/src/local-first/Protocol.d.ts +14 -76
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +29 -206
- package/dist/src/local-first/Query.d.ts +11 -11
- package/dist/src/local-first/Query.js +6 -6
- package/dist/src/local-first/Relay.d.ts +2 -2
- package/dist/src/local-first/Schema.d.ts +3 -5
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +2 -4
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +5 -5
- package/dist/src/local-first/Storage.js +4 -4
- package/dist/src/local-first/Timestamp.d.ts +9 -0
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +13 -10
- package/package.json +7 -4
- package/src/Array.test.ts +809 -0
- package/src/Array.ts +21 -21
- package/src/Assert.test.ts +1181 -0
- package/src/Assert.ts +971 -227
- package/src/BigInt.test.ts +28 -0
- package/src/Binary.test.ts +1128 -0
- package/src/{Buffer.ts → Binary.ts} +273 -46
- package/src/Brand.test.ts +90 -0
- package/src/Cache.test.ts +183 -0
- package/src/Callbacks.test.ts +66 -0
- package/src/Console.test.ts +809 -0
- package/src/Console.ts +9 -17
- package/src/Crypto.test.ts +138 -0
- package/src/Crypto.ts +5 -5
- package/src/Eq.test.ts +333 -0
- package/src/Eq.ts +110 -176
- package/src/Error.test.ts +118 -0
- package/src/Function.test.ts +204 -0
- package/src/Function.ts +10 -15
- package/src/Http.test.ts +816 -0
- package/src/Http.ts +6 -7
- package/src/LeakDetector.test.ts +210 -0
- package/src/LeakDetector.ts +1 -0
- package/src/LockManager.ts +2 -2
- package/src/Lookup.test.ts +418 -0
- package/src/Lookup.ts +4 -3
- package/src/Microtask.test.ts +140 -0
- package/src/Number.test.ts +218 -0
- package/src/Number.ts +2 -2
- package/src/Object.test.ts +293 -0
- package/src/Object.ts +5 -4
- package/src/Option.test.ts +104 -0
- package/src/Option.ts +1 -2
- package/src/Order.test.ts +56 -0
- package/src/Platform.ts +2 -0
- package/src/Polyfills.ts +3 -4
- package/src/Random.test.ts +89 -0
- package/src/Random.ts +1 -1
- package/src/Ref.test.ts +139 -0
- package/src/RefCount.test.ts +184 -0
- package/src/Relation.test.ts +505 -0
- package/src/Resource.test.ts +4623 -0
- package/src/Result.test.ts +1829 -0
- package/src/Result.ts +49 -54
- package/src/Schedule.test.ts +1554 -0
- package/src/Set.test.ts +279 -0
- package/src/Set.ts +6 -6
- package/src/Sqlite.ts +1 -1
- package/src/Store.test.ts +288 -0
- package/src/String.test.ts +54 -0
- package/src/String.ts +3 -3
- package/src/Task.test.ts +10820 -0
- package/src/Task.ts +38 -43
- package/src/Test.test.ts +88 -0
- package/src/Test.ts +49 -3
- package/src/Time.test.ts +926 -0
- package/src/Time.ts +13 -21
- package/src/Type.base64.test.ts +88 -0
- package/src/Type.test.ts +20465 -0
- package/src/Type.ts +114 -111
- package/src/Types.test.ts +324 -0
- package/src/Types.ts +13 -13
- package/src/WebSocket.ts +4 -7
- package/src/Worker.test.ts +755 -0
- package/src/Worker.ts +13 -18
- package/src/index.ts +1 -1
- package/src/intl/_en.ts +1 -1
- package/src/intl/ar.ts +1 -1
- package/src/intl/bn.ts +1 -1
- package/src/intl/ca.ts +1 -1
- package/src/intl/cs.ts +1 -1
- package/src/intl/da.ts +1 -1
- package/src/intl/de.ts +1 -1
- package/src/intl/el.ts +1 -1
- package/src/intl/es.ts +1 -1
- package/src/intl/fa.ts +1 -1
- package/src/intl/fi.ts +1 -1
- package/src/intl/fil.ts +1 -1
- package/src/intl/fr.ts +1 -1
- package/src/intl/he.ts +1 -1
- package/src/intl/hi.ts +1 -1
- package/src/intl/hr.ts +1 -1
- package/src/intl/hu.ts +1 -1
- package/src/intl/id.ts +1 -1
- package/src/intl/intl.test.ts +3106 -0
- package/src/intl/it.ts +1 -1
- package/src/intl/ja.ts +1 -1
- package/src/intl/ko.ts +1 -1
- package/src/intl/ml.ts +1 -1
- package/src/intl/mr.ts +1 -1
- package/src/intl/ms.ts +1 -1
- package/src/intl/nb.ts +1 -1
- package/src/intl/nl.ts +1 -1
- package/src/intl/pa.ts +1 -1
- package/src/intl/pl.ts +1 -1
- package/src/intl/pt-BR.ts +1 -1
- package/src/intl/pt.ts +1 -1
- package/src/intl/ro.ts +1 -1
- package/src/intl/sk.ts +1 -1
- package/src/intl/sl.ts +1 -1
- package/src/intl/sv.ts +1 -1
- package/src/intl/sw.ts +1 -1
- package/src/intl/ta.ts +1 -1
- package/src/intl/te.ts +1 -1
- package/src/intl/th.ts +1 -1
- package/src/intl/tr.ts +1 -1
- package/src/intl/uk.ts +1 -1
- package/src/intl/ur.ts +1 -1
- package/src/intl/vi.ts +1 -1
- package/src/intl/zh-CN.ts +1 -1
- package/src/intl/zh-TW.ts +1 -1
- package/src/local-first/Evolu.test.ts +1921 -0
- package/src/local-first/Evolu.ts +18 -14
- package/src/local-first/Owner.test.ts +83 -0
- package/src/local-first/Protocol.test.ts +1037 -0
- package/src/local-first/Protocol.ts +58 -261
- package/src/local-first/Query.test.ts +349 -0
- package/src/local-first/Query.ts +11 -11
- package/src/local-first/Relay.ts +2 -2
- package/src/local-first/Schema.ts +3 -5
- package/src/local-first/Shared.test.ts +1828 -0
- package/src/local-first/Shared.ts +9 -8
- package/src/local-first/Storage.ts +4 -4
- package/src/local-first/Timestamp.test.ts +374 -0
- package/src/local-first/Timestamp.ts +22 -10
- package/dist/src/Buffer.d.ts.map +0 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it, test } from "node:test";
|
|
2
|
+
import { assertEqual, assertSame, assertTrue } from "./Assert.ts";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
fromNullable,
|
|
6
|
+
type InferOption,
|
|
7
|
+
isNone,
|
|
8
|
+
isSome,
|
|
9
|
+
none,
|
|
10
|
+
type None,
|
|
11
|
+
type Option,
|
|
12
|
+
some,
|
|
13
|
+
type Some,
|
|
14
|
+
} from "./Option.ts";
|
|
15
|
+
import { assertType } from "./Type.ts";
|
|
16
|
+
|
|
17
|
+
test("some creates Some", () => {
|
|
18
|
+
assertEqual(some(42), { type: "Some", value: 42 });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("none is None", () => {
|
|
22
|
+
assertEqual(none, { type: "None" });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("isSome narrows type", () => {
|
|
26
|
+
const option: Option<string> = some("test");
|
|
27
|
+
if (isSome(option)) {
|
|
28
|
+
assertType<typeof option, Some<string>>();
|
|
29
|
+
assertType<typeof option.value, string>();
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("isNone narrows type", () => {
|
|
34
|
+
const option: Option<string> = none;
|
|
35
|
+
if (isNone(option)) {
|
|
36
|
+
assertType<typeof option, None>();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("fromNullable maps null and undefined to none", () => {
|
|
41
|
+
assertTrue(isNone(fromNullable(null)));
|
|
42
|
+
assertTrue(isNone(fromNullable(undefined)));
|
|
43
|
+
{
|
|
44
|
+
const actual = fromNullable(null);
|
|
45
|
+
assertType<typeof actual, Option<never>>();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("fromNullable maps values to some", () => {
|
|
50
|
+
const option = fromNullable("value");
|
|
51
|
+
|
|
52
|
+
assertEqual(option, some("value"));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("fromNullable strips null and undefined from type", () => {
|
|
56
|
+
const value: string | null | undefined = "test";
|
|
57
|
+
const option = fromNullable(value);
|
|
58
|
+
assertType<typeof option, Option<string>>();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("InferOption", () => {
|
|
62
|
+
it("extracts value type from Option", () => {
|
|
63
|
+
type MyOption = Option<string>;
|
|
64
|
+
assertType<InferOption<MyOption>, string>();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("extracts value type from Some", () => {
|
|
68
|
+
type MySome = Some<number>;
|
|
69
|
+
assertType<InferOption<MySome>, number>();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("returns never for None", () => {
|
|
73
|
+
assertType<InferOption<None>, never>();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("works at runtime", () => {
|
|
77
|
+
type MyOption = Option<string>;
|
|
78
|
+
const value: InferOption<MyOption> = "hello";
|
|
79
|
+
assertEqual(value, "hello");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("examples", () => {
|
|
84
|
+
it("cache that can store null and undefined", () => {
|
|
85
|
+
const cache = new Map<string, Option<unknown>>();
|
|
86
|
+
|
|
87
|
+
const get = (key: string): Option<unknown> => cache.get(key) ?? none;
|
|
88
|
+
|
|
89
|
+
cache.set("a", some(null));
|
|
90
|
+
cache.set("b", some(undefined));
|
|
91
|
+
|
|
92
|
+
assertTrue(isSome(get("a")));
|
|
93
|
+
assertTrue(isSome(get("b")));
|
|
94
|
+
assertTrue(isNone(get("c")));
|
|
95
|
+
|
|
96
|
+
const a = get("a");
|
|
97
|
+
assertTrue(isSome(a));
|
|
98
|
+
assertSame(a.value, null);
|
|
99
|
+
|
|
100
|
+
const b = get("b");
|
|
101
|
+
assertTrue(isSome(b));
|
|
102
|
+
assertSame(b.value, undefined);
|
|
103
|
+
});
|
|
104
|
+
});
|
package/src/Option.ts
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*
|
|
11
11
|
* ```ts
|
|
12
12
|
* import {
|
|
13
|
-
* assert,
|
|
14
13
|
* assertSame,
|
|
15
14
|
* assertTrue,
|
|
16
15
|
* isNone,
|
|
@@ -28,7 +27,7 @@
|
|
|
28
27
|
* cache.set("b", some(undefined));
|
|
29
28
|
*
|
|
30
29
|
* const a = get("a");
|
|
31
|
-
*
|
|
30
|
+
* assertTrue(isSome(a));
|
|
32
31
|
* assertSame(a.value, null);
|
|
33
32
|
* assertTrue(isSome(get("b")));
|
|
34
33
|
* assertTrue(isNone(get("c")));
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import { assertEqual } from "./Assert.ts";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
createOrder,
|
|
6
|
+
orderBigInt,
|
|
7
|
+
orderNumber,
|
|
8
|
+
orderString,
|
|
9
|
+
orderUint8Array,
|
|
10
|
+
reverseOrder,
|
|
11
|
+
} from "./Order.ts";
|
|
12
|
+
|
|
13
|
+
test("createOrder", () => {
|
|
14
|
+
const orderNumber = createOrder<number>((x, y) => x < y);
|
|
15
|
+
assertEqual(orderNumber(1, 2), -1);
|
|
16
|
+
assertEqual(orderNumber(2, 1), 1);
|
|
17
|
+
assertEqual(orderNumber(1, 1), 0);
|
|
18
|
+
|
|
19
|
+
const orderString = createOrder<string>((x, y) => x.localeCompare(y) < 0);
|
|
20
|
+
assertEqual(orderString("a", "b"), -1);
|
|
21
|
+
assertEqual(orderString("b", "a"), 1);
|
|
22
|
+
assertEqual(orderString("a", "a"), 0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("reverseOrder", () => {
|
|
26
|
+
const orderNumberDesc = reverseOrder(orderNumber);
|
|
27
|
+
assertEqual([2, 1, 3].toSorted(orderNumberDesc), [3, 2, 1]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("orderString", () => {
|
|
31
|
+
assertEqual(["b", "a", "c"].toSorted(orderString), ["a", "b", "c"]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("orderNumber", () => {
|
|
35
|
+
assertEqual([2, 1, 3].toSorted(orderNumber), [1, 2, 3]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("orderBigInt", () => {
|
|
39
|
+
assertEqual([2n, 1n, 3n].toSorted(orderBigInt), [1n, 2n, 3n]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("orderUint8Array", () => {
|
|
43
|
+
const a = new Uint8Array([0x01, 0x02, 0x03]);
|
|
44
|
+
const b = new Uint8Array([0x01, 0x02, 0x04]);
|
|
45
|
+
const c = new Uint8Array([0x01, 0x02, 0x03]);
|
|
46
|
+
// shorter
|
|
47
|
+
const d = new Uint8Array([0x01, 0x02]);
|
|
48
|
+
// longer
|
|
49
|
+
const e = new Uint8Array([0x01, 0x02, 0x03, 0x00]);
|
|
50
|
+
|
|
51
|
+
assertEqual(orderUint8Array(a, b), -1);
|
|
52
|
+
assertEqual(orderUint8Array(b, a), 1);
|
|
53
|
+
assertEqual(orderUint8Array(a, c), 0);
|
|
54
|
+
assertEqual(orderUint8Array(a, d), 1);
|
|
55
|
+
assertEqual(orderUint8Array(a, e), -1);
|
|
56
|
+
});
|
package/src/Platform.ts
CHANGED
|
@@ -171,8 +171,10 @@ const createTestGlobalErrors = (
|
|
|
171
171
|
record((event as Record<typeof webErrorKey, unknown>)[webErrorKey]);
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
+
// oxlint-disable-next-line evolu/no-unnecessary-global-this -- Call the same global object event API verified by the feature check.
|
|
174
175
|
globalThis.addEventListener(webEvent, listener);
|
|
175
176
|
disposableStack.defer(() => {
|
|
177
|
+
// oxlint-disable-next-line evolu/no-unnecessary-global-this -- Remove the listener through the same global object event API used to add it.
|
|
176
178
|
globalThis.removeEventListener(webEvent, listener);
|
|
177
179
|
});
|
|
178
180
|
} else {
|
package/src/Polyfills.ts
CHANGED
|
@@ -125,6 +125,7 @@ const appendDisposeError = (
|
|
|
125
125
|
currentError: unknown,
|
|
126
126
|
previousError: unknown,
|
|
127
127
|
): unknown =>
|
|
128
|
+
// oxlint-disable-next-line evolu/no-unnecessary-global-this -- Use the constructor installed on the global object even if a realm lexical binding shadows it.
|
|
128
129
|
new globalThis.SuppressedError(
|
|
129
130
|
currentError,
|
|
130
131
|
previousError,
|
|
@@ -158,6 +159,7 @@ const getOrInstallSymbol = (
|
|
|
158
159
|
key: "dispose" | "asyncDispose",
|
|
159
160
|
description: string,
|
|
160
161
|
): symbol => {
|
|
162
|
+
// oxlint-disable-next-line evolu/no-unnecessary-global-this -- Patch the global object constructor even if a realm lexical binding shadows it.
|
|
161
163
|
const SymbolCtor = globalThis.Symbol as SymbolWithDisposable;
|
|
162
164
|
const installedValue = Object.getOwnPropertyDescriptor(SymbolCtor, key)
|
|
163
165
|
?.value as unknown;
|
|
@@ -178,10 +180,7 @@ const getOrInstallSymbol = (
|
|
|
178
180
|
const installSuppressedError = (): void => {
|
|
179
181
|
if (typeof globalThis.SuppressedError === "function") return;
|
|
180
182
|
|
|
181
|
-
class SuppressedErrorPolyfill
|
|
182
|
-
extends Error
|
|
183
|
-
implements globalThis.SuppressedError
|
|
184
|
-
{
|
|
183
|
+
class SuppressedErrorPolyfill extends Error implements SuppressedError {
|
|
185
184
|
readonly error: unknown;
|
|
186
185
|
readonly suppressed: unknown;
|
|
187
186
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { afterEach, describe, it, mock } from "node:test";
|
|
2
|
+
import { assertEqual } from "./Assert.ts";
|
|
3
|
+
|
|
4
|
+
const randomLibConstructor = mock.fn<(seed?: string) => void>();
|
|
5
|
+
const randomLibNext = mock.fn<() => number>();
|
|
6
|
+
|
|
7
|
+
mock.module("random", {
|
|
8
|
+
// @ts-expect-error -- Node.js 24.20 replaces the deprecated namedExports option with exports, which @types/node 24.13 does not declare yet.
|
|
9
|
+
exports: {
|
|
10
|
+
Random: class {
|
|
11
|
+
next = randomLibNext;
|
|
12
|
+
|
|
13
|
+
constructor(seed?: string) {
|
|
14
|
+
if (seed === undefined) randomLibConstructor();
|
|
15
|
+
else randomLibConstructor(seed);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const { createRandom, createRandomLib, testCreateRandom, testCreateRandomLib } =
|
|
22
|
+
await import("./Random.ts");
|
|
23
|
+
|
|
24
|
+
describe("Random", () => {
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
mock.restoreAll();
|
|
27
|
+
randomLibConstructor.mock.resetCalls();
|
|
28
|
+
randomLibNext.mock.resetCalls();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("createRandom delegates to Math.random", () => {
|
|
32
|
+
const mathRandom = mock.method(Math, "random", () => 0.25);
|
|
33
|
+
|
|
34
|
+
assertEqual(createRandom().next(), 0.25);
|
|
35
|
+
assertEqual(mathRandom.mock.callCount(), 1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("testCreateRandom constructs RandomLib with the default seed", () => {
|
|
39
|
+
testCreateRandom();
|
|
40
|
+
|
|
41
|
+
assertEqual(
|
|
42
|
+
randomLibConstructor.mock.calls.map(({ arguments: args }) => args),
|
|
43
|
+
[["evolu"]],
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("testCreateRandom constructs RandomLib with a custom seed", () => {
|
|
48
|
+
testCreateRandom("test");
|
|
49
|
+
|
|
50
|
+
assertEqual(
|
|
51
|
+
randomLibConstructor.mock.calls.map(({ arguments: args }) => args),
|
|
52
|
+
[["test"]],
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("testCreateRandom delegates next to RandomLib", () => {
|
|
57
|
+
randomLibNext.mock.mockImplementation(() => 0.25);
|
|
58
|
+
|
|
59
|
+
assertEqual(testCreateRandom().next(), 0.25);
|
|
60
|
+
assertEqual(randomLibNext.mock.callCount(), 1);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("createRandomLib constructs RandomLib without a seed", () => {
|
|
64
|
+
createRandomLib();
|
|
65
|
+
|
|
66
|
+
assertEqual(
|
|
67
|
+
randomLibConstructor.mock.calls.map(({ arguments: args }) => args),
|
|
68
|
+
[[]],
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("testCreateRandomLib constructs RandomLib with the default seed", () => {
|
|
73
|
+
testCreateRandomLib();
|
|
74
|
+
|
|
75
|
+
assertEqual(
|
|
76
|
+
randomLibConstructor.mock.calls.map(({ arguments: args }) => args),
|
|
77
|
+
[["evolu"]],
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("testCreateRandomLib constructs RandomLib with a custom seed", () => {
|
|
82
|
+
testCreateRandomLib("test");
|
|
83
|
+
|
|
84
|
+
assertEqual(
|
|
85
|
+
randomLibConstructor.mock.calls.map(({ arguments: args }) => args),
|
|
86
|
+
[["test"]],
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
});
|
package/src/Random.ts
CHANGED
|
@@ -34,7 +34,7 @@ export type RandomNumber = number & Brand<"RandomNumber">;
|
|
|
34
34
|
* // For apps, use the Math.random-backed implementation.
|
|
35
35
|
* const random = createRandom();
|
|
36
36
|
* const value = random.next();
|
|
37
|
-
* assertType<
|
|
37
|
+
* assertType<typeof value, RandomNumber>();
|
|
38
38
|
* assertTrue(value >= 0);
|
|
39
39
|
* assertTrue(value < 1);
|
|
40
40
|
*
|
package/src/Ref.test.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import { assertEqual } from "./Assert.ts";
|
|
3
|
+
|
|
4
|
+
import { createRef } from "./Ref.ts";
|
|
5
|
+
|
|
6
|
+
describe("Ref", () => {
|
|
7
|
+
describe("get", () => {
|
|
8
|
+
it("returns initial value", () => {
|
|
9
|
+
const ref = createRef(42);
|
|
10
|
+
assertEqual(ref.get(), 42);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("set", () => {
|
|
15
|
+
it("updates value", () => {
|
|
16
|
+
const ref = createRef(0);
|
|
17
|
+
ref.set(1);
|
|
18
|
+
assertEqual(ref.get(), 1);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("accepts a value equal to the current value", () => {
|
|
22
|
+
const ref = createRef(1);
|
|
23
|
+
ref.set(1);
|
|
24
|
+
assertEqual(ref.get(), 1);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("getAndSet", () => {
|
|
29
|
+
it("returns previous value and updates value", () => {
|
|
30
|
+
const ref = createRef(1);
|
|
31
|
+
|
|
32
|
+
assertEqual(ref.getAndSet(2), 1);
|
|
33
|
+
assertEqual(ref.get(), 2);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("accepts a value equal to the current value", () => {
|
|
37
|
+
const ref = createRef(1);
|
|
38
|
+
|
|
39
|
+
assertEqual(ref.getAndSet(1), 1);
|
|
40
|
+
assertEqual(ref.get(), 1);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("setAndGet", () => {
|
|
45
|
+
it("returns updated value", () => {
|
|
46
|
+
const ref = createRef(1);
|
|
47
|
+
|
|
48
|
+
assertEqual(ref.setAndGet(2), 2);
|
|
49
|
+
assertEqual(ref.get(), 2);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("accepts a value equal to the current value", () => {
|
|
53
|
+
const ref = createRef(1);
|
|
54
|
+
|
|
55
|
+
assertEqual(ref.setAndGet(1), 1);
|
|
56
|
+
assertEqual(ref.get(), 1);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("update", () => {
|
|
61
|
+
it("updates value", () => {
|
|
62
|
+
const ref = createRef(1);
|
|
63
|
+
|
|
64
|
+
ref.update((n) => n + 1);
|
|
65
|
+
|
|
66
|
+
assertEqual(ref.get(), 2);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("can keep the same value", () => {
|
|
70
|
+
const ref = createRef(1);
|
|
71
|
+
|
|
72
|
+
ref.update((n) => n);
|
|
73
|
+
|
|
74
|
+
assertEqual(ref.get(), 1);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("getAndUpdate", () => {
|
|
79
|
+
it("returns previous value and updates value", () => {
|
|
80
|
+
const ref = createRef(1);
|
|
81
|
+
|
|
82
|
+
assertEqual(
|
|
83
|
+
ref.getAndUpdate((n) => n + 1),
|
|
84
|
+
1,
|
|
85
|
+
);
|
|
86
|
+
assertEqual(ref.get(), 2);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("accepts an identity updater", () => {
|
|
90
|
+
const ref = createRef(1);
|
|
91
|
+
|
|
92
|
+
assertEqual(
|
|
93
|
+
ref.getAndUpdate((n) => n),
|
|
94
|
+
1,
|
|
95
|
+
);
|
|
96
|
+
assertEqual(ref.get(), 1);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("updateAndGet", () => {
|
|
101
|
+
it("returns updated value", () => {
|
|
102
|
+
const ref = createRef(1);
|
|
103
|
+
|
|
104
|
+
assertEqual(
|
|
105
|
+
ref.updateAndGet((n) => n + 1),
|
|
106
|
+
2,
|
|
107
|
+
);
|
|
108
|
+
assertEqual(ref.get(), 2);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("accepts an identity updater", () => {
|
|
112
|
+
const ref = createRef(1);
|
|
113
|
+
|
|
114
|
+
assertEqual(
|
|
115
|
+
ref.updateAndGet((n) => n),
|
|
116
|
+
1,
|
|
117
|
+
);
|
|
118
|
+
assertEqual(ref.get(), 1);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("modify", () => {
|
|
123
|
+
it("returns a computed result and updates value", () => {
|
|
124
|
+
const ref = createRef(0);
|
|
125
|
+
const result = ref.modify((current) => [current, current + 1]);
|
|
126
|
+
|
|
127
|
+
assertEqual(result, 0);
|
|
128
|
+
assertEqual(ref.get(), 1);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("can keep the same value while returning a result", () => {
|
|
132
|
+
const ref = createRef(1);
|
|
133
|
+
const result = ref.modify((current) => [`current:${current}`, current]);
|
|
134
|
+
|
|
135
|
+
assertEqual(result, "current:1");
|
|
136
|
+
assertEqual(ref.get(), 1);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import {
|
|
3
|
+
assertEqual,
|
|
4
|
+
assertFalse,
|
|
5
|
+
assertThrowsInstanceOf,
|
|
6
|
+
assertTrue,
|
|
7
|
+
} from "./Assert.ts";
|
|
8
|
+
|
|
9
|
+
import { installPolyfills } from "./Polyfills.ts";
|
|
10
|
+
import { createRefCount, createRefCountByKey } from "./RefCount.ts";
|
|
11
|
+
|
|
12
|
+
installPolyfills();
|
|
13
|
+
|
|
14
|
+
describe("createRefCount", () => {
|
|
15
|
+
it("increments and decrements the count", () => {
|
|
16
|
+
const refCount = createRefCount();
|
|
17
|
+
|
|
18
|
+
assertEqual(refCount.getCount(), 0);
|
|
19
|
+
assertEqual(refCount.increment(), 1);
|
|
20
|
+
assertEqual(refCount.increment(), 2);
|
|
21
|
+
assertEqual(refCount.getCount(), 2);
|
|
22
|
+
assertEqual(refCount.decrement(), 1);
|
|
23
|
+
assertEqual(refCount.decrement(), 0);
|
|
24
|
+
assertEqual(refCount.getCount(), 0);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("decrement throws on underflow", () => {
|
|
28
|
+
const refCount = createRefCount();
|
|
29
|
+
|
|
30
|
+
const error = assertThrowsInstanceOf(() => refCount.decrement(), Error);
|
|
31
|
+
assertTrue(
|
|
32
|
+
error.message.includes("RefCount must not be decremented below zero."),
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("dispose invalidates the helper", () => {
|
|
37
|
+
const refCount = createRefCount();
|
|
38
|
+
|
|
39
|
+
refCount.increment();
|
|
40
|
+
refCount.increment();
|
|
41
|
+
refCount[Symbol.dispose]();
|
|
42
|
+
|
|
43
|
+
const incrementError = assertThrowsInstanceOf(
|
|
44
|
+
() => refCount.increment(),
|
|
45
|
+
Error,
|
|
46
|
+
);
|
|
47
|
+
assertTrue(
|
|
48
|
+
incrementError.message.includes("Cannot use a disposed object."),
|
|
49
|
+
);
|
|
50
|
+
const decrementError = assertThrowsInstanceOf(
|
|
51
|
+
() => refCount.decrement(),
|
|
52
|
+
Error,
|
|
53
|
+
);
|
|
54
|
+
assertTrue(
|
|
55
|
+
decrementError.message.includes("Cannot use a disposed object."),
|
|
56
|
+
);
|
|
57
|
+
const getCountError = assertThrowsInstanceOf(
|
|
58
|
+
() => refCount.getCount(),
|
|
59
|
+
Error,
|
|
60
|
+
);
|
|
61
|
+
assertTrue(getCountError.message.includes("Cannot use a disposed object."));
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("createRefCountByKey", () => {
|
|
66
|
+
it("tracks counts per key", () => {
|
|
67
|
+
const refCount = createRefCountByKey<string>();
|
|
68
|
+
|
|
69
|
+
assertEqual(refCount.increment("a"), 1);
|
|
70
|
+
assertEqual(refCount.increment("a"), 2);
|
|
71
|
+
assertEqual(refCount.increment("b"), 1);
|
|
72
|
+
assertEqual(refCount.getCount("a"), 2);
|
|
73
|
+
assertEqual(refCount.getCount("b"), 1);
|
|
74
|
+
assertEqual(refCount.keys(), new Set(["a", "b"]));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("decrement removes key at zero", () => {
|
|
78
|
+
const refCount = createRefCountByKey<string>();
|
|
79
|
+
|
|
80
|
+
refCount.increment("a");
|
|
81
|
+
|
|
82
|
+
assertEqual(refCount.decrement("a"), 0);
|
|
83
|
+
assertEqual(refCount.getCount("a"), 0);
|
|
84
|
+
assertFalse(refCount.has("a"));
|
|
85
|
+
assertEqual(refCount.keys(), new Set());
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("decrement throws on missing key", () => {
|
|
89
|
+
const refCount = createRefCountByKey<string>();
|
|
90
|
+
|
|
91
|
+
const error = assertThrowsInstanceOf(
|
|
92
|
+
() => refCount.decrement("missing"),
|
|
93
|
+
Error,
|
|
94
|
+
);
|
|
95
|
+
assertTrue(
|
|
96
|
+
error.message.includes(
|
|
97
|
+
"RefCount must not be decremented for an untracked key.",
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
assertFalse(refCount.has("missing"));
|
|
101
|
+
assertEqual(refCount.keys(), new Set());
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("decrement keeps key while count stays positive", () => {
|
|
105
|
+
const refCount = createRefCountByKey<string>();
|
|
106
|
+
|
|
107
|
+
refCount.increment("a");
|
|
108
|
+
refCount.increment("a");
|
|
109
|
+
|
|
110
|
+
assertEqual(refCount.decrement("a"), 1);
|
|
111
|
+
assertEqual(refCount.getCount("a"), 1);
|
|
112
|
+
assertTrue(refCount.has("a"));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("keys returns a snapshot set", () => {
|
|
116
|
+
const refCount = createRefCountByKey<string>();
|
|
117
|
+
refCount.increment("a");
|
|
118
|
+
|
|
119
|
+
const keys = refCount.keys();
|
|
120
|
+
refCount.increment("b");
|
|
121
|
+
|
|
122
|
+
assertEqual(keys, new Set(["a"]));
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("dispose invalidates the helper", () => {
|
|
126
|
+
const refCount = createRefCountByKey<string>();
|
|
127
|
+
|
|
128
|
+
refCount.increment("a");
|
|
129
|
+
refCount.increment("b");
|
|
130
|
+
refCount[Symbol.dispose]();
|
|
131
|
+
|
|
132
|
+
const incrementError = assertThrowsInstanceOf(
|
|
133
|
+
() => refCount.increment("c"),
|
|
134
|
+
Error,
|
|
135
|
+
);
|
|
136
|
+
assertTrue(
|
|
137
|
+
incrementError.message.includes("Cannot use a disposed object."),
|
|
138
|
+
);
|
|
139
|
+
const decrementError = assertThrowsInstanceOf(
|
|
140
|
+
() => refCount.decrement("a"),
|
|
141
|
+
Error,
|
|
142
|
+
);
|
|
143
|
+
assertTrue(
|
|
144
|
+
decrementError.message.includes("Cannot use a disposed object."),
|
|
145
|
+
);
|
|
146
|
+
const getCountError = assertThrowsInstanceOf(
|
|
147
|
+
() => refCount.getCount("a"),
|
|
148
|
+
Error,
|
|
149
|
+
);
|
|
150
|
+
assertTrue(getCountError.message.includes("Cannot use a disposed object."));
|
|
151
|
+
const hasError = assertThrowsInstanceOf(() => refCount.has("a"), Error);
|
|
152
|
+
assertTrue(hasError.message.includes("Cannot use a disposed object."));
|
|
153
|
+
const keysError = assertThrowsInstanceOf(() => refCount.keys(), Error);
|
|
154
|
+
assertTrue(keysError.message.includes("Cannot use a disposed object."));
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("uses reference identity for object keys", () => {
|
|
158
|
+
const refCount = createRefCountByKey<{ readonly id: string }>();
|
|
159
|
+
const keyA = { id: "same" };
|
|
160
|
+
const keyB = { id: "same" };
|
|
161
|
+
|
|
162
|
+
refCount.increment(keyA);
|
|
163
|
+
refCount.increment(keyB);
|
|
164
|
+
|
|
165
|
+
assertEqual(refCount.getCount(keyA), 1);
|
|
166
|
+
assertEqual(refCount.getCount(keyB), 1);
|
|
167
|
+
assertEqual(refCount.keys().size, 2);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("uses lookup for logical key equality", () => {
|
|
171
|
+
const refCount = createRefCountByKey<{ readonly id: string }, string>({
|
|
172
|
+
lookup: (key) => key.id,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
assertEqual(refCount.increment({ id: "same" }), 1);
|
|
176
|
+
assertEqual(refCount.increment({ id: "same" }), 2);
|
|
177
|
+
assertEqual(refCount.getCount({ id: "same" }), 2);
|
|
178
|
+
assertEqual(refCount.keys().size, 1);
|
|
179
|
+
|
|
180
|
+
assertEqual(refCount.decrement({ id: "same" }), 1);
|
|
181
|
+
assertEqual(refCount.decrement({ id: "same" }), 0);
|
|
182
|
+
assertFalse(refCount.has({ id: "same" }));
|
|
183
|
+
});
|
|
184
|
+
});
|