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