@evolu/common 8.7.0 → 8.9.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 +498 -141
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +648 -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.d.ts +6 -0
- package/dist/src/Polyfills.d.ts.map +1 -1
- package/dist/src/Polyfills.js +10 -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 +1232 -0
- package/src/Assert.ts +1023 -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 +9 -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
|
@@ -16,6 +16,12 @@
|
|
|
16
16
|
* `Map.prototype.getOrInsertComputed`, `WeakMap.prototype.getOrInsert`, and
|
|
17
17
|
* `WeakMap.prototype.getOrInsertComputed`) for Node 24 and React Native
|
|
18
18
|
*
|
|
19
|
+
* The polyfills are implemented directly in the Evolu Library instead of being
|
|
20
|
+
* third-party runtime dependencies. We audit their behavior against
|
|
21
|
+
* specifications, conformance tests, and native implementations, and fix issues
|
|
22
|
+
* when necessary. This lets Evolu understand and control every line of this
|
|
23
|
+
* runtime code.
|
|
24
|
+
*
|
|
19
25
|
* `@evolu/react-native` has its own `Polyfills` module and its
|
|
20
26
|
* `installPolyfills` calls this function first, then installs React Native
|
|
21
27
|
* specific polyfills.
|
|
@@ -125,6 +131,7 @@ const appendDisposeError = (
|
|
|
125
131
|
currentError: unknown,
|
|
126
132
|
previousError: unknown,
|
|
127
133
|
): unknown =>
|
|
134
|
+
// 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
135
|
new globalThis.SuppressedError(
|
|
129
136
|
currentError,
|
|
130
137
|
previousError,
|
|
@@ -158,6 +165,7 @@ const getOrInstallSymbol = (
|
|
|
158
165
|
key: "dispose" | "asyncDispose",
|
|
159
166
|
description: string,
|
|
160
167
|
): symbol => {
|
|
168
|
+
// oxlint-disable-next-line evolu/no-unnecessary-global-this -- Patch the global object constructor even if a realm lexical binding shadows it.
|
|
161
169
|
const SymbolCtor = globalThis.Symbol as SymbolWithDisposable;
|
|
162
170
|
const installedValue = Object.getOwnPropertyDescriptor(SymbolCtor, key)
|
|
163
171
|
?.value as unknown;
|
|
@@ -178,10 +186,7 @@ const getOrInstallSymbol = (
|
|
|
178
186
|
const installSuppressedError = (): void => {
|
|
179
187
|
if (typeof globalThis.SuppressedError === "function") return;
|
|
180
188
|
|
|
181
|
-
class SuppressedErrorPolyfill
|
|
182
|
-
extends Error
|
|
183
|
-
implements globalThis.SuppressedError
|
|
184
|
-
{
|
|
189
|
+
class SuppressedErrorPolyfill extends Error implements SuppressedError {
|
|
185
190
|
readonly error: unknown;
|
|
186
191
|
readonly suppressed: unknown;
|
|
187
192
|
|
|
@@ -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
|
+
});
|