@evolu/common 8.9.0 → 8.11.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/Bytes.d.ts +647 -0
- package/dist/src/Bytes.d.ts.map +1 -0
- package/dist/src/{Binary.js → Bytes.js} +266 -16
- package/dist/src/Config.d.ts +142 -0
- package/dist/src/Config.d.ts.map +1 -0
- package/dist/src/Config.js +181 -0
- package/dist/src/Console.d.ts +62 -7
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +20 -4
- package/dist/src/Crypto.d.ts +76 -4
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +55 -4
- package/dist/src/Error.d.ts +45 -0
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +69 -0
- package/dist/src/Fs.d.ts +376 -0
- package/dist/src/Fs.d.ts.map +1 -0
- package/dist/src/Fs.js +113 -0
- package/dist/src/Identicon.d.ts +2 -2
- package/dist/src/Identicon.js +2 -2
- package/dist/src/LeakDetector.d.ts +22 -3
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +12 -2
- package/dist/src/LockManager.d.ts +8 -0
- package/dist/src/LockManager.d.ts.map +1 -1
- package/dist/src/LockManager.js +6 -0
- package/dist/src/Number.d.ts +50 -7
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +47 -8
- package/dist/src/Object.d.ts +32 -0
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +46 -0
- package/dist/src/Platform.d.ts +47 -7
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +24 -5
- package/dist/src/Random.d.ts +25 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +14 -2
- package/dist/src/Resource.d.ts +156 -1
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +201 -72
- package/dist/src/Schedule.d.ts +11 -10
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +1 -1
- package/dist/src/Sqlite.d.ts +132 -16
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +64 -10
- package/dist/src/Task.d.ts +15 -4
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +41 -15
- package/dist/src/Test.d.ts +9 -0
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +4 -0
- package/dist/src/Time.d.ts +179 -20
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +95 -6
- package/dist/src/Type.d.ts +3056 -1539
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +2548 -584
- package/dist/src/WebSocket.d.ts +164 -13
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +133 -24
- package/dist/src/Worker.d.ts +90 -8
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +28 -2
- package/dist/src/index.d.ts +9 -8
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -4
- package/dist/src/intl/_en.d.ts +24 -1
- package/dist/src/intl/_en.d.ts.map +1 -1
- package/dist/src/intl/_en.js +20 -0
- package/dist/src/intl/ar.d.ts +24 -1
- package/dist/src/intl/ar.d.ts.map +1 -1
- package/dist/src/intl/ar.js +20 -0
- package/dist/src/intl/bn.d.ts +24 -1
- package/dist/src/intl/bn.d.ts.map +1 -1
- package/dist/src/intl/bn.js +20 -0
- package/dist/src/intl/ca.d.ts +24 -1
- package/dist/src/intl/ca.d.ts.map +1 -1
- package/dist/src/intl/ca.js +20 -0
- package/dist/src/intl/cs.d.ts +24 -1
- package/dist/src/intl/cs.d.ts.map +1 -1
- package/dist/src/intl/cs.js +20 -0
- package/dist/src/intl/da.d.ts +24 -1
- package/dist/src/intl/da.d.ts.map +1 -1
- package/dist/src/intl/da.js +20 -0
- package/dist/src/intl/de.d.ts +24 -1
- package/dist/src/intl/de.d.ts.map +1 -1
- package/dist/src/intl/de.js +20 -0
- package/dist/src/intl/el.d.ts +24 -1
- package/dist/src/intl/el.d.ts.map +1 -1
- package/dist/src/intl/el.js +20 -0
- package/dist/src/intl/es.d.ts +24 -1
- package/dist/src/intl/es.d.ts.map +1 -1
- package/dist/src/intl/es.js +20 -0
- package/dist/src/intl/fa.d.ts +24 -1
- package/dist/src/intl/fa.d.ts.map +1 -1
- package/dist/src/intl/fa.js +20 -0
- package/dist/src/intl/fi.d.ts +24 -1
- package/dist/src/intl/fi.d.ts.map +1 -1
- package/dist/src/intl/fi.js +20 -0
- package/dist/src/intl/fil.d.ts +24 -1
- package/dist/src/intl/fil.d.ts.map +1 -1
- package/dist/src/intl/fil.js +20 -0
- package/dist/src/intl/fr.d.ts +24 -1
- package/dist/src/intl/fr.d.ts.map +1 -1
- package/dist/src/intl/fr.js +20 -0
- package/dist/src/intl/he.d.ts +24 -1
- package/dist/src/intl/he.d.ts.map +1 -1
- package/dist/src/intl/he.js +20 -0
- package/dist/src/intl/hi.d.ts +24 -1
- package/dist/src/intl/hi.d.ts.map +1 -1
- package/dist/src/intl/hi.js +20 -0
- package/dist/src/intl/hr.d.ts +24 -1
- package/dist/src/intl/hr.d.ts.map +1 -1
- package/dist/src/intl/hr.js +20 -0
- package/dist/src/intl/hu.d.ts +22 -1
- package/dist/src/intl/hu.d.ts.map +1 -1
- package/dist/src/intl/hu.js +18 -0
- package/dist/src/intl/id.d.ts +24 -1
- package/dist/src/intl/id.d.ts.map +1 -1
- package/dist/src/intl/id.js +20 -0
- package/dist/src/intl/it.d.ts +24 -1
- package/dist/src/intl/it.d.ts.map +1 -1
- package/dist/src/intl/it.js +20 -0
- package/dist/src/intl/ja.d.ts +24 -1
- package/dist/src/intl/ja.d.ts.map +1 -1
- package/dist/src/intl/ja.js +20 -0
- package/dist/src/intl/ko.d.ts +24 -1
- package/dist/src/intl/ko.d.ts.map +1 -1
- package/dist/src/intl/ko.js +20 -0
- package/dist/src/intl/ml.d.ts +24 -1
- package/dist/src/intl/ml.d.ts.map +1 -1
- package/dist/src/intl/ml.js +20 -0
- package/dist/src/intl/mr.d.ts +24 -1
- package/dist/src/intl/mr.d.ts.map +1 -1
- package/dist/src/intl/mr.js +20 -0
- package/dist/src/intl/ms.d.ts +24 -1
- package/dist/src/intl/ms.d.ts.map +1 -1
- package/dist/src/intl/ms.js +20 -0
- package/dist/src/intl/nb.d.ts +22 -1
- package/dist/src/intl/nb.d.ts.map +1 -1
- package/dist/src/intl/nb.js +18 -0
- package/dist/src/intl/nl.d.ts +24 -1
- package/dist/src/intl/nl.d.ts.map +1 -1
- package/dist/src/intl/nl.js +20 -0
- package/dist/src/intl/pa.d.ts +24 -1
- package/dist/src/intl/pa.d.ts.map +1 -1
- package/dist/src/intl/pa.js +20 -0
- package/dist/src/intl/pl.d.ts +23 -0
- package/dist/src/intl/pl.d.ts.map +1 -1
- package/dist/src/intl/pl.js +20 -0
- package/dist/src/intl/pt-BR.d.ts +24 -1
- package/dist/src/intl/pt-BR.d.ts.map +1 -1
- package/dist/src/intl/pt-BR.js +20 -0
- package/dist/src/intl/pt.d.ts +24 -1
- package/dist/src/intl/pt.d.ts.map +1 -1
- package/dist/src/intl/pt.js +20 -0
- package/dist/src/intl/ro.d.ts +24 -1
- package/dist/src/intl/ro.d.ts.map +1 -1
- package/dist/src/intl/ro.js +20 -0
- package/dist/src/intl/sk.d.ts +24 -1
- package/dist/src/intl/sk.d.ts.map +1 -1
- package/dist/src/intl/sk.js +20 -0
- package/dist/src/intl/sl.d.ts +24 -1
- package/dist/src/intl/sl.d.ts.map +1 -1
- package/dist/src/intl/sl.js +20 -0
- package/dist/src/intl/sv.d.ts +24 -1
- package/dist/src/intl/sv.d.ts.map +1 -1
- package/dist/src/intl/sv.js +20 -0
- package/dist/src/intl/sw.d.ts +21 -0
- package/dist/src/intl/sw.d.ts.map +1 -1
- package/dist/src/intl/sw.js +18 -0
- package/dist/src/intl/ta.d.ts +24 -1
- package/dist/src/intl/ta.d.ts.map +1 -1
- package/dist/src/intl/ta.js +20 -0
- package/dist/src/intl/te.d.ts +24 -1
- package/dist/src/intl/te.d.ts.map +1 -1
- package/dist/src/intl/te.js +20 -0
- package/dist/src/intl/th.d.ts +24 -1
- package/dist/src/intl/th.d.ts.map +1 -1
- package/dist/src/intl/th.js +20 -0
- package/dist/src/intl/tr.d.ts +24 -1
- package/dist/src/intl/tr.d.ts.map +1 -1
- package/dist/src/intl/tr.js +20 -0
- package/dist/src/intl/uk.d.ts +80 -57
- package/dist/src/intl/uk.d.ts.map +1 -1
- package/dist/src/intl/uk.js +174 -149
- package/dist/src/intl/ur.d.ts +24 -1
- package/dist/src/intl/ur.d.ts.map +1 -1
- package/dist/src/intl/ur.js +20 -0
- package/dist/src/intl/vi.d.ts +24 -1
- package/dist/src/intl/vi.d.ts.map +1 -1
- package/dist/src/intl/vi.js +20 -0
- package/dist/src/intl/zh-CN.d.ts +24 -1
- package/dist/src/intl/zh-CN.d.ts.map +1 -1
- package/dist/src/intl/zh-CN.js +20 -0
- package/dist/src/intl/zh-TW.d.ts +24 -1
- package/dist/src/intl/zh-TW.d.ts.map +1 -1
- package/dist/src/intl/zh-TW.js +20 -0
- package/dist/src/local-first/Db.d.ts +52 -3
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +412 -137
- package/dist/src/local-first/Evolu.d.ts +336 -211
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +102 -15
- package/dist/src/local-first/Owner.d.ts +13 -30
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +13 -30
- package/dist/src/local-first/Protocol.d.ts +95 -17
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +119 -39
- package/dist/src/local-first/Query.d.ts +8 -15
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Schema.d.ts +345 -21
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +214 -17
- package/dist/src/local-first/Shared.d.ts +537 -22
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +1437 -234
- package/dist/src/local-first/Storage.d.ts +192 -14
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +82 -21
- package/dist/src/local-first/Timestamp.d.ts +392 -41
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +404 -82
- package/dist/src/local-first/index.d.ts +0 -1
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +0 -1
- package/package.json +1 -1
- package/src/Assert.test.ts +2 -5
- package/src/{Binary.test.ts → Bytes.test.ts} +286 -1
- package/src/{Binary.ts → Bytes.ts} +652 -21
- package/src/Config.test.ts +668 -0
- package/src/Config.ts +410 -0
- package/src/Console.ts +62 -7
- package/src/Crypto.ts +76 -4
- package/src/Eq.test.ts +2 -3
- package/src/Error.test.ts +76 -3
- package/src/Error.ts +71 -0
- package/src/Fs.test.ts +105 -0
- package/src/Fs.ts +488 -0
- package/src/Identicon.ts +2 -2
- package/src/LeakDetector.ts +22 -3
- package/src/LockManager.ts +8 -0
- package/src/Number.test.ts +82 -18
- package/src/Number.ts +76 -8
- package/src/Object.test.ts +139 -10
- package/src/Object.ts +49 -0
- package/src/Platform.ts +50 -8
- package/src/Random.ts +25 -2
- package/src/Resource.test.ts +837 -0
- package/src/Resource.ts +235 -15
- package/src/Schedule.test.ts +50 -12
- package/src/Schedule.ts +24 -14
- package/src/Sqlite.ts +138 -18
- package/src/Task.test.ts +189 -8
- package/src/Task.ts +56 -17
- package/src/Test.ts +9 -0
- package/src/Time.test.ts +82 -11
- package/src/Time.ts +246 -24
- package/src/Type.test.ts +3994 -1119
- package/src/Type.ts +7258 -3842
- package/src/Types.test.ts +4 -14
- package/src/WebSocket.ts +313 -40
- package/src/Worker.ts +90 -8
- package/src/index.ts +18 -7
- package/src/intl/_en.ts +70 -0
- package/src/intl/ar.ts +71 -0
- package/src/intl/bn.ts +70 -0
- package/src/intl/ca.ts +70 -0
- package/src/intl/cs.ts +70 -0
- package/src/intl/da.ts +70 -0
- package/src/intl/de.ts +70 -0
- package/src/intl/el.ts +70 -0
- package/src/intl/es.ts +70 -0
- package/src/intl/fa.ts +70 -0
- package/src/intl/fi.ts +70 -0
- package/src/intl/fil.ts +70 -0
- package/src/intl/fr.ts +70 -0
- package/src/intl/he.ts +70 -0
- package/src/intl/hi.ts +70 -0
- package/src/intl/hr.ts +70 -0
- package/src/intl/hu.ts +69 -0
- package/src/intl/id.ts +70 -0
- package/src/intl/intl.test.ts +819 -1
- package/src/intl/it.ts +70 -0
- package/src/intl/ja.ts +70 -0
- package/src/intl/ko.ts +68 -0
- package/src/intl/ml.ts +70 -0
- package/src/intl/mr.ts +70 -0
- package/src/intl/ms.ts +71 -0
- package/src/intl/nb.ts +69 -0
- package/src/intl/nl.ts +70 -0
- package/src/intl/pa.ts +70 -0
- package/src/intl/pl.ts +63 -0
- package/src/intl/pt-BR.ts +70 -0
- package/src/intl/pt.ts +71 -0
- package/src/intl/ro.ts +70 -0
- package/src/intl/sk.ts +71 -0
- package/src/intl/sl.ts +70 -0
- package/src/intl/sv.ts +70 -0
- package/src/intl/sw.ts +62 -0
- package/src/intl/ta.ts +70 -0
- package/src/intl/te.ts +70 -0
- package/src/intl/th.ts +68 -0
- package/src/intl/tr.ts +70 -0
- package/src/intl/uk.ts +228 -155
- package/src/intl/ur.ts +70 -0
- package/src/intl/vi.ts +70 -0
- package/src/intl/zh-CN.ts +68 -0
- package/src/intl/zh-TW.ts +68 -0
- package/src/local-first/Db.ts +644 -339
- package/src/local-first/Evolu.test.ts +686 -21
- package/src/local-first/Evolu.ts +450 -228
- package/src/local-first/Owner.ts +13 -30
- package/src/local-first/Protocol.test.ts +618 -11
- package/src/local-first/Protocol.ts +197 -73
- package/src/local-first/Query.ts +8 -15
- package/src/local-first/Schema.test.ts +143 -0
- package/src/local-first/Schema.ts +374 -24
- package/src/local-first/Shared.test.ts +7731 -559
- package/src/local-first/Shared.ts +2036 -267
- package/src/local-first/Storage.ts +219 -33
- package/src/local-first/Timestamp.test.ts +344 -70
- package/src/local-first/Timestamp.ts +435 -119
- package/src/local-first/index.ts +0 -1
- package/dist/src/Binary.d.ts +0 -254
- package/dist/src/Binary.d.ts.map +0 -1
- package/dist/src/local-first/Error.d.ts +0 -12
- package/dist/src/local-first/Error.d.ts.map +0 -1
- package/dist/src/local-first/Error.js +0 -6
- package/dist/src/local-first/LocalAuth.d.ts +0 -150
- package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
- package/dist/src/local-first/LocalAuth.js +0 -179
- package/src/local-first/Error.ts +0 -17
- package/src/local-first/LocalAuth.ts +0 -457
package/src/Number.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { test } from "node:test";
|
|
1
|
+
import { describe, test } from "node:test";
|
|
2
2
|
import {
|
|
3
3
|
assertEqual,
|
|
4
|
+
assertErr,
|
|
4
5
|
assertFalse,
|
|
5
6
|
assertOk,
|
|
6
7
|
assertSame,
|
|
@@ -26,7 +27,16 @@ import {
|
|
|
26
27
|
percentageToRatio,
|
|
27
28
|
} from "./Number.ts";
|
|
28
29
|
import { err, ok } from "./Result.ts";
|
|
29
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
localizeTypes,
|
|
32
|
+
object,
|
|
33
|
+
typeErrorToIssues,
|
|
34
|
+
type InferErrors,
|
|
35
|
+
assertType,
|
|
36
|
+
NonNegativeInt,
|
|
37
|
+
PositiveInt,
|
|
38
|
+
Ratio,
|
|
39
|
+
} from "./Type.ts";
|
|
30
40
|
|
|
31
41
|
test("bounded integer literal types", () => {
|
|
32
42
|
assertType<1 extends Int1To99 ? true : false, true>();
|
|
@@ -53,22 +63,76 @@ test("bounded integer literal types", () => {
|
|
|
53
63
|
assertType<PositiveInt extends Int1To100OrPositiveInt ? true : false, true>();
|
|
54
64
|
});
|
|
55
65
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
describe("PercentageLiteral", () => {
|
|
67
|
+
test("reports a dedicated error with the original validation failure", () => {
|
|
68
|
+
for (const value of ["101%", "", 1, null, undefined]) {
|
|
69
|
+
const result = PercentageLiteral.fromUnknown(value);
|
|
70
|
+
assertErr(result);
|
|
71
|
+
assertType<typeof result.error.type, "PercentageLiteral">();
|
|
72
|
+
assertSame(result.error.value, value);
|
|
73
|
+
assertEqual(result.error.cause.type, "Union");
|
|
74
|
+
assertEqual(result.error.cause.errors.length, 1);
|
|
75
|
+
}
|
|
76
|
+
const result = PercentageLiteral.fromUnknown("101%", { errors: "all" });
|
|
77
|
+
assertErr(result);
|
|
78
|
+
assertEqual(result.error.cause.errors.length, 4);
|
|
79
|
+
assertEqual(
|
|
80
|
+
PercentageLiteral.formatError(result.error),
|
|
81
|
+
'The value "101%" is not a percentage literal. Use a value such as "50%" or "12.5%".',
|
|
82
|
+
);
|
|
83
|
+
assertType<
|
|
84
|
+
InferErrors<typeof PercentageLiteral>["type"],
|
|
85
|
+
"PercentageLiteral"
|
|
86
|
+
>();
|
|
87
|
+
assertOk(PercentageLiteral.from("12.5%"), "12.5%");
|
|
88
|
+
assertEqual(PercentageLiteral.to("12.5%"), "12.5%");
|
|
89
|
+
// @ts-expect-error PercentageLiteral Input rejects "101%".
|
|
90
|
+
const _invalidInput: typeof PercentageLiteral.Input = "101%";
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("localizes the named error after composition and preserves its path", async () => {
|
|
94
|
+
const { first } = localizeTypes(
|
|
95
|
+
{ Value: PercentageLiteral },
|
|
96
|
+
{ first: { PercentageLiteral: () => "First message." } },
|
|
97
|
+
);
|
|
98
|
+
const { second } = localizeTypes(
|
|
99
|
+
{ Settings: object({ value: first.Value }) },
|
|
100
|
+
{
|
|
101
|
+
second: {
|
|
102
|
+
Object: () => "Object.",
|
|
103
|
+
PercentageLiteral: () => "Second message.",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
const result = second.Settings.fromUnknown({ value: "101%" });
|
|
108
|
+
assertErr(result);
|
|
109
|
+
assertEqual(second.Settings.formatError(result.error), "Second message.");
|
|
110
|
+
assertEqual(typeErrorToIssues(second.Settings, result.error), [
|
|
111
|
+
{ path: ["value"], message: "Second message." },
|
|
112
|
+
]);
|
|
113
|
+
assertEqual(
|
|
114
|
+
await second.Settings["~standard"].validate({ value: "101%" }),
|
|
115
|
+
{ issues: [{ path: ["value"], message: "Second message." }] },
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("accepts canonical literals or Ratio", () => {
|
|
120
|
+
assertType<"0%" extends PercentageLiteral ? true : false, true>();
|
|
121
|
+
assertType<"25%" extends PercentageLiteral ? true : false, true>();
|
|
122
|
+
assertType<"12.5%" extends PercentageLiteral ? true : false, true>();
|
|
123
|
+
assertType<"100%" extends PercentageLiteral ? true : false, true>();
|
|
124
|
+
assertType<"01%" extends PercentageLiteral ? true : false, false>();
|
|
125
|
+
assertType<"10.0%" extends PercentageLiteral ? true : false, false>();
|
|
126
|
+
assertType<"100.1%" extends PercentageLiteral ? true : false, false>();
|
|
127
|
+
assertType<Ratio extends Percentage ? true : false, true>();
|
|
128
|
+
assertTrue(PercentageLiteral.is("0%"));
|
|
129
|
+
assertTrue(PercentageLiteral.is("25%"));
|
|
130
|
+
assertTrue(PercentageLiteral.is("12.5%"));
|
|
131
|
+
assertTrue(PercentageLiteral.is("100%"));
|
|
132
|
+
assertFalse(PercentageLiteral.is("01%"));
|
|
133
|
+
assertFalse(PercentageLiteral.is("10.0%"));
|
|
134
|
+
assertFalse(PercentageLiteral.is("100.1%"));
|
|
135
|
+
});
|
|
72
136
|
});
|
|
73
137
|
|
|
74
138
|
test("percentageToRatio converts percentage literals and preserves Ratio", () => {
|
package/src/Number.ts
CHANGED
|
@@ -7,10 +7,15 @@
|
|
|
7
7
|
import type { NonEmptyReadonlyArray } from "./Array.ts";
|
|
8
8
|
import { assertNonEmptyReadonlyArray } from "./Assert.ts";
|
|
9
9
|
import type { IsBranded } from "./Brand.ts";
|
|
10
|
+
import { safelyStringifyUnknownValue } from "./String.ts";
|
|
10
11
|
import type { Result } from "./Result.ts";
|
|
11
12
|
import { err, ok } from "./Result.ts";
|
|
12
13
|
import {
|
|
13
14
|
brand,
|
|
15
|
+
createTypeWithError,
|
|
16
|
+
type Type,
|
|
17
|
+
type TypeError,
|
|
18
|
+
type UnionError,
|
|
14
19
|
Digit1To9,
|
|
15
20
|
Digit1To99,
|
|
16
21
|
lessThanOrEqualTo,
|
|
@@ -98,13 +103,10 @@ export type Int1To100OrPositiveInt = Int1To100 | PositiveInt;
|
|
|
98
103
|
*/
|
|
99
104
|
export type Percentage = PercentageLiteral | Ratio;
|
|
100
105
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
* values or greater precision.
|
|
106
|
-
*/
|
|
107
|
-
export const PercentageLiteral = /*#__PURE__*/ union(
|
|
106
|
+
export type PercentageLiteral =
|
|
107
|
+
"0%" | "100%" | `${Digit1To99}%` | `${"0" | Digit1To99}.${Digit1To9}%`;
|
|
108
|
+
|
|
109
|
+
const percentageLiteralSyntax = /*#__PURE__*/ union(
|
|
108
110
|
"0%",
|
|
109
111
|
"100%",
|
|
110
112
|
/*#__PURE__*/ templateLiteral(Digit1To99, "%"),
|
|
@@ -115,7 +117,73 @@ export const PercentageLiteral = /*#__PURE__*/ union(
|
|
|
115
117
|
"%",
|
|
116
118
|
),
|
|
117
119
|
);
|
|
118
|
-
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Percentage literal Type with compile-time and runtime validation.
|
|
123
|
+
*
|
|
124
|
+
* Supported formats:
|
|
125
|
+
*
|
|
126
|
+
* - Integers: `0%`, `50%`, `100%` (0-100)
|
|
127
|
+
* - One decimal place: `0.1%`, `12.5%`, `99.9%` (0.1-99.9)
|
|
128
|
+
*
|
|
129
|
+
* The decimal digit is never zero, so `12.0%` must be written as `"12%"`, and
|
|
130
|
+
* `100%` has no decimal form. For computed values or more precision, use
|
|
131
|
+
* {@link Ratio} directly.
|
|
132
|
+
*
|
|
133
|
+
* See {@link Percentage} for a type that also accepts {@link Ratio}. Use
|
|
134
|
+
* {@link percentageToRatio} to convert.
|
|
135
|
+
*
|
|
136
|
+
* Invalid values produce a {@link PercentageLiteralError}.
|
|
137
|
+
*
|
|
138
|
+
* ### Example
|
|
139
|
+
*
|
|
140
|
+
* ```ts
|
|
141
|
+
* import {
|
|
142
|
+
* assertFalse,
|
|
143
|
+
* assertOk,
|
|
144
|
+
* assertType,
|
|
145
|
+
* PercentageLiteral,
|
|
146
|
+
* } from "@evolu/common";
|
|
147
|
+
*
|
|
148
|
+
* // The TypeScript type accepts valid spellings and rejects the rest.
|
|
149
|
+
* const literal: PercentageLiteral = "12.5%";
|
|
150
|
+
* assertType<
|
|
151
|
+
* Extract<PercentageLiteral, "101%" | "12.0%" | "12.55%">,
|
|
152
|
+
* never
|
|
153
|
+
* >();
|
|
154
|
+
*
|
|
155
|
+
* // The runtime Type validates the same grammar.
|
|
156
|
+
* assertOk(PercentageLiteral.fromUnknown(literal), "12.5%");
|
|
157
|
+
* assertFalse(PercentageLiteral.is("101%"));
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export const PercentageLiteral: Type<
|
|
161
|
+
"PercentageLiteral",
|
|
162
|
+
PercentageLiteral,
|
|
163
|
+
PercentageLiteral,
|
|
164
|
+
PercentageLiteralError
|
|
165
|
+
> = /*#__PURE__*/ createTypeWithError(
|
|
166
|
+
"PercentageLiteral",
|
|
167
|
+
percentageLiteralSyntax,
|
|
168
|
+
(cause, value): PercentageLiteralError => ({
|
|
169
|
+
type: "PercentageLiteral",
|
|
170
|
+
value,
|
|
171
|
+
cause,
|
|
172
|
+
}),
|
|
173
|
+
(error) =>
|
|
174
|
+
`The value ${safelyStringifyUnknownValue(error.value)} is not a percentage literal. Use a value such as "50%" or "12.5%".`,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
/** Error returned when {@link PercentageLiteral} rejects a value. */
|
|
178
|
+
export interface PercentageLiteralError extends TypeError<"PercentageLiteral"> {
|
|
179
|
+
readonly value: unknown;
|
|
180
|
+
/**
|
|
181
|
+
* The underlying union failure, retained for diagnostics.
|
|
182
|
+
*
|
|
183
|
+
* With `{ errors: "all" }`, includes every failed alternative.
|
|
184
|
+
*/
|
|
185
|
+
readonly cause: UnionError;
|
|
186
|
+
}
|
|
119
187
|
|
|
120
188
|
/** Converts a {@link Percentage} to its numeric {@link Ratio}. */
|
|
121
189
|
export const percentageToRatio = (percentage: Percentage): Ratio =>
|
package/src/Object.test.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import nodeAssert from "node:assert/strict";
|
|
2
|
+
import { describe, it, test } from "node:test";
|
|
3
|
+
import {
|
|
4
|
+
assertEqual,
|
|
5
|
+
assertFalse,
|
|
6
|
+
assertNonNullable,
|
|
7
|
+
assertSame,
|
|
8
|
+
assertTrue,
|
|
9
|
+
} from "./Assert.ts";
|
|
3
10
|
|
|
4
11
|
import type { Brand } from "./Brand.ts";
|
|
5
12
|
import type { ReadonlyRecord } from "./Object.ts";
|
|
@@ -8,6 +15,7 @@ import {
|
|
|
8
15
|
createMutableRecord,
|
|
9
16
|
emptyRecord,
|
|
10
17
|
excludeProp,
|
|
18
|
+
filterObjectKeys,
|
|
11
19
|
getObjectKind,
|
|
12
20
|
getOwnProp,
|
|
13
21
|
isFunction,
|
|
@@ -103,6 +111,22 @@ test("isPlainObject", () => {
|
|
|
103
111
|
assertFalse(isPlainObject(Object.create(partialObjectPrototype)));
|
|
104
112
|
});
|
|
105
113
|
|
|
114
|
+
test("isPlainObject checks the root markers of Object.prototype", () => {
|
|
115
|
+
for (const key of ["hasOwnProperty", "isPrototypeOf"]) {
|
|
116
|
+
const descriptor = Object.getOwnPropertyDescriptor(Object.prototype, key);
|
|
117
|
+
assertNonNullable(descriptor);
|
|
118
|
+
Reflect.deleteProperty(Object.prototype, key);
|
|
119
|
+
try {
|
|
120
|
+
assertFalse(isPlainObject({}));
|
|
121
|
+
assertTrue(isPlainObject(Object.create(null)));
|
|
122
|
+
} finally {
|
|
123
|
+
// oxlint-disable-next-line eslint/no-extend-native -- Restores the built-in property this test deleted.
|
|
124
|
+
Object.defineProperty(Object.prototype, key, descriptor);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
assertTrue(isPlainObject({}));
|
|
128
|
+
});
|
|
129
|
+
|
|
106
130
|
test("isFunction", () => {
|
|
107
131
|
assertTrue(isFunction(() => {}));
|
|
108
132
|
assertTrue(isFunction(function () {}));
|
|
@@ -205,6 +229,117 @@ test("mapObject", () => {
|
|
|
205
229
|
assertEqual(mapped, { u1: "u1:10", u2: "u2:20" });
|
|
206
230
|
});
|
|
207
231
|
|
|
232
|
+
describe("filterObjectKeys", () => {
|
|
233
|
+
it("preserves field types and makes selected fields optional", () => {
|
|
234
|
+
const symbol = Symbol("ignored");
|
|
235
|
+
const source = {
|
|
236
|
+
APP_PORT: "4000",
|
|
237
|
+
other: 1,
|
|
238
|
+
42: true,
|
|
239
|
+
[symbol]: "ignored",
|
|
240
|
+
};
|
|
241
|
+
const selected = filterObjectKeys(source, (key) => {
|
|
242
|
+
assertType<typeof key, string>();
|
|
243
|
+
return key.startsWith("APP_") || key === "42";
|
|
244
|
+
});
|
|
245
|
+
assertType<
|
|
246
|
+
typeof selected,
|
|
247
|
+
{
|
|
248
|
+
readonly APP_PORT?: string;
|
|
249
|
+
readonly other?: number;
|
|
250
|
+
readonly 42?: boolean;
|
|
251
|
+
}
|
|
252
|
+
>();
|
|
253
|
+
assertEqual(selected, { APP_PORT: "4000", 42: true });
|
|
254
|
+
assertFalse(selected === source);
|
|
255
|
+
assertEqual(
|
|
256
|
+
filterObjectKeys(source, () => false),
|
|
257
|
+
{},
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
type UserId = string & Brand<"UserId">;
|
|
261
|
+
const users: ReadonlyRecord<UserId, number> = { ["u1" as UserId]: 1 };
|
|
262
|
+
const selectedUsers = filterObjectKeys(users, () => true);
|
|
263
|
+
assertType<
|
|
264
|
+
typeof selectedUsers,
|
|
265
|
+
Readonly<Partial<Record<UserId, number>>>
|
|
266
|
+
>();
|
|
267
|
+
assertEqual(selectedUsers, { u1: 1 });
|
|
268
|
+
|
|
269
|
+
void (() => {
|
|
270
|
+
// @ts-expect-error filterObjectKeys requires an object source.
|
|
271
|
+
filterObjectKeys("text", () => true);
|
|
272
|
+
// @ts-expect-error Selected properties are readonly.
|
|
273
|
+
selected.APP_PORT = "5000";
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("preserves descriptors without reading getters", () => {
|
|
278
|
+
let reads = 0;
|
|
279
|
+
const getter = () => {
|
|
280
|
+
reads++;
|
|
281
|
+
return "value";
|
|
282
|
+
};
|
|
283
|
+
const source = Object.defineProperties(Object.create({ inherited: 1 }), {
|
|
284
|
+
visible: { value: { nested: true }, enumerable: true, writable: true },
|
|
285
|
+
hidden: { value: undefined },
|
|
286
|
+
accessor: { get: getter, enumerable: true, configurable: true },
|
|
287
|
+
excluded: { get: getter },
|
|
288
|
+
[Symbol("ignored")]: { get: getter },
|
|
289
|
+
});
|
|
290
|
+
const visited: Array<string> = [];
|
|
291
|
+
const selected = filterObjectKeys(source, (key) => {
|
|
292
|
+
visited.push(key);
|
|
293
|
+
return key !== "excluded";
|
|
294
|
+
});
|
|
295
|
+
assertEqual(visited, ["visible", "hidden", "accessor", "excluded"]);
|
|
296
|
+
assertEqual(Object.getOwnPropertyNames(selected), [
|
|
297
|
+
"visible",
|
|
298
|
+
"hidden",
|
|
299
|
+
"accessor",
|
|
300
|
+
]);
|
|
301
|
+
for (const key of Object.getOwnPropertyNames(selected)) {
|
|
302
|
+
nodeAssert.deepEqual(
|
|
303
|
+
Object.getOwnPropertyDescriptor(selected, key),
|
|
304
|
+
Object.getOwnPropertyDescriptor(source, key),
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
assertEqual(reads, 0);
|
|
308
|
+
assertFalse(Object.hasOwn(selected, "inherited"));
|
|
309
|
+
assertEqual(Object.getOwnPropertySymbols(selected), []);
|
|
310
|
+
assertSame(Object.getPrototypeOf(selected), Object.prototype);
|
|
311
|
+
assertSame(
|
|
312
|
+
Reflect.get(selected, "visible"),
|
|
313
|
+
Reflect.get(source, "visible"),
|
|
314
|
+
);
|
|
315
|
+
assertSame(Reflect.get(selected, "accessor"), "value");
|
|
316
|
+
assertEqual(reads, 1);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("safely copies special keys and handles arrays as objects", () => {
|
|
320
|
+
const source = createMutableRecord();
|
|
321
|
+
source.__proto__ = "value";
|
|
322
|
+
Object.defineProperty(source, "constructor", {
|
|
323
|
+
value: "constructor",
|
|
324
|
+
enumerable: true,
|
|
325
|
+
});
|
|
326
|
+
const selected = filterObjectKeys(source, () => true);
|
|
327
|
+
assertTrue(Object.hasOwn(selected, "__proto__"));
|
|
328
|
+
assertSame(Object.getPrototypeOf(selected), Object.prototype);
|
|
329
|
+
assertEqual(selected, {
|
|
330
|
+
["__proto__"]: "value",
|
|
331
|
+
constructor: "constructor",
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const values: ReadonlyArray<number> = [10, 20];
|
|
335
|
+
const array = filterObjectKeys(values, (key) => key === "0");
|
|
336
|
+
assertFalse(Array.isArray(array));
|
|
337
|
+
assertEqual(array, { 0: 10 });
|
|
338
|
+
assertType<(typeof array)[0], number | undefined>();
|
|
339
|
+
assertType<typeof array.map, ReadonlyArray<number>["map"] | undefined>();
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
208
343
|
test("excludeProp", () => {
|
|
209
344
|
const obj = { a: 1, b: 2, c: 3 };
|
|
210
345
|
|
|
@@ -246,16 +381,10 @@ test("createMutableRecord", () => {
|
|
|
246
381
|
assertEqual(source, { name: "Ada" });
|
|
247
382
|
assertSame(Object.getPrototypeOf(copy), null);
|
|
248
383
|
|
|
249
|
-
|
|
384
|
+
void (() => {
|
|
250
385
|
// @ts-expect-error createMutableRecord source must be an object.
|
|
251
386
|
createMutableRecord("Ada");
|
|
252
|
-
};
|
|
253
|
-
assertType<
|
|
254
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
255
|
-
? true
|
|
256
|
-
: false,
|
|
257
|
-
true
|
|
258
|
-
>();
|
|
387
|
+
});
|
|
259
388
|
});
|
|
260
389
|
|
|
261
390
|
test("emptyRecord", () => {
|
package/src/Object.ts
CHANGED
|
@@ -87,6 +87,11 @@ export const isPlainObject = (
|
|
|
87
87
|
|
|
88
88
|
const prototype = Object.getPrototypeOf(value) as object | null;
|
|
89
89
|
if (prototype === null) return true;
|
|
90
|
+
// This realm's Object.prototype has an immutable null prototype, so `in`
|
|
91
|
+
// checks the same own properties as the structural test below, faster.
|
|
92
|
+
if (prototype === Object.prototype) {
|
|
93
|
+
return "hasOwnProperty" in prototype && "isPrototypeOf" in prototype;
|
|
94
|
+
}
|
|
90
95
|
return (
|
|
91
96
|
Object.getPrototypeOf(prototype) === null &&
|
|
92
97
|
Object.hasOwn(prototype, "hasOwnProperty") &&
|
|
@@ -243,6 +248,50 @@ export const mapObject = <K extends string, V, U>(
|
|
|
243
248
|
return out;
|
|
244
249
|
};
|
|
245
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Selects own string-keyed properties while preserving their descriptors.
|
|
253
|
+
*
|
|
254
|
+
* Visits enumerable and non-enumerable properties, ignoring inherited and
|
|
255
|
+
* symbol properties. The predicate receives only the key; property values and
|
|
256
|
+
* getters are never read. The result is a new ordinary object with matching
|
|
257
|
+
* descriptors, including accessor functions and property flags. Declared
|
|
258
|
+
* properties are optional because the predicate may exclude any of them.
|
|
259
|
+
* Numeric property names are visited as strings.
|
|
260
|
+
*
|
|
261
|
+
* ### Example
|
|
262
|
+
*
|
|
263
|
+
* ```ts
|
|
264
|
+
* import {
|
|
265
|
+
* assertEqual,
|
|
266
|
+
* assertType,
|
|
267
|
+
* filterObjectKeys,
|
|
268
|
+
* } from "@evolu/common";
|
|
269
|
+
*
|
|
270
|
+
* const source = { APP_PORT: "4000", HOME: "/home/evolu" };
|
|
271
|
+
* const selected = filterObjectKeys(source, (key) =>
|
|
272
|
+
* key.startsWith("APP_"),
|
|
273
|
+
* );
|
|
274
|
+
*
|
|
275
|
+
* assertEqual(selected, { APP_PORT: "4000" });
|
|
276
|
+
* assertType<
|
|
277
|
+
* typeof selected,
|
|
278
|
+
* { readonly APP_PORT?: string; readonly HOME?: string }
|
|
279
|
+
* >();
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
export const filterObjectKeys = <T extends object>(
|
|
283
|
+
source: T,
|
|
284
|
+
predicate: (key: string) => boolean,
|
|
285
|
+
): Readonly<Partial<Pick<T, Exclude<keyof T, symbol>>>> => {
|
|
286
|
+
const selected: Partial<Pick<T, Exclude<keyof T, symbol>>> = {};
|
|
287
|
+
for (const key of Object.getOwnPropertyNames(source)) {
|
|
288
|
+
if (!predicate(key)) continue;
|
|
289
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key)!;
|
|
290
|
+
Object.defineProperty(selected, key, descriptor);
|
|
291
|
+
}
|
|
292
|
+
return selected;
|
|
293
|
+
};
|
|
294
|
+
|
|
246
295
|
/** Conditionally excludes a property from an object. */
|
|
247
296
|
export const excludeProp = <T extends object, K extends keyof T>(
|
|
248
297
|
obj: T,
|
package/src/Platform.ts
CHANGED
|
@@ -4,10 +4,18 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Returns true if running in React Native with Hermes engine.
|
|
9
|
+
*
|
|
10
|
+
* @group Detection
|
|
11
|
+
*/
|
|
8
12
|
export const isHermes = "HermesInternal" in globalThis;
|
|
9
13
|
|
|
10
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Returns true if running in a server environment (no DOM).
|
|
16
|
+
*
|
|
17
|
+
* @group Detection
|
|
18
|
+
*/
|
|
11
19
|
export const isServer = typeof document === "undefined";
|
|
12
20
|
|
|
13
21
|
/**
|
|
@@ -20,6 +28,8 @@ export const isServer = typeof document === "undefined";
|
|
|
20
28
|
* where no bundler ran, such as un-bundled browser ESM, where it fails closed
|
|
21
29
|
* to production behavior. Node.js reads it natively; React Native polyfills it.
|
|
22
30
|
* A missing `NODE_ENV` counts as development, matching React semantics.
|
|
31
|
+
*
|
|
32
|
+
* @group Detection
|
|
23
33
|
*/
|
|
24
34
|
export const isDev =
|
|
25
35
|
typeof process === "undefined"
|
|
@@ -35,6 +45,7 @@ export const isDev =
|
|
|
35
45
|
* Returns false in React Native even if Buffer is polyfilled, as we prefer
|
|
36
46
|
* native methods in that environment.
|
|
37
47
|
*
|
|
48
|
+
* @group Detection
|
|
38
49
|
* @see https://github.com/craftzdog/react-native-quick-base64#installation
|
|
39
50
|
*/
|
|
40
51
|
export const hasNodeBuffer =
|
|
@@ -49,9 +60,16 @@ export const hasNodeBuffer =
|
|
|
49
60
|
* if an onComplete callback is used.
|
|
50
61
|
*
|
|
51
62
|
* https://react.dev/reference/react-dom/flushSync
|
|
63
|
+
*
|
|
64
|
+
* @group Integration
|
|
52
65
|
*/
|
|
53
66
|
export type FlushSync = (callback: () => void) => void;
|
|
54
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Dependency wrapper for {@link FlushSync}.
|
|
70
|
+
*
|
|
71
|
+
* @group Integration
|
|
72
|
+
*/
|
|
55
73
|
export interface FlushSyncDep {
|
|
56
74
|
readonly flushSync: FlushSync;
|
|
57
75
|
}
|
|
@@ -59,18 +77,32 @@ export interface FlushSyncDep {
|
|
|
59
77
|
/**
|
|
60
78
|
* Reload the app in a platform-specific way.
|
|
61
79
|
*
|
|
62
|
-
*
|
|
63
|
-
* the
|
|
80
|
+
* On the web, Evolu reloads tabs of a build when another build of the app waits
|
|
81
|
+
* for the local databases, so they load the build the server now serves. It can
|
|
82
|
+
* also clear in-memory state after persistent storage was purged; it does not
|
|
83
|
+
* purge storage itself.
|
|
64
84
|
*
|
|
65
|
-
* - Web:
|
|
85
|
+
* - Web: Reloads the page, or loads the specified URL instead
|
|
66
86
|
* - React Native: Restarts the app (URL ignored)
|
|
87
|
+
*
|
|
88
|
+
* @group Integration
|
|
67
89
|
*/
|
|
68
90
|
export type ReloadApp = (url?: string) => void;
|
|
69
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Dependency wrapper for {@link ReloadApp}.
|
|
94
|
+
*
|
|
95
|
+
* @group Integration
|
|
96
|
+
*/
|
|
70
97
|
export interface ReloadAppDep {
|
|
71
98
|
readonly reloadApp: ReloadApp;
|
|
72
99
|
}
|
|
73
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Records platform global errors until disposed.
|
|
103
|
+
*
|
|
104
|
+
* @group Testing
|
|
105
|
+
*/
|
|
74
106
|
export interface TestGlobalErrors extends Disposable {
|
|
75
107
|
readonly errors: ReadonlyArray<unknown>;
|
|
76
108
|
readonly next: () => Promise<unknown>;
|
|
@@ -88,15 +120,25 @@ export interface TestGlobalErrors extends Disposable {
|
|
|
88
120
|
readonly settle: () => Promise<ReadonlyArray<unknown>>;
|
|
89
121
|
}
|
|
90
122
|
|
|
91
|
-
/**
|
|
123
|
+
/**
|
|
124
|
+
* Records platform global uncaught-error reporting until disposed.
|
|
125
|
+
*
|
|
126
|
+
* @group Testing
|
|
127
|
+
*/
|
|
92
128
|
export const testGlobalUncaughtErrors = (): TestGlobalErrors =>
|
|
93
129
|
createTestGlobalErrors("uncaughtErrors");
|
|
94
130
|
|
|
95
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Records platform global unhandled-rejection reporting until disposed.
|
|
133
|
+
*
|
|
134
|
+
* @group Testing
|
|
135
|
+
*/
|
|
96
136
|
export const testGlobalUnhandledRejections = (): TestGlobalErrors =>
|
|
97
137
|
createTestGlobalErrors("unhandledRejection");
|
|
98
138
|
|
|
99
|
-
const settleSentinel = new Error(
|
|
139
|
+
const settleSentinel = /*#__PURE__*/ new Error(
|
|
140
|
+
"TestGlobalErrors.settle sentinel",
|
|
141
|
+
);
|
|
100
142
|
|
|
101
143
|
const createTestGlobalErrors = (
|
|
102
144
|
kind: "uncaughtErrors" | "unhandledRejection",
|
package/src/Random.ts
CHANGED
|
@@ -11,6 +11,8 @@ import type { Brand } from "./Brand.ts";
|
|
|
11
11
|
* A random floating point number in [0, 1).
|
|
12
12
|
*
|
|
13
13
|
* Branded to distinguish random values from arbitrary numbers.
|
|
14
|
+
*
|
|
15
|
+
* @group Core
|
|
14
16
|
*/
|
|
15
17
|
export type RandomNumber = number & Brand<"RandomNumber">;
|
|
16
18
|
|
|
@@ -43,17 +45,28 @@ export type RandomNumber = number & Brand<"RandomNumber">;
|
|
|
43
45
|
* const secondTestRandom = testCreateRandom("test");
|
|
44
46
|
* assertEqual(firstTestRandom.next(), secondTestRandom.next());
|
|
45
47
|
* ```
|
|
48
|
+
*
|
|
49
|
+
* @group Core
|
|
46
50
|
*/
|
|
47
51
|
export interface Random {
|
|
48
52
|
/** Returns a floating point number in [0, 1). Just like Math.random(). */
|
|
49
53
|
readonly next: () => RandomNumber;
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Dependency wrapper for {@link Random}.
|
|
58
|
+
*
|
|
59
|
+
* @group Core
|
|
60
|
+
*/
|
|
52
61
|
export interface RandomDep {
|
|
53
62
|
readonly random: Random;
|
|
54
63
|
}
|
|
55
64
|
|
|
56
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Creates a {@link Random} using Math.random().
|
|
67
|
+
*
|
|
68
|
+
* @group Core
|
|
69
|
+
*/
|
|
57
70
|
export const createRandom = (): Random => ({
|
|
58
71
|
next: () => Math.random() as RandomNumber,
|
|
59
72
|
});
|
|
@@ -62,6 +75,8 @@ export const createRandom = (): Random => ({
|
|
|
62
75
|
* Creates a seeded {@link Random} for deterministic tests.
|
|
63
76
|
*
|
|
64
77
|
* Default seed "evolu".
|
|
78
|
+
*
|
|
79
|
+
* @group Testing
|
|
65
80
|
*/
|
|
66
81
|
export const testCreateRandom = (seed = "evolu"): Random => {
|
|
67
82
|
const random = new RandomLib(seed);
|
|
@@ -75,12 +90,18 @@ export const testCreateRandom = (seed = "evolu"): Random => {
|
|
|
75
90
|
* provided by the NPM `random` package.
|
|
76
91
|
*
|
|
77
92
|
* https://github.com/transitive-bullshit/random
|
|
93
|
+
*
|
|
94
|
+
* @group Core
|
|
78
95
|
*/
|
|
79
96
|
export interface RandomLibDep {
|
|
80
97
|
readonly randomLib: RandomLib;
|
|
81
98
|
}
|
|
82
99
|
|
|
83
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* Creates a random number generator from the NPM `random` package.
|
|
102
|
+
*
|
|
103
|
+
* @group Core
|
|
104
|
+
*/
|
|
84
105
|
export const createRandomLib = (): RandomLib => new RandomLib();
|
|
85
106
|
|
|
86
107
|
/**
|
|
@@ -88,6 +109,8 @@ export const createRandomLib = (): RandomLib => new RandomLib();
|
|
|
88
109
|
* deterministic tests.
|
|
89
110
|
*
|
|
90
111
|
* Default seed "evolu".
|
|
112
|
+
*
|
|
113
|
+
* @group Testing
|
|
91
114
|
*/
|
|
92
115
|
export const testCreateRandomLib = (seed = "evolu"): RandomLib =>
|
|
93
116
|
new RandomLib(seed);
|