@evolu/common 8.0.0-next.5 → 8.0.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 (236) hide show
  1. package/dist/src/Array.d.ts +279 -173
  2. package/dist/src/Array.d.ts.map +1 -1
  3. package/dist/src/Array.js +105 -101
  4. package/dist/src/Assert.d.ts +39 -44
  5. package/dist/src/Assert.d.ts.map +1 -1
  6. package/dist/src/Assert.js +38 -31
  7. package/dist/src/BigInt.d.ts +6 -3
  8. package/dist/src/BigInt.d.ts.map +1 -1
  9. package/dist/src/BigInt.js +5 -2
  10. package/dist/src/Brand.d.ts +27 -40
  11. package/dist/src/Brand.d.ts.map +1 -1
  12. package/dist/src/Buffer.d.ts +27 -11
  13. package/dist/src/Buffer.d.ts.map +1 -1
  14. package/dist/src/Cache.d.ts +9 -4
  15. package/dist/src/Cache.d.ts.map +1 -1
  16. package/dist/src/Cache.js +8 -3
  17. package/dist/src/Callbacks.d.ts +30 -19
  18. package/dist/src/Callbacks.d.ts.map +1 -1
  19. package/dist/src/Console.d.ts +145 -66
  20. package/dist/src/Console.d.ts.map +1 -1
  21. package/dist/src/Console.js +91 -43
  22. package/dist/src/Crypto.d.ts +85 -42
  23. package/dist/src/Crypto.d.ts.map +1 -1
  24. package/dist/src/Crypto.js +44 -20
  25. package/dist/src/Eq.d.ts +87 -21
  26. package/dist/src/Eq.d.ts.map +1 -1
  27. package/dist/src/Eq.js +67 -19
  28. package/dist/src/Error.d.ts +3 -3
  29. package/dist/src/Error.d.ts.map +1 -1
  30. package/dist/src/Function.d.ts +119 -58
  31. package/dist/src/Function.d.ts.map +1 -1
  32. package/dist/src/Function.js +86 -43
  33. package/dist/src/Http.d.ts +394 -0
  34. package/dist/src/Http.d.ts.map +1 -0
  35. package/dist/src/Http.js +102 -0
  36. package/dist/src/Identicon.d.ts +16 -7
  37. package/dist/src/Identicon.d.ts.map +1 -1
  38. package/dist/src/Identicon.js +14 -5
  39. package/dist/src/LeakDetector.d.ts +88 -0
  40. package/dist/src/LeakDetector.d.ts.map +1 -0
  41. package/dist/src/LeakDetector.js +86 -0
  42. package/dist/src/LockManager.d.ts +18 -9
  43. package/dist/src/LockManager.d.ts.map +1 -1
  44. package/dist/src/LockManager.js +7 -8
  45. package/dist/src/Lookup.d.ts +43 -13
  46. package/dist/src/Lookup.d.ts.map +1 -1
  47. package/dist/src/Lookup.js +28 -8
  48. package/dist/src/Microtask.d.ts +1 -1
  49. package/dist/src/Microtask.d.ts.map +1 -1
  50. package/dist/src/Microtask.js +3 -7
  51. package/dist/src/Number.d.ts +49 -13
  52. package/dist/src/Number.d.ts.map +1 -1
  53. package/dist/src/Number.js +34 -5
  54. package/dist/src/Object.d.ts +160 -40
  55. package/dist/src/Object.d.ts.map +1 -1
  56. package/dist/src/Object.js +92 -46
  57. package/dist/src/Option.d.ts +10 -7
  58. package/dist/src/Option.d.ts.map +1 -1
  59. package/dist/src/Option.js +9 -6
  60. package/dist/src/Order.d.ts +21 -21
  61. package/dist/src/Order.d.ts.map +1 -1
  62. package/dist/src/Order.js +18 -21
  63. package/dist/src/Platform.d.ts +31 -0
  64. package/dist/src/Platform.d.ts.map +1 -1
  65. package/dist/src/Platform.js +98 -0
  66. package/dist/src/Random.d.ts +32 -17
  67. package/dist/src/Random.d.ts.map +1 -1
  68. package/dist/src/Random.js +12 -8
  69. package/dist/src/Redacted.d.ts +43 -29
  70. package/dist/src/Redacted.d.ts.map +1 -1
  71. package/dist/src/Redacted.js +14 -3
  72. package/dist/src/Ref.d.ts +11 -3
  73. package/dist/src/Ref.d.ts.map +1 -1
  74. package/dist/src/Ref.js +2 -2
  75. package/dist/src/RefCount.d.ts +2 -6
  76. package/dist/src/RefCount.d.ts.map +1 -1
  77. package/dist/src/RefCount.js +33 -90
  78. package/dist/src/Relation.d.ts +70 -5
  79. package/dist/src/Relation.d.ts.map +1 -1
  80. package/dist/src/Relation.js +77 -2
  81. package/dist/src/Resource.d.ts +590 -155
  82. package/dist/src/Resource.d.ts.map +1 -1
  83. package/dist/src/Resource.js +592 -343
  84. package/dist/src/Result.d.ts +838 -191
  85. package/dist/src/Result.d.ts.map +1 -1
  86. package/dist/src/Result.js +163 -102
  87. package/dist/src/Schedule.d.ts +684 -231
  88. package/dist/src/Schedule.d.ts.map +1 -1
  89. package/dist/src/Schedule.js +766 -295
  90. package/dist/src/Set.d.ts +89 -40
  91. package/dist/src/Set.d.ts.map +1 -1
  92. package/dist/src/Set.js +50 -29
  93. package/dist/src/Sqlite.d.ts +29 -72
  94. package/dist/src/Sqlite.d.ts.map +1 -1
  95. package/dist/src/Sqlite.js +39 -45
  96. package/dist/src/StackTrace.d.ts +40 -0
  97. package/dist/src/StackTrace.d.ts.map +1 -0
  98. package/dist/src/StackTrace.js +140 -0
  99. package/dist/src/Store.d.ts +5 -4
  100. package/dist/src/Store.d.ts.map +1 -1
  101. package/dist/src/Store.js +5 -4
  102. package/dist/src/String.d.ts +7 -1
  103. package/dist/src/String.d.ts.map +1 -1
  104. package/dist/src/String.js +20 -7
  105. package/dist/src/Task.d.ts +3428 -1629
  106. package/dist/src/Task.d.ts.map +1 -1
  107. package/dist/src/Task.js +1876 -1279
  108. package/dist/src/Test.d.ts +17 -124
  109. package/dist/src/Test.d.ts.map +1 -1
  110. package/dist/src/Test.js +17 -68
  111. package/dist/src/Time.d.ts +108 -49
  112. package/dist/src/Time.d.ts.map +1 -1
  113. package/dist/src/Time.js +172 -73
  114. package/dist/src/Type.d.ts +3542 -1926
  115. package/dist/src/Type.d.ts.map +1 -1
  116. package/dist/src/Type.js +3070 -1784
  117. package/dist/src/Types.d.ts +200 -100
  118. package/dist/src/Types.d.ts.map +1 -1
  119. package/dist/src/Types.js +39 -8
  120. package/dist/src/WebSocket.d.ts +48 -23
  121. package/dist/src/WebSocket.d.ts.map +1 -1
  122. package/dist/src/WebSocket.js +15 -6
  123. package/dist/src/Worker.d.ts +94 -36
  124. package/dist/src/Worker.d.ts.map +1 -1
  125. package/dist/src/Worker.js +58 -24
  126. package/dist/src/index.d.ts +55 -52
  127. package/dist/src/index.d.ts.map +1 -1
  128. package/dist/src/index.js +3 -1
  129. package/dist/src/intl/cs.d.ts +94 -0
  130. package/dist/src/intl/cs.d.ts.map +1 -0
  131. package/dist/src/intl/cs.js +220 -0
  132. package/dist/src/intl/index.d.ts +7 -0
  133. package/dist/src/intl/index.d.ts.map +1 -0
  134. package/dist/src/intl/index.js +6 -0
  135. package/dist/src/local-first/Db.d.ts +12 -8
  136. package/dist/src/local-first/Db.d.ts.map +1 -1
  137. package/dist/src/local-first/Db.js +107 -130
  138. package/dist/src/local-first/Error.d.ts +4 -4
  139. package/dist/src/local-first/Evolu.d.ts +338 -92
  140. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  141. package/dist/src/local-first/Evolu.js +47 -55
  142. package/dist/src/local-first/LocalAuth.d.ts +4 -4
  143. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  144. package/dist/src/local-first/Owner.d.ts +61 -24
  145. package/dist/src/local-first/Owner.d.ts.map +1 -1
  146. package/dist/src/local-first/Owner.js +45 -8
  147. package/dist/src/local-first/Protocol.d.ts +44 -31
  148. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  149. package/dist/src/local-first/Protocol.js +36 -13
  150. package/dist/src/local-first/Query.d.ts +147 -62
  151. package/dist/src/local-first/Query.d.ts.map +1 -1
  152. package/dist/src/local-first/Query.js +112 -58
  153. package/dist/src/local-first/Relay.d.ts +44 -14
  154. package/dist/src/local-first/Relay.d.ts.map +1 -1
  155. package/dist/src/local-first/Schema.d.ts +92 -41
  156. package/dist/src/local-first/Schema.d.ts.map +1 -1
  157. package/dist/src/local-first/Schema.js +25 -8
  158. package/dist/src/local-first/Shared.d.ts +23 -21
  159. package/dist/src/local-first/Shared.d.ts.map +1 -1
  160. package/dist/src/local-first/Shared.js +85 -42
  161. package/dist/src/local-first/Storage.d.ts +53 -32
  162. package/dist/src/local-first/Storage.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +194 -234
  164. package/dist/src/local-first/Timestamp.d.ts +21 -21
  165. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  166. package/dist/src/local-first/Timestamp.js +2 -2
  167. package/dist/src/local-first/index.d.ts +11 -11
  168. package/package.json +14 -13
  169. package/src/Array.ts +306 -205
  170. package/src/Assert.ts +48 -68
  171. package/src/BigInt.ts +6 -3
  172. package/src/Brand.ts +27 -40
  173. package/src/Buffer.ts +28 -12
  174. package/src/Cache.ts +9 -4
  175. package/src/Callbacks.ts +30 -19
  176. package/src/Console.ts +156 -84
  177. package/src/Crypto.ts +83 -41
  178. package/src/Eq.ts +95 -22
  179. package/src/Error.ts +5 -2
  180. package/src/Function.ts +167 -59
  181. package/src/Http.ts +549 -0
  182. package/src/Identicon.ts +16 -7
  183. package/src/LeakDetector.ts +182 -0
  184. package/src/LockManager.ts +43 -39
  185. package/src/Lookup.ts +62 -28
  186. package/src/Microtask.ts +19 -23
  187. package/src/Number.ts +64 -12
  188. package/src/Object.ts +173 -49
  189. package/src/Option.ts +10 -7
  190. package/src/Order.ts +23 -21
  191. package/src/Platform.ts +136 -0
  192. package/src/Polyfills.ts +6 -12
  193. package/src/Random.ts +32 -19
  194. package/src/Redacted.ts +44 -30
  195. package/src/Ref.ts +14 -6
  196. package/src/RefCount.ts +38 -80
  197. package/src/Relation.ts +202 -10
  198. package/src/Resource.ts +1199 -591
  199. package/src/Result.ts +930 -271
  200. package/src/Schedule.ts +859 -343
  201. package/src/Set.ts +89 -40
  202. package/src/Sqlite.ts +142 -137
  203. package/src/StackTrace.ts +230 -0
  204. package/src/Store.ts +8 -7
  205. package/src/String.ts +22 -5
  206. package/src/Task.ts +5109 -2845
  207. package/src/Test.ts +20 -167
  208. package/src/Time.ts +293 -90
  209. package/src/Type.ts +10034 -4117
  210. package/src/Types.ts +227 -101
  211. package/src/WebSocket.ts +64 -36
  212. package/src/Worker.ts +180 -72
  213. package/src/index.ts +55 -52
  214. package/src/intl/cs.ts +388 -0
  215. package/src/intl/index.ts +7 -0
  216. package/src/local-first/Db.ts +161 -190
  217. package/src/local-first/Error.ts +4 -4
  218. package/src/local-first/Evolu.ts +495 -250
  219. package/src/local-first/LocalAuth.ts +6 -12
  220. package/src/local-first/Owner.ts +58 -21
  221. package/src/local-first/Protocol.ts +49 -31
  222. package/src/local-first/Query.ts +146 -61
  223. package/src/local-first/Relay.ts +51 -21
  224. package/src/local-first/Schema.ts +109 -51
  225. package/src/local-first/Shared.ts +253 -214
  226. package/src/local-first/Storage.ts +266 -264
  227. package/src/local-first/Timestamp.ts +25 -18
  228. package/src/local-first/index.ts +11 -11
  229. package/dist/src/Skiplist.d.ts +0 -28
  230. package/dist/src/Skiplist.d.ts.map +0 -1
  231. package/dist/src/Skiplist.js +0 -64
  232. package/dist/src/Tracer.d.ts +0 -48
  233. package/dist/src/Tracer.d.ts.map +0 -1
  234. package/dist/src/Tracer.js +0 -6
  235. package/src/Skiplist.ts +0 -108
  236. package/src/Tracer.ts +0 -54
@@ -8,37 +8,43 @@ import {
8
8
  emptyArray,
9
9
  firstInArray,
10
10
  isNonEmptyArray,
11
- shiftFromArray,
12
11
  type NonEmptyReadonlyArray,
13
- } from "../Array.js";
14
- import { assert, assertNotAborted, assertNotDisposed } from "../Assert.js";
15
- import type { Brand } from "../Brand.js";
16
- import { createCallbacks } from "../Callbacks.js";
17
- import type { ConsoleEntry, ConsoleLevel } from "../Console.js";
18
- import type { EncryptionKey } from "../Crypto.js";
19
- import { acquireLeaderLock, type LockManagerDep } from "../LockManager.js";
20
- import { structuralLookup, type StructuralLookupKey } from "../Lookup.js";
21
- import { createRefCountByKey, type RefCountByKey } from "../RefCount.js";
12
+ } from "../Array.ts";
13
+ import { assert } from "../Assert.ts";
14
+ import type { Brand } from "../Brand.ts";
15
+ import { createCallbacks } from "../Callbacks.ts";
16
+ import type { ConsoleEntry, ConsoleLevel } from "../Console.ts";
17
+ import type { EncryptionKey } from "../Crypto.ts";
18
+ import { disposable } from "../Function.ts";
19
+ import { acquireLeaderLock, type LockManagerDep } from "../LockManager.ts";
20
+ import {
21
+ createLookupMap,
22
+ structuralLookup,
23
+ type LookupMap,
24
+ type StructuralLookupKey,
25
+ } from "../Lookup.ts";
26
+ import { createRefCountByKey, type RefCountByKey } from "../RefCount.ts";
22
27
  import {
23
28
  createSharedResourceByKey,
24
29
  createSharedResourceByKeyWithClaims,
25
30
  type BorrowedResource,
31
+ type ClaimLease,
26
32
  type SharedResourceByKeyWithClaims,
27
- } from "../Resource.js";
28
- import { ok, type Result } from "../Result.js";
29
- import type { NonEmptyReadonlySet } from "../Set.js";
30
- import type { SqliteSchema } from "../Sqlite.js";
31
- import { createStore, type Store } from "../Store.js";
33
+ } from "../Resource.ts";
34
+ import { ok, type Result } from "../Result.ts";
35
+ import type { NonEmptyReadonlySet } from "../Set.ts";
36
+ import type { SqliteSchema } from "../Sqlite.ts";
37
+ import { createStore, type Store } from "../Store.ts";
32
38
  import {
33
39
  AbortError,
34
40
  createMutex,
35
41
  unabortable,
36
42
  type Mutex,
37
43
  type Task,
38
- } from "../Task.js";
39
- import { type Id, type Name, type Typed } from "../Type.js";
40
- import type { Callback, ExtractType } from "../Types.js";
41
- import type { CreateWebSocketDep, WebSocket } from "../WebSocket.js";
44
+ } from "../Task.ts";
45
+ import type { ExtractTyped, Id, Name, Typed } from "../Type.ts";
46
+ import type { Callback } from "../Types.ts";
47
+ import type { CreateWebSocketDep, WebSocket } from "../WebSocket.ts";
42
48
  import type {
43
49
  SharedWorker as CommonSharedWorker,
44
50
  CreateBroadcastChannelDep,
@@ -47,10 +53,10 @@ import type {
47
53
  NativeMessagePort,
48
54
  SharedWorkerSelf,
49
55
  WorkerDeps,
50
- } from "../Worker.js";
51
- import type { DbWorkerInit } from "./Db.js";
52
- import type { EvoluError } from "./Error.js";
53
- import type { Owner, OwnerId, OwnerTransport, SyncOwner } from "./Owner.js";
56
+ } from "../Worker.ts";
57
+ import type { DbWorkerInit } from "./Db.ts";
58
+ import type { EvoluError } from "./Error.ts";
59
+ import type { Owner, OwnerId, OwnerTransport, SyncOwner } from "./Owner.ts";
54
60
  import {
55
61
  createProtocolMessageForUnsubscribe,
56
62
  createProtocolMessageFromCrdtMessages,
@@ -58,16 +64,16 @@ import {
58
64
  type ApplyProtocolMessageAsClientResult,
59
65
  type ProtocolError,
60
66
  type ProtocolMessage,
61
- } from "./Protocol.js";
67
+ } from "./Protocol.ts";
62
68
  import {
63
69
  makePatches,
64
70
  type Patch,
65
71
  type Query,
66
72
  type Row,
67
73
  type RowsByQueryMap,
68
- } from "./Query.js";
69
- import type { MutationChange } from "./Schema.js";
70
- import type { CrdtMessage } from "./Storage.js";
74
+ } from "./Query.ts";
75
+ import type { MutationChange } from "./Schema.ts";
76
+ import type { CrdtMessage } from "./Storage.ts";
71
77
 
72
78
  export type SharedWorker = CommonSharedWorker<
73
79
  SharedWorkerInput,
@@ -156,7 +162,9 @@ export type DbWorkerRequest =
156
162
  | {
157
163
  readonly type: "ForEvolu";
158
164
  readonly id: EvoluInstanceId;
159
- readonly message: ExtractType<EvoluInput, "Mutate" | "Query" | "Export">;
165
+ readonly message: {
166
+ readonly [Message in EvoluInput as Message["type"]]: Message;
167
+ }["Mutate" | "Query" | "Export"];
160
168
  }
161
169
  | {
162
170
  readonly type: "ForSharedWorker";
@@ -234,7 +242,7 @@ export type SharedWorkerDeps = WorkerDeps &
234
242
 
235
243
  interface EvoluTenant extends AsyncDisposable {
236
244
  readonly addInstance: (
237
- message: ExtractType<SharedWorkerInput, "CreateEvolu">,
245
+ message: ExtractTyped<SharedWorkerInput, "CreateEvolu">,
238
246
  onDisposed: () => void,
239
247
  ) => void;
240
248
 
@@ -265,8 +273,7 @@ interface TransportsDep {
265
273
  readonly transports: SharedResourceByKeyWithClaims<
266
274
  OwnerTransport,
267
275
  OwnerId,
268
- WebSocket,
269
- SharedWorkerDeps
276
+ WebSocket
270
277
  >;
271
278
  }
272
279
 
@@ -274,6 +281,7 @@ export type EvoluInstanceId = Id & Brand<"EvoluInstance">;
274
281
 
275
282
  export type SyncState = 123;
276
283
 
284
+ /** Initializes the platform-agnostic Evolu SharedWorker. */
277
285
  export const initSharedWorker =
278
286
  (
279
287
  self: SharedWorkerSelf<SharedWorkerInput, SharedWorkerOutput>,
@@ -313,15 +321,10 @@ export const initSharedWorker =
313
321
 
314
322
  case "CreateEvolu": {
315
323
  void sharedWorkerRun(async (run) => {
316
- const tenant = await run(
324
+ const tenantLease = await run.ok(
317
325
  unabortable(tenantsByName.acquire(message)),
318
326
  );
319
- if (!tenant.ok) return tenant;
320
-
321
- tenant.value.addInstance(
322
- message,
323
- () => void sharedWorkerRun(tenantsByName.release(message)),
324
- );
327
+ tenantLease.resource.addInstance(message, tenantLease.release);
325
328
  return ok();
326
329
  });
327
330
  break;
@@ -340,12 +343,13 @@ export const initSharedWorker =
340
343
  }),
341
344
  );
342
345
 
346
+ const currentTenantsByName = new Map<Name, BorrowedResource<EvoluTenant>>();
343
347
  const transports = disposer.use(
344
- await run.orThrow(
348
+ await run.ok(
345
349
  createSharedResourceByKeyWithClaims<
346
- WebSocket,
347
350
  OwnerTransport,
348
351
  OwnerId,
352
+ WebSocket,
349
353
  SharedWorkerDeps,
350
354
  StructuralLookupKey
351
355
  >(
@@ -420,11 +424,15 @@ export const initSharedWorker =
420
424
  });
421
425
 
422
426
  const tenantsByName = disposer.use(
423
- await sharedWorkerRun.orThrow(
424
- createSharedResourceByKey(createEvoluTenant, {
425
- idleDisposeAfter: "3s",
426
- lookup: (message) => message.name,
427
- }),
427
+ await sharedWorkerRun.ok(
428
+ createSharedResourceByKey(
429
+ (message: ExtractTyped<SharedWorkerInput, "CreateEvolu">) =>
430
+ createEvoluTenant(message, currentTenantsByName),
431
+ {
432
+ idleDisposeAfter: "3s",
433
+ lookup: (message) => message.name,
434
+ },
435
+ ),
428
436
  ),
429
437
  );
430
438
 
@@ -433,26 +441,23 @@ export const initSharedWorker =
433
441
  const forEachTenant = (
434
442
  callback: Callback<BorrowedResource<EvoluTenant>>,
435
443
  ): void => {
436
- for (const tenant of tenantsByName.snapshot().resourcesByKey.values()) {
437
- callback(tenant);
438
- }
444
+ for (const tenant of currentTenantsByName.values()) callback(tenant);
439
445
  };
440
446
 
441
447
  return ok(disposer.move());
442
448
  };
443
449
 
444
450
  const createEvoluTenant =
445
- ({
446
- name,
447
- consoleLevel,
448
- sqliteSchema,
449
- encryptionKey,
450
- memoryOnly,
451
- }: ExtractType<SharedWorkerInput, "CreateEvolu">): Task<
452
- EvoluTenant,
453
- never,
454
- EvoluTenantDeps
455
- > =>
451
+ (
452
+ {
453
+ name,
454
+ consoleLevel,
455
+ sqliteSchema,
456
+ encryptionKey,
457
+ memoryOnly,
458
+ }: ExtractTyped<SharedWorkerInput, "CreateEvolu">,
459
+ currentTenantsByName: Map<Name, BorrowedResource<EvoluTenant>>,
460
+ ): Task<EvoluTenant, never, EvoluTenantDeps> =>
456
461
  async (run) => {
457
462
  await using disposer = new AsyncDisposableStack();
458
463
  const tenantRun = disposer.use(run.create());
@@ -462,6 +467,7 @@ const createEvoluTenant =
462
467
 
463
468
  interface EvoluInstance extends AsyncDisposable {
464
469
  readonly id: EvoluInstanceId;
470
+ readonly claimLeasesBySyncOwner: LookupMap<SyncOwner, Array<ClaimLease>>;
465
471
  readonly onDisposed: () => void;
466
472
  readonly port: MessagePort<EvoluOutput, EvoluInput>;
467
473
  readonly useOwnerMutex: Mutex;
@@ -533,7 +539,7 @@ const createEvoluTenant =
533
539
 
534
540
  const queue: Array<DbWorkerRequest> = [];
535
541
  const callbacks = disposer.use(
536
- createCallbacks<ExtractType<DbWorkerOutput, "OnQueuedResponse">>(
542
+ createCallbacks<ExtractTyped<DbWorkerOutput, "OnQueuedResponse">>(
537
543
  run.deps,
538
544
  ),
539
545
  );
@@ -559,7 +565,7 @@ const createEvoluTenant =
559
565
  }
560
566
 
561
567
  // Complete the current queue item and continue with the next one.
562
- shiftFromArray(queue);
568
+ queue.shift();
563
569
  queueRequestInFlight = false;
564
570
  runQueue();
565
571
  });
@@ -578,21 +584,16 @@ const createEvoluTenant =
578
584
  // DbWorker releases it: either because Dispose was delivered or because
579
585
  // the hosting tab closed. The wait is unabortable because tenant disposal
580
586
  // must finish even after tenantRun receives an abort request.
581
- const lock = await tenantRun(acquireLeaderLock(name));
582
- // AbortError here means tenantRun was already disposed before this
583
- // cleanup could start, violating the disposal ordering above.
584
- assertNotAborted(lock);
585
- await using _ = lock.value;
587
+ await using _ = await tenantRun.ok(acquireLeaderLock(name));
586
588
  });
587
589
 
588
590
  disposer.defer(deps.tabLeaderPortStore.subscribe(initDbWorker));
589
591
 
590
592
  initDbWorker();
591
593
  await dbWorkerInited.promise;
592
- const disposables = disposer.move();
593
594
 
594
595
  const handleResponseForEvolu = (
595
- response: ExtractType<DbWorkerQueuedResponse, "ForEvolu">,
596
+ response: ExtractTyped<DbWorkerQueuedResponse, "ForEvolu">,
596
597
  first: DbWorkerRequest,
597
598
  ): void => {
598
599
  const instance = instancesById.get(response.id);
@@ -669,7 +670,7 @@ const createEvoluTenant =
669
670
  };
670
671
 
671
672
  const handleResponseForSharedWorker = (
672
- response: ExtractType<DbWorkerQueuedResponse, "ForSharedWorker">,
673
+ response: ExtractTyped<DbWorkerQueuedResponse, "ForSharedWorker">,
673
674
  ): void => {
674
675
  switch (response.message.type) {
675
676
  case "CreateSyncMessages":
@@ -718,20 +719,20 @@ const createEvoluTenant =
718
719
  protocolMessagesByOwnerId: ReadonlyMap<OwnerId, ProtocolMessage>,
719
720
  ): void => {
720
721
  for (const [ownerId, protocolMessage] of protocolMessagesByOwnerId) {
721
- for (const transport of deps.transports.getResourceKeysForClaim(
722
+ deps.transports.forEachResourceForClaim(
722
723
  ownerId,
723
- )) {
724
- const webSocket = deps.transports.getResource(transport);
725
- if (!webSocket?.isOpen()) continue;
724
+ (webSocket, transport) => {
725
+ if (!webSocket.isOpen()) return;
726
726
 
727
- console.debug("sendProtocolMessage", {
728
- ownerId,
729
- url: transport.url,
730
- byteLength: protocolMessage.byteLength,
731
- });
727
+ console.debug("sendProtocolMessage", {
728
+ ownerId,
729
+ url: transport.url,
730
+ byteLength: protocolMessage.byteLength,
731
+ });
732
732
 
733
- webSocket.send(protocolMessage);
734
- }
733
+ webSocket.send(protocolMessage);
734
+ },
735
+ );
735
736
  }
736
737
  };
737
738
 
@@ -757,156 +758,187 @@ const createEvoluTenant =
757
758
  async (run) => {
758
759
  if (action === "add") {
759
760
  instance.usedSyncOwners.increment(syncOwner);
760
- return await run(
761
- deps.transports.addClaim(syncOwner.owner.id, syncOwner.transports),
761
+ let succeeded = false;
762
+ using compensation = new DisposableStack();
763
+ compensation.defer(() => {
764
+ if (!succeeded) instance.usedSyncOwners.decrement(syncOwner);
765
+ });
766
+ const claimLease = await run.ok(
767
+ deps.transports.claim(syncOwner.owner.id, syncOwner.transports),
762
768
  );
769
+ instance.claimLeasesBySyncOwner
770
+ .getOrInsertComputed(syncOwner, () => [])
771
+ .push(claimLease);
772
+ succeeded = true;
763
773
  } else {
764
774
  instance.usedSyncOwners.decrement(syncOwner);
765
- return await run(
766
- deps.transports.removeClaim(
767
- syncOwner.owner.id,
768
- syncOwner.transports,
769
- ),
770
- );
775
+ const claimLeases = instance.claimLeasesBySyncOwner.get(syncOwner);
776
+ assert(claimLeases, "Expected a ClaimLease for the used owner.");
777
+ const claimLease = claimLeases.pop();
778
+ assert(claimLease, "Expected a ClaimLease for the used owner.");
779
+ claimLease.release();
780
+ if (claimLeases.length === 0) {
781
+ instance.claimLeasesBySyncOwner.delete(syncOwner);
782
+ }
771
783
  }
784
+ return ok();
772
785
  };
773
786
 
774
- return ok({
775
- addInstance: (message, onDisposed) => {
776
- assertNotDisposed(disposables);
777
-
778
- const instance: EvoluInstance = {
779
- id: message.id,
780
- port: deps.createMessagePort<EvoluOutput, EvoluInput>(
781
- message.evoluPort,
782
- ),
783
- onDisposed,
784
- rowsByQuery: new Map<Query, ReadonlyArray<Row>>(),
785
- useOwnerMutex: createMutex(),
786
- usedSyncOwners: createRefCountByKey<SyncOwner, OwnerId>({
787
- lookup: (syncOwner) => syncOwner.owner.id,
788
- }),
789
- [Symbol.asyncDispose]: () => disposer.disposeAsync(),
790
- };
791
-
792
- instancesById.set(instance.id, instance);
787
+ disposer.defer(() => {
788
+ currentTenantsByName.delete(name);
789
+ });
790
+ const tenant = disposable<EvoluTenant>(
791
+ {
792
+ addInstance: (message, onDisposed) => {
793
+ const disposer = new AsyncDisposableStack();
794
+ const instance: EvoluInstance = {
795
+ id: message.id,
796
+ claimLeasesBySyncOwner: createLookupMap({
797
+ lookup: (syncOwner: SyncOwner) =>
798
+ structuralLookup<{
799
+ readonly ownerId: OwnerId;
800
+ readonly transports: ReadonlyArray<StructuralLookupKey>;
801
+ }>({
802
+ ownerId: syncOwner.owner.id,
803
+ transports: syncOwner.transports
804
+ .map(structuralLookup)
805
+ .toSorted(),
806
+ }),
807
+ }),
808
+ port: deps.createMessagePort<EvoluOutput, EvoluInput>(
809
+ message.evoluPort,
810
+ ),
811
+ onDisposed,
812
+ rowsByQuery: new Map<Query, ReadonlyArray<Row>>(),
813
+ useOwnerMutex: createMutex(),
814
+ usedSyncOwners: createRefCountByKey<SyncOwner, OwnerId>({
815
+ lookup: (syncOwner) => syncOwner.owner.id,
816
+ }),
817
+ [Symbol.asyncDispose]: () => disposer.disposeAsync(),
818
+ };
793
819
 
794
- const disposer = new AsyncDisposableStack();
820
+ instancesById.set(instance.id, instance);
795
821
 
796
- disposer.defer(instance.onDisposed);
797
- disposer.use(instance.usedSyncOwners);
798
- disposer.use(instance.useOwnerMutex);
822
+ disposer.defer(instance.onDisposed);
823
+ disposer.use(instance.usedSyncOwners);
799
824
 
800
- disposer.defer(async () => {
801
- await tenantRun(
802
- instance.useOwnerMutex.withLock(async (run) => {
803
- for (const syncOwner of instance.usedSyncOwners.keys()) {
804
- while (instance.usedSyncOwners.has(syncOwner)) {
805
- await run(toggleSyncOwner(instance, syncOwner, "remove"));
825
+ disposer.defer(async () => {
826
+ await tenantRun(
827
+ instance.useOwnerMutex.withLock(async (run) => {
828
+ for (const syncOwner of instance.usedSyncOwners.keys()) {
829
+ while (instance.usedSyncOwners.has(syncOwner)) {
830
+ await run(toggleSyncOwner(instance, syncOwner, "remove"));
831
+ }
806
832
  }
833
+ return ok();
834
+ }),
835
+ );
836
+ });
837
+
838
+ disposer.defer(() => {
839
+ instancesById.delete(instance.id);
840
+ console.info("evoluDispose", { name, id: instance.id });
841
+ });
842
+ disposer.use(instance.port);
843
+ disposer.defer(() => {
844
+ instance.port.onMessage = null;
845
+ });
846
+
847
+ // The main-thread Evolu instance holds this per-instance leader lock
848
+ // while it is alive. Acquiring the same lock here means the main
849
+ // thread instance was disposed or its tab closed, so the tenant-side
850
+ // instance must dispose itself.
851
+ void tenantRun
852
+ .abortable(acquireLeaderLock(message.id))
853
+ .then((lock) => {
854
+ if (!lock.ok) return;
855
+
856
+ disposer.use(lock.value);
857
+ return instance[Symbol.asyncDispose]();
858
+ });
859
+
860
+ instance.port.onMessage = (message) => {
861
+ switch (message.type) {
862
+ case "Query":
863
+ case "Export": {
864
+ queue.push({ type: "ForEvolu", id: instance.id, message });
865
+ runQueue();
866
+ break;
867
+ }
868
+ case "Mutate": {
869
+ // TODO: Delegate do vsech evolu instances, co to pouzivaji
870
+ queue.push({ type: "ForEvolu", id: instance.id, message });
871
+ runQueue();
872
+ break;
873
+ }
874
+ case "UseOwner": {
875
+ void tenantRun(
876
+ instance.useOwnerMutex.withLock(async (run) => {
877
+ for (const { owner, action } of message.actions) {
878
+ console.debug("useOwner", {
879
+ id: instance.id,
880
+ action,
881
+ ownerId: owner.owner.id,
882
+ transportUrls: owner.transports.map(({ url }) => url),
883
+ });
884
+
885
+ await run(toggleSyncOwner(instance, owner, action));
886
+ }
887
+
888
+ return ok();
889
+ }),
890
+ );
891
+ break;
807
892
  }
808
- return ok();
809
- }),
810
- );
811
- });
812
-
813
- disposer.defer(() => {
814
- instancesById.delete(instance.id);
815
- console.info("evoluDispose", { name, id: instance.id });
816
- });
817
- disposer.use(instance.port);
818
-
819
- // The main-thread Evolu instance holds this per-instance leader lock
820
- // while it is alive. Acquiring the same lock here means the main
821
- // thread instance was disposed or its tab closed, so the tenant-side
822
- // instance must dispose itself.
823
- void tenantRun(acquireLeaderLock(message.id)).then((lock) => {
824
- if (!lock.ok) return;
825
-
826
- disposer.use(lock.value);
827
- return disposer.disposeAsync();
828
- });
829
-
830
- instance.port.onMessage = (message) => {
831
- switch (message.type) {
832
- case "Query":
833
- case "Export": {
834
- queue.push({ type: "ForEvolu", id: instance.id, message });
835
- runQueue();
836
- break;
837
- }
838
- case "Mutate": {
839
- // TODO: Delegate do vsech evolu instances, co to pouzivaji
840
- queue.push({ type: "ForEvolu", id: instance.id, message });
841
- runQueue();
842
- break;
843
893
  }
844
- case "UseOwner": {
845
- void tenantRun(
846
- instance.useOwnerMutex.withLock(async (run) => {
847
- for (const { owner, action } of message.actions) {
848
- console.debug("useOwner", {
849
- id: instance.id,
850
- action,
851
- ownerId: owner.owner.id,
852
- transportUrls: owner.transports.map(({ url }) => url),
853
- });
854
-
855
- await run(toggleSyncOwner(instance, owner, action));
856
- }
894
+ };
895
+ },
857
896
 
858
- return ok();
859
- }),
860
- );
861
- break;
862
- }
863
- }
864
- };
865
- },
897
+ requestCreateSyncMessages: (ownerIds): void => {
898
+ const ownersToSync = [...getUsedOwnersById(ownerIds).values()];
866
899
 
867
- requestCreateSyncMessages: (ownerIds): void => {
868
- const ownersToSync = [...getUsedOwnersById(ownerIds).values()];
900
+ if (!isNonEmptyArray(ownersToSync)) return;
869
901
 
870
- if (!isNonEmptyArray(ownersToSync)) return;
902
+ console.debug("requestCreateSyncMessages", {
903
+ ownerIds: ownersToSync.map(({ id }) => id),
904
+ });
871
905
 
872
- console.debug("requestCreateSyncMessages", {
873
- ownerIds: ownersToSync.map(({ id }) => id),
874
- });
906
+ queue.push({
907
+ type: "ForSharedWorker",
908
+ message: {
909
+ type: "CreateSyncMessages",
910
+ owners: ownersToSync,
911
+ },
912
+ });
875
913
 
876
- queue.push({
877
- type: "ForSharedWorker",
878
- message: {
879
- type: "CreateSyncMessages",
880
- owners: ownersToSync,
881
- },
882
- });
914
+ runQueue();
915
+ },
883
916
 
884
- runQueue();
885
- },
917
+ requestApplySyncMessage: (ownerId, inputMessage): void => {
918
+ const owner = getUsedOwnersById(new Set([ownerId])).get(ownerId);
919
+ if (!owner) return;
886
920
 
887
- requestApplySyncMessage: (ownerId, inputMessage): void => {
888
- const owner = getUsedOwnersById(new Set([ownerId])).get(ownerId);
889
- if (!owner) return;
921
+ console.debug("requestApplySyncMessage", {
922
+ ownerId,
923
+ byteLength: inputMessage.byteLength,
924
+ });
890
925
 
891
- console.debug("requestApplySyncMessage", {
892
- ownerId,
893
- byteLength: inputMessage.byteLength,
894
- });
895
-
896
- queue.push({
897
- type: "ForSharedWorker",
898
- message: {
899
- type: "ApplySyncMessage",
900
- owner,
901
- inputMessage,
902
- },
903
- });
926
+ queue.push({
927
+ type: "ForSharedWorker",
928
+ message: {
929
+ type: "ApplySyncMessage",
930
+ owner,
931
+ inputMessage,
932
+ },
933
+ });
904
934
 
905
- runQueue();
935
+ runQueue();
936
+ },
906
937
  },
907
-
908
- [Symbol.asyncDispose]: () => disposables.disposeAsync(),
909
- });
938
+ disposer,
939
+ );
940
+ currentTenantsByName.set(name, tenant);
941
+ return ok(tenant);
910
942
  };
911
943
 
912
944
  // | (Typed<"reset"> & {
@@ -932,10 +964,17 @@ const createEvoluTenant =
932
964
  // readonly reload: boolean;
933
965
  // })
934
966
 
935
- // TODO: Remaining SharedWorker lifetime issues.
936
- // - Investigate heartbeat for ports and where liveness tracking is needed.
937
- // - Sync state for monitoring.
967
+ // TODO: SharedWorker follow-ups.
968
+ // - Complete the queue head when a DbWorker mutation returns an error.
969
+ // - Make retried DbWorker requests deterministic by materializing clocks and
970
+ // timestamps in the SharedWorker before enqueueing them.
971
+ // - Replace the callback registry and queueRequestInFlight flag with one
972
+ // explicit in-flight request state.
973
+ // - Detect DbWorker and port liveness so a worker-only crash resumes the queue.
974
+ // - Consolidate usedSyncOwners and claimLeasesBySyncOwner into one owner-use
975
+ // state abstraction without changing repeated-use semantics.
976
+ // - Split worker protocol types and the EvoluTenant implementation into focused
977
+ // modules.
978
+ // - Remove the obsolete commented protocol block above.
979
+ // - Replace the SyncState placeholder with actual sync monitoring state.
938
980
  // - Propagate invalid protocol messages to sync state.
939
- // - Make DbWorker logically stateless by making all requests idempotent.
940
- // - Investigate WebKit bug 301520 relevance to DbWorker disposal. (again)
941
- // https://bugs.webkit.org/show_bug.cgi?id=301520