@evolu/common 8.10.0 → 8.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/src/Bytes.d.ts +39 -2
  2. package/dist/src/Bytes.d.ts.map +1 -1
  3. package/dist/src/Bytes.js +50 -2
  4. package/dist/src/Config.d.ts +22 -22
  5. package/dist/src/Config.d.ts.map +1 -1
  6. package/dist/src/Console.d.ts +62 -7
  7. package/dist/src/Console.d.ts.map +1 -1
  8. package/dist/src/Console.js +20 -4
  9. package/dist/src/Crypto.d.ts +76 -4
  10. package/dist/src/Crypto.d.ts.map +1 -1
  11. package/dist/src/Crypto.js +55 -4
  12. package/dist/src/Error.d.ts +45 -0
  13. package/dist/src/Error.d.ts.map +1 -1
  14. package/dist/src/Error.js +69 -0
  15. package/dist/src/Fs.d.ts +92 -18
  16. package/dist/src/Fs.d.ts.map +1 -1
  17. package/dist/src/Fs.js +2 -0
  18. package/dist/src/Identicon.d.ts +2 -2
  19. package/dist/src/Identicon.js +2 -2
  20. package/dist/src/LeakDetector.d.ts +22 -3
  21. package/dist/src/LeakDetector.d.ts.map +1 -1
  22. package/dist/src/LeakDetector.js +12 -2
  23. package/dist/src/LockManager.d.ts +8 -0
  24. package/dist/src/LockManager.d.ts.map +1 -1
  25. package/dist/src/LockManager.js +6 -0
  26. package/dist/src/Object.d.ts.map +1 -1
  27. package/dist/src/Object.js +5 -0
  28. package/dist/src/Platform.d.ts +47 -7
  29. package/dist/src/Platform.d.ts.map +1 -1
  30. package/dist/src/Platform.js +24 -5
  31. package/dist/src/Random.d.ts +25 -2
  32. package/dist/src/Random.d.ts.map +1 -1
  33. package/dist/src/Random.js +14 -2
  34. package/dist/src/Resource.d.ts +156 -1
  35. package/dist/src/Resource.d.ts.map +1 -1
  36. package/dist/src/Resource.js +201 -72
  37. package/dist/src/Schedule.d.ts +11 -10
  38. package/dist/src/Schedule.d.ts.map +1 -1
  39. package/dist/src/Schedule.js +1 -1
  40. package/dist/src/Sqlite.d.ts +132 -16
  41. package/dist/src/Sqlite.d.ts.map +1 -1
  42. package/dist/src/Sqlite.js +63 -9
  43. package/dist/src/Task.d.ts +15 -4
  44. package/dist/src/Task.d.ts.map +1 -1
  45. package/dist/src/Task.js +41 -15
  46. package/dist/src/Test.d.ts +9 -0
  47. package/dist/src/Test.d.ts.map +1 -1
  48. package/dist/src/Test.js +4 -0
  49. package/dist/src/Time.d.ts +106 -9
  50. package/dist/src/Time.d.ts.map +1 -1
  51. package/dist/src/Time.js +55 -4
  52. package/dist/src/Type.d.ts +1455 -1310
  53. package/dist/src/Type.d.ts.map +1 -1
  54. package/dist/src/Type.js +1274 -517
  55. package/dist/src/WebSocket.d.ts +164 -13
  56. package/dist/src/WebSocket.d.ts.map +1 -1
  57. package/dist/src/WebSocket.js +133 -24
  58. package/dist/src/Worker.d.ts +90 -8
  59. package/dist/src/Worker.d.ts.map +1 -1
  60. package/dist/src/Worker.js +28 -2
  61. package/dist/src/index.d.ts +6 -7
  62. package/dist/src/index.d.ts.map +1 -1
  63. package/dist/src/index.js +2 -3
  64. package/dist/src/local-first/Db.d.ts +52 -3
  65. package/dist/src/local-first/Db.d.ts.map +1 -1
  66. package/dist/src/local-first/Db.js +412 -137
  67. package/dist/src/local-first/Evolu.d.ts +412 -213
  68. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  69. package/dist/src/local-first/Evolu.js +181 -18
  70. package/dist/src/local-first/Owner.d.ts +13 -30
  71. package/dist/src/local-first/Owner.d.ts.map +1 -1
  72. package/dist/src/local-first/Owner.js +13 -30
  73. package/dist/src/local-first/Protocol.d.ts +106 -19
  74. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  75. package/dist/src/local-first/Protocol.js +162 -60
  76. package/dist/src/local-first/Query.d.ts +8 -15
  77. package/dist/src/local-first/Query.d.ts.map +1 -1
  78. package/dist/src/local-first/Relay.d.ts.map +1 -1
  79. package/dist/src/local-first/Relay.js +4 -2
  80. package/dist/src/local-first/Schema.d.ts +346 -23
  81. package/dist/src/local-first/Schema.d.ts.map +1 -1
  82. package/dist/src/local-first/Schema.js +214 -17
  83. package/dist/src/local-first/Shared.d.ts +537 -22
  84. package/dist/src/local-first/Shared.d.ts.map +1 -1
  85. package/dist/src/local-first/Shared.js +1437 -234
  86. package/dist/src/local-first/Storage.d.ts +195 -17
  87. package/dist/src/local-first/Storage.d.ts.map +1 -1
  88. package/dist/src/local-first/Storage.js +85 -22
  89. package/dist/src/local-first/Timestamp.d.ts +392 -41
  90. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  91. package/dist/src/local-first/Timestamp.js +403 -81
  92. package/dist/src/local-first/index.d.ts +0 -1
  93. package/dist/src/local-first/index.d.ts.map +1 -1
  94. package/dist/src/local-first/index.js +0 -1
  95. package/package.json +1 -1
  96. package/src/Assert.test.ts +2 -5
  97. package/src/Bytes.test.ts +27 -0
  98. package/src/Bytes.ts +58 -2
  99. package/src/Config.test.ts +2 -6
  100. package/src/Config.ts +133 -133
  101. package/src/Console.ts +62 -7
  102. package/src/Crypto.ts +76 -4
  103. package/src/Eq.test.ts +2 -3
  104. package/src/Error.test.ts +76 -3
  105. package/src/Error.ts +71 -0
  106. package/src/Fs.ts +92 -18
  107. package/src/Identicon.ts +2 -2
  108. package/src/LeakDetector.ts +22 -3
  109. package/src/LockManager.ts +8 -0
  110. package/src/Object.test.ts +27 -12
  111. package/src/Object.ts +5 -0
  112. package/src/Platform.ts +50 -8
  113. package/src/Random.ts +25 -2
  114. package/src/Resource.test.ts +837 -0
  115. package/src/Resource.ts +235 -15
  116. package/src/Schedule.test.ts +50 -12
  117. package/src/Schedule.ts +24 -14
  118. package/src/Sqlite.ts +137 -17
  119. package/src/Task.test.ts +189 -8
  120. package/src/Task.ts +56 -17
  121. package/src/Test.ts +9 -0
  122. package/src/Time.ts +106 -9
  123. package/src/Type.test.ts +946 -1028
  124. package/src/Type.ts +4195 -3136
  125. package/src/Types.test.ts +4 -14
  126. package/src/WebSocket.ts +313 -40
  127. package/src/Worker.ts +90 -8
  128. package/src/index.ts +20 -6
  129. package/src/local-first/Db.ts +644 -339
  130. package/src/local-first/Evolu.test.ts +994 -22
  131. package/src/local-first/Evolu.ts +625 -232
  132. package/src/local-first/Owner.ts +13 -30
  133. package/src/local-first/Protocol.test.ts +634 -10
  134. package/src/local-first/Protocol.ts +255 -109
  135. package/src/local-first/Query.ts +8 -15
  136. package/src/local-first/Relay.ts +4 -2
  137. package/src/local-first/Schema.test.ts +143 -0
  138. package/src/local-first/Schema.ts +376 -26
  139. package/src/local-first/Shared.test.ts +7731 -559
  140. package/src/local-first/Shared.ts +2036 -267
  141. package/src/local-first/Storage.ts +224 -36
  142. package/src/local-first/Timestamp.test.ts +344 -70
  143. package/src/local-first/Timestamp.ts +434 -118
  144. package/src/local-first/index.ts +0 -1
  145. package/dist/src/local-first/Error.d.ts +0 -12
  146. package/dist/src/local-first/Error.d.ts.map +0 -1
  147. package/dist/src/local-first/Error.js +0 -6
  148. package/dist/src/local-first/LocalAuth.d.ts +0 -150
  149. package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
  150. package/dist/src/local-first/LocalAuth.js +0 -179
  151. package/src/local-first/Error.ts +0 -17
  152. package/src/local-first/LocalAuth.ts +0 -457
@@ -5,29 +5,47 @@
5
5
  */
6
6
  import { type NonEmptyReadonlyArray } from "../Array.ts";
7
7
  import type { ConsoleDep } from "../Console.ts";
8
+ import type { DecryptWithXChaCha20Poly1305Error } from "../Crypto.ts";
9
+ import type { UnknownError } from "../Error.ts";
8
10
  import { type LockManagerDep } from "../LockManager.ts";
9
11
  import type { FlushSyncDep, ReloadAppDep } from "../Platform.ts";
10
12
  import type { Listener, ReadonlyStore, Unsubscribe } from "../Store.ts";
11
13
  import type { Task } from "../Task.ts";
12
- import { Name, type TypeError, UrlSafeString } from "../Type.ts";
14
+ import { Name, PositiveInt, type TypeError, UrlSafeString } from "../Type.ts";
13
15
  import type { CreateBroadcastChannelDep, CreateMessageChannelDep } from "../Worker.ts";
14
- import type { CreateDbWorkerDep } from "./Db.ts";
15
- import type { EvoluError } from "./Error.ts";
16
- import type { AppOwner, Owner, OwnerTransport, ReadonlyOwner } from "./Owner.ts";
16
+ import type { CreateDbWorkerDep, UnsupportedDbVersionError } from "./Db.ts";
17
+ import type { AppOwner, Owner, OwnerId, OwnerTransport, ReadonlyOwner } from "./Owner.ts";
18
+ import { type ProtocolError } from "./Protocol.ts";
17
19
  import type { Queries, QueriesToQueryRowsPromises, Query, QueryRows, Row } from "./Query.ts";
18
- import type { EvoluSchema, IndexesConfig, Mutation, ValidateSchema } from "./Schema.ts";
19
- import type { SharedWorkerDep } from "./Shared.ts";
20
+ import type { EvoluSchema, IndexesConfig, Mutation, MutationValues, ValidateSchema } from "./Schema.ts";
21
+ import type { OtherBuildRunningError, SharedWorkerDep, SyncState } from "./Shared.ts";
22
+ import { type StorageQuotaError } from "./Storage.ts";
23
+ /**
24
+ * Configuration for {@link createEvolu}.
25
+ *
26
+ * @group Configuration
27
+ */
20
28
  export interface EvoluConfig {
21
29
  /**
22
- * The app name. Evolu is multitenant - it can run multiple instances
23
- * concurrently. The same app can have multiple instances for different
24
- * accounts.
30
+ * An application name used in logs and local database names.
31
+ *
32
+ * Evolu combines `appName` with the {@link AppOwner} identity to identify the
33
+ * local database. Changing either opens a different database. Keep `appName`
34
+ * stable across ordinary application updates.
25
35
  *
26
- * Evolu derives the final instance name from `appName` and `appOwner` in
27
- * {@link EvoluConfig}. The derived instance name is used as the SQLite
28
- * database filename and as the log prefix. This ensures that each
29
- * {@link Owner} gets a separate local database while preserving a readable app
30
- * prefix.
36
+ * Instances that share an `appName` and AppOwner open the same database and
37
+ * must use the same schema. Evolu applies the schema of the first instance
38
+ * and later instances join it.
39
+ *
40
+ * A different app name lets you create a separate local replica for the same
41
+ * AppOwner—for example, to test a different SQLite implementation or index
42
+ * configuration, or rebuild a replica for debugging while preserving the
43
+ * existing database.
44
+ *
45
+ * Evolu supports running these databases concurrently. Their local separation
46
+ * does not isolate synchronization: replicas synchronizing the same owners
47
+ * through the same relay can still exchange changes. Changing `appName`
48
+ * neither migrates existing local data nor isolates incompatible schemas.
31
49
  *
32
50
  * ### Example
33
51
  *
@@ -61,6 +79,69 @@ export interface EvoluConfig {
61
79
  * flow).
62
80
  */
63
81
  readonly appOwner: AppOwner;
82
+ /**
83
+ * Keep the database in memory instead of persisting it on this device.
84
+ *
85
+ * This option controls device persistence independently of synchronization.
86
+ * When synchronization is enabled, data can still be synchronized and
87
+ * persisted remotely.
88
+ *
89
+ * Useful for testing or temporary sessions. Data that exists only in this
90
+ * database is lost when the database closes.
91
+ *
92
+ * The default value is: `false`.
93
+ */
94
+ readonly memoryOnly?: boolean;
95
+ /**
96
+ * Use the `indexes` option to define SQLite indexes.
97
+ *
98
+ * Table and column names are not typed because Kysely doesn't support it.
99
+ *
100
+ * https://medium.com/@JasonWyatt/squeezing-performance-from-sqlite-indexes-indexes-c4e175f3c346
101
+ *
102
+ * ### Example
103
+ *
104
+ * ```ts
105
+ * import {
106
+ * createEvolu,
107
+ * id,
108
+ * testAppName,
109
+ * testAppOwner,
110
+ * } from "@evolu/common";
111
+ *
112
+ * const Schema = {
113
+ * todo: { id: id("Todo") },
114
+ * todoCategory: { id: id("TodoCategory") },
115
+ * };
116
+ *
117
+ * const _createTodoEvolu = createEvolu(Schema, {
118
+ * appName: testAppName,
119
+ * appOwner: testAppOwner,
120
+ * transports: [],
121
+ * indexes: (create) => [
122
+ * create("todoCreatedAt").on("todo").column("createdAt"),
123
+ * create("todoCategoryCreatedAt")
124
+ * .on("todoCategory")
125
+ * .column("createdAt"),
126
+ * ],
127
+ * });
128
+ * ```
129
+ */
130
+ readonly indexes?: IndexesConfig;
131
+ /**
132
+ * Called when this instance's local database is deleted.
133
+ *
134
+ * Apps can use this to update UI immediately because the corresponding
135
+ * {@link Evolu} instance becomes unusable after local database deletion.
136
+ */
137
+ readonly onDatabaseDeleted?: () => void;
138
+ /**
139
+ * Called when local data for an {@link Owner} is deleted.
140
+ *
141
+ * Apps can use this to update UI immediately because that owner stops being
142
+ * used across tabs and instances.
143
+ */
144
+ readonly onOwnerDeleted?: (owner: Owner) => void;
64
145
  /**
65
146
  * Transport configuration for sync and backup.
66
147
  *
@@ -98,18 +179,11 @@ export interface EvoluConfig {
98
179
  * ```ts
99
180
  * import {
100
181
  * assertEqual,
101
- * createAppOwner,
102
182
  * createOwnerWebSocketTransport,
103
- * createOwnerSecret,
104
- * createRandomBytes,
183
+ * testAppOwner,
105
184
  * type OwnerTransport,
106
185
  * } from "@evolu/common";
107
186
  *
108
- * // Create once, persist the mnemonic securely, and restore it on later runs.
109
- * const appOwner = createAppOwner(
110
- * createOwnerSecret({ randomBytes: createRandomBytes() }),
111
- * );
112
- *
113
187
  * // Use one relay.
114
188
  * const _singleRelay = [
115
189
  * { type: "WebSocket", url: "wss://relay1.example.com" },
@@ -130,87 +204,17 @@ export interface EvoluConfig {
130
204
  * const authenticatedRelay = [
131
205
  * createOwnerWebSocketTransport({
132
206
  * url: "wss://relay.example.com",
133
- * ownerId: appOwner.id,
207
+ * ownerId: testAppOwner.id,
134
208
  * }),
135
209
  * ];
136
210
  *
137
211
  * assertEqual(
138
212
  * authenticatedRelay[0]?.url,
139
- * `wss://relay.example.com?ownerId=${appOwner.id}`,
213
+ * `wss://relay.example.com?ownerId=${testAppOwner.id}`,
140
214
  * );
141
215
  * ```
142
216
  */
143
217
  readonly transports?: ReadonlyArray<OwnerTransport>;
144
- /**
145
- * Keep local data only in memory instead of persisting it on this device.
146
- * Useful for testing, temporary data, or sensitive data that should not be
147
- * recoverable from local storage after the process ends.
148
- *
149
- * Local data stored in memory is completely destroyed when the process ends.
150
- * Sync can still persist data remotely when transports are enabled.
151
- *
152
- * The default value is: `false`.
153
- */
154
- readonly memoryOnly?: boolean;
155
- /**
156
- * Use the `indexes` option to define SQLite indexes.
157
- *
158
- * Table and column names are not typed because Kysely doesn't support it.
159
- *
160
- * https://medium.com/@JasonWyatt/squeezing-performance-from-sqlite-indexes-indexes-c4e175f3c346
161
- *
162
- * ### Example
163
- *
164
- * ```ts
165
- * import {
166
- * AppName,
167
- * assertTrue,
168
- * createAppOwner,
169
- * createEvolu,
170
- * createOwnerSecret,
171
- * createRandomBytes,
172
- * id,
173
- * } from "@evolu/common";
174
- *
175
- * const Schema = {
176
- * todo: { id: id("Todo") },
177
- * todoCategory: { id: id("TodoCategory") },
178
- * };
179
- * // Create once, persist the mnemonic securely, and restore it on later runs.
180
- * const appOwner = createAppOwner(
181
- * createOwnerSecret({ randomBytes: createRandomBytes() }),
182
- * );
183
- *
184
- * const createTodoEvolu = createEvolu(Schema, {
185
- * appName: AppName.orThrow("IndexedTodos"),
186
- * appOwner,
187
- * transports: [],
188
- * indexes: (create) => [
189
- * create("todoCreatedAt").on("todo").column("createdAt"),
190
- * create("todoCategoryCreatedAt")
191
- * .on("todoCategory")
192
- * .column("createdAt"),
193
- * ],
194
- * });
195
- *
196
- * assertTrue(typeof createTodoEvolu === "function");
197
- * ```
198
- */
199
- readonly indexes?: IndexesConfig;
200
- /**
201
- * Called when this instance's local database is deleted.
202
- *
203
- * Apps can use this to update UI immediately because the corresponding
204
- * {@link Evolu} instance becomes unusable after local database deletion.
205
- */
206
- readonly onDatabaseDeleted?: () => void;
207
- /**
208
- * Called when local data for an {@link Owner} is deleted.
209
- *
210
- * Apps can use this to update UI immediately because that owner stops being
211
- * used across tabs and instances.
212
- */
213
- readonly onOwnerDeleted?: (owner: Owner) => void;
214
218
  }
215
219
  /**
216
220
  * Application name.
@@ -221,17 +225,38 @@ export interface EvoluConfig {
221
225
  *
222
226
  * Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
223
227
  * `_`) and must be between 1 and 41 characters.
228
+ *
229
+ * @group Configuration
224
230
  */
225
231
  export declare const AppName: import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"String", string, string, import("../Type.ts").TypeOfError<"String">, null, import("../Type.ts").TypeOfError<"String">, never, string, true>, "UrlSafeString", import("../Type.ts").RegexError<"UrlSafeString">>, "AppName", AppNameError>;
226
232
  export type AppName = typeof AppName.Output;
233
+ /**
234
+ * Error produced when a value is not a valid {@link AppName}.
235
+ *
236
+ * @group Configuration
237
+ */
227
238
  export interface AppNameError extends TypeError<"AppName"> {
228
239
  readonly value: UrlSafeString;
229
240
  }
241
+ /**
242
+ * Stable valid {@link AppName} for tests and examples.
243
+ *
244
+ * @group Testing
245
+ */
230
246
  export declare const testAppName: string & import("../Brand.ts").Brand<"UrlSafeString"> & import("../Brand.ts").Brand<"AppName">;
231
247
  /**
232
- * Local-first SQL database with typed queries, mutations, and sync.
248
+ * A local-first SQL database.
249
+ *
250
+ * Stores application data in SQLite on the device, so reads and writes work
251
+ * offline. Provides typed queries, mutations, and reactive subscriptions, with
252
+ * synchronization between devices. Persistent SQLite is encrypted with the
253
+ * {@link AppOwner} key, and synchronized data is end-to-end encrypted.
254
+ *
255
+ * Tables whose names start with `_` stay local, even when the instance
256
+ * synchronizes other data. Use them for device-local data such as application
257
+ * settings or an app-owner registry.
233
258
  *
234
- * TODO: Better docs.
259
+ * @group Core
235
260
  */
236
261
  export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposable {
237
262
  /**
@@ -249,32 +274,29 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
249
274
  * every row has a globally unique, conflict-free identifier without
250
275
  * coordination.
251
276
  *
252
- * Pass `onComplete` when follow-up work must wait until the mutation and its
253
- * query patches have been applied.
277
+ * Pass `onComplete` when follow-up work must wait until the mutation is
278
+ * stored and subscribed queries reflect it. It never runs when the database
279
+ * is unavailable; see {@link Evolu.loadQuery}. A stored change is not always
280
+ * visible; see {@link MutationOptions.onComplete}.
254
281
  *
255
282
  * ### Example
256
283
  *
257
284
  * ```ts
258
285
  * import {
259
286
  * assertType,
260
- * id,
287
+ * type TestEvoluSchema,
261
288
  * NonEmptyTrimmedString100,
262
289
  * type Evolu,
290
+ * type TestTodoId,
263
291
  * } from "@evolu/common";
264
292
  *
265
- * const TodoId = id("Todo");
266
- * type TodoId = typeof TodoId.Output;
267
- * const Schema = {
268
- * todo: { id: TodoId, title: NonEmptyTrimmedString100 },
269
- * };
270
- *
271
- * const insertTodo = (evolu: Evolu<typeof Schema>) =>
293
+ * const insertTodo = (evolu: Evolu<TestEvoluSchema>) =>
272
294
  * evolu.insert("todo", {
273
295
  * title: NonEmptyTrimmedString100.orThrow("Learn Evolu"),
274
296
  * }).id;
275
297
  *
276
298
  * const insertTodoAndNotify = (
277
- * evolu: Evolu<typeof Schema>,
299
+ * evolu: Evolu<TestEvoluSchema>,
278
300
  * onComplete: () => void,
279
301
  * ) =>
280
302
  * evolu.insert(
@@ -288,7 +310,7 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
288
310
  * ReturnType<typeof insertTodo>,
289
311
  * ReturnType<typeof insertTodoAndNotify>,
290
312
  * ],
291
- * [TodoId, TodoId]
313
+ * [TestTodoId, TestTodoId]
292
314
  * >();
293
315
  * ```
294
316
  *
@@ -305,30 +327,30 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
305
327
  * ```ts
306
328
  * import {
307
329
  * assertType,
308
- * id,
330
+ * type TestEvoluSchema,
309
331
  * NonEmptyTrimmedString100,
310
332
  * sqliteTrue,
311
333
  * type Evolu,
334
+ * type TestTodoId,
312
335
  * } from "@evolu/common";
313
336
  *
314
- * const TodoId = id("Todo");
315
- * type TodoId = typeof TodoId.Output;
316
- * const Schema = {
317
- * todo: { id: TodoId, title: NonEmptyTrimmedString100 },
318
- * };
319
- *
320
- * const renameTodo = (evolu: Evolu<typeof Schema>, todoId: TodoId) =>
337
+ * const renameTodo = (
338
+ * evolu: Evolu<TestEvoluSchema>,
339
+ * todoId: TestTodoId,
340
+ * ) =>
321
341
  * evolu.update("todo", {
322
342
  * id: todoId,
323
343
  * title: NonEmptyTrimmedString100.orThrow("Updated title"),
324
344
  * }).id;
325
345
  *
326
- * const softDeleteTodo = (evolu: Evolu<typeof Schema>, todoId: TodoId) =>
327
- * evolu.update("todo", { id: todoId, isDeleted: sqliteTrue }).id;
346
+ * const softDeleteTodo = (
347
+ * evolu: Evolu<TestEvoluSchema>,
348
+ * todoId: TestTodoId,
349
+ * ) => evolu.update("todo", { id: todoId, isDeleted: sqliteTrue }).id;
328
350
  *
329
351
  * assertType<
330
352
  * [ReturnType<typeof renameTodo>, ReturnType<typeof softDeleteTodo>],
331
- * [TodoId, TodoId]
353
+ * [TestTodoId, TestTodoId]
332
354
  * >();
333
355
  * ```
334
356
  *
@@ -353,40 +375,72 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
353
375
  * import {
354
376
  * assertType,
355
377
  * createIdFromString,
356
- * id,
378
+ * type TestEvoluSchema,
357
379
  * NonEmptyTrimmedString100,
358
380
  * type Evolu,
381
+ * TestTodoId,
359
382
  * } from "@evolu/common";
360
383
  *
361
- * const TodoId = id("Todo");
362
- * type TodoId = typeof TodoId.Output;
363
- * const Schema = {
364
- * todo: { id: TodoId, title: NonEmptyTrimmedString100 },
365
- * };
366
- * const stableId = TodoId.orThrow(createIdFromString("my-todo-1"));
367
- * const upsertTodo = (evolu: Evolu<typeof Schema>) =>
384
+ * const stableId = TestTodoId.orThrow(createIdFromString("my-todo-1"));
385
+ * const upsertTodo = (evolu: Evolu<TestEvoluSchema>) =>
368
386
  * evolu.upsert("todo", {
369
387
  * id: stableId,
370
388
  * title: NonEmptyTrimmedString100.orThrow("Learn Evolu"),
371
389
  * }).id;
372
390
  *
373
- * assertType<ReturnType<typeof upsertTodo>, TodoId>();
391
+ * assertType<ReturnType<typeof upsertTodo>, TestTodoId>();
374
392
  * ```
375
393
  *
376
394
  * @see {@link Mutation}
377
395
  */
378
396
  readonly upsert: Mutation<S, "upsert">;
397
+ /**
398
+ * Returns the size of a mutation of `table` with `values`, measured as
399
+ * {@link maxMutationSize} describes.
400
+ *
401
+ * Use it to check values that column Types do not bound before mutating, or
402
+ * to show how much of the limit a mutation uses. It accepts any of the
403
+ * table's columns, so the values of an insert, update, or upsert fit, and it
404
+ * ignores `id` and `isDeleted`, which are not columns. Mutations of
405
+ * local-only tables are measured too, although they are exempt from the
406
+ * limit.
407
+ *
408
+ * ### Example
409
+ *
410
+ * ```ts
411
+ * import {
412
+ * assertType,
413
+ * type Evolu,
414
+ * maxMutationSize,
415
+ * type NonEmptyTrimmedString100,
416
+ * type TestEvoluSchema,
417
+ * } from "@evolu/common";
418
+ *
419
+ * const fitsTodo = (
420
+ * evolu: Evolu<TestEvoluSchema>,
421
+ * title: NonEmptyTrimmedString100,
422
+ * ) => evolu.getMutationSize("todo", { title }) <= maxMutationSize;
423
+ *
424
+ * assertType<ReturnType<typeof fitsTodo>, boolean>();
425
+ * ```
426
+ */
427
+ readonly getMutationSize: <TableName extends keyof S>(table: TableName, values: Partial<MutationValues<S[TableName], "update">>) => PositiveInt;
379
428
  /**
380
429
  * Load {@link Query} and return a promise with {@link QueryRows}.
381
430
  *
382
- * The returned promise always resolves successfully because there is no
383
- * reason why loading should fail. All data are local, and the query is
384
- * typed.
431
+ * The returned promise always resolves successfully because all data are
432
+ * local and the query is typed. If the database refuses startup, unanswered
433
+ * loads stay pending until disposal resolves them with empty rows. Observe
434
+ * {@link EvoluErrorDep.evoluError} to display the refusal independently of
435
+ * query loading.
385
436
  *
386
437
  * Loading is batched. Returned promises are cached while pending and can be
387
- * reused after fulfillment until mutation-driven invalidation, which prevents
388
- * redundant database queries and supports React Suspense (stable references
389
- * while pending).
438
+ * reused after fulfillment, which prevents redundant database queries and
439
+ * supports React Suspense (stable references while pending). A mutation or
440
+ * incoming sync invalidates the cache of an unsubscribed query, so its next
441
+ * load reads again. A subscribed query keeps its cached rows and the
442
+ * subscription refreshes them, so a load can return rows that a pending
443
+ * refresh is about to replace.
390
444
  *
391
445
  * To subscribe a query for automatic updates, use
392
446
  * {@link Evolu.subscribeQuery}.
@@ -397,20 +451,17 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
397
451
  * import {
398
452
  * assertType,
399
453
  * createQueryBuilder,
400
- * id,
401
- * NonEmptyTrimmedString100,
454
+ * testEvoluSchema,
455
+ * type TestEvoluSchema,
402
456
  * type Evolu,
403
457
  * type QueryRows,
404
458
  * } from "@evolu/common";
405
459
  *
406
- * const Schema = {
407
- * todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
408
- * };
409
- * const createQuery = createQueryBuilder(Schema);
460
+ * const createQuery = createQueryBuilder(testEvoluSchema);
410
461
  * const allTodos = createQuery((db) =>
411
462
  * db.selectFrom("todo").selectAll(),
412
463
  * );
413
- * const loadTodos = async (evolu: Evolu<typeof Schema>) => {
464
+ * const loadTodos = async (evolu: Evolu<TestEvoluSchema>) => {
414
465
  * const rows = await evolu.loadQuery(allTodos);
415
466
  * return rows;
416
467
  * };
@@ -432,31 +483,22 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
432
483
  * ```ts
433
484
  * import {
434
485
  * assertType,
435
- * createIdFromString,
436
486
  * createQueryBuilder,
437
- * id,
438
- * NonEmptyTrimmedString100,
487
+ * testEvoluSchema,
488
+ * type TestEvoluSchema,
489
+ * testTodoId,
439
490
  * type Evolu,
440
491
  * type QueryRows,
441
492
  * } from "@evolu/common";
442
493
  *
443
- * const TodoId = id("Todo");
444
- * type TodoId = typeof TodoId.Output;
445
- * const Schema = {
446
- * todo: { id: TodoId, title: NonEmptyTrimmedString100 },
447
- * };
448
- * const createQuery = createQueryBuilder(Schema);
494
+ * const createQuery = createQueryBuilder(testEvoluSchema);
449
495
  * const allTodos = createQuery((db) =>
450
496
  * db.selectFrom("todo").select(["id", "title"]),
451
497
  * );
452
- * const todoById = (todoId: TodoId) =>
453
- * createQuery((db) =>
454
- * db.selectFrom("todo").select("title").where("id", "=", todoId),
455
- * );
456
- * const firstTodo = todoById(
457
- * TodoId.orThrow(createIdFromString("first-todo")),
498
+ * const firstTodo = createQuery((db) =>
499
+ * db.selectFrom("todo").select("title").where("id", "=", testTodoId),
458
500
  * );
459
- * const loadTodoQueries = (evolu: Evolu<typeof Schema>) =>
501
+ * const loadTodoQueries = (evolu: Evolu<TestEvoluSchema>) =>
460
502
  * evolu.loadQueries([allTodos, firstTodo]);
461
503
  *
462
504
  * assertType<
@@ -472,28 +514,28 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
472
514
  /**
473
515
  * Subscribe to {@link Query} {@link QueryRows} changes.
474
516
  *
517
+ * Cached rows invalidated before subscription are refreshed. Subscribing
518
+ * alone does not load a query that has no cached rows.
519
+ *
475
520
  * ### Example
476
521
  *
477
522
  * ```ts
478
523
  * import {
479
524
  * assertType,
480
525
  * createQueryBuilder,
481
- * id,
482
- * NonEmptyTrimmedString100,
526
+ * testEvoluSchema,
527
+ * type TestEvoluSchema,
483
528
  * type Evolu,
484
529
  * type QueryRows,
485
530
  * type Unsubscribe,
486
531
  * } from "@evolu/common";
487
532
  *
488
- * const Schema = {
489
- * todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
490
- * };
491
- * const createQuery = createQueryBuilder(Schema);
533
+ * const createQuery = createQueryBuilder(testEvoluSchema);
492
534
  * const allTodos = createQuery((db) =>
493
535
  * db.selectFrom("todo").select("title"),
494
536
  * );
495
537
  * const subscribeToTodos = (
496
- * evolu: Evolu<typeof Schema>,
538
+ * evolu: Evolu<TestEvoluSchema>,
497
539
  * onRows: (rows: QueryRows<typeof allTodos.Row>) => void,
498
540
  * ) =>
499
541
  * evolu.subscribeQuery(allTodos)(() => {
@@ -513,20 +555,17 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
513
555
  * import {
514
556
  * assertType,
515
557
  * createQueryBuilder,
516
- * id,
517
- * NonEmptyTrimmedString100,
558
+ * testEvoluSchema,
559
+ * type TestEvoluSchema,
518
560
  * type Evolu,
519
561
  * type QueryRows,
520
562
  * } from "@evolu/common";
521
563
  *
522
- * const Schema = {
523
- * todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
524
- * };
525
- * const createQuery = createQueryBuilder(Schema);
564
+ * const createQuery = createQueryBuilder(testEvoluSchema);
526
565
  * const allTodos = createQuery((db) =>
527
566
  * db.selectFrom("todo").select("title"),
528
567
  * );
529
- * const getTodos = (evolu: Evolu<typeof Schema>) =>
568
+ * const getTodos = (evolu: Evolu<TestEvoluSchema>) =>
530
569
  * evolu.getQueryRows(allTodos);
531
570
  *
532
571
  * assertType<
@@ -543,7 +582,8 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
543
582
  * of starting parallel exports.
544
583
  *
545
584
  * The pending promise rejects if this {@link Evolu} instance is disposed
546
- * before export completion.
585
+ * before export completion. If the database refuses startup, export stays
586
+ * pending until disposal; see {@link Evolu.loadQuery}.
547
587
  */
548
588
  readonly exportDatabase: () => Promise<Uint8Array<ArrayBuffer>>;
549
589
  /**
@@ -594,21 +634,15 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
594
634
  * ```ts
595
635
  * import {
596
636
  * assertEqual,
597
- * createAppOwner,
598
637
  * createOwnerWebSocketTransport,
599
- * createOwnerSecret,
600
- * createRandomBytes,
601
638
  * deriveShardOwner,
639
+ * testAppOwner,
602
640
  * type Evolu,
603
641
  * type ReadonlyOwner,
604
642
  * type UnuseOwner,
605
643
  * } from "@evolu/common";
606
644
  *
607
- * // Create once, persist the mnemonic securely, and restore it on later runs.
608
- * const appOwner = createAppOwner(
609
- * createOwnerSecret({ randomBytes: createRandomBytes() }),
610
- * );
611
- * const shardOwner = deriveShardOwner(appOwner, ["todos", 1]);
645
+ * const shardOwner = deriveShardOwner(testAppOwner, ["todos", 1]);
612
646
  * const shardTransport = createOwnerWebSocketTransport({
613
647
  * url: "wss://relay.example.com",
614
648
  * ownerId: shardOwner.id,
@@ -641,15 +675,123 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
641
675
  * ```
642
676
  */
643
677
  readonly useOwner: (owner: ReadonlyOwner | Owner, transports?: NonEmptyReadonlyArray<OwnerTransport>) => UnuseOwner;
678
+ /**
679
+ * Requests a new synchronization round for an active {@link OwnerId}.
680
+ *
681
+ * Reconciles locally stored changes, including writes previously rejected
682
+ * with {@link ProtocolQuotaError}, through the owner's active transports. Call
683
+ * this after the relay provider confirms additional quota is available.
684
+ * Existing connections and {@link Evolu.useOwner} registrations are retained,
685
+ * including registrations shared by multiple instances or tabs.
686
+ *
687
+ * The owner must have an active writable registration in this database.
688
+ * Unregistered or read-only owners are ignored. Requests are skipped while
689
+ * all of the owner's transports are closed; they synchronize when they
690
+ * reopen. Disposal drops locally buffered requests. Calling a disposed
691
+ * instance throws, like other Evolu operations.
692
+ *
693
+ * Returns immediately, without waiting for synchronization to complete.
694
+ * Errors are reported through {@link EvoluErrorDep.evoluError}.
695
+ *
696
+ * ### Example
697
+ *
698
+ * ```ts
699
+ * import { assertType, type Evolu, type OwnerId } from "@evolu/common";
700
+ *
701
+ * // Call after successfully increasing the affected owner's relay quota.
702
+ * const onQuotaIncreased = (evolu: Evolu, ownerId: OwnerId) => {
703
+ * evolu.requestSync(ownerId);
704
+ * };
705
+ *
706
+ * assertType<
707
+ * typeof onQuotaIncreased,
708
+ * (evolu: Evolu, ownerId: OwnerId) => void
709
+ * >();
710
+ * ```
711
+ */
712
+ readonly requestSync: (ownerId: OwnerId) => void;
644
713
  }
645
- /** Function returned by {@link Evolu.useOwner} to stop using an Owner for sync. */
714
+ /**
715
+ * Function returned by {@link Evolu.useOwner} to stop using an Owner for sync.
716
+ *
717
+ * @group Core
718
+ */
646
719
  export type UnuseOwner = () => void;
720
+ /**
721
+ * Represents errors that can occur in {@link Evolu}.
722
+ *
723
+ * Apps show them from {@link EvoluErrorDep.evoluError}.
724
+ *
725
+ * An error that leaves the app unusable deserves a modal dialog, which moves
726
+ * focus into itself and restores it when closed. Any other error is a status
727
+ * message: show it in a region with `role="alert"`, which screen readers
728
+ * announce without moving focus, so the user keeps working and a background tab
729
+ * shows it when the user returns. Avoid `alert()`, which blocks the page, once
730
+ * in every open tab.
731
+ *
732
+ * - {@link UnsupportedDbVersionError} blocks the app: the local data needs a newer
733
+ * version of it. Ask the user to update the app or close all its tabs.
734
+ * - {@link OtherBuildRunningError} blocks the app while it lasts: another version
735
+ * of the app holds the local data. Ask the user to close the app's other
736
+ * tabs. It clears when the wait ends.
737
+ * - {@link ProtocolError} does not block the app: sync with a relay failed for an
738
+ * owner, because the relay rejected or failed a request, or sent data that
739
+ * could not be decoded or verified; sync state shows the affected routes. A
740
+ * {@link ProtocolQuotaError} needs more relay quota, then
741
+ * {@link Evolu.requestSync}; a {@link ProtocolVersionError} needs an app or
742
+ * relay update.
743
+ * - {@link StorageQuotaError} does not block the app: a storage or billing quota
744
+ * was exceeded, so a batch of an owner's changes was not stored. The built-in
745
+ * client storage does not report it yet; a relay's quota arrives as
746
+ * {@link ProtocolQuotaError}.
747
+ * - {@link DecryptWithXChaCha20Poly1305Error} does not block the app: changes
748
+ * received for an owner could not be decrypted, so none of their batch was
749
+ * stored.
750
+ * - {@link UnknownError} does not block the app: Evolu logged an unexpected
751
+ * failure. Show a generic message.
752
+ *
753
+ * @group Core
754
+ */
755
+ export type EvoluError = DecryptWithXChaCha20Poly1305Error | OtherBuildRunningError | ProtocolError | StorageQuotaError | UnknownError | UnsupportedDbVersionError;
756
+ /**
757
+ * The largest {@link Mutation}, in bytes.
758
+ *
759
+ * A mutation's size is its change as encoded for sync: the table name, the ID,
760
+ * and every column name and value, with a few bytes of overhead. A string takes
761
+ * at most three bytes per UTF-16 code unit. Every mutation within the limit
762
+ * fits one protocol message, so it can always sync. A larger mutation throws
763
+ * and is not saved. Mutations of local-only tables are exempt, because they
764
+ * never sync. Measure a mutation in advance with {@link Evolu.getMutationSize}.
765
+ *
766
+ * @group Core
767
+ */
768
+ export declare const maxMutationSize: PositiveInt;
769
+ /**
770
+ * Dependency wrapper for the shared {@link EvoluError} store.
771
+ *
772
+ * @group Construction
773
+ */
647
774
  export interface EvoluErrorDep {
648
775
  /**
649
776
  * {@link ReadonlyStore} of {@link EvoluError} shared by all {@link Evolu}
650
777
  * instances created from the same {@link createEvoluDeps} result.
651
778
  *
779
+ * Starts at `null` and otherwise holds the latest reported error until the
780
+ * first {@link UnsupportedDbVersionError}. That refusal remains for the
781
+ * lifetime of these dependencies, even if a tenant is disposed and recreated.
782
+ * Later errors are still logged but do not replace it or notify this store's
783
+ * subscribers. Fresh dependencies start with a fresh error store. An
784
+ * {@link OtherBuildRunningError} reports a wait, so the store returns to
785
+ * `null` when the wait ends, unless another error replaced it.
786
+ *
652
787
  * Subscribe once to show user-facing error messages across all instances.
788
+ * While a refused database's tenant remains alive, the SharedWorker sends the
789
+ * refusal to each tab once, including tabs that connect later, and starts no
790
+ * replacement database workers. After all instances release that tenant and
791
+ * it is disposed when idle, creating another instance retries startup and may
792
+ * send the refusal again. On the web, a refused tab first reloads once
793
+ * instead; see {@link UnsupportedDbVersionError}. Show that blocking message
794
+ * outside any query-loading boundary, so pending queries do not hide it.
653
795
  *
654
796
  * ### Example
655
797
  *
@@ -657,62 +799,115 @@ export interface EvoluErrorDep {
657
799
  * import {
658
800
  * assertEqual,
659
801
  * createStore,
660
- * Millis,
661
802
  * type EvoluError,
662
803
  * } from "@evolu/common";
663
804
  * import type { EvoluErrorDep } from "@evolu/common/local-first";
664
805
  *
806
+ * // The message for the current error, or null for none.
807
+ * const errorMessage = (error: EvoluError | null): string | null => {
808
+ * if (!error) return null;
809
+ * // oxlint-disable-next-line typescript/switch-exhaustiveness-check -- The default handles every other EvoluError.
810
+ * switch (error.type) {
811
+ * case "UnsupportedDbVersionError":
812
+ * return "Your data requires a newer version of this app. Please update it.";
813
+ * case "OtherBuildRunningError":
814
+ * return "This app is open in another tab with a different version. Close that tab to continue.";
815
+ * default:
816
+ * return "Something went wrong. Please try again.";
817
+ * }
818
+ * };
819
+ *
665
820
  * // Stand-in for run.deps.evoluError from createEvoluDeps.
666
821
  * using evoluError = createStore<EvoluError | null>(null);
667
822
  * const deps = { evoluError } satisfies EvoluErrorDep;
668
- * let displayedMessage = "";
669
- * const showMessage = (message: string) => {
670
- * displayedMessage = message;
671
- * };
823
+ * // What the app showed over time; null hides the message.
824
+ * const shown: Array<string | null> = [];
672
825
  *
673
826
  * deps.evoluError.subscribe(() => {
674
- * const error = deps.evoluError.get();
675
- * if (!error) return;
676
- *
677
- * // oxlint-disable-next-line typescript/switch-exhaustiveness-check -- The default intentionally handles every other EvoluError.
678
- * switch (error.type) {
679
- * case "TimestampDriftError":
680
- * // Show guidance specific to the detected error.
681
- * showMessage(
682
- * "Your system clock appears incorrect. Please fix it.",
683
- * );
684
- * break;
685
- * default:
686
- * // Show a generic user message for other operational errors.
687
- * showMessage("Something went wrong. Please try again.");
688
- * }
827
+ * shown.push(errorMessage(deps.evoluError.get()));
689
828
  * });
690
829
  *
691
- * deps.evoluError.set({
692
- * type: "TimestampDriftError",
693
- * next: Millis.orThrow(360000),
694
- * now: Millis.orThrow(0),
695
- * });
696
- * assertEqual(
697
- * displayedMessage,
698
- * "Your system clock appears incorrect. Please fix it.",
699
- * );
830
+ * // Another version keeps this tab waiting, then the wait ends.
831
+ * deps.evoluError.set({ type: "OtherBuildRunningError" });
832
+ * deps.evoluError.set(null);
833
+ *
834
+ * assertEqual(shown, [
835
+ * "This app is open in another tab with a different version. Close that tab to continue.",
836
+ * null,
837
+ * ]);
700
838
  * ```
701
839
  */
702
840
  readonly evoluError: ReadonlyStore<EvoluError | null>;
703
841
  }
842
+ /**
843
+ * Dependency wrapper for the shared {@link SyncState} store.
844
+ *
845
+ * @group Construction
846
+ */
847
+ export interface SyncStateDep {
848
+ /**
849
+ * {@link ReadonlyStore} of the latest {@link SyncState} shared by all
850
+ * {@link Evolu} instances, or null before the shared worker sends its first
851
+ * snapshot. Derive what to show from it, such as one indicator per relay, or
852
+ * use {@link syncStateToOwnerSyncStates} for one state per owner.
853
+ *
854
+ * ### Example
855
+ *
856
+ * ```ts
857
+ * import {
858
+ * assertEqual,
859
+ * createId,
860
+ * createStore,
861
+ * testCreateDeps,
862
+ * } from "@evolu/common";
863
+ * import type {
864
+ * SyncState,
865
+ * SyncStateDep,
866
+ * } from "@evolu/common/local-first";
867
+ *
868
+ * const openRelayLabels = (deps: SyncStateDep): ReadonlyArray<string> =>
869
+ * (deps.syncState.get()?.transports ?? [])
870
+ * .filter(({ readyState }) => readyState === "open")
871
+ * .map(({ label }) => label);
872
+ *
873
+ * using syncState = createStore<SyncState | null>(null);
874
+ * assertEqual(openRelayLabels({ syncState }), []);
875
+ *
876
+ * const deps = testCreateDeps();
877
+ * syncState.set({
878
+ * transports: [
879
+ * {
880
+ * id: createId<"SyncTransport">(deps),
881
+ * label: "wss://relay.example",
882
+ * readyState: "open",
883
+ * openedAt: null,
884
+ * closedAt: null,
885
+ * error: null,
886
+ * },
887
+ * ],
888
+ * tenants: [],
889
+ * });
890
+ * assertEqual(openRelayLabels({ syncState }), ["wss://relay.example"]);
891
+ * ```
892
+ */
893
+ readonly syncState: ReadonlyStore<SyncState | null>;
894
+ }
704
895
  /**
705
896
  * Shared platform dependencies for creating {@link Evolu} instances.
706
897
  *
707
898
  * Includes platform adapters, the shared {@link EvoluErrorDep.evoluError} store,
708
899
  * and disposal for owned resources.
900
+ *
901
+ * @group Construction
709
902
  */
710
- export type EvoluDeps = EvoluPlatformDeps & ConsoleDep & EvoluErrorDep & Disposable;
903
+ export type EvoluDeps = EvoluPlatformDeps & ConsoleDep & EvoluErrorDep & SyncStateDep & Disposable;
711
904
  /**
712
905
  * Platform-specific dependencies required to create {@link EvoluDeps}.
713
906
  *
714
907
  * Provides worker and channel adapters plus optional platform integrations for
715
908
  * logging and synchronous UI flush.
909
+ *
910
+ * @group Construction
716
911
  */
717
912
  export type EvoluPlatformDeps = CreateDbWorkerDep & CreateBroadcastChannelDep & CreateMessageChannelDep & LockManagerDep & ReloadAppDep & SharedWorkerDep & Partial<ConsoleDep> & Partial<FlushSyncDep>;
718
913
  /**
@@ -721,15 +916,19 @@ export type EvoluPlatformDeps = CreateDbWorkerDep & CreateBroadcastChannelDep &
721
916
  *
722
917
  * Call this once per platform and reuse the returned deps when creating
723
918
  * multiple Evolu instances. The returned deps object owns long-lived resources
724
- * such as worker channels and the shared {@link EvoluErrorDep.evoluError}
725
- * store.
919
+ * such as worker channels and the shared {@link EvoluErrorDep.evoluError} and
920
+ * {@link SyncStateDep.syncState} stores.
726
921
  *
727
922
  * Dispose it only during app shutdown.
923
+ *
924
+ * @group Construction
728
925
  */
729
926
  export declare const createEvoluDeps: (deps: EvoluPlatformDeps) => EvoluDeps;
730
927
  /**
731
928
  * Creates an {@link Evolu} instance from {@link EvoluSchema} and
732
929
  * {@link EvoluConfig}.
930
+ *
931
+ * @group Construction
733
932
  */
734
933
  export declare const createEvolu: <S extends EvoluSchema>(schema: ValidateSchema<S> extends never ? S : ValidateSchema<S>, config: EvoluConfig) => Task<Evolu<S>, never, EvoluPlatformDeps>;
735
934
  //# sourceMappingURL=Evolu.d.ts.map