@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
@@ -9,46 +9,43 @@ import {
9
9
  isNonEmptyArray,
10
10
  mapArray,
11
11
  type NonEmptyReadonlyArray,
12
- } from "../Array.js";
13
- import {
14
- assert,
15
- assertNonEmptyReadonlyArray,
16
- assertNotDisposed,
17
- } from "../Assert.js";
18
- import { createCallbacks } from "../Callbacks.js";
19
- import type { ConsoleDep } from "../Console.js";
20
- import { createConsole } from "../Console.js";
21
- import { createUnknownError } from "../Error.js";
22
- import { exhaustiveCheck, todo } from "../Function.js";
12
+ } from "../Array.ts";
13
+ import { assert, assertNonEmptyReadonlyArray } from "../Assert.ts";
14
+ import { createCallbacks } from "../Callbacks.ts";
15
+ import type { ConsoleDep } from "../Console.ts";
16
+ import { createConsole } from "../Console.ts";
17
+ import { createUnknownError } from "../Error.ts";
18
+ import { disposable, exhaustiveCheck, todo } from "../Function.ts";
23
19
  import {
24
20
  acquireLeaderLock,
25
21
  acquireLeaderLockCallback,
26
22
  type LockManagerDep,
27
- } from "../LockManager.js";
28
- import { createMicrotaskBatch } from "../Microtask.js";
29
- import type { FlushSyncDep, ReloadAppDep } from "../Platform.js";
30
- import { createRefCountByKey } from "../RefCount.js";
31
- import { err, ok } from "../Result.js";
32
- import { isNonEmptySet } from "../Set.js";
33
- import { SqliteBoolean, sqliteBooleanToBoolean } from "../Sqlite.js";
34
- import type { Listener, ReadonlyStore, Unsubscribe } from "../Store.js";
35
- import { createStore } from "../Store.js";
36
- import { type createRun, type Task } from "../Task.js";
37
- import type { Id, TypeError } from "../Type.js";
23
+ } from "../LockManager.ts";
24
+ import { createMicrotaskBatch } from "../Microtask.ts";
25
+ import type { FlushSyncDep, ReloadAppDep } from "../Platform.ts";
26
+ import { createRefCountByKey } from "../RefCount.ts";
27
+ import { err, ok } from "../Result.ts";
28
+ import { isNonEmptySet } from "../Set.ts";
29
+ import { SqliteBoolean, sqliteBooleanToBoolean } from "../Sqlite.ts";
30
+ import type { Listener, ReadonlyStore, Unsubscribe } from "../Store.ts";
31
+ import { createStore } from "../Store.ts";
32
+ import type { Task } from "../Task.ts";
38
33
  import {
39
34
  brand,
40
35
  createId,
41
36
  createIdFromString,
37
+ type ExtractTyped,
38
+ type Id,
42
39
  Name,
40
+ type TypeError,
43
41
  UrlSafeString,
44
- } from "../Type.js";
45
- import type { ExtractType } from "../Types.js";
42
+ } from "../Type.ts";
46
43
  import type {
47
44
  CreateBroadcastChannelDep,
48
45
  CreateMessageChannelDep,
49
- } from "../Worker.js";
50
- import type { CreateDbWorkerDep } from "./Db.js";
51
- import type { EvoluError } from "./Error.js";
46
+ } from "../Worker.ts";
47
+ import type { CreateDbWorkerDep } from "./Db.ts";
48
+ import type { EvoluError } from "./Error.ts";
52
49
  import type {
53
50
  AppOwner,
54
51
  Owner,
@@ -56,8 +53,8 @@ import type {
56
53
  OwnerTransport,
57
54
  ReadonlyOwner,
58
55
  SyncOwner,
59
- } from "./Owner.js";
60
- import { createOwnerWebSocketTransport } from "./Owner.js";
56
+ } from "./Owner.ts";
57
+ import { createOwnerWebSocketTransport } from "./Owner.ts";
61
58
  import type {
62
59
  Queries,
63
60
  QueriesToQueryRowsPromises,
@@ -65,25 +62,25 @@ import type {
65
62
  QueryRows,
66
63
  Row,
67
64
  RowsByQueryMap,
68
- } from "./Query.js";
69
- import { applyPatches } from "./Query.js";
65
+ } from "./Query.ts";
66
+ import { applyPatches } from "./Query.ts";
70
67
  import type {
71
68
  EvoluSchema,
72
69
  IndexesConfig,
73
70
  Mutation,
74
71
  MutationChange,
75
72
  ValidateSchema,
76
- } from "./Schema.js";
77
- import { evoluSchemaToSqliteSchema } from "./Schema.js";
73
+ } from "./Schema.ts";
74
+ import { evoluSchemaToSqliteSchema } from "./Schema.ts";
78
75
  import type {
79
76
  ConsoleEntryOrError,
80
77
  EvoluInput,
81
78
  EvoluOutput,
82
79
  SharedWorkerDep,
83
- } from "./Shared.js";
84
- import { consoleEntryOrErrorBroadcastChannelName } from "./Shared.js";
85
- import { DbChange } from "./Storage.js";
86
- import type { Timestamp } from "./Timestamp.js";
80
+ } from "./Shared.ts";
81
+ import { consoleEntryOrErrorBroadcastChannelName } from "./Shared.ts";
82
+ import { DbChange } from "./Storage.ts";
83
+ import type { Timestamp } from "./Timestamp.ts";
87
84
 
88
85
  export interface EvoluConfig {
89
86
  /**
@@ -100,7 +97,10 @@ export interface EvoluConfig {
100
97
  * ### Example
101
98
  *
102
99
  * ```ts
103
- * // appName: AppName.orThrow("MyApp")
100
+ * import { AppName } from "@evolu/common";
101
+ *
102
+ * const appName = AppName.orThrow("MyApp");
103
+ * expect(appName).toBe("MyApp");
104
104
  * ```
105
105
  */
106
106
  readonly appName: AppName;
@@ -108,20 +108,20 @@ export interface EvoluConfig {
108
108
  /**
109
109
  * {@link AppOwner} used to create this {@link Evolu} instance.
110
110
  *
111
- * Exposed as {@link Evolu.appOwner}. If `appOwner` is not passed, Evolu
112
- * creates one.
111
+ * Exposed as {@link Evolu.appOwner}. Create an AppOwner before the first run,
112
+ * or restore one from secure storage before creating Evolu.
113
113
  *
114
114
  * AppOwner controls access to the encrypted local SQLite database. If its
115
115
  * secret material (Owner secret / Mnemonic) is not stored safely, data
116
116
  * written by that instance is permanently inaccessible.
117
117
  *
118
- * Best onboarding UX is accountless first use: let users try a ready-to-use
119
- * app, then prompt backup of `evolu.appOwner`.
118
+ * Best onboarding UX is accountless first use: create an AppOwner, let users
119
+ * try a ready-to-use app, then prompt backup of `evolu.appOwner`.
120
120
  *
121
121
  * Recommended usage:
122
122
  *
123
- * - Omit `appOwner` for first run, then persist `evolu.appOwner` after user
124
- * activity and guide the user to back it up.
123
+ * - Create and persist an `appOwner` for first run, then guide the user to back
124
+ * it up after user activity.
125
125
  * - Pass `appOwner` restored from secure storage (for example, Expo
126
126
  * SecureStore, WebAuthn-backed storage, or app-managed account recovery
127
127
  * flow).
@@ -158,33 +158,51 @@ export interface EvoluConfig {
158
158
  *
159
159
  * The default value is:
160
160
  *
161
- * `{ type: "WebSocket", url: "wss://free.evoluhq.com" }`.
161
+ * `[{ type: "WebSocket", url: "wss://free.evoluhq.com" }]`.
162
162
  *
163
163
  * ### Example
164
164
  *
165
165
  * ```ts
166
- * // Single WebSocket relay
167
- * transports: [{ type: "WebSocket", url: "wss://relay1.example.com" }];
166
+ * import {
167
+ * createAppOwner,
168
+ * createOwnerWebSocketTransport,
169
+ * createOwnerSecret,
170
+ * createRandomBytes,
171
+ * type OwnerTransport,
172
+ * } from "@evolu/common";
173
+ *
174
+ * // Create once, persist the mnemonic securely, and restore it on later runs.
175
+ * const appOwner = createAppOwner(
176
+ * createOwnerSecret({ randomBytes: createRandomBytes() }),
177
+ * );
178
+ *
179
+ * // Use one relay.
180
+ * const singleRelay = [
181
+ * { type: "WebSocket", url: "wss://relay1.example.com" },
182
+ * ] satisfies ReadonlyArray<OwnerTransport>;
168
183
  *
169
- * // Multiple WebSocket relays for redundancy
170
- * transports: [
184
+ * // Use independent relays for redundancy.
185
+ * const redundantRelays = [
171
186
  * { type: "WebSocket", url: "wss://relay1.example.com" },
172
187
  * { type: "WebSocket", url: "wss://relay2.example.com" },
173
- * { type: "WebSocket", url: "wss://relay3.example.com" },
174
- * ];
188
+ * ] satisfies ReadonlyArray<OwnerTransport>;
175
189
  *
176
- * // Local-only instance (no sync) - useful for device settings or when relay
177
- * // URL will be provided later (e.g., after authentication), allowing users
178
- * // to work offline before the app connects
179
- * transports: [];
190
+ * // Start local-only before authentication. After authentication, pass an
191
+ * // owner-scoped transport to evolu.useOwner.
192
+ * const localOnlyBeforeAuthentication =
193
+ * [] satisfies ReadonlyArray<OwnerTransport>;
180
194
  *
181
- * // Using createOwnerWebSocketTransport helper for relay authentication
182
- * transports: [
195
+ * // Include the OwnerId when the relay authenticates owners.
196
+ * const authenticatedRelay = [
183
197
  * createOwnerWebSocketTransport({
184
- * url: "ws://localhost:4000",
185
- * ownerId,
198
+ * url: "wss://relay.example.com",
199
+ * ownerId: appOwner.id,
186
200
  * }),
187
201
  * ];
202
+ *
203
+ * expect(authenticatedRelay[0]?.url).toBe(
204
+ * `wss://relay.example.com?ownerId=${appOwner.id}`,
205
+ * );
188
206
  * ```
189
207
  */
190
208
  readonly transports?: ReadonlyArray<OwnerTransport>;
@@ -211,7 +229,28 @@ export interface EvoluConfig {
211
229
  * ### Example
212
230
  *
213
231
  * ```ts
214
- * const evolu = createEvolu(evoluReactDeps)(Schema, {
232
+ * import {
233
+ * AppName,
234
+ * createAppOwner,
235
+ * createEvolu,
236
+ * createOwnerSecret,
237
+ * createRandomBytes,
238
+ * id,
239
+ * } from "@evolu/common";
240
+ *
241
+ * const Schema = {
242
+ * todo: { id: id("Todo") },
243
+ * todoCategory: { id: id("TodoCategory") },
244
+ * };
245
+ * // Create once, persist the mnemonic securely, and restore it on later runs.
246
+ * const appOwner = createAppOwner(
247
+ * createOwnerSecret({ randomBytes: createRandomBytes() }),
248
+ * );
249
+ *
250
+ * const createTodoEvolu = createEvolu(Schema, {
251
+ * appName: AppName.orThrow("IndexedTodos"),
252
+ * appOwner,
253
+ * transports: [],
215
254
  * indexes: (create) => [
216
255
  * create("todoCreatedAt").on("todo").column("createdAt"),
217
256
  * create("todoCategoryCreatedAt")
@@ -219,6 +258,8 @@ export interface EvoluConfig {
219
258
  * .column("createdAt"),
220
259
  * ],
221
260
  * });
261
+ *
262
+ * expect(createTodoEvolu).toBeTypeOf("function");
222
263
  * ```
223
264
  */
224
265
  readonly indexes?: IndexesConfig;
@@ -250,13 +291,20 @@ export interface EvoluConfig {
250
291
  * Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
251
292
  * `_`) and must be between 1 and 41 characters.
252
293
  */
253
- export const AppName = /*#__PURE__*/ brand("AppName", UrlSafeString, (value) =>
254
- value.length >= 1 && value.length <= 41
255
- ? ok(value)
256
- : err<AppNameError>({ type: "AppName", value }),
294
+ export const AppName = /*#__PURE__*/ brand(
295
+ "AppName",
296
+ UrlSafeString,
297
+ (value) =>
298
+ value.length >= 1 && value.length <= 41
299
+ ? ok()
300
+ : err<AppNameError>({ type: "AppName", value }),
301
+ (error) =>
302
+ `The value ${JSON.stringify(error.value)} is not between 1 and 41 characters.`,
257
303
  );
258
- export type AppName = typeof AppName.Type;
259
- export interface AppNameError extends TypeError<"AppName"> {}
304
+ export type AppName = typeof AppName.Output;
305
+ export interface AppNameError extends TypeError<"AppName"> {
306
+ readonly value: UrlSafeString;
307
+ }
260
308
 
261
309
  export const testAppName = /*#__PURE__*/ AppName.orThrow("AppName");
262
310
 
@@ -285,19 +333,45 @@ export interface Evolu<
285
333
  * every row has a globally unique, conflict-free identifier without
286
334
  * coordination.
287
335
  *
336
+ * Pass `onComplete` when follow-up work must wait until the mutation and its
337
+ * query patches have been applied.
338
+ *
288
339
  * ### Example
289
340
  *
290
341
  * ```ts
291
- * const { id } = evolu.insert("todo", {
292
- * title: NonEmptyString100.orThrow("Learn Evolu"),
293
- * });
294
- *
295
- * // With onComplete callback
296
- * evolu.insert(
297
- * "todo",
298
- * { title: NonEmptyString100.orThrow("Another todo") },
299
- * { onComplete: () => console.log("Insert completed") },
300
- * );
342
+ * import {
343
+ * id,
344
+ * NonEmptyTrimmedString100,
345
+ * type Evolu,
346
+ * } from "@evolu/common";
347
+ *
348
+ * const TodoId = id("Todo");
349
+ * type TodoId = typeof TodoId.Output;
350
+ * const Schema = {
351
+ * todo: { id: TodoId, title: NonEmptyTrimmedString100 },
352
+ * };
353
+ *
354
+ * const insertTodo = (evolu: Evolu<typeof Schema>) =>
355
+ * evolu.insert("todo", {
356
+ * title: NonEmptyTrimmedString100.orThrow("Learn Evolu"),
357
+ * }).id;
358
+ *
359
+ * const insertTodoAndNotify = (
360
+ * evolu: Evolu<typeof Schema>,
361
+ * onComplete: () => void,
362
+ * ) =>
363
+ * evolu.insert(
364
+ * "todo",
365
+ * { title: NonEmptyTrimmedString100.orThrow("Another todo") },
366
+ * { onComplete },
367
+ * ).id;
368
+ *
369
+ * expectTypeOf<
370
+ * [
371
+ * ReturnType<typeof insertTodo>,
372
+ * ReturnType<typeof insertTodoAndNotify>,
373
+ * ]
374
+ * >().toEqualTypeOf<[TodoId, TodoId]>();
301
375
  * ```
302
376
  *
303
377
  * @see {@link Mutation}
@@ -312,13 +386,31 @@ export interface Evolu<
312
386
  * ### Example
313
387
  *
314
388
  * ```ts
315
- * const { id } = evolu.update("todo", {
316
- * id: todoId,
317
- * title: NonEmptyString100.orThrow("Updated title"),
318
- * });
319
- *
320
- * // Soft delete
321
- * evolu.update("todo", { id: todoId, isDeleted: sqliteTrue });
389
+ * import {
390
+ * id,
391
+ * NonEmptyTrimmedString100,
392
+ * sqliteTrue,
393
+ * type Evolu,
394
+ * } from "@evolu/common";
395
+ *
396
+ * const TodoId = id("Todo");
397
+ * type TodoId = typeof TodoId.Output;
398
+ * const Schema = {
399
+ * todo: { id: TodoId, title: NonEmptyTrimmedString100 },
400
+ * };
401
+ *
402
+ * const renameTodo = (evolu: Evolu<typeof Schema>, todoId: TodoId) =>
403
+ * evolu.update("todo", {
404
+ * id: todoId,
405
+ * title: NonEmptyTrimmedString100.orThrow("Updated title"),
406
+ * }).id;
407
+ *
408
+ * const softDeleteTodo = (evolu: Evolu<typeof Schema>, todoId: TodoId) =>
409
+ * evolu.update("todo", { id: todoId, isDeleted: sqliteTrue }).id;
410
+ *
411
+ * expectTypeOf<
412
+ * [ReturnType<typeof renameTodo>, ReturnType<typeof softDeleteTodo>]
413
+ * >().toEqualTypeOf<[TodoId, TodoId]>();
322
414
  * ```
323
415
  *
324
416
  * @see {@link Mutation}
@@ -340,11 +432,26 @@ export interface Evolu<
340
432
  * ### Example
341
433
  *
342
434
  * ```ts
343
- * const stableId = createIdFromString("my-todo-1");
344
- * const { id } = evolu.upsert("todo", {
345
- * id: stableId,
346
- * title: NonEmptyString100.orThrow("Learn Evolu"),
347
- * });
435
+ * import {
436
+ * createIdFromString,
437
+ * id,
438
+ * NonEmptyTrimmedString100,
439
+ * type Evolu,
440
+ * } from "@evolu/common";
441
+ *
442
+ * const TodoId = id("Todo");
443
+ * type TodoId = typeof TodoId.Output;
444
+ * const Schema = {
445
+ * todo: { id: TodoId, title: NonEmptyTrimmedString100 },
446
+ * };
447
+ * const stableId = TodoId.orThrow(createIdFromString("my-todo-1"));
448
+ * const upsertTodo = (evolu: Evolu<typeof Schema>) =>
449
+ * evolu.upsert("todo", {
450
+ * id: stableId,
451
+ * title: NonEmptyTrimmedString100.orThrow("Learn Evolu"),
452
+ * }).id;
453
+ *
454
+ * expectTypeOf(upsertTodo).returns.toEqualTypeOf<TodoId>();
348
455
  * ```
349
456
  *
350
457
  * @see {@link Mutation}
@@ -369,13 +476,29 @@ export interface Evolu<
369
476
  * ### Example
370
477
  *
371
478
  * ```ts
479
+ * import {
480
+ * createQueryBuilder,
481
+ * id,
482
+ * NonEmptyTrimmedString100,
483
+ * type Evolu,
484
+ * type QueryRows,
485
+ * } from "@evolu/common";
486
+ *
487
+ * const Schema = {
488
+ * todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
489
+ * };
372
490
  * const createQuery = createQueryBuilder(Schema);
373
491
  * const allTodos = createQuery((db) =>
374
492
  * db.selectFrom("todo").selectAll(),
375
493
  * );
376
- * evolu.loadQuery(allTodos).then((rows) => {
377
- * console.log(rows);
378
- * });
494
+ * const loadTodos = async (evolu: Evolu<typeof Schema>) => {
495
+ * const rows = await evolu.loadQuery(allTodos);
496
+ * return rows;
497
+ * };
498
+ *
499
+ * expectTypeOf(loadTodos).returns.toEqualTypeOf<
500
+ * Promise<QueryRows<typeof allTodos.Row>>
501
+ * >();
379
502
  * ```
380
503
  */
381
504
  readonly loadQuery: <R extends Row>(
@@ -383,14 +506,47 @@ export interface Evolu<
383
506
  ) => Promise<QueryRows<R>>;
384
507
 
385
508
  /**
386
- * Load an array of {@link Query} queries and return an array of
509
+ * Load an array of {@link Query} values and return an array of
387
510
  * {@link QueryRows} promises. It's like `queries.map(loadQuery)` but with
388
511
  * proper types for returned promises.
389
512
  *
390
513
  * ### Example
391
514
  *
392
515
  * ```ts
393
- * evolu.loadQueries([allTodos, todoById(1)]);
516
+ * import {
517
+ * createIdFromString,
518
+ * createQueryBuilder,
519
+ * id,
520
+ * NonEmptyTrimmedString100,
521
+ * type Evolu,
522
+ * type QueryRows,
523
+ * } from "@evolu/common";
524
+ *
525
+ * const TodoId = id("Todo");
526
+ * type TodoId = typeof TodoId.Output;
527
+ * const Schema = {
528
+ * todo: { id: TodoId, title: NonEmptyTrimmedString100 },
529
+ * };
530
+ * const createQuery = createQueryBuilder(Schema);
531
+ * const allTodos = createQuery((db) =>
532
+ * db.selectFrom("todo").select(["id", "title"]),
533
+ * );
534
+ * const todoById = (todoId: TodoId) =>
535
+ * createQuery((db) =>
536
+ * db.selectFrom("todo").select("title").where("id", "=", todoId),
537
+ * );
538
+ * const firstTodo = todoById(
539
+ * TodoId.orThrow(createIdFromString("first-todo")),
540
+ * );
541
+ * const loadTodoQueries = (evolu: Evolu<typeof Schema>) =>
542
+ * evolu.loadQueries([allTodos, firstTodo]);
543
+ *
544
+ * expectTypeOf(loadTodoQueries).returns.toEqualTypeOf<
545
+ * [
546
+ * Promise<QueryRows<typeof allTodos.Row>>,
547
+ * Promise<QueryRows<typeof firstTodo.Row>>,
548
+ * ]
549
+ * >();
394
550
  * ```
395
551
  */
396
552
  readonly loadQueries: <Q extends Queries<S>>(
@@ -403,9 +559,31 @@ export interface Evolu<
403
559
  * ### Example
404
560
  *
405
561
  * ```ts
406
- * const unsubscribe = evolu.subscribeQuery(allTodos)(() => {
407
- * const rows = evolu.getQueryRows(allTodos);
408
- * });
562
+ * import {
563
+ * createQueryBuilder,
564
+ * id,
565
+ * NonEmptyTrimmedString100,
566
+ * type Evolu,
567
+ * type QueryRows,
568
+ * type Unsubscribe,
569
+ * } from "@evolu/common";
570
+ *
571
+ * const Schema = {
572
+ * todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
573
+ * };
574
+ * const createQuery = createQueryBuilder(Schema);
575
+ * const allTodos = createQuery((db) =>
576
+ * db.selectFrom("todo").select("title"),
577
+ * );
578
+ * const subscribeToTodos = (
579
+ * evolu: Evolu<typeof Schema>,
580
+ * onRows: (rows: QueryRows<typeof allTodos.Row>) => void,
581
+ * ) =>
582
+ * evolu.subscribeQuery(allTodos)(() => {
583
+ * onRows(evolu.getQueryRows(allTodos));
584
+ * });
585
+ *
586
+ * expectTypeOf(subscribeToTodos).returns.toEqualTypeOf<Unsubscribe>();
409
587
  * ```
410
588
  */
411
589
  readonly subscribeQuery: (
@@ -418,9 +596,27 @@ export interface Evolu<
418
596
  * ### Example
419
597
  *
420
598
  * ```ts
421
- * const unsubscribe = evolu.subscribeQuery(allTodos)(() => {
422
- * const rows = evolu.getQueryRows(allTodos);
423
- * });
599
+ * import {
600
+ * createQueryBuilder,
601
+ * id,
602
+ * NonEmptyTrimmedString100,
603
+ * type Evolu,
604
+ * type QueryRows,
605
+ * } from "@evolu/common";
606
+ *
607
+ * const Schema = {
608
+ * todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
609
+ * };
610
+ * const createQuery = createQueryBuilder(Schema);
611
+ * const allTodos = createQuery((db) =>
612
+ * db.selectFrom("todo").select("title"),
613
+ * );
614
+ * const getTodos = (evolu: Evolu<typeof Schema>) =>
615
+ * evolu.getQueryRows(allTodos);
616
+ *
617
+ * expectTypeOf(getTodos).returns.toEqualTypeOf<
618
+ * QueryRows<typeof allTodos.Row>
619
+ * >();
424
620
  * ```
425
621
  */
426
622
  readonly getQueryRows: <R extends Row>(query: Query<S, R>) => QueryRows<R>;
@@ -452,6 +648,8 @@ export interface Evolu<
452
648
  *
453
649
  * All instances identified by {@link Evolu.name} will be self-disposed. Use
454
650
  * {@link EvoluConfig.onDatabaseDeleted} to update app UI when that happens.
651
+ *
652
+ * Currently not implemented.
455
653
  */
456
654
  readonly deleteDatabase: () => void;
457
655
 
@@ -463,6 +661,8 @@ export interface Evolu<
463
661
  *
464
662
  * It also stops using that owner across all tabs and instances. Use
465
663
  * {@link EvoluConfig.onOwnerDeleted} to update app UI when that happens.
664
+ *
665
+ * Currently not implemented.
466
666
  */
467
667
  readonly deleteOwner: (owner: Owner) => void;
468
668
 
@@ -482,20 +682,51 @@ export interface Evolu<
482
682
  * ### Example
483
683
  *
484
684
  * ```ts
485
- * // Use an Owner (starts syncing).
486
- * const unuseOwner = evolu.useOwner(shardOwner, [
487
- * createOwnerWebSocketTransport({
488
- * url: "ws://localhost:4000",
489
- * ownerId: shardOwner.id,
490
- * }),
491
- * ]);
492
- *
493
- * // Later, stop using the Owner.
494
- * unuseOwner();
495
- *
496
- * // Bulk operations.
497
- * const unuseOwners = owners.map(evolu.useOwner);
498
- * // Later: for (const unuse of unuseOwners) unuse();
685
+ * import {
686
+ * createAppOwner,
687
+ * createOwnerWebSocketTransport,
688
+ * createOwnerSecret,
689
+ * createRandomBytes,
690
+ * deriveShardOwner,
691
+ * type Evolu,
692
+ * type ReadonlyOwner,
693
+ * type UnuseOwner,
694
+ * } from "@evolu/common";
695
+ *
696
+ * // Create once, persist the mnemonic securely, and restore it on later runs.
697
+ * const appOwner = createAppOwner(
698
+ * createOwnerSecret({ randomBytes: createRandomBytes() }),
699
+ * );
700
+ * const shardOwner = deriveShardOwner(appOwner, ["todos", 1]);
701
+ * const shardTransport = createOwnerWebSocketTransport({
702
+ * url: "wss://relay.example.com",
703
+ * ownerId: shardOwner.id,
704
+ * });
705
+ * const useShardOwner = (evolu: Evolu): UnuseOwner =>
706
+ * evolu.useOwner(shardOwner, [shardTransport]);
707
+ *
708
+ * const useOwners = (
709
+ * evolu: Evolu,
710
+ * owners: ReadonlyArray<ReadonlyOwner>,
711
+ * ): UnuseOwner => {
712
+ * const unuseOwners = owners.map((owner) =>
713
+ * evolu.useOwner(owner, [
714
+ * createOwnerWebSocketTransport({
715
+ * url: "wss://relay.example.com",
716
+ * ownerId: owner.id,
717
+ * }),
718
+ * ]),
719
+ * );
720
+ * return () => {
721
+ * for (const unuse of unuseOwners) unuse();
722
+ * };
723
+ * };
724
+ *
725
+ * // Call each returned UnuseOwner once to stop syncing.
726
+ * expect(shardTransport).toEqual({
727
+ * type: "WebSocket",
728
+ * url: `wss://relay.example.com?ownerId=${shardOwner.id}`,
729
+ * });
499
730
  * ```
500
731
  */
501
732
  readonly useOwner: (
@@ -512,18 +743,28 @@ export interface EvoluErrorDep {
512
743
  * {@link ReadonlyStore} of {@link EvoluError} shared by all {@link Evolu}
513
744
  * instances created from the same {@link createEvoluDeps} result.
514
745
  *
515
- * Subscribe once to show user-facing messages across all instances. Logging
516
- * is handled by platform {@link createRun} global error handlers.
746
+ * Subscribe once to show user-facing error messages across all instances.
517
747
  *
518
748
  * ### Example
519
749
  *
520
750
  * ```ts
751
+ * import { createStore, Millis, type EvoluError } from "@evolu/common";
752
+ * import type { EvoluErrorDep } from "@evolu/common/local-first";
753
+ *
754
+ * // Stand-in for run.deps.evoluError from createEvoluDeps.
755
+ * using evoluError = createStore<EvoluError | null>(null);
756
+ * const deps = { evoluError } satisfies EvoluErrorDep;
757
+ * let displayedMessage = "";
758
+ * const showMessage = (message: string) => {
759
+ * displayedMessage = message;
760
+ * };
761
+ *
521
762
  * deps.evoluError.subscribe(() => {
522
763
  * const error = deps.evoluError.get();
523
764
  * if (!error) return;
524
765
  *
525
766
  * switch (error.type) {
526
- * case "InvalidComputerClock":
767
+ * case "TimestampDriftError":
527
768
  * // Show guidance specific to the detected error.
528
769
  * showMessage(
529
770
  * "Your system clock appears incorrect. Please fix it.",
@@ -534,6 +775,15 @@ export interface EvoluErrorDep {
534
775
  * showMessage("Something went wrong. Please try again.");
535
776
  * }
536
777
  * });
778
+ *
779
+ * deps.evoluError.set({
780
+ * type: "TimestampDriftError",
781
+ * next: Millis.orThrow(360000),
782
+ * now: Millis.orThrow(0),
783
+ * });
784
+ * expect(displayedMessage).toBe(
785
+ * "Your system clock appears incorrect. Please fix it.",
786
+ * );
537
787
  * ```
538
788
  */
539
789
  readonly evoluError: ReadonlyStore<EvoluError | null>;
@@ -545,7 +795,10 @@ export interface EvoluErrorDep {
545
795
  * Includes platform adapters, the shared {@link EvoluErrorDep.evoluError} store,
546
796
  * and disposal for owned resources.
547
797
  */
548
- export type EvoluDeps = EvoluPlatformDeps & EvoluErrorDep & Disposable;
798
+ export type EvoluDeps = EvoluPlatformDeps &
799
+ ConsoleDep &
800
+ EvoluErrorDep &
801
+ Disposable;
549
802
 
550
803
  /**
551
804
  * Platform-specific dependencies required to create {@link EvoluDeps}.
@@ -620,13 +873,14 @@ export const createEvoluDeps = (deps: EvoluPlatformDeps): EvoluDeps => {
620
873
  }),
621
874
  );
622
875
 
623
- const disposables = disposer.move();
624
-
625
- return {
626
- ...deps,
627
- evoluError,
628
- [Symbol.dispose]: () => disposables.dispose(),
629
- };
876
+ return disposable<EvoluDeps>(
877
+ {
878
+ ...deps,
879
+ console,
880
+ evoluError,
881
+ },
882
+ disposer,
883
+ );
630
884
  };
631
885
 
632
886
  /**
@@ -745,52 +999,13 @@ export const createEvolu =
745
999
  exportDatabasePending = null;
746
1000
  });
747
1001
 
748
- const mutateBatch = disposer.use(
749
- createMicrotaskBatch<{
750
- readonly change: MutationChange;
751
- readonly onComplete: (() => void) | undefined;
752
- }>((items) => {
753
- console.debug("mutateBatch", { changeCount: items.length });
754
- releaseUnsubscribedLoadingPromises();
755
-
756
- postMessage({
757
- type: "Mutate",
758
- changes: mapArray(items, (item) => item.change),
759
- onCompleteIds: items.flatMap((item) =>
760
- item.onComplete
761
- ? [onMutateCompleteCallbacks.register(item.onComplete)]
762
- : [],
763
- ),
764
- subscribedQueries: subscribedQueriesRefCount.keys(),
765
- });
766
- }),
767
- );
768
-
769
- const queryBatch = disposer.use(
770
- createMicrotaskBatch<Query>((queries) => {
771
- const dedupedQueries = new Set(queries);
772
- assert(
773
- isNonEmptySet(dedupedQueries),
774
- "Expected non-empty query batch.",
775
- );
776
- console.debug("queryBatch", { queryCount: dedupedQueries.size });
777
- postMessage({ type: "Query", queries: dedupedQueries });
778
- }),
779
- );
780
-
781
- const useOwnerBatch = disposer.use(
782
- createMicrotaskBatch<
783
- ExtractType<EvoluInput, "UseOwner">["actions"][number]
784
- >((actions) => postMessage({ type: "UseOwner", actions })),
785
- );
786
-
787
1002
  let postMessage: (input: EvoluInput) => void;
788
1003
 
789
1004
  // Scope worker/channel wiring and keep only postMessage outside.
790
1005
  {
791
1006
  const id = createId<"EvoluInstance">(run.deps);
792
1007
  const { createMessageChannel, sharedWorker } = run.deps;
793
- disposer.use(await run.orThrow(acquireLeaderLock(id)));
1008
+ disposer.use(await run.ok(acquireLeaderLock(id)));
794
1009
 
795
1010
  const evoluChannel = disposer.use(
796
1011
  createMessageChannel<EvoluInput, EvoluOutput>(),
@@ -890,13 +1105,55 @@ export const createEvolu =
890
1105
  postMessage = evoluChannel.port1.postMessage;
891
1106
  }
892
1107
 
1108
+ const mutateBatch = disposer.use(
1109
+ createMicrotaskBatch<{
1110
+ readonly change: MutationChange;
1111
+ readonly onComplete: (() => void) | undefined;
1112
+ }>((items) => {
1113
+ if (disposed) return;
1114
+ console.debug("mutateBatch", { changeCount: items.length });
1115
+ releaseUnsubscribedLoadingPromises();
1116
+
1117
+ postMessage({
1118
+ type: "Mutate",
1119
+ changes: mapArray(items, (item) => item.change),
1120
+ onCompleteIds: items.flatMap((item) =>
1121
+ item.onComplete
1122
+ ? [onMutateCompleteCallbacks.register(item.onComplete)]
1123
+ : [],
1124
+ ),
1125
+ subscribedQueries: subscribedQueriesRefCount.keys(),
1126
+ });
1127
+ }),
1128
+ );
1129
+
1130
+ const queryBatch = disposer.use(
1131
+ createMicrotaskBatch<Query>((queries) => {
1132
+ if (disposed) return;
1133
+ const dedupedQueries = new Set(queries);
1134
+ assert(
1135
+ isNonEmptySet(dedupedQueries),
1136
+ "Expected non-empty query batch.",
1137
+ );
1138
+ console.debug("queryBatch", { queryCount: dedupedQueries.size });
1139
+ postMessage({ type: "Query", queries: dedupedQueries });
1140
+ }),
1141
+ );
1142
+
1143
+ const useOwnerBatch = disposer.use(
1144
+ createMicrotaskBatch<
1145
+ ExtractTyped<EvoluInput, "UseOwner">["actions"][number]
1146
+ >((actions) => {
1147
+ if (disposed) return;
1148
+ postMessage({ type: "UseOwner", actions });
1149
+ }),
1150
+ );
1151
+
893
1152
  const createMutation =
894
1153
  <Kind extends "insert" | "update" | "upsert">(
895
1154
  kind: Kind,
896
1155
  ): Mutation<S, Kind> =>
897
1156
  (table, values, options) => {
898
- assertNotDisposed(disposables);
899
-
900
1157
  const {
901
1158
  id = createId(run.deps),
902
1159
  isDeleted,
@@ -929,8 +1186,6 @@ export const createEvolu =
929
1186
  const loadQuery = <R extends Row>(
930
1187
  query: Query<S, R>,
931
1188
  ): Promise<QueryRows<R>> => {
932
- assertNotDisposed(disposables);
933
-
934
1189
  const loadingPromise = loadingPromisesByQuery.get(query);
935
1190
  if (loadingPromise) {
936
1191
  return loadingPromise.promise as Promise<QueryRows<R>>;
@@ -951,19 +1206,13 @@ export const createEvolu =
951
1206
  return typedPromise as Promise<QueryRows<R>>;
952
1207
  };
953
1208
 
954
- const getQueryRows = <R extends Row>(query: Query<S, R>): QueryRows<R> => {
955
- assertNotDisposed(disposables);
956
-
957
- return (rowsByQueryMapStore.get().get(query) ??
958
- emptyArray) as QueryRows<R>;
959
- };
1209
+ const getQueryRows = <R extends Row>(query: Query<S, R>): QueryRows<R> =>
1210
+ (rowsByQueryMapStore.get().get(query) ?? emptyArray) as QueryRows<R>;
960
1211
 
961
1212
  const useOwner = (
962
1213
  owner: ReadonlyOwner | Owner,
963
1214
  ownerTransports?: NonEmptyReadonlyArray<OwnerTransport>,
964
1215
  ): UnuseOwner => {
965
- assertNotDisposed(disposables);
966
-
967
1216
  const effectiveTransports = ownerTransports ?? transports;
968
1217
  assertNonEmptyReadonlyArray(
969
1218
  effectiveTransports,
@@ -983,7 +1232,7 @@ export const createEvolu =
983
1232
  let isUsed = true;
984
1233
 
985
1234
  return () => {
986
- if (disposables.disposed) return;
1235
+ if (disposed) return;
987
1236
  assert(isUsed, "UnuseOwner can be called only once.");
988
1237
  isUsed = false;
989
1238
  useOwnerBatch.push({
@@ -993,91 +1242,87 @@ export const createEvolu =
993
1242
  };
994
1243
  };
995
1244
 
996
- const disposables = disposer.move();
1245
+ let disposed = false;
1246
+ disposer.defer(() => {
1247
+ disposed = true;
1248
+ console.info("disposeEvolu");
1249
+ });
997
1250
 
998
1251
  if (isNonEmptyArray(transports)) useOwner(appOwner);
999
1252
 
1000
- if (run.signal.aborted) {
1001
- await disposables.disposeAsync();
1002
- }
1003
-
1004
- return ok({
1005
- name,
1006
- appOwner,
1007
-
1008
- insert: createMutation("insert"),
1009
- update: createMutation("update"),
1010
- upsert: createMutation("upsert"),
1011
-
1012
- loadQuery,
1013
- loadQueries: <Q extends Queries<S>>(
1014
- queries: [...Q],
1015
- ): [...QueriesToQueryRowsPromises<Q>] =>
1016
- queries.map((query) => loadQuery(query)) as [
1017
- ...QueriesToQueryRowsPromises<Q>,
1018
- ],
1019
-
1020
- subscribeQuery: (query) => (listener) => {
1021
- assertNotDisposed(disposables);
1253
+ run.signal.throwIfAborted();
1022
1254
 
1023
- subscribedQueriesRefCount.increment(query);
1024
- let isSubscribed = true;
1025
-
1026
- let previousRows: unknown = null;
1027
-
1028
- const unsubscribe = rowsByQueryMapStore.subscribe(() => {
1029
- const rows = getQueryRows(query);
1030
- if (previousRows === rows) return;
1031
- previousRows = rows;
1032
- listener();
1033
- });
1034
-
1035
- return () => {
1036
- assert(
1037
- isSubscribed,
1038
- "subscribeQuery unsubscribe can be called only once.",
1039
- );
1040
- isSubscribed = false;
1255
+ return ok(
1256
+ disposable<Evolu<S>>(
1257
+ {
1258
+ name,
1259
+ appOwner,
1260
+
1261
+ insert: createMutation("insert"),
1262
+ update: createMutation("update"),
1263
+ upsert: createMutation("upsert"),
1264
+
1265
+ loadQuery,
1266
+ loadQueries: <Q extends Queries<S>>(
1267
+ queries: [...Q],
1268
+ ): [...QueriesToQueryRowsPromises<Q>] =>
1269
+ queries.map((query) => loadQuery(query)) as [
1270
+ ...QueriesToQueryRowsPromises<Q>,
1271
+ ],
1272
+
1273
+ subscribeQuery: (query) => (listener) => {
1274
+ subscribedQueriesRefCount.increment(query);
1275
+ let isSubscribed = true;
1276
+
1277
+ let previousRows: unknown = null;
1278
+
1279
+ const unsubscribe = rowsByQueryMapStore.subscribe(() => {
1280
+ const rows = getQueryRows(query);
1281
+ if (previousRows === rows) return;
1282
+ previousRows = rows;
1283
+ listener();
1284
+ });
1041
1285
 
1042
- previousRows = null;
1043
- unsubscribe();
1286
+ return () => {
1287
+ assert(
1288
+ isSubscribed,
1289
+ "subscribeQuery unsubscribe can be called only once.",
1290
+ );
1291
+ isSubscribed = false;
1044
1292
 
1045
- if (disposables.disposed) return;
1293
+ previousRows = null;
1046
1294
 
1047
- subscribedQueriesRefCount.decrement(query);
1048
- };
1049
- },
1050
- getQueryRows,
1295
+ if (disposed) return;
1051
1296
 
1052
- exportDatabase: () => {
1053
- assertNotDisposed(disposables);
1297
+ unsubscribe();
1298
+ subscribedQueriesRefCount.decrement(query);
1299
+ };
1300
+ },
1301
+ getQueryRows,
1054
1302
 
1055
- if (!exportDatabasePending) {
1056
- exportDatabasePending =
1057
- Promise.withResolvers<Uint8Array<ArrayBuffer>>();
1058
- postMessage({ type: "Export" });
1059
- }
1060
- return exportDatabasePending.promise;
1061
- },
1303
+ exportDatabase: () => {
1304
+ if (!exportDatabasePending) {
1305
+ exportDatabasePending =
1306
+ Promise.withResolvers<Uint8Array<ArrayBuffer>>();
1307
+ postMessage({ type: "Export" });
1308
+ }
1309
+ return exportDatabasePending.promise;
1310
+ },
1062
1311
 
1063
- deleteDatabase: () => {
1064
- assertNotDisposed(disposables);
1065
- todo();
1066
- },
1312
+ deleteDatabase: () => {
1313
+ todo();
1314
+ },
1067
1315
 
1068
- deleteOwner: (owner) => {
1069
- assertNotDisposed(disposables);
1070
- void owner;
1071
- todo();
1072
- },
1316
+ deleteOwner: (owner) => {
1317
+ void owner;
1318
+ todo();
1319
+ },
1073
1320
 
1074
- useOwner,
1075
-
1076
- [Symbol.asyncDispose]: () => {
1077
- if (!disposables.disposed) console.info("disposeEvolu");
1078
- return disposables.disposeAsync();
1079
- },
1080
- } as Evolu<S>);
1321
+ useOwner,
1322
+ },
1323
+ disposer,
1324
+ ),
1325
+ );
1081
1326
  };
1082
1327
 
1083
1328
  // case "onReset": {