@evolu/common 6.0.1-preview.9 → 7.1.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 (195) hide show
  1. package/README.md +27 -29
  2. package/dist/src/Array.d.ts +317 -12
  3. package/dist/src/Array.d.ts.map +1 -1
  4. package/dist/src/Array.js +219 -9
  5. package/dist/src/Assert.d.ts +0 -13
  6. package/dist/src/Assert.d.ts.map +1 -1
  7. package/dist/src/Assert.js +0 -15
  8. package/dist/src/Brand.d.ts +75 -0
  9. package/dist/src/Brand.d.ts.map +1 -0
  10. package/dist/src/Brand.js +1 -0
  11. package/dist/src/Buffer.d.ts +1 -1
  12. package/dist/src/Buffer.d.ts.map +1 -1
  13. package/dist/src/Buffer.js +8 -7
  14. package/dist/src/Cache.d.ts +39 -0
  15. package/dist/src/Cache.d.ts.map +1 -0
  16. package/dist/src/Cache.js +47 -0
  17. package/dist/src/Callbacks.d.ts +45 -12
  18. package/dist/src/Callbacks.d.ts.map +1 -1
  19. package/dist/src/Callbacks.js +14 -7
  20. package/dist/src/Console.d.ts +32 -7
  21. package/dist/src/Console.d.ts.map +1 -1
  22. package/dist/src/Console.js +73 -10
  23. package/dist/src/Crypto.d.ts +56 -42
  24. package/dist/src/Crypto.d.ts.map +1 -1
  25. package/dist/src/Crypto.js +45 -54
  26. package/dist/src/Evolu/Db.d.ts +162 -74
  27. package/dist/src/Evolu/Db.d.ts.map +1 -1
  28. package/dist/src/Evolu/Db.js +284 -702
  29. package/dist/src/Evolu/Diff.d.ts +3 -3
  30. package/dist/src/Evolu/Diff.d.ts.map +1 -1
  31. package/dist/src/Evolu/Diff.js +7 -5
  32. package/dist/src/Evolu/Evolu.d.ts +213 -133
  33. package/dist/src/Evolu/Evolu.d.ts.map +1 -1
  34. package/dist/src/Evolu/Evolu.js +189 -180
  35. package/dist/src/Evolu/Internal.d.ts +2 -4
  36. package/dist/src/Evolu/Internal.d.ts.map +1 -1
  37. package/dist/src/Evolu/Internal.js +2 -4
  38. package/dist/src/Evolu/LocalAuth.d.ts +150 -0
  39. package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
  40. package/dist/src/Evolu/LocalAuth.js +174 -0
  41. package/dist/src/Evolu/Owner.d.ts +273 -120
  42. package/dist/src/Evolu/Owner.d.ts.map +1 -1
  43. package/dist/src/Evolu/Owner.js +130 -104
  44. package/dist/src/Evolu/Platform.d.ts +9 -7
  45. package/dist/src/Evolu/Platform.d.ts.map +1 -1
  46. package/dist/src/Evolu/Protocol.d.ts +268 -240
  47. package/dist/src/Evolu/Protocol.d.ts.map +1 -1
  48. package/dist/src/Evolu/Protocol.js +595 -447
  49. package/dist/src/Evolu/Public.d.ts +7 -9
  50. package/dist/src/Evolu/Public.d.ts.map +1 -1
  51. package/dist/src/Evolu/Public.js +3 -4
  52. package/dist/src/Evolu/PublicKysely.d.ts.map +1 -1
  53. package/dist/src/Evolu/PublicKysely.js +3 -4
  54. package/dist/src/Evolu/Query.d.ts +2 -1
  55. package/dist/src/Evolu/Query.d.ts.map +1 -1
  56. package/dist/src/Evolu/Query.js +1 -1
  57. package/dist/src/Evolu/Relay.d.ts +91 -8
  58. package/dist/src/Evolu/Relay.d.ts.map +1 -1
  59. package/dist/src/Evolu/Relay.js +214 -88
  60. package/dist/src/Evolu/Schema.d.ts +125 -47
  61. package/dist/src/Evolu/Schema.d.ts.map +1 -1
  62. package/dist/src/Evolu/Schema.js +175 -31
  63. package/dist/src/Evolu/Storage.d.ts +249 -27
  64. package/dist/src/Evolu/Storage.d.ts.map +1 -1
  65. package/dist/src/Evolu/Storage.js +198 -92
  66. package/dist/src/Evolu/Sync.d.ts +68 -13
  67. package/dist/src/Evolu/Sync.d.ts.map +1 -1
  68. package/dist/src/Evolu/Sync.js +469 -20
  69. package/dist/src/Evolu/Timestamp.d.ts +83 -30
  70. package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
  71. package/dist/src/Evolu/Timestamp.js +79 -34
  72. package/dist/src/Identicon.d.ts +35 -0
  73. package/dist/src/Identicon.d.ts.map +1 -0
  74. package/dist/src/Identicon.js +143 -0
  75. package/dist/src/Instances.d.ts +34 -0
  76. package/dist/src/Instances.d.ts.map +1 -0
  77. package/dist/src/Instances.js +44 -0
  78. package/dist/src/Number.d.ts +4 -3
  79. package/dist/src/Number.d.ts.map +1 -1
  80. package/dist/src/Number.js +5 -4
  81. package/dist/src/Object.d.ts +10 -4
  82. package/dist/src/Object.d.ts.map +1 -1
  83. package/dist/src/Object.js +9 -3
  84. package/dist/src/Order.d.ts +0 -5
  85. package/dist/src/Order.d.ts.map +1 -1
  86. package/dist/src/Order.js +0 -5
  87. package/dist/src/Platform.d.ts +15 -0
  88. package/dist/src/Platform.d.ts.map +1 -0
  89. package/dist/src/Platform.js +17 -0
  90. package/dist/src/Random.d.ts +3 -7
  91. package/dist/src/Random.d.ts.map +1 -1
  92. package/dist/src/Random.js +0 -5
  93. package/dist/src/Ref.d.ts.map +1 -1
  94. package/dist/src/Relation.d.ts +79 -0
  95. package/dist/src/Relation.d.ts.map +1 -0
  96. package/dist/src/Relation.js +127 -0
  97. package/dist/src/Resources.d.ts +118 -0
  98. package/dist/src/Resources.d.ts.map +1 -0
  99. package/dist/src/Resources.js +197 -0
  100. package/dist/src/Result.d.ts +189 -59
  101. package/dist/src/Result.d.ts.map +1 -1
  102. package/dist/src/Result.js +30 -241
  103. package/dist/src/Skiplist.js +2 -1
  104. package/dist/src/Sqlite.d.ts +89 -5
  105. package/dist/src/Sqlite.d.ts.map +1 -1
  106. package/dist/src/Sqlite.js +136 -9
  107. package/dist/src/Store.d.ts +0 -5
  108. package/dist/src/Store.d.ts.map +1 -1
  109. package/dist/src/Store.js +0 -5
  110. package/dist/src/Task.d.ts +580 -0
  111. package/dist/src/Task.d.ts.map +1 -0
  112. package/dist/src/Task.js +464 -0
  113. package/dist/src/Time.d.ts +66 -6
  114. package/dist/src/Time.d.ts.map +1 -1
  115. package/dist/src/Time.js +96 -7
  116. package/dist/src/Type.d.ts +677 -344
  117. package/dist/src/Type.d.ts.map +1 -1
  118. package/dist/src/Type.js +718 -467
  119. package/dist/src/Types.d.ts +38 -75
  120. package/dist/src/Types.d.ts.map +1 -1
  121. package/dist/src/WebSocket.d.ts +5 -7
  122. package/dist/src/WebSocket.d.ts.map +1 -1
  123. package/dist/src/WebSocket.js +12 -23
  124. package/dist/src/Worker.d.ts +39 -16
  125. package/dist/src/Worker.d.ts.map +1 -1
  126. package/dist/src/Worker.js +22 -9
  127. package/dist/src/index.d.ts +8 -3
  128. package/dist/src/index.d.ts.map +1 -1
  129. package/dist/src/index.js +8 -3
  130. package/package.json +14 -13
  131. package/src/Array.ts +396 -19
  132. package/src/Assert.ts +0 -35
  133. package/src/Brand.ts +75 -0
  134. package/src/Buffer.ts +7 -7
  135. package/src/Cache.ts +79 -0
  136. package/src/Callbacks.ts +62 -22
  137. package/src/Console.ts +92 -12
  138. package/src/Crypto.ts +99 -91
  139. package/src/Evolu/Db.ts +519 -1026
  140. package/src/Evolu/Diff.ts +7 -5
  141. package/src/Evolu/Evolu.ts +476 -360
  142. package/src/Evolu/Internal.ts +2 -4
  143. package/src/Evolu/LocalAuth.ts +463 -0
  144. package/src/Evolu/Owner.ts +355 -228
  145. package/src/Evolu/Platform.ts +9 -9
  146. package/src/Evolu/Protocol.ts +846 -742
  147. package/src/Evolu/Public.ts +8 -15
  148. package/src/Evolu/PublicKysely.ts +4 -5
  149. package/src/Evolu/Query.ts +3 -2
  150. package/src/Evolu/Relay.ts +406 -103
  151. package/src/Evolu/Schema.ts +323 -91
  152. package/src/Evolu/Storage.ts +559 -137
  153. package/src/Evolu/Sync.ts +819 -36
  154. package/src/Evolu/Timestamp.ts +90 -58
  155. package/src/Identicon.ts +197 -0
  156. package/src/Instances.ts +90 -0
  157. package/src/Number.ts +6 -10
  158. package/src/Object.ts +13 -5
  159. package/src/Order.ts +0 -6
  160. package/src/Platform.ts +20 -0
  161. package/src/Random.ts +3 -8
  162. package/src/Ref.ts +0 -5
  163. package/src/Relation.ts +234 -0
  164. package/src/Resources.ts +367 -0
  165. package/src/Result.ts +196 -61
  166. package/src/Skiplist.ts +1 -1
  167. package/src/Sqlite.ts +152 -17
  168. package/src/Store.ts +0 -5
  169. package/src/Task.ts +894 -0
  170. package/src/Time.ts +179 -10
  171. package/src/Type.ts +1136 -731
  172. package/src/Types.ts +43 -77
  173. package/src/WebSocket.ts +27 -31
  174. package/src/Worker.ts +72 -29
  175. package/src/index.ts +8 -3
  176. package/dist/src/Evolu/Config.d.ts +0 -69
  177. package/dist/src/Evolu/Config.d.ts.map +0 -1
  178. package/dist/src/Evolu/Config.js +0 -9
  179. package/dist/src/Evolu/Kysely.d.ts +0 -6
  180. package/dist/src/Evolu/Kysely.d.ts.map +0 -1
  181. package/dist/src/Evolu/Kysely.js +0 -21
  182. package/dist/src/ManyToManyMap.d.ts +0 -26
  183. package/dist/src/ManyToManyMap.d.ts.map +0 -1
  184. package/dist/src/ManyToManyMap.js +0 -92
  185. package/dist/src/NanoId.d.ts +0 -27
  186. package/dist/src/NanoId.d.ts.map +0 -1
  187. package/dist/src/NanoId.js +0 -6
  188. package/dist/src/Promise.d.ts +0 -180
  189. package/dist/src/Promise.d.ts.map +0 -1
  190. package/dist/src/Promise.js +0 -176
  191. package/src/Evolu/Config.ts +0 -83
  192. package/src/Evolu/Kysely.ts +0 -38
  193. package/src/ManyToManyMap.ts +0 -140
  194. package/src/NanoId.ts +0 -39
  195. package/src/Promise.ts +0 -295
package/src/Types.ts CHANGED
@@ -22,6 +22,23 @@ import * as Kysely from "kysely";
22
22
  */
23
23
  export type Predicate<T> = (value: T) => boolean;
24
24
 
25
+ /**
26
+ * Checks a condition on a value at a given index and returns a boolean.
27
+ *
28
+ * Useful for callbacks that need both the element and its position.
29
+ *
30
+ * ### Example
31
+ *
32
+ * ```ts
33
+ * const isEvenIndex: PredicateWithIndex<string> = (value, index) =>
34
+ * index % 2 === 0;
35
+ *
36
+ * const items = ["a", "b", "c", "d"];
37
+ * const evenIndexItems = items.filter(isEvenIndex); // ["a", "c"]
38
+ * ```
39
+ */
40
+ export type PredicateWithIndex<T> = (value: T, index: number) => boolean;
41
+
25
42
  /**
26
43
  * A type guard function that refines type `A` to a narrower type `B`.
27
44
  *
@@ -42,6 +59,31 @@ export type Predicate<T> = (value: T) => boolean;
42
59
  */
43
60
  export type Refinement<in A, out B extends A> = (a: A) => a is B;
44
61
 
62
+ /**
63
+ * A type guard function that refines type `A` to a narrower type `B` at a given
64
+ * index.
65
+ *
66
+ * Useful for callbacks that need both the element and its position while
67
+ * maintaining type narrowing.
68
+ *
69
+ * ### Example
70
+ *
71
+ * ```ts
72
+ * type Item = { type: "number" | "string"; value: unknown };
73
+ *
74
+ * const isNumberItem: RefinementWithIndex<Item, Item & { type: "number" }> =
75
+ * (item, index): item is Item & { type: "number" } =>
76
+ * index > 0 && item.type === "number";
77
+ *
78
+ * const items: ReadonlyArray<Item> = [...];
79
+ * const [numbers, others] = partitionArray(items, isNumberItem);
80
+ * ```
81
+ */
82
+ export type RefinementWithIndex<in A, out B extends A> = (
83
+ a: A,
84
+ index: number,
85
+ ) => a is B;
86
+
45
87
  /**
46
88
  * Makes properties optional if they accept `null` as a value.
47
89
  *
@@ -79,83 +121,7 @@ export type NullablePartial<
79
121
  export type IntentionalNever = never;
80
122
 
81
123
  /**
82
- * A utility interface for creating branded types.
83
- *
84
- * Branded types enhance type safety by differentiating otherwise identical base
85
- * types, such as `number` or `string`, to enforce stricter type checks.
86
- *
87
- * Supports multiple brands, allowing types to act like flags.
88
- *
89
- * ### Example 1: Single Brand
90
- *
91
- * ```ts
92
- * // A branded type definition
93
- * type UserId = number & Brand<"UserId">;
94
- *
95
- * // A function that creates `UserId` values.
96
- * // Casting with `as UserId` is unsafe, so `createUserId` must be unit-tested.
97
- * const createUserId = (): UserId => {
98
- * return 123 as UserId; // Unsafe casting
99
- * };
100
- *
101
- * const userId = createUserId();
102
- *
103
- * // A function that accepts only `UserId`.
104
- * const getUser = (id: UserId) => {
105
- * // Implementation
106
- * };
107
- *
108
- * getUser(userId); // ✅ Valid
109
- * getUser(123); // ❌ TypeScript error
110
- * getUser("123"); // ❌ TypeScript error
111
- * ```
112
- *
113
- * ### Example 2: Multiple Brands
114
- *
115
- * ```ts
116
- * // Define branded types
117
- * type Min1 = string & Brand<"Min1">;
118
- * type Max100 = string & Brand<"Max100">;
119
- * type Min1Max100 = string & Brand<"Min1" | "Max100">;
120
- *
121
- * // Functions requiring specific brands
122
- * const requiresMin1 = (value: Min1): void => {};
123
- * const requiresMax100 = (value: Max100): void => {};
124
- *
125
- * // Values with single brands
126
- * const min1Value: Min1 = "hello" as Min1;
127
- * const max100Value: Max100 = "world" as Max100;
128
- *
129
- * // Value with multiple brands
130
- * const min1Max100Value: Min1Max100 = "typescript" as Min1Max100;
131
- *
132
- * // Valid cases
133
- * requiresMin1(min1Value); // ✅ Valid
134
- * requiresMax100(max100Value); // ✅ Valid
135
- * requiresMin1(min1Max100Value); // ✅ Valid: Min1Max100 satisfies Min1
136
- * requiresMax100(min1Max100Value); // ✅ Valid: Min1Max100 satisfies Max100
137
- * ```
138
- */
139
- export interface Brand<B extends string> {
140
- readonly [__brand]: Readonly<Record<B, true>>;
141
- }
142
-
143
- declare const __brand: unique symbol;
144
-
145
- /**
146
- * Determines whether a type `T` is a branded type.
147
- *
148
- * Works with any base type intersected with a `Brand`.
149
- *
150
- * ### Examples
151
- *
152
- * - `IsBranded<string>` -> false
153
- * - `IsBranded<string & Brand<"X">>` -> true
154
- */
155
- export type IsBranded<T> = T extends Brand<string> ? true : false;
156
-
157
- /**
158
- * String | number | bigint | boolean | undefined | null
124
+ * String, number, bigint, boolean, undefined, null
159
125
  *
160
126
  * https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types
161
127
  */
package/src/WebSocket.ts CHANGED
@@ -1,13 +1,7 @@
1
- /**
2
- * Websocket with auto-reconnect and offline support
3
- *
4
- * @module
5
- */
6
-
7
- import { assertNoErrorInCatch } from "./Assert.js";
8
1
  import { constVoid } from "./Function.js";
9
- import { retry, RetryError, RetryOptions } from "./Promise.js";
10
2
  import { err, ok, Result } from "./Result.js";
3
+ import { retry, RetryError, RetryOptions } from "./Task.js";
4
+ import { maxPositiveInt } from "./Type.js";
11
5
 
12
6
  /** WebSocket with auto-reconnect and offline support. */
13
7
  export interface WebSocket extends Disposable {
@@ -20,10 +14,14 @@ export interface WebSocket extends Disposable {
20
14
  ) => Result<void, WebSocketSendError>;
21
15
 
22
16
  readonly getReadyState: () => WebSocketReadyState;
17
+
18
+ /** Returns true if the WebSocket is open and ready to send data. */
19
+ readonly isOpen: () => boolean;
23
20
  }
24
21
 
25
22
  /**
26
- * An error that occurs when trying to send data but WebSocket is connecting.
23
+ * An error that occurs when trying to send data but WebSocket is not available
24
+ * or is in the CONNECTING state.
27
25
  *
28
26
  * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send
29
27
  */
@@ -141,8 +139,9 @@ export interface WebSocketConnectionCloseError {
141
139
  *
142
140
  * TODO:
143
141
  */
144
- export const createWebSocket: CreateWebSocket = (url, options = {}) => {
145
- const {
142
+ export const createWebSocket: CreateWebSocket = (
143
+ url,
144
+ {
146
145
  protocols,
147
146
  binaryType,
148
147
  onOpen,
@@ -151,14 +150,14 @@ export const createWebSocket: CreateWebSocket = (url, options = {}) => {
151
150
  onError,
152
151
  retryOptions,
153
152
  WebSocketConstructor = globalThis.WebSocket,
154
- } = options;
153
+ } = {},
154
+ ) => {
155
155
  let isDisposed = false;
156
156
 
157
157
  const reconnectController = new AbortController();
158
158
 
159
159
  const defaultRetryOptions: RetryOptions<WebSocketRetryError> = {
160
- signal: reconnectController.signal,
161
- maxRetries: Infinity,
160
+ retries: maxPositiveInt, // Practically infinite retries
162
161
  };
163
162
 
164
163
  let socket: globalThis.WebSocket | null = null;
@@ -186,7 +185,11 @@ export const createWebSocket: CreateWebSocket = (url, options = {}) => {
186
185
  * - Is rejected when a connection is closed.
187
186
  * - Is resolved when WebSocket is disposed().
188
187
  */
189
- retry(
188
+ void retry(
189
+ {
190
+ ...defaultRetryOptions,
191
+ ...retryOptions,
192
+ },
190
193
  (): Promise<Result<void, WebSocketRetryError>> =>
191
194
  new Promise((resolve) => {
192
195
  disposePromise = () => {
@@ -212,6 +215,7 @@ export const createWebSocket: CreateWebSocket = (url, options = {}) => {
212
215
  ? { type: "WebSocketConnectionError", event }
213
216
  : { type: "WebSocketConnectError", event };
214
217
  onError?.(error);
218
+
215
219
  // Trigger reconnect only on WebSocketConnectError.
216
220
  if (error.type === "WebSocketConnectError") {
217
221
  resolve(err(error));
@@ -227,20 +231,12 @@ export const createWebSocket: CreateWebSocket = (url, options = {}) => {
227
231
  onMessage?.(event.data as string | ArrayBuffer | Blob);
228
232
  };
229
233
  }),
230
- {
231
- ...defaultRetryOptions,
232
- ...retryOptions,
233
- },
234
- )
235
- .then((result) => {
236
- if (result.ok || result.error.type === "RetryAbortError") return;
237
- onError?.(result.error);
238
- })
239
- .catch((error: unknown) => {
240
- assertNoErrorInCatch("WebSocket retry", error);
241
- });
242
-
243
- const webSocket: WebSocket = {
234
+ )(reconnectController).then((result) => {
235
+ if (result.ok || result.error.type === "AbortError") return;
236
+ onError?.(result.error as WebSocketError);
237
+ });
238
+
239
+ return {
244
240
  send: (data) => {
245
241
  // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send
246
242
  if (!socket || socket.readyState === socket.CONNECTING) {
@@ -253,6 +249,8 @@ export const createWebSocket: CreateWebSocket = (url, options = {}) => {
253
249
  getReadyState: () =>
254
250
  socket ? nativeToStringState[socket.readyState] : "connecting",
255
251
 
252
+ isOpen: () => (socket ? socket.readyState === socket.OPEN : false),
253
+
256
254
  [Symbol.dispose]() {
257
255
  if (isDisposed) return;
258
256
  isDisposed = true;
@@ -261,8 +259,6 @@ export const createWebSocket: CreateWebSocket = (url, options = {}) => {
261
259
  disposePromise?.();
262
260
  },
263
261
  };
264
-
265
- return webSocket;
266
262
  };
267
263
 
268
264
  const nativeToStringState: Record<number, WebSocketReadyState> = {
package/src/Worker.ts CHANGED
@@ -1,9 +1,3 @@
1
- /**
2
- * Cross-platform worker abstraction
3
- *
4
- * @module
5
- */
6
-
7
1
  import { assert } from "./Assert.js";
8
2
  import { createTransferableError, TransferableError } from "./Error.js";
9
3
 
@@ -20,6 +14,32 @@ export interface WorkerPostMessageDep<Output> {
20
14
  readonly postMessage: (message: Output) => void;
21
15
  }
22
16
 
17
+ /**
18
+ * Error reporting wrapper that catches synchronous errors in handlers and
19
+ * converts them to transferable error messages sent to the main thread.
20
+ */
21
+ export type WithErrorReporting = <A extends Array<any>>(
22
+ handler: (...args: A) => void,
23
+ ) => (...args: A) => void;
24
+
25
+ type HasInit<Input> =
26
+ Extract<Input, { type: "init" }> extends never
27
+ ? ["Input must contain a variant with { type: 'init' }"]
28
+ : unknown;
29
+
30
+ type HasWorkerErrorOutput<T> =
31
+ Extract<T, { type: "onError" }> extends infer E
32
+ ? [E] extends [never]
33
+ ? [
34
+ "Output must contain { type: 'onError'; error: TransferableError | ... }",
35
+ ]
36
+ : E extends { error: infer Err }
37
+ ? TransferableError extends Err
38
+ ? unknown
39
+ : ["Output.onError.error must include TransferableError"]
40
+ : ["Output.onError must have an error property"]
41
+ : never;
42
+
23
43
  /**
24
44
  * Creates a {@link Worker} that supports initialization with dependencies and
25
45
  * safe error handling.
@@ -35,9 +55,7 @@ export const createInitializedWorker = <
35
55
  readonly init: (
36
56
  initMessage: Extract<Input, { type: "init" }>,
37
57
  postMessage: (msg: Output) => void,
38
- safeHandler: <A extends Array<any>>(
39
- handler: (...args: A) => void,
40
- ) => (...args: A) => void,
58
+ withErrorReporting: WithErrorReporting,
41
59
  ) => Promise<Deps | null>;
42
60
  readonly onMessage: (
43
61
  deps: Deps,
@@ -62,7 +80,11 @@ export const createInitializedWorker = <
62
80
  } as unknown as Output);
63
81
  };
64
82
 
65
- const safeHandler =
83
+ /**
84
+ * Wraps function to catch errors and send them to the main thread instead of
85
+ * crashing the worker.
86
+ */
87
+ const withErrorReporting =
66
88
  <A extends Array<any>>(handler: (...args: A) => void) =>
67
89
  (...args: A) => {
68
90
  try {
@@ -78,7 +100,7 @@ export const createInitializedWorker = <
78
100
  if (!deps) {
79
101
  pendingMessages.push(message);
80
102
  } else {
81
- safeHandler(onMessage(deps))(message as NonInitMessage);
103
+ withErrorReporting(onMessage(deps))(message as NonInitMessage);
82
104
  }
83
105
  return;
84
106
  }
@@ -89,13 +111,13 @@ export const createInitializedWorker = <
89
111
  init(
90
112
  message as Extract<Input, { type: "init" }>,
91
113
  postMessage,
92
- safeHandler,
114
+ withErrorReporting,
93
115
  )
94
116
  .then((_deps) => {
95
117
  if (_deps == null) return;
96
118
  deps = _deps;
97
119
  for (const message of pendingMessages) {
98
- safeHandler(onMessage(deps))(message as NonInitMessage);
120
+ withErrorReporting(onMessage(deps))(message as NonInitMessage);
99
121
  }
100
122
  pendingMessages.length = 0;
101
123
  })
@@ -110,20 +132,41 @@ export const createInitializedWorker = <
110
132
  return worker;
111
133
  };
112
134
 
113
- type HasInit<Input> =
114
- Extract<Input, { type: "init" }> extends never
115
- ? ["Input must contain a variant with { type: 'init' }"]
116
- : unknown;
135
+ /** Type helper to extract message types from a union type */
136
+ export type MessageHandlers<Input extends { readonly type: string }, Deps> = {
137
+ readonly [K in Input["type"]]: (
138
+ deps: Deps,
139
+ ) => (message: Extract<Input, { type: K }>) => void;
140
+ };
117
141
 
118
- type HasWorkerErrorOutput<T> =
119
- Extract<T, { type: "onError" }> extends infer E
120
- ? [E] extends [never]
121
- ? [
122
- "Output must contain { type: 'onError'; error: TransferableError | ... }",
123
- ]
124
- : E extends { error: infer Err }
125
- ? TransferableError extends Err
126
- ? unknown
127
- : ["Output.onError.error must include TransferableError"]
128
- : ["Output.onError must have an error property"]
129
- : never;
142
+ /**
143
+ * Creates a {@link Worker} with type-safe message handlers for each message
144
+ * type. This provides better type safety and organization compared to a single
145
+ * onMessage handler.
146
+ */
147
+ export const createInitializedWorkerWithHandlers = <
148
+ Input extends { readonly type: string } & HasInit<Input>,
149
+ Output extends { readonly type: string } & HasWorkerErrorOutput<Output>,
150
+ Deps,
151
+ >({
152
+ init,
153
+ handlers,
154
+ }: {
155
+ readonly init: (
156
+ initMessage: Extract<Input, { type: "init" }>,
157
+ postMessage: (msg: Output) => void,
158
+ withErrorReporting: WithErrorReporting,
159
+ ) => Promise<Deps | null>;
160
+ readonly handlers: Omit<MessageHandlers<Input, Deps>, "init">;
161
+ }): Worker<Input, Output> =>
162
+ createInitializedWorker({
163
+ init,
164
+ onMessage: (deps) => (message) => {
165
+ type NonInitMessageType = Exclude<Input["type"], "init">;
166
+ const messageType = message.type as NonInitMessageType;
167
+ const handler = handlers[messageType];
168
+
169
+ // TypeScript knows handler exists because handlers covers all non-init message types
170
+ handler(deps)(message as Extract<Input, { type: typeof messageType }>);
171
+ },
172
+ });
package/src/index.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export * from "./Array.js";
2
2
  export * from "./Assert.js";
3
3
  export * from "./BigInt.js";
4
+ export * from "./Brand.js";
4
5
  export * from "./Buffer.js";
6
+ export * from "./Cache.js";
5
7
  export * from "./Callbacks.js";
6
8
  export * from "./Console.js";
7
9
  export * from "./Crypto.js";
@@ -9,18 +11,21 @@ export * from "./Eq.js";
9
11
  export * from "./Error.js";
10
12
  export * from "./Evolu/Public.js";
11
13
  export * from "./Function.js";
12
- export * from "./ManyToManyMap.js";
13
- export * from "./NanoId.js";
14
+ export * from "./Identicon.js";
15
+ export * from "./Instances.js";
14
16
  export * from "./Number.js";
15
17
  export * from "./Object.js";
16
18
  export * from "./Order.js";
17
- export * from "./Promise.js";
19
+ export * from "./Platform.js";
18
20
  export * from "./Random.js";
19
21
  export * from "./Ref.js";
22
+ export * from "./Relation.js";
23
+ export * from "./Resources.js";
20
24
  export * from "./Result.js";
21
25
  export * from "./Sqlite.js";
22
26
  export * from "./Store.js";
23
27
  export * from "./String.js";
28
+ export * from "./Task.js";
24
29
  export * from "./Time.js";
25
30
  export * from "./Type.js";
26
31
  export * from "./Types.js";
@@ -1,69 +0,0 @@
1
- import { ConsoleConfig } from "../Console.js";
2
- import { Mnemonic, SimpleName } from "../Type.js";
3
- import type { DbIndexesBuilder } from "./Kysely.js";
4
- export interface Config extends ConsoleConfig {
5
- /**
6
- * The name of Evolu instances. Evolu is multitenant - it can run more
7
- * instances concurrently. Every Evolu instance has to have its own unique
8
- * name. Database files are separated and invisible to each other.
9
- *
10
- * The default value is: `Evolu`.
11
- *
12
- * ### Example
13
- *
14
- * ```ts
15
- * // name: getOrThrow(SimpleName.from("MyApp"))
16
- * ```
17
- */
18
- readonly name: SimpleName;
19
- /**
20
- * URL for Evolu sync and backup server.
21
- *
22
- * The default value is `https://free.evoluhq.com`.
23
- */
24
- readonly syncUrl: string;
25
- /**
26
- * URL to reload browser tabs after reset or restore.
27
- *
28
- * The default value is `/`.
29
- */
30
- readonly reloadUrl: string;
31
- /**
32
- * Maximum physical clock drift allowed in ms.
33
- *
34
- * The default value is 5 * 60 * 1000 (5 minutes).
35
- */
36
- readonly maxDrift: number;
37
- /**
38
- * Use the `indexes` option to define SQLite indexes.
39
- *
40
- * Table and column names are not typed because Kysely doesn't support it.
41
- *
42
- * https://medium.com/@JasonWyatt/squeezing-performance-from-sqlite-indexes-indexes-c4e175f3c346
43
- *
44
- * ### Example
45
- *
46
- * ```ts
47
- * const evolu = createEvolu(evoluReactDeps)(Schema, {
48
- * indexes: (create) => [
49
- * create("todoCreatedAt").on("todo").column("createdAt"),
50
- * create("todoCategoryCreatedAt")
51
- * .on("todoCategory")
52
- * .column("createdAt"),
53
- * ],
54
- * });
55
- * ```
56
- */
57
- readonly indexes?: DbIndexesBuilder;
58
- /**
59
- * Use this option to create Evolu with the specified mnemonic. If omitted,
60
- * the mnemonic will be autogenerated. That should be the default behavior
61
- * until special UX requirements are needed (e.g., multitenancy).
62
- */
63
- readonly mnemonic?: Mnemonic;
64
- }
65
- export interface ConfigDep {
66
- readonly config: Config;
67
- }
68
- export declare const defaultConfig: Config;
69
- //# sourceMappingURL=Config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,MAAO,SAAQ,aAAa;IAC3C;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC9B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,aAAa,EAAE,MAM3B,CAAC"}
@@ -1,9 +0,0 @@
1
- import { getOrThrow } from "../Result.js";
2
- import { SimpleName } from "../Type.js";
3
- export const defaultConfig = {
4
- name: getOrThrow(SimpleName.fromParent("Evolu")),
5
- syncUrl: "wss://free.evoluhq.com",
6
- reloadUrl: "/",
7
- maxDrift: 5 * 60 * 1000,
8
- enableLogging: false,
9
- };
@@ -1,6 +0,0 @@
1
- import { CreateIndexBuilder, Kysely } from "kysely";
2
- import { DbIndex } from "./Db.js";
3
- export declare const kysely: Kysely<unknown>;
4
- export type DbIndexesBuilder = (create: (indexName: string) => CreateIndexBuilder) => ReadonlyArray<CreateIndexBuilder<any>>;
5
- export declare const createIndexes: (indexes?: DbIndexesBuilder) => ReadonlyArray<DbIndex>;
6
- //# sourceMappingURL=Kysely.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Kysely.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Kysely.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAElB,MAAM,EAGP,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,eAAO,MAAM,MAAM,iBASjB,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,kBAAkB,KAC9C,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;AAI5C,eAAO,MAAM,aAAa,GACxB,UAAU,gBAAgB,KACzB,aAAa,CAAC,OAAO,CAQvB,CAAC"}
@@ -1,21 +0,0 @@
1
- import { DummyDriver, Kysely, SqliteAdapter, SqliteQueryCompiler, } from "kysely";
2
- // https://kysely.dev/docs/recipes/splitting-query-building-and-execution
3
- export const kysely = new Kysely({
4
- dialect: {
5
- createAdapter: () => new SqliteAdapter(),
6
- createDriver: () => new DummyDriver(),
7
- createIntrospector() {
8
- throw new Error("Not implemeneted");
9
- },
10
- createQueryCompiler: () => new SqliteQueryCompiler(),
11
- },
12
- });
13
- const createIndex = kysely.schema.createIndex.bind(kysely.schema);
14
- export const createIndexes = (indexes) => {
15
- if (!indexes)
16
- return [];
17
- return indexes(createIndex).map((index) => ({
18
- name: index.toOperationNode().name.name,
19
- sql: index.compile().sql,
20
- }));
21
- };
@@ -1,26 +0,0 @@
1
- /** Represents a many-to-many mapping between keys and values. */
2
- export interface ManyToManyMap<K, V> {
3
- /** Adds a key-value association to the map. */
4
- add: (key: K, value: V) => ManyToManyMap<K, V>;
5
- /** Removes a specific key-value association. */
6
- remove: (key: K, value: V) => boolean;
7
- /** Gets all values associated with a key. */
8
- getValues: (key: K) => Set<V> | undefined;
9
- /** Gets all keys associated with a value. */
10
- getKeys: (value: V) => Set<K> | undefined;
11
- /** Checks if a specific key-value pair exists. */
12
- hasPair: (key: K, value: V) => boolean;
13
- /** Checks if a key exists in the map. */
14
- hasKey(key: K): boolean;
15
- /** Checks if a value exists in the map. */
16
- hasValue: (value: V) => boolean;
17
- /** Deletes all associations for a key. */
18
- deleteKey: (key: K) => boolean;
19
- /** Deletes all associations for a value. */
20
- deleteValue: (value: V) => boolean;
21
- /** Clears all associations in the map. */
22
- clear(): void;
23
- }
24
- /** Creates a {@link ManyToManyMap}. */
25
- export declare const createManyToManyMap: <K, V>() => ManyToManyMap<K, V>;
26
- //# sourceMappingURL=ManyToManyMap.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ManyToManyMap.d.ts","sourceRoot":"","sources":["../../src/ManyToManyMap.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC;IACjC,+CAA+C;IAC/C,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE/C,gDAAgD;IAChD,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;IAEtC,6CAA6C;IAC7C,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAE1C,6CAA6C;IAC7C,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAE1C,kDAAkD;IAClD,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;IAEvC,yCAAyC;IACzC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IAExB,2CAA2C;IAC3C,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;IAEhC,0CAA0C;IAC1C,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;IAE/B,4CAA4C;IAC5C,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;IAEnC,0CAA0C;IAC1C,KAAK,IAAI,IAAI,CAAC;CACf;AAED,uCAAuC;AACvC,eAAO,MAAM,mBAAmB,GAAI,CAAC,EAAE,CAAC,OAAK,aAAa,CAAC,CAAC,EAAE,CAAC,CAyG9D,CAAC"}