@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.
- package/dist/src/Bytes.d.ts +39 -2
- package/dist/src/Bytes.d.ts.map +1 -1
- package/dist/src/Bytes.js +50 -2
- package/dist/src/Config.d.ts +22 -22
- package/dist/src/Config.d.ts.map +1 -1
- package/dist/src/Console.d.ts +62 -7
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +20 -4
- package/dist/src/Crypto.d.ts +76 -4
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +55 -4
- package/dist/src/Error.d.ts +45 -0
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +69 -0
- package/dist/src/Fs.d.ts +92 -18
- package/dist/src/Fs.d.ts.map +1 -1
- package/dist/src/Fs.js +2 -0
- package/dist/src/Identicon.d.ts +2 -2
- package/dist/src/Identicon.js +2 -2
- package/dist/src/LeakDetector.d.ts +22 -3
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +12 -2
- package/dist/src/LockManager.d.ts +8 -0
- package/dist/src/LockManager.d.ts.map +1 -1
- package/dist/src/LockManager.js +6 -0
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +5 -0
- package/dist/src/Platform.d.ts +47 -7
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +24 -5
- package/dist/src/Random.d.ts +25 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +14 -2
- package/dist/src/Resource.d.ts +156 -1
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +201 -72
- package/dist/src/Schedule.d.ts +11 -10
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +1 -1
- package/dist/src/Sqlite.d.ts +132 -16
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +63 -9
- package/dist/src/Task.d.ts +15 -4
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +41 -15
- package/dist/src/Test.d.ts +9 -0
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +4 -0
- package/dist/src/Time.d.ts +106 -9
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +55 -4
- package/dist/src/Type.d.ts +1455 -1310
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +1274 -517
- package/dist/src/WebSocket.d.ts +164 -13
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +133 -24
- package/dist/src/Worker.d.ts +90 -8
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +28 -2
- package/dist/src/index.d.ts +6 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -3
- package/dist/src/local-first/Db.d.ts +52 -3
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +412 -137
- package/dist/src/local-first/Evolu.d.ts +412 -213
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +181 -18
- package/dist/src/local-first/Owner.d.ts +13 -30
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +13 -30
- package/dist/src/local-first/Protocol.d.ts +106 -19
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +162 -60
- package/dist/src/local-first/Query.d.ts +8 -15
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +4 -2
- package/dist/src/local-first/Schema.d.ts +346 -23
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +214 -17
- package/dist/src/local-first/Shared.d.ts +537 -22
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +1437 -234
- package/dist/src/local-first/Storage.d.ts +195 -17
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +85 -22
- package/dist/src/local-first/Timestamp.d.ts +392 -41
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +403 -81
- package/dist/src/local-first/index.d.ts +0 -1
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +0 -1
- package/package.json +1 -1
- package/src/Assert.test.ts +2 -5
- package/src/Bytes.test.ts +27 -0
- package/src/Bytes.ts +58 -2
- package/src/Config.test.ts +2 -6
- package/src/Config.ts +133 -133
- package/src/Console.ts +62 -7
- package/src/Crypto.ts +76 -4
- package/src/Eq.test.ts +2 -3
- package/src/Error.test.ts +76 -3
- package/src/Error.ts +71 -0
- package/src/Fs.ts +92 -18
- package/src/Identicon.ts +2 -2
- package/src/LeakDetector.ts +22 -3
- package/src/LockManager.ts +8 -0
- package/src/Object.test.ts +27 -12
- package/src/Object.ts +5 -0
- package/src/Platform.ts +50 -8
- package/src/Random.ts +25 -2
- package/src/Resource.test.ts +837 -0
- package/src/Resource.ts +235 -15
- package/src/Schedule.test.ts +50 -12
- package/src/Schedule.ts +24 -14
- package/src/Sqlite.ts +137 -17
- package/src/Task.test.ts +189 -8
- package/src/Task.ts +56 -17
- package/src/Test.ts +9 -0
- package/src/Time.ts +106 -9
- package/src/Type.test.ts +946 -1028
- package/src/Type.ts +4195 -3136
- package/src/Types.test.ts +4 -14
- package/src/WebSocket.ts +313 -40
- package/src/Worker.ts +90 -8
- package/src/index.ts +20 -6
- package/src/local-first/Db.ts +644 -339
- package/src/local-first/Evolu.test.ts +994 -22
- package/src/local-first/Evolu.ts +625 -232
- package/src/local-first/Owner.ts +13 -30
- package/src/local-first/Protocol.test.ts +634 -10
- package/src/local-first/Protocol.ts +255 -109
- package/src/local-first/Query.ts +8 -15
- package/src/local-first/Relay.ts +4 -2
- package/src/local-first/Schema.test.ts +143 -0
- package/src/local-first/Schema.ts +376 -26
- package/src/local-first/Shared.test.ts +7731 -559
- package/src/local-first/Shared.ts +2036 -267
- package/src/local-first/Storage.ts +224 -36
- package/src/local-first/Timestamp.test.ts +344 -70
- package/src/local-first/Timestamp.ts +434 -118
- package/src/local-first/index.ts +0 -1
- package/dist/src/local-first/Error.d.ts +0 -12
- package/dist/src/local-first/Error.d.ts.map +0 -1
- package/dist/src/local-first/Error.js +0 -6
- package/dist/src/local-first/LocalAuth.d.ts +0 -150
- package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
- package/dist/src/local-first/LocalAuth.js +0 -179
- package/src/local-first/Error.ts +0 -17
- 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 {
|
|
16
|
-
import
|
|
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
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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
|
-
*
|
|
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:
|
|
207
|
+
* ownerId: testAppOwner.id,
|
|
134
208
|
* }),
|
|
135
209
|
* ];
|
|
136
210
|
*
|
|
137
211
|
* assertEqual(
|
|
138
212
|
* authenticatedRelay[0]?.url,
|
|
139
|
-
* `wss://relay.example.com?ownerId=${
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
253
|
-
*
|
|
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
|
-
*
|
|
287
|
+
* type TestEvoluSchema,
|
|
261
288
|
* NonEmptyTrimmedString100,
|
|
262
289
|
* type Evolu,
|
|
290
|
+
* type TestTodoId,
|
|
263
291
|
* } from "@evolu/common";
|
|
264
292
|
*
|
|
265
|
-
* const
|
|
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<
|
|
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
|
-
* [
|
|
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
|
-
*
|
|
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
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
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 = (
|
|
327
|
-
* evolu
|
|
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
|
-
* [
|
|
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
|
-
*
|
|
378
|
+
* type TestEvoluSchema,
|
|
357
379
|
* NonEmptyTrimmedString100,
|
|
358
380
|
* type Evolu,
|
|
381
|
+
* TestTodoId,
|
|
359
382
|
* } from "@evolu/common";
|
|
360
383
|
*
|
|
361
|
-
* const
|
|
362
|
-
*
|
|
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>,
|
|
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
|
|
383
|
-
*
|
|
384
|
-
*
|
|
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
|
|
388
|
-
*
|
|
389
|
-
*
|
|
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
|
-
*
|
|
401
|
-
*
|
|
454
|
+
* testEvoluSchema,
|
|
455
|
+
* type TestEvoluSchema,
|
|
402
456
|
* type Evolu,
|
|
403
457
|
* type QueryRows,
|
|
404
458
|
* } from "@evolu/common";
|
|
405
459
|
*
|
|
406
|
-
* const
|
|
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<
|
|
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
|
-
*
|
|
438
|
-
*
|
|
487
|
+
* testEvoluSchema,
|
|
488
|
+
* type TestEvoluSchema,
|
|
489
|
+
* testTodoId,
|
|
439
490
|
* type Evolu,
|
|
440
491
|
* type QueryRows,
|
|
441
492
|
* } from "@evolu/common";
|
|
442
493
|
*
|
|
443
|
-
* const
|
|
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
|
|
453
|
-
*
|
|
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<
|
|
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
|
-
*
|
|
482
|
-
*
|
|
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
|
|
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<
|
|
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
|
-
*
|
|
517
|
-
*
|
|
558
|
+
* testEvoluSchema,
|
|
559
|
+
* type TestEvoluSchema,
|
|
518
560
|
* type Evolu,
|
|
519
561
|
* type QueryRows,
|
|
520
562
|
* } from "@evolu/common";
|
|
521
563
|
*
|
|
522
|
-
* const
|
|
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<
|
|
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
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
669
|
-
* const
|
|
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
|
-
*
|
|
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
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
698
|
-
*
|
|
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
|
-
*
|
|
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
|