@evolu/common 8.10.0 → 8.12.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.
Files changed (152) hide show
  1. package/dist/src/Bytes.d.ts +39 -2
  2. package/dist/src/Bytes.d.ts.map +1 -1
  3. package/dist/src/Bytes.js +50 -2
  4. package/dist/src/Config.d.ts +22 -22
  5. package/dist/src/Config.d.ts.map +1 -1
  6. package/dist/src/Console.d.ts +62 -7
  7. package/dist/src/Console.d.ts.map +1 -1
  8. package/dist/src/Console.js +20 -4
  9. package/dist/src/Crypto.d.ts +76 -4
  10. package/dist/src/Crypto.d.ts.map +1 -1
  11. package/dist/src/Crypto.js +55 -4
  12. package/dist/src/Error.d.ts +45 -0
  13. package/dist/src/Error.d.ts.map +1 -1
  14. package/dist/src/Error.js +69 -0
  15. package/dist/src/Fs.d.ts +92 -18
  16. package/dist/src/Fs.d.ts.map +1 -1
  17. package/dist/src/Fs.js +2 -0
  18. package/dist/src/Identicon.d.ts +2 -2
  19. package/dist/src/Identicon.js +2 -2
  20. package/dist/src/LeakDetector.d.ts +22 -3
  21. package/dist/src/LeakDetector.d.ts.map +1 -1
  22. package/dist/src/LeakDetector.js +12 -2
  23. package/dist/src/LockManager.d.ts +8 -0
  24. package/dist/src/LockManager.d.ts.map +1 -1
  25. package/dist/src/LockManager.js +6 -0
  26. package/dist/src/Object.d.ts.map +1 -1
  27. package/dist/src/Object.js +5 -0
  28. package/dist/src/Platform.d.ts +47 -7
  29. package/dist/src/Platform.d.ts.map +1 -1
  30. package/dist/src/Platform.js +24 -5
  31. package/dist/src/Random.d.ts +25 -2
  32. package/dist/src/Random.d.ts.map +1 -1
  33. package/dist/src/Random.js +14 -2
  34. package/dist/src/Resource.d.ts +156 -1
  35. package/dist/src/Resource.d.ts.map +1 -1
  36. package/dist/src/Resource.js +201 -72
  37. package/dist/src/Schedule.d.ts +11 -10
  38. package/dist/src/Schedule.d.ts.map +1 -1
  39. package/dist/src/Schedule.js +1 -1
  40. package/dist/src/Sqlite.d.ts +132 -16
  41. package/dist/src/Sqlite.d.ts.map +1 -1
  42. package/dist/src/Sqlite.js +63 -9
  43. package/dist/src/Task.d.ts +15 -4
  44. package/dist/src/Task.d.ts.map +1 -1
  45. package/dist/src/Task.js +41 -15
  46. package/dist/src/Test.d.ts +9 -0
  47. package/dist/src/Test.d.ts.map +1 -1
  48. package/dist/src/Test.js +4 -0
  49. package/dist/src/Time.d.ts +106 -9
  50. package/dist/src/Time.d.ts.map +1 -1
  51. package/dist/src/Time.js +55 -4
  52. package/dist/src/Type.d.ts +1455 -1310
  53. package/dist/src/Type.d.ts.map +1 -1
  54. package/dist/src/Type.js +1274 -517
  55. package/dist/src/WebSocket.d.ts +164 -13
  56. package/dist/src/WebSocket.d.ts.map +1 -1
  57. package/dist/src/WebSocket.js +133 -24
  58. package/dist/src/Worker.d.ts +90 -8
  59. package/dist/src/Worker.d.ts.map +1 -1
  60. package/dist/src/Worker.js +28 -2
  61. package/dist/src/index.d.ts +6 -7
  62. package/dist/src/index.d.ts.map +1 -1
  63. package/dist/src/index.js +2 -3
  64. package/dist/src/local-first/Db.d.ts +52 -3
  65. package/dist/src/local-first/Db.d.ts.map +1 -1
  66. package/dist/src/local-first/Db.js +412 -137
  67. package/dist/src/local-first/Evolu.d.ts +412 -213
  68. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  69. package/dist/src/local-first/Evolu.js +181 -18
  70. package/dist/src/local-first/Owner.d.ts +13 -30
  71. package/dist/src/local-first/Owner.d.ts.map +1 -1
  72. package/dist/src/local-first/Owner.js +13 -30
  73. package/dist/src/local-first/Protocol.d.ts +106 -19
  74. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  75. package/dist/src/local-first/Protocol.js +162 -60
  76. package/dist/src/local-first/Query.d.ts +8 -15
  77. package/dist/src/local-first/Query.d.ts.map +1 -1
  78. package/dist/src/local-first/Relay.d.ts.map +1 -1
  79. package/dist/src/local-first/Relay.js +4 -2
  80. package/dist/src/local-first/Schema.d.ts +346 -23
  81. package/dist/src/local-first/Schema.d.ts.map +1 -1
  82. package/dist/src/local-first/Schema.js +214 -17
  83. package/dist/src/local-first/Shared.d.ts +537 -22
  84. package/dist/src/local-first/Shared.d.ts.map +1 -1
  85. package/dist/src/local-first/Shared.js +1437 -234
  86. package/dist/src/local-first/Storage.d.ts +195 -17
  87. package/dist/src/local-first/Storage.d.ts.map +1 -1
  88. package/dist/src/local-first/Storage.js +85 -22
  89. package/dist/src/local-first/Timestamp.d.ts +392 -41
  90. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  91. package/dist/src/local-first/Timestamp.js +403 -81
  92. package/dist/src/local-first/index.d.ts +0 -1
  93. package/dist/src/local-first/index.d.ts.map +1 -1
  94. package/dist/src/local-first/index.js +0 -1
  95. package/package.json +1 -1
  96. package/src/Assert.test.ts +2 -5
  97. package/src/Bytes.test.ts +27 -0
  98. package/src/Bytes.ts +58 -2
  99. package/src/Config.test.ts +2 -6
  100. package/src/Config.ts +133 -133
  101. package/src/Console.ts +62 -7
  102. package/src/Crypto.ts +76 -4
  103. package/src/Eq.test.ts +2 -3
  104. package/src/Error.test.ts +76 -3
  105. package/src/Error.ts +71 -0
  106. package/src/Fs.ts +92 -18
  107. package/src/Identicon.ts +2 -2
  108. package/src/LeakDetector.ts +22 -3
  109. package/src/LockManager.ts +8 -0
  110. package/src/Object.test.ts +27 -12
  111. package/src/Object.ts +5 -0
  112. package/src/Platform.ts +50 -8
  113. package/src/Random.ts +25 -2
  114. package/src/Resource.test.ts +837 -0
  115. package/src/Resource.ts +235 -15
  116. package/src/Schedule.test.ts +50 -12
  117. package/src/Schedule.ts +24 -14
  118. package/src/Sqlite.ts +137 -17
  119. package/src/Task.test.ts +189 -8
  120. package/src/Task.ts +56 -17
  121. package/src/Test.ts +9 -0
  122. package/src/Time.ts +106 -9
  123. package/src/Type.test.ts +946 -1028
  124. package/src/Type.ts +4195 -3136
  125. package/src/Types.test.ts +4 -14
  126. package/src/WebSocket.ts +313 -40
  127. package/src/Worker.ts +90 -8
  128. package/src/index.ts +20 -6
  129. package/src/local-first/Db.ts +644 -339
  130. package/src/local-first/Evolu.test.ts +994 -22
  131. package/src/local-first/Evolu.ts +625 -232
  132. package/src/local-first/Owner.ts +13 -30
  133. package/src/local-first/Protocol.test.ts +634 -10
  134. package/src/local-first/Protocol.ts +255 -109
  135. package/src/local-first/Query.ts +8 -15
  136. package/src/local-first/Relay.ts +4 -2
  137. package/src/local-first/Schema.test.ts +143 -0
  138. package/src/local-first/Schema.ts +376 -26
  139. package/src/local-first/Shared.test.ts +7731 -559
  140. package/src/local-first/Shared.ts +2036 -267
  141. package/src/local-first/Storage.ts +224 -36
  142. package/src/local-first/Timestamp.test.ts +344 -70
  143. package/src/local-first/Timestamp.ts +434 -118
  144. package/src/local-first/index.ts +0 -1
  145. package/dist/src/local-first/Error.d.ts +0 -12
  146. package/dist/src/local-first/Error.d.ts.map +0 -1
  147. package/dist/src/local-first/Error.js +0 -6
  148. package/dist/src/local-first/LocalAuth.d.ts +0 -150
  149. package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
  150. package/dist/src/local-first/LocalAuth.js +0 -179
  151. package/src/local-first/Error.ts +0 -17
  152. package/src/local-first/LocalAuth.ts +0 -457
@@ -47,6 +47,8 @@ import type { Callback } from "./Types.ts";
47
47
  * );
48
48
  * assertEqual(result, "example");
49
49
  * ```
50
+ *
51
+ * @group Core
50
52
  */
51
53
 
52
54
  export interface LockManagerDep {
@@ -65,6 +67,8 @@ export interface LockManagerDep {
65
67
  * via internal namespacing, so tests can reuse the same lock names without
66
68
  * contending through the global Web Locks. Query results are filtered to that
67
69
  * private namespace and returned with the original visible names.
70
+ *
71
+ * @group Testing
68
72
  */
69
73
  export const testCreateLockManager = (
70
74
  nativeLockManager: LockManager = navigator.locks,
@@ -119,6 +123,8 @@ export const testCreateLockManager = (
119
123
  * Leadership is held until the returned handle is disposed. Once released,
120
124
  * another waiting caller may become the next leader. Waiting for leadership is
121
125
  * abortable via the calling {@link Task}'s signal.
126
+ *
127
+ * @group Leader election
122
128
  */
123
129
  export const acquireLeaderLock =
124
130
  (name: string): Task<AsyncDisposable, never, LockManagerDep> =>
@@ -156,6 +162,8 @@ export const acquireLeaderLock =
156
162
  * Leadership is held until the returned handle is disposed. Once released,
157
163
  * another waiting caller may become the next leader. Waiting for leadership is
158
164
  * abortable by disposing the returned handle.
165
+ *
166
+ * @group Leader election
159
167
  */
160
168
  export const acquireLeaderLockCallback =
161
169
  (deps: LockManagerDep) =>
@@ -1,6 +1,12 @@
1
1
  import nodeAssert from "node:assert/strict";
2
2
  import { describe, it, test } from "node:test";
3
- import { assertEqual, assertFalse, assertSame, assertTrue } from "./Assert.ts";
3
+ import {
4
+ assertEqual,
5
+ assertFalse,
6
+ assertNonNullable,
7
+ assertSame,
8
+ assertTrue,
9
+ } from "./Assert.ts";
4
10
 
5
11
  import type { Brand } from "./Brand.ts";
6
12
  import type { ReadonlyRecord } from "./Object.ts";
@@ -105,6 +111,22 @@ test("isPlainObject", () => {
105
111
  assertFalse(isPlainObject(Object.create(partialObjectPrototype)));
106
112
  });
107
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
+
108
130
  test("isFunction", () => {
109
131
  assertTrue(isFunction(() => {}));
110
132
  assertTrue(isFunction(function () {}));
@@ -244,13 +266,12 @@ describe("filterObjectKeys", () => {
244
266
  >();
245
267
  assertEqual(selectedUsers, { u1: 1 });
246
268
 
247
- const reject = () => {
269
+ void (() => {
248
270
  // @ts-expect-error filterObjectKeys requires an object source.
249
271
  filterObjectKeys("text", () => true);
250
272
  // @ts-expect-error Selected properties are readonly.
251
273
  selected.APP_PORT = "5000";
252
- };
253
- assertType<typeof reject, () => void>();
274
+ });
254
275
  });
255
276
 
256
277
  it("preserves descriptors without reading getters", () => {
@@ -360,16 +381,10 @@ test("createMutableRecord", () => {
360
381
  assertEqual(source, { name: "Ada" });
361
382
  assertSame(Object.getPrototypeOf(copy), null);
362
383
 
363
- const compileTimeAssertions = () => {
384
+ void (() => {
364
385
  // @ts-expect-error createMutableRecord source must be an object.
365
386
  createMutableRecord("Ada");
366
- };
367
- assertType<
368
- typeof compileTimeAssertions extends (...args: Array<never>) => unknown
369
- ? true
370
- : false,
371
- true
372
- >();
387
+ });
373
388
  });
374
389
 
375
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") &&
package/src/Platform.ts CHANGED
@@ -4,10 +4,18 @@
4
4
  * @module
5
5
  */
6
6
 
7
- /** Returns true if running in React Native with Hermes engine. */
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
- /** Returns true if running in a server environment (no DOM). */
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
- * Use this after purging persistent storage to clear in-memory state and ensure
63
- * the app starts fresh. It does not purge storage itself.
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: Redirects to the specified URL (defaults to `/`)
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
- /** Records platform global uncaught-error reporting until disposed. */
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
- /** Records platform global unhandled-rejection reporting until disposed. */
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("TestGlobalErrors.settle sentinel");
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
- /** Creates a {@link Random} using Math.random(). */
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
- /** Creates a random number generator from the NPM `random` package. */
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);