@evolu/common 8.9.0 → 8.11.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 +647 -0
- package/dist/src/Bytes.d.ts.map +1 -0
- package/dist/src/{Binary.js → Bytes.js} +266 -16
- package/dist/src/Config.d.ts +142 -0
- package/dist/src/Config.d.ts.map +1 -0
- package/dist/src/Config.js +181 -0
- 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 +376 -0
- package/dist/src/Fs.d.ts.map +1 -0
- package/dist/src/Fs.js +113 -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/Number.d.ts +50 -7
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +47 -8
- package/dist/src/Object.d.ts +32 -0
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +46 -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 +64 -10
- 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 +179 -20
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +95 -6
- package/dist/src/Type.d.ts +3056 -1539
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +2548 -584
- 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 +9 -8
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -4
- package/dist/src/intl/_en.d.ts +24 -1
- package/dist/src/intl/_en.d.ts.map +1 -1
- package/dist/src/intl/_en.js +20 -0
- package/dist/src/intl/ar.d.ts +24 -1
- package/dist/src/intl/ar.d.ts.map +1 -1
- package/dist/src/intl/ar.js +20 -0
- package/dist/src/intl/bn.d.ts +24 -1
- package/dist/src/intl/bn.d.ts.map +1 -1
- package/dist/src/intl/bn.js +20 -0
- package/dist/src/intl/ca.d.ts +24 -1
- package/dist/src/intl/ca.d.ts.map +1 -1
- package/dist/src/intl/ca.js +20 -0
- package/dist/src/intl/cs.d.ts +24 -1
- package/dist/src/intl/cs.d.ts.map +1 -1
- package/dist/src/intl/cs.js +20 -0
- package/dist/src/intl/da.d.ts +24 -1
- package/dist/src/intl/da.d.ts.map +1 -1
- package/dist/src/intl/da.js +20 -0
- package/dist/src/intl/de.d.ts +24 -1
- package/dist/src/intl/de.d.ts.map +1 -1
- package/dist/src/intl/de.js +20 -0
- package/dist/src/intl/el.d.ts +24 -1
- package/dist/src/intl/el.d.ts.map +1 -1
- package/dist/src/intl/el.js +20 -0
- package/dist/src/intl/es.d.ts +24 -1
- package/dist/src/intl/es.d.ts.map +1 -1
- package/dist/src/intl/es.js +20 -0
- package/dist/src/intl/fa.d.ts +24 -1
- package/dist/src/intl/fa.d.ts.map +1 -1
- package/dist/src/intl/fa.js +20 -0
- package/dist/src/intl/fi.d.ts +24 -1
- package/dist/src/intl/fi.d.ts.map +1 -1
- package/dist/src/intl/fi.js +20 -0
- package/dist/src/intl/fil.d.ts +24 -1
- package/dist/src/intl/fil.d.ts.map +1 -1
- package/dist/src/intl/fil.js +20 -0
- package/dist/src/intl/fr.d.ts +24 -1
- package/dist/src/intl/fr.d.ts.map +1 -1
- package/dist/src/intl/fr.js +20 -0
- package/dist/src/intl/he.d.ts +24 -1
- package/dist/src/intl/he.d.ts.map +1 -1
- package/dist/src/intl/he.js +20 -0
- package/dist/src/intl/hi.d.ts +24 -1
- package/dist/src/intl/hi.d.ts.map +1 -1
- package/dist/src/intl/hi.js +20 -0
- package/dist/src/intl/hr.d.ts +24 -1
- package/dist/src/intl/hr.d.ts.map +1 -1
- package/dist/src/intl/hr.js +20 -0
- package/dist/src/intl/hu.d.ts +22 -1
- package/dist/src/intl/hu.d.ts.map +1 -1
- package/dist/src/intl/hu.js +18 -0
- package/dist/src/intl/id.d.ts +24 -1
- package/dist/src/intl/id.d.ts.map +1 -1
- package/dist/src/intl/id.js +20 -0
- package/dist/src/intl/it.d.ts +24 -1
- package/dist/src/intl/it.d.ts.map +1 -1
- package/dist/src/intl/it.js +20 -0
- package/dist/src/intl/ja.d.ts +24 -1
- package/dist/src/intl/ja.d.ts.map +1 -1
- package/dist/src/intl/ja.js +20 -0
- package/dist/src/intl/ko.d.ts +24 -1
- package/dist/src/intl/ko.d.ts.map +1 -1
- package/dist/src/intl/ko.js +20 -0
- package/dist/src/intl/ml.d.ts +24 -1
- package/dist/src/intl/ml.d.ts.map +1 -1
- package/dist/src/intl/ml.js +20 -0
- package/dist/src/intl/mr.d.ts +24 -1
- package/dist/src/intl/mr.d.ts.map +1 -1
- package/dist/src/intl/mr.js +20 -0
- package/dist/src/intl/ms.d.ts +24 -1
- package/dist/src/intl/ms.d.ts.map +1 -1
- package/dist/src/intl/ms.js +20 -0
- package/dist/src/intl/nb.d.ts +22 -1
- package/dist/src/intl/nb.d.ts.map +1 -1
- package/dist/src/intl/nb.js +18 -0
- package/dist/src/intl/nl.d.ts +24 -1
- package/dist/src/intl/nl.d.ts.map +1 -1
- package/dist/src/intl/nl.js +20 -0
- package/dist/src/intl/pa.d.ts +24 -1
- package/dist/src/intl/pa.d.ts.map +1 -1
- package/dist/src/intl/pa.js +20 -0
- package/dist/src/intl/pl.d.ts +23 -0
- package/dist/src/intl/pl.d.ts.map +1 -1
- package/dist/src/intl/pl.js +20 -0
- package/dist/src/intl/pt-BR.d.ts +24 -1
- package/dist/src/intl/pt-BR.d.ts.map +1 -1
- package/dist/src/intl/pt-BR.js +20 -0
- package/dist/src/intl/pt.d.ts +24 -1
- package/dist/src/intl/pt.d.ts.map +1 -1
- package/dist/src/intl/pt.js +20 -0
- package/dist/src/intl/ro.d.ts +24 -1
- package/dist/src/intl/ro.d.ts.map +1 -1
- package/dist/src/intl/ro.js +20 -0
- package/dist/src/intl/sk.d.ts +24 -1
- package/dist/src/intl/sk.d.ts.map +1 -1
- package/dist/src/intl/sk.js +20 -0
- package/dist/src/intl/sl.d.ts +24 -1
- package/dist/src/intl/sl.d.ts.map +1 -1
- package/dist/src/intl/sl.js +20 -0
- package/dist/src/intl/sv.d.ts +24 -1
- package/dist/src/intl/sv.d.ts.map +1 -1
- package/dist/src/intl/sv.js +20 -0
- package/dist/src/intl/sw.d.ts +21 -0
- package/dist/src/intl/sw.d.ts.map +1 -1
- package/dist/src/intl/sw.js +18 -0
- package/dist/src/intl/ta.d.ts +24 -1
- package/dist/src/intl/ta.d.ts.map +1 -1
- package/dist/src/intl/ta.js +20 -0
- package/dist/src/intl/te.d.ts +24 -1
- package/dist/src/intl/te.d.ts.map +1 -1
- package/dist/src/intl/te.js +20 -0
- package/dist/src/intl/th.d.ts +24 -1
- package/dist/src/intl/th.d.ts.map +1 -1
- package/dist/src/intl/th.js +20 -0
- package/dist/src/intl/tr.d.ts +24 -1
- package/dist/src/intl/tr.d.ts.map +1 -1
- package/dist/src/intl/tr.js +20 -0
- package/dist/src/intl/uk.d.ts +80 -57
- package/dist/src/intl/uk.d.ts.map +1 -1
- package/dist/src/intl/uk.js +174 -149
- package/dist/src/intl/ur.d.ts +24 -1
- package/dist/src/intl/ur.d.ts.map +1 -1
- package/dist/src/intl/ur.js +20 -0
- package/dist/src/intl/vi.d.ts +24 -1
- package/dist/src/intl/vi.d.ts.map +1 -1
- package/dist/src/intl/vi.js +20 -0
- package/dist/src/intl/zh-CN.d.ts +24 -1
- package/dist/src/intl/zh-CN.d.ts.map +1 -1
- package/dist/src/intl/zh-CN.js +20 -0
- package/dist/src/intl/zh-TW.d.ts +24 -1
- package/dist/src/intl/zh-TW.d.ts.map +1 -1
- package/dist/src/intl/zh-TW.js +20 -0
- 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 +336 -211
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +102 -15
- 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 +95 -17
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +119 -39
- 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/Schema.d.ts +345 -21
- 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 +192 -14
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +82 -21
- 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 +404 -82
- 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/{Binary.test.ts → Bytes.test.ts} +286 -1
- package/src/{Binary.ts → Bytes.ts} +652 -21
- package/src/Config.test.ts +668 -0
- package/src/Config.ts +410 -0
- 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.test.ts +105 -0
- package/src/Fs.ts +488 -0
- package/src/Identicon.ts +2 -2
- package/src/LeakDetector.ts +22 -3
- package/src/LockManager.ts +8 -0
- package/src/Number.test.ts +82 -18
- package/src/Number.ts +76 -8
- package/src/Object.test.ts +139 -10
- package/src/Object.ts +49 -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 +138 -18
- package/src/Task.test.ts +189 -8
- package/src/Task.ts +56 -17
- package/src/Test.ts +9 -0
- package/src/Time.test.ts +82 -11
- package/src/Time.ts +246 -24
- package/src/Type.test.ts +3994 -1119
- package/src/Type.ts +7258 -3842
- package/src/Types.test.ts +4 -14
- package/src/WebSocket.ts +313 -40
- package/src/Worker.ts +90 -8
- package/src/index.ts +18 -7
- package/src/intl/_en.ts +70 -0
- package/src/intl/ar.ts +71 -0
- package/src/intl/bn.ts +70 -0
- package/src/intl/ca.ts +70 -0
- package/src/intl/cs.ts +70 -0
- package/src/intl/da.ts +70 -0
- package/src/intl/de.ts +70 -0
- package/src/intl/el.ts +70 -0
- package/src/intl/es.ts +70 -0
- package/src/intl/fa.ts +70 -0
- package/src/intl/fi.ts +70 -0
- package/src/intl/fil.ts +70 -0
- package/src/intl/fr.ts +70 -0
- package/src/intl/he.ts +70 -0
- package/src/intl/hi.ts +70 -0
- package/src/intl/hr.ts +70 -0
- package/src/intl/hu.ts +69 -0
- package/src/intl/id.ts +70 -0
- package/src/intl/intl.test.ts +819 -1
- package/src/intl/it.ts +70 -0
- package/src/intl/ja.ts +70 -0
- package/src/intl/ko.ts +68 -0
- package/src/intl/ml.ts +70 -0
- package/src/intl/mr.ts +70 -0
- package/src/intl/ms.ts +71 -0
- package/src/intl/nb.ts +69 -0
- package/src/intl/nl.ts +70 -0
- package/src/intl/pa.ts +70 -0
- package/src/intl/pl.ts +63 -0
- package/src/intl/pt-BR.ts +70 -0
- package/src/intl/pt.ts +71 -0
- package/src/intl/ro.ts +70 -0
- package/src/intl/sk.ts +71 -0
- package/src/intl/sl.ts +70 -0
- package/src/intl/sv.ts +70 -0
- package/src/intl/sw.ts +62 -0
- package/src/intl/ta.ts +70 -0
- package/src/intl/te.ts +70 -0
- package/src/intl/th.ts +68 -0
- package/src/intl/tr.ts +70 -0
- package/src/intl/uk.ts +228 -155
- package/src/intl/ur.ts +70 -0
- package/src/intl/vi.ts +70 -0
- package/src/intl/zh-CN.ts +68 -0
- package/src/intl/zh-TW.ts +68 -0
- package/src/local-first/Db.ts +644 -339
- package/src/local-first/Evolu.test.ts +686 -21
- package/src/local-first/Evolu.ts +450 -228
- package/src/local-first/Owner.ts +13 -30
- package/src/local-first/Protocol.test.ts +618 -11
- package/src/local-first/Protocol.ts +197 -73
- package/src/local-first/Query.ts +8 -15
- package/src/local-first/Schema.test.ts +143 -0
- package/src/local-first/Schema.ts +374 -24
- package/src/local-first/Shared.test.ts +7731 -559
- package/src/local-first/Shared.ts +2036 -267
- package/src/local-first/Storage.ts +219 -33
- package/src/local-first/Timestamp.test.ts +344 -70
- package/src/local-first/Timestamp.ts +435 -119
- package/src/local-first/index.ts +0 -1
- package/dist/src/Binary.d.ts +0 -254
- package/dist/src/Binary.d.ts.map +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
package/src/local-first/Evolu.ts
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
import { createCallbacks } from "../Callbacks.ts";
|
|
19
19
|
import type { ConsoleDep } from "../Console.ts";
|
|
20
20
|
import { createConsole } from "../Console.ts";
|
|
21
|
+
import type { DecryptWithXChaCha20Poly1305Error } from "../Crypto.ts";
|
|
22
|
+
import type { UnknownError } from "../Error.ts";
|
|
21
23
|
import { createUnknownError } from "../Error.ts";
|
|
22
24
|
import { disposable, exhaustiveCheck, todo } from "../Function.ts";
|
|
23
25
|
import {
|
|
@@ -45,11 +47,11 @@ import {
|
|
|
45
47
|
UrlSafeString,
|
|
46
48
|
} from "../Type.ts";
|
|
47
49
|
import type {
|
|
50
|
+
BroadcastChannel,
|
|
48
51
|
CreateBroadcastChannelDep,
|
|
49
52
|
CreateMessageChannelDep,
|
|
50
53
|
} from "../Worker.ts";
|
|
51
|
-
import type { CreateDbWorkerDep } from "./Db.ts";
|
|
52
|
-
import type { EvoluError } from "./Error.ts";
|
|
54
|
+
import type { CreateDbWorkerDep, UnsupportedDbVersionError } from "./Db.ts";
|
|
53
55
|
import type {
|
|
54
56
|
AppOwner,
|
|
55
57
|
Owner,
|
|
@@ -59,6 +61,7 @@ import type {
|
|
|
59
61
|
SyncOwner,
|
|
60
62
|
} from "./Owner.ts";
|
|
61
63
|
import { createOwnerWebSocketTransport } from "./Owner.ts";
|
|
64
|
+
import type { ProtocolError, ProtocolQuotaError } from "./Protocol.ts";
|
|
62
65
|
import type {
|
|
63
66
|
Queries,
|
|
64
67
|
QueriesToQueryRowsPromises,
|
|
@@ -73,6 +76,7 @@ import type {
|
|
|
73
76
|
IndexesConfig,
|
|
74
77
|
Mutation,
|
|
75
78
|
MutationChange,
|
|
79
|
+
MutationOptions,
|
|
76
80
|
ValidateSchema,
|
|
77
81
|
} from "./Schema.ts";
|
|
78
82
|
import { evoluSchemaToSqliteSchema } from "./Schema.ts";
|
|
@@ -80,23 +84,41 @@ import type {
|
|
|
80
84
|
ConsoleEntryOrError,
|
|
81
85
|
EvoluInput,
|
|
82
86
|
EvoluOutput,
|
|
87
|
+
OtherBuildRunningError,
|
|
83
88
|
SharedWorkerDep,
|
|
89
|
+
SyncState,
|
|
90
|
+
syncStateToOwnerSyncStates,
|
|
84
91
|
} from "./Shared.ts";
|
|
85
92
|
import { consoleEntryOrErrorBroadcastChannelName } from "./Shared.ts";
|
|
86
|
-
import { DbChange } from "./Storage.ts";
|
|
93
|
+
import { DbChange, type StorageQuotaError } from "./Storage.ts";
|
|
87
94
|
import type { Timestamp } from "./Timestamp.ts";
|
|
88
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Configuration for {@link createEvolu}.
|
|
98
|
+
*
|
|
99
|
+
* @group Configuration
|
|
100
|
+
*/
|
|
89
101
|
export interface EvoluConfig {
|
|
90
102
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
103
|
+
* An application name used in logs and local database names.
|
|
104
|
+
*
|
|
105
|
+
* Evolu combines `appName` with the {@link AppOwner} identity to identify the
|
|
106
|
+
* local database. Changing either opens a different database. Keep `appName`
|
|
107
|
+
* stable across ordinary application updates.
|
|
94
108
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
109
|
+
* Instances that share an `appName` and AppOwner open the same database and
|
|
110
|
+
* must use the same schema. Evolu applies the schema of the first instance
|
|
111
|
+
* and later instances join it.
|
|
112
|
+
*
|
|
113
|
+
* A different app name lets you create a separate local replica for the same
|
|
114
|
+
* AppOwner—for example, to test a different SQLite implementation or index
|
|
115
|
+
* configuration, or rebuild a replica for debugging while preserving the
|
|
116
|
+
* existing database.
|
|
117
|
+
*
|
|
118
|
+
* Evolu supports running these databases concurrently. Their local separation
|
|
119
|
+
* does not isolate synchronization: replicas synchronizing the same owners
|
|
120
|
+
* through the same relay can still exchange changes. Changing `appName`
|
|
121
|
+
* neither migrates existing local data nor isolates incompatible schemas.
|
|
100
122
|
*
|
|
101
123
|
* ### Example
|
|
102
124
|
*
|
|
@@ -132,6 +154,73 @@ export interface EvoluConfig {
|
|
|
132
154
|
*/
|
|
133
155
|
readonly appOwner: AppOwner;
|
|
134
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Keep the database in memory instead of persisting it on this device.
|
|
159
|
+
*
|
|
160
|
+
* This option controls device persistence independently of synchronization.
|
|
161
|
+
* When synchronization is enabled, data can still be synchronized and
|
|
162
|
+
* persisted remotely.
|
|
163
|
+
*
|
|
164
|
+
* Useful for testing or temporary sessions. Data that exists only in this
|
|
165
|
+
* database is lost when the database closes.
|
|
166
|
+
*
|
|
167
|
+
* The default value is: `false`.
|
|
168
|
+
*/
|
|
169
|
+
readonly memoryOnly?: boolean;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Use the `indexes` option to define SQLite indexes.
|
|
173
|
+
*
|
|
174
|
+
* Table and column names are not typed because Kysely doesn't support it.
|
|
175
|
+
*
|
|
176
|
+
* https://medium.com/@JasonWyatt/squeezing-performance-from-sqlite-indexes-indexes-c4e175f3c346
|
|
177
|
+
*
|
|
178
|
+
* ### Example
|
|
179
|
+
*
|
|
180
|
+
* ```ts
|
|
181
|
+
* import {
|
|
182
|
+
* createEvolu,
|
|
183
|
+
* id,
|
|
184
|
+
* testAppName,
|
|
185
|
+
* testAppOwner,
|
|
186
|
+
* } from "@evolu/common";
|
|
187
|
+
*
|
|
188
|
+
* const Schema = {
|
|
189
|
+
* todo: { id: id("Todo") },
|
|
190
|
+
* todoCategory: { id: id("TodoCategory") },
|
|
191
|
+
* };
|
|
192
|
+
*
|
|
193
|
+
* const _createTodoEvolu = createEvolu(Schema, {
|
|
194
|
+
* appName: testAppName,
|
|
195
|
+
* appOwner: testAppOwner,
|
|
196
|
+
* transports: [],
|
|
197
|
+
* indexes: (create) => [
|
|
198
|
+
* create("todoCreatedAt").on("todo").column("createdAt"),
|
|
199
|
+
* create("todoCategoryCreatedAt")
|
|
200
|
+
* .on("todoCategory")
|
|
201
|
+
* .column("createdAt"),
|
|
202
|
+
* ],
|
|
203
|
+
* });
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
readonly indexes?: IndexesConfig;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Called when this instance's local database is deleted.
|
|
210
|
+
*
|
|
211
|
+
* Apps can use this to update UI immediately because the corresponding
|
|
212
|
+
* {@link Evolu} instance becomes unusable after local database deletion.
|
|
213
|
+
*/
|
|
214
|
+
readonly onDatabaseDeleted?: () => void;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Called when local data for an {@link Owner} is deleted.
|
|
218
|
+
*
|
|
219
|
+
* Apps can use this to update UI immediately because that owner stops being
|
|
220
|
+
* used across tabs and instances.
|
|
221
|
+
*/
|
|
222
|
+
readonly onOwnerDeleted?: (owner: Owner) => void;
|
|
223
|
+
|
|
135
224
|
/**
|
|
136
225
|
* Transport configuration for sync and backup.
|
|
137
226
|
*
|
|
@@ -169,18 +258,11 @@ export interface EvoluConfig {
|
|
|
169
258
|
* ```ts
|
|
170
259
|
* import {
|
|
171
260
|
* assertEqual,
|
|
172
|
-
* createAppOwner,
|
|
173
261
|
* createOwnerWebSocketTransport,
|
|
174
|
-
*
|
|
175
|
-
* createRandomBytes,
|
|
262
|
+
* testAppOwner,
|
|
176
263
|
* type OwnerTransport,
|
|
177
264
|
* } from "@evolu/common";
|
|
178
265
|
*
|
|
179
|
-
* // Create once, persist the mnemonic securely, and restore it on later runs.
|
|
180
|
-
* const appOwner = createAppOwner(
|
|
181
|
-
* createOwnerSecret({ randomBytes: createRandomBytes() }),
|
|
182
|
-
* );
|
|
183
|
-
*
|
|
184
266
|
* // Use one relay.
|
|
185
267
|
* const _singleRelay = [
|
|
186
268
|
* { type: "WebSocket", url: "wss://relay1.example.com" },
|
|
@@ -201,91 +283,17 @@ export interface EvoluConfig {
|
|
|
201
283
|
* const authenticatedRelay = [
|
|
202
284
|
* createOwnerWebSocketTransport({
|
|
203
285
|
* url: "wss://relay.example.com",
|
|
204
|
-
* ownerId:
|
|
286
|
+
* ownerId: testAppOwner.id,
|
|
205
287
|
* }),
|
|
206
288
|
* ];
|
|
207
289
|
*
|
|
208
290
|
* assertEqual(
|
|
209
291
|
* authenticatedRelay[0]?.url,
|
|
210
|
-
* `wss://relay.example.com?ownerId=${
|
|
292
|
+
* `wss://relay.example.com?ownerId=${testAppOwner.id}`,
|
|
211
293
|
* );
|
|
212
294
|
* ```
|
|
213
295
|
*/
|
|
214
296
|
readonly transports?: ReadonlyArray<OwnerTransport>;
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Keep local data only in memory instead of persisting it on this device.
|
|
218
|
-
* Useful for testing, temporary data, or sensitive data that should not be
|
|
219
|
-
* recoverable from local storage after the process ends.
|
|
220
|
-
*
|
|
221
|
-
* Local data stored in memory is completely destroyed when the process ends.
|
|
222
|
-
* Sync can still persist data remotely when transports are enabled.
|
|
223
|
-
*
|
|
224
|
-
* The default value is: `false`.
|
|
225
|
-
*/
|
|
226
|
-
readonly memoryOnly?: boolean;
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Use the `indexes` option to define SQLite indexes.
|
|
230
|
-
*
|
|
231
|
-
* Table and column names are not typed because Kysely doesn't support it.
|
|
232
|
-
*
|
|
233
|
-
* https://medium.com/@JasonWyatt/squeezing-performance-from-sqlite-indexes-indexes-c4e175f3c346
|
|
234
|
-
*
|
|
235
|
-
* ### Example
|
|
236
|
-
*
|
|
237
|
-
* ```ts
|
|
238
|
-
* import {
|
|
239
|
-
* AppName,
|
|
240
|
-
* assertTrue,
|
|
241
|
-
* createAppOwner,
|
|
242
|
-
* createEvolu,
|
|
243
|
-
* createOwnerSecret,
|
|
244
|
-
* createRandomBytes,
|
|
245
|
-
* id,
|
|
246
|
-
* } from "@evolu/common";
|
|
247
|
-
*
|
|
248
|
-
* const Schema = {
|
|
249
|
-
* todo: { id: id("Todo") },
|
|
250
|
-
* todoCategory: { id: id("TodoCategory") },
|
|
251
|
-
* };
|
|
252
|
-
* // Create once, persist the mnemonic securely, and restore it on later runs.
|
|
253
|
-
* const appOwner = createAppOwner(
|
|
254
|
-
* createOwnerSecret({ randomBytes: createRandomBytes() }),
|
|
255
|
-
* );
|
|
256
|
-
*
|
|
257
|
-
* const createTodoEvolu = createEvolu(Schema, {
|
|
258
|
-
* appName: AppName.orThrow("IndexedTodos"),
|
|
259
|
-
* appOwner,
|
|
260
|
-
* transports: [],
|
|
261
|
-
* indexes: (create) => [
|
|
262
|
-
* create("todoCreatedAt").on("todo").column("createdAt"),
|
|
263
|
-
* create("todoCategoryCreatedAt")
|
|
264
|
-
* .on("todoCategory")
|
|
265
|
-
* .column("createdAt"),
|
|
266
|
-
* ],
|
|
267
|
-
* });
|
|
268
|
-
*
|
|
269
|
-
* assertTrue(typeof createTodoEvolu === "function");
|
|
270
|
-
* ```
|
|
271
|
-
*/
|
|
272
|
-
readonly indexes?: IndexesConfig;
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Called when this instance's local database is deleted.
|
|
276
|
-
*
|
|
277
|
-
* Apps can use this to update UI immediately because the corresponding
|
|
278
|
-
* {@link Evolu} instance becomes unusable after local database deletion.
|
|
279
|
-
*/
|
|
280
|
-
readonly onDatabaseDeleted?: () => void;
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Called when local data for an {@link Owner} is deleted.
|
|
284
|
-
*
|
|
285
|
-
* Apps can use this to update UI immediately because that owner stops being
|
|
286
|
-
* used across tabs and instances.
|
|
287
|
-
*/
|
|
288
|
-
readonly onOwnerDeleted?: (owner: Owner) => void;
|
|
289
297
|
}
|
|
290
298
|
|
|
291
299
|
/**
|
|
@@ -297,6 +305,8 @@ export interface EvoluConfig {
|
|
|
297
305
|
*
|
|
298
306
|
* Uses the same safe alphabet as {@link UrlSafeString} (letters, digits, `-`,
|
|
299
307
|
* `_`) and must be between 1 and 41 characters.
|
|
308
|
+
*
|
|
309
|
+
* @group Configuration
|
|
300
310
|
*/
|
|
301
311
|
export const AppName = /*#__PURE__*/ brand(
|
|
302
312
|
"AppName",
|
|
@@ -309,16 +319,35 @@ export const AppName = /*#__PURE__*/ brand(
|
|
|
309
319
|
`The value ${JSON.stringify(error.value)} is not between 1 and 41 characters.`,
|
|
310
320
|
);
|
|
311
321
|
export type AppName = typeof AppName.Output;
|
|
322
|
+
/**
|
|
323
|
+
* Error produced when a value is not a valid {@link AppName}.
|
|
324
|
+
*
|
|
325
|
+
* @group Configuration
|
|
326
|
+
*/
|
|
312
327
|
export interface AppNameError extends TypeError<"AppName"> {
|
|
313
328
|
readonly value: UrlSafeString;
|
|
314
329
|
}
|
|
315
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Stable valid {@link AppName} for tests and examples.
|
|
333
|
+
*
|
|
334
|
+
* @group Testing
|
|
335
|
+
*/
|
|
316
336
|
export const testAppName = /*#__PURE__*/ AppName.orThrow("AppName");
|
|
317
337
|
|
|
318
338
|
/**
|
|
319
|
-
*
|
|
339
|
+
* A local-first SQL database.
|
|
340
|
+
*
|
|
341
|
+
* Stores application data in SQLite on the device, so reads and writes work
|
|
342
|
+
* offline. Provides typed queries, mutations, and reactive subscriptions, with
|
|
343
|
+
* synchronization between devices. Persistent SQLite is encrypted with the
|
|
344
|
+
* {@link AppOwner} key, and synchronized data is end-to-end encrypted.
|
|
320
345
|
*
|
|
321
|
-
*
|
|
346
|
+
* Tables whose names start with `_` stay local, even when the instance
|
|
347
|
+
* synchronizes other data. Use them for device-local data such as application
|
|
348
|
+
* settings or an app-owner registry.
|
|
349
|
+
*
|
|
350
|
+
* @group Core
|
|
322
351
|
*/
|
|
323
352
|
export interface Evolu<
|
|
324
353
|
S extends EvoluSchema = EvoluSchema,
|
|
@@ -340,32 +369,29 @@ export interface Evolu<
|
|
|
340
369
|
* every row has a globally unique, conflict-free identifier without
|
|
341
370
|
* coordination.
|
|
342
371
|
*
|
|
343
|
-
* Pass `onComplete` when follow-up work must wait until the mutation
|
|
344
|
-
*
|
|
372
|
+
* Pass `onComplete` when follow-up work must wait until the mutation is
|
|
373
|
+
* stored and subscribed queries reflect it. It never runs when the database
|
|
374
|
+
* is unavailable; see {@link Evolu.loadQuery}. A stored change is not always
|
|
375
|
+
* visible; see {@link MutationOptions.onComplete}.
|
|
345
376
|
*
|
|
346
377
|
* ### Example
|
|
347
378
|
*
|
|
348
379
|
* ```ts
|
|
349
380
|
* import {
|
|
350
381
|
* assertType,
|
|
351
|
-
*
|
|
382
|
+
* type TestEvoluSchema,
|
|
352
383
|
* NonEmptyTrimmedString100,
|
|
353
384
|
* type Evolu,
|
|
385
|
+
* type TestTodoId,
|
|
354
386
|
* } from "@evolu/common";
|
|
355
387
|
*
|
|
356
|
-
* const
|
|
357
|
-
* type TodoId = typeof TodoId.Output;
|
|
358
|
-
* const Schema = {
|
|
359
|
-
* todo: { id: TodoId, title: NonEmptyTrimmedString100 },
|
|
360
|
-
* };
|
|
361
|
-
*
|
|
362
|
-
* const insertTodo = (evolu: Evolu<typeof Schema>) =>
|
|
388
|
+
* const insertTodo = (evolu: Evolu<TestEvoluSchema>) =>
|
|
363
389
|
* evolu.insert("todo", {
|
|
364
390
|
* title: NonEmptyTrimmedString100.orThrow("Learn Evolu"),
|
|
365
391
|
* }).id;
|
|
366
392
|
*
|
|
367
393
|
* const insertTodoAndNotify = (
|
|
368
|
-
* evolu: Evolu<
|
|
394
|
+
* evolu: Evolu<TestEvoluSchema>,
|
|
369
395
|
* onComplete: () => void,
|
|
370
396
|
* ) =>
|
|
371
397
|
* evolu.insert(
|
|
@@ -379,7 +405,7 @@ export interface Evolu<
|
|
|
379
405
|
* ReturnType<typeof insertTodo>,
|
|
380
406
|
* ReturnType<typeof insertTodoAndNotify>,
|
|
381
407
|
* ],
|
|
382
|
-
* [
|
|
408
|
+
* [TestTodoId, TestTodoId]
|
|
383
409
|
* >();
|
|
384
410
|
* ```
|
|
385
411
|
*
|
|
@@ -397,30 +423,30 @@ export interface Evolu<
|
|
|
397
423
|
* ```ts
|
|
398
424
|
* import {
|
|
399
425
|
* assertType,
|
|
400
|
-
*
|
|
426
|
+
* type TestEvoluSchema,
|
|
401
427
|
* NonEmptyTrimmedString100,
|
|
402
428
|
* sqliteTrue,
|
|
403
429
|
* type Evolu,
|
|
430
|
+
* type TestTodoId,
|
|
404
431
|
* } from "@evolu/common";
|
|
405
432
|
*
|
|
406
|
-
* const
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
* };
|
|
411
|
-
*
|
|
412
|
-
* const renameTodo = (evolu: Evolu<typeof Schema>, todoId: TodoId) =>
|
|
433
|
+
* const renameTodo = (
|
|
434
|
+
* evolu: Evolu<TestEvoluSchema>,
|
|
435
|
+
* todoId: TestTodoId,
|
|
436
|
+
* ) =>
|
|
413
437
|
* evolu.update("todo", {
|
|
414
438
|
* id: todoId,
|
|
415
439
|
* title: NonEmptyTrimmedString100.orThrow("Updated title"),
|
|
416
440
|
* }).id;
|
|
417
441
|
*
|
|
418
|
-
* const softDeleteTodo = (
|
|
419
|
-
* evolu
|
|
442
|
+
* const softDeleteTodo = (
|
|
443
|
+
* evolu: Evolu<TestEvoluSchema>,
|
|
444
|
+
* todoId: TestTodoId,
|
|
445
|
+
* ) => evolu.update("todo", { id: todoId, isDeleted: sqliteTrue }).id;
|
|
420
446
|
*
|
|
421
447
|
* assertType<
|
|
422
448
|
* [ReturnType<typeof renameTodo>, ReturnType<typeof softDeleteTodo>],
|
|
423
|
-
* [
|
|
449
|
+
* [TestTodoId, TestTodoId]
|
|
424
450
|
* >();
|
|
425
451
|
* ```
|
|
426
452
|
*
|
|
@@ -446,24 +472,20 @@ export interface Evolu<
|
|
|
446
472
|
* import {
|
|
447
473
|
* assertType,
|
|
448
474
|
* createIdFromString,
|
|
449
|
-
*
|
|
475
|
+
* type TestEvoluSchema,
|
|
450
476
|
* NonEmptyTrimmedString100,
|
|
451
477
|
* type Evolu,
|
|
478
|
+
* TestTodoId,
|
|
452
479
|
* } from "@evolu/common";
|
|
453
480
|
*
|
|
454
|
-
* const
|
|
455
|
-
*
|
|
456
|
-
* const Schema = {
|
|
457
|
-
* todo: { id: TodoId, title: NonEmptyTrimmedString100 },
|
|
458
|
-
* };
|
|
459
|
-
* const stableId = TodoId.orThrow(createIdFromString("my-todo-1"));
|
|
460
|
-
* const upsertTodo = (evolu: Evolu<typeof Schema>) =>
|
|
481
|
+
* const stableId = TestTodoId.orThrow(createIdFromString("my-todo-1"));
|
|
482
|
+
* const upsertTodo = (evolu: Evolu<TestEvoluSchema>) =>
|
|
461
483
|
* evolu.upsert("todo", {
|
|
462
484
|
* id: stableId,
|
|
463
485
|
* title: NonEmptyTrimmedString100.orThrow("Learn Evolu"),
|
|
464
486
|
* }).id;
|
|
465
487
|
*
|
|
466
|
-
* assertType<ReturnType<typeof upsertTodo>,
|
|
488
|
+
* assertType<ReturnType<typeof upsertTodo>, TestTodoId>();
|
|
467
489
|
* ```
|
|
468
490
|
*
|
|
469
491
|
* @see {@link Mutation}
|
|
@@ -473,14 +495,19 @@ export interface Evolu<
|
|
|
473
495
|
/**
|
|
474
496
|
* Load {@link Query} and return a promise with {@link QueryRows}.
|
|
475
497
|
*
|
|
476
|
-
* The returned promise always resolves successfully because
|
|
477
|
-
*
|
|
478
|
-
*
|
|
498
|
+
* The returned promise always resolves successfully because all data are
|
|
499
|
+
* local and the query is typed. If the database refuses startup, unanswered
|
|
500
|
+
* loads stay pending until disposal resolves them with empty rows. Observe
|
|
501
|
+
* {@link EvoluErrorDep.evoluError} to display the refusal independently of
|
|
502
|
+
* query loading.
|
|
479
503
|
*
|
|
480
504
|
* Loading is batched. Returned promises are cached while pending and can be
|
|
481
|
-
* reused after fulfillment
|
|
482
|
-
*
|
|
483
|
-
*
|
|
505
|
+
* reused after fulfillment, which prevents redundant database queries and
|
|
506
|
+
* supports React Suspense (stable references while pending). A mutation or
|
|
507
|
+
* incoming sync invalidates the cache of an unsubscribed query, so its next
|
|
508
|
+
* load reads again. A subscribed query keeps its cached rows and the
|
|
509
|
+
* subscription refreshes them, so a load can return rows that a pending
|
|
510
|
+
* refresh is about to replace.
|
|
484
511
|
*
|
|
485
512
|
* To subscribe a query for automatic updates, use
|
|
486
513
|
* {@link Evolu.subscribeQuery}.
|
|
@@ -491,20 +518,17 @@ export interface Evolu<
|
|
|
491
518
|
* import {
|
|
492
519
|
* assertType,
|
|
493
520
|
* createQueryBuilder,
|
|
494
|
-
*
|
|
495
|
-
*
|
|
521
|
+
* testEvoluSchema,
|
|
522
|
+
* type TestEvoluSchema,
|
|
496
523
|
* type Evolu,
|
|
497
524
|
* type QueryRows,
|
|
498
525
|
* } from "@evolu/common";
|
|
499
526
|
*
|
|
500
|
-
* const
|
|
501
|
-
* todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
|
|
502
|
-
* };
|
|
503
|
-
* const createQuery = createQueryBuilder(Schema);
|
|
527
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
504
528
|
* const allTodos = createQuery((db) =>
|
|
505
529
|
* db.selectFrom("todo").selectAll(),
|
|
506
530
|
* );
|
|
507
|
-
* const loadTodos = async (evolu: Evolu<
|
|
531
|
+
* const loadTodos = async (evolu: Evolu<TestEvoluSchema>) => {
|
|
508
532
|
* const rows = await evolu.loadQuery(allTodos);
|
|
509
533
|
* return rows;
|
|
510
534
|
* };
|
|
@@ -529,31 +553,22 @@ export interface Evolu<
|
|
|
529
553
|
* ```ts
|
|
530
554
|
* import {
|
|
531
555
|
* assertType,
|
|
532
|
-
* createIdFromString,
|
|
533
556
|
* createQueryBuilder,
|
|
534
|
-
*
|
|
535
|
-
*
|
|
557
|
+
* testEvoluSchema,
|
|
558
|
+
* type TestEvoluSchema,
|
|
559
|
+
* testTodoId,
|
|
536
560
|
* type Evolu,
|
|
537
561
|
* type QueryRows,
|
|
538
562
|
* } from "@evolu/common";
|
|
539
563
|
*
|
|
540
|
-
* const
|
|
541
|
-
* type TodoId = typeof TodoId.Output;
|
|
542
|
-
* const Schema = {
|
|
543
|
-
* todo: { id: TodoId, title: NonEmptyTrimmedString100 },
|
|
544
|
-
* };
|
|
545
|
-
* const createQuery = createQueryBuilder(Schema);
|
|
564
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
546
565
|
* const allTodos = createQuery((db) =>
|
|
547
566
|
* db.selectFrom("todo").select(["id", "title"]),
|
|
548
567
|
* );
|
|
549
|
-
* const
|
|
550
|
-
*
|
|
551
|
-
* db.selectFrom("todo").select("title").where("id", "=", todoId),
|
|
552
|
-
* );
|
|
553
|
-
* const firstTodo = todoById(
|
|
554
|
-
* TodoId.orThrow(createIdFromString("first-todo")),
|
|
568
|
+
* const firstTodo = createQuery((db) =>
|
|
569
|
+
* db.selectFrom("todo").select("title").where("id", "=", testTodoId),
|
|
555
570
|
* );
|
|
556
|
-
* const loadTodoQueries = (evolu: Evolu<
|
|
571
|
+
* const loadTodoQueries = (evolu: Evolu<TestEvoluSchema>) =>
|
|
557
572
|
* evolu.loadQueries([allTodos, firstTodo]);
|
|
558
573
|
*
|
|
559
574
|
* assertType<
|
|
@@ -572,28 +587,28 @@ export interface Evolu<
|
|
|
572
587
|
/**
|
|
573
588
|
* Subscribe to {@link Query} {@link QueryRows} changes.
|
|
574
589
|
*
|
|
590
|
+
* Cached rows invalidated before subscription are refreshed. Subscribing
|
|
591
|
+
* alone does not load a query that has no cached rows.
|
|
592
|
+
*
|
|
575
593
|
* ### Example
|
|
576
594
|
*
|
|
577
595
|
* ```ts
|
|
578
596
|
* import {
|
|
579
597
|
* assertType,
|
|
580
598
|
* createQueryBuilder,
|
|
581
|
-
*
|
|
582
|
-
*
|
|
599
|
+
* testEvoluSchema,
|
|
600
|
+
* type TestEvoluSchema,
|
|
583
601
|
* type Evolu,
|
|
584
602
|
* type QueryRows,
|
|
585
603
|
* type Unsubscribe,
|
|
586
604
|
* } from "@evolu/common";
|
|
587
605
|
*
|
|
588
|
-
* const
|
|
589
|
-
* todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
|
|
590
|
-
* };
|
|
591
|
-
* const createQuery = createQueryBuilder(Schema);
|
|
606
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
592
607
|
* const allTodos = createQuery((db) =>
|
|
593
608
|
* db.selectFrom("todo").select("title"),
|
|
594
609
|
* );
|
|
595
610
|
* const subscribeToTodos = (
|
|
596
|
-
* evolu: Evolu<
|
|
611
|
+
* evolu: Evolu<TestEvoluSchema>,
|
|
597
612
|
* onRows: (rows: QueryRows<typeof allTodos.Row>) => void,
|
|
598
613
|
* ) =>
|
|
599
614
|
* evolu.subscribeQuery(allTodos)(() => {
|
|
@@ -616,20 +631,17 @@ export interface Evolu<
|
|
|
616
631
|
* import {
|
|
617
632
|
* assertType,
|
|
618
633
|
* createQueryBuilder,
|
|
619
|
-
*
|
|
620
|
-
*
|
|
634
|
+
* testEvoluSchema,
|
|
635
|
+
* type TestEvoluSchema,
|
|
621
636
|
* type Evolu,
|
|
622
637
|
* type QueryRows,
|
|
623
638
|
* } from "@evolu/common";
|
|
624
639
|
*
|
|
625
|
-
* const
|
|
626
|
-
* todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
|
|
627
|
-
* };
|
|
628
|
-
* const createQuery = createQueryBuilder(Schema);
|
|
640
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
629
641
|
* const allTodos = createQuery((db) =>
|
|
630
642
|
* db.selectFrom("todo").select("title"),
|
|
631
643
|
* );
|
|
632
|
-
* const getTodos = (evolu: Evolu<
|
|
644
|
+
* const getTodos = (evolu: Evolu<TestEvoluSchema>) =>
|
|
633
645
|
* evolu.getQueryRows(allTodos);
|
|
634
646
|
*
|
|
635
647
|
* assertType<
|
|
@@ -647,7 +659,8 @@ export interface Evolu<
|
|
|
647
659
|
* of starting parallel exports.
|
|
648
660
|
*
|
|
649
661
|
* The pending promise rejects if this {@link Evolu} instance is disposed
|
|
650
|
-
* before export completion.
|
|
662
|
+
* before export completion. If the database refuses startup, export stays
|
|
663
|
+
* pending until disposal; see {@link Evolu.loadQuery}.
|
|
651
664
|
*/
|
|
652
665
|
readonly exportDatabase: () => Promise<Uint8Array<ArrayBuffer>>;
|
|
653
666
|
|
|
@@ -703,21 +716,15 @@ export interface Evolu<
|
|
|
703
716
|
* ```ts
|
|
704
717
|
* import {
|
|
705
718
|
* assertEqual,
|
|
706
|
-
* createAppOwner,
|
|
707
719
|
* createOwnerWebSocketTransport,
|
|
708
|
-
* createOwnerSecret,
|
|
709
|
-
* createRandomBytes,
|
|
710
720
|
* deriveShardOwner,
|
|
721
|
+
* testAppOwner,
|
|
711
722
|
* type Evolu,
|
|
712
723
|
* type ReadonlyOwner,
|
|
713
724
|
* type UnuseOwner,
|
|
714
725
|
* } from "@evolu/common";
|
|
715
726
|
*
|
|
716
|
-
*
|
|
717
|
-
* const appOwner = createAppOwner(
|
|
718
|
-
* createOwnerSecret({ randomBytes: createRandomBytes() }),
|
|
719
|
-
* );
|
|
720
|
-
* const shardOwner = deriveShardOwner(appOwner, ["todos", 1]);
|
|
727
|
+
* const shardOwner = deriveShardOwner(testAppOwner, ["todos", 1]);
|
|
721
728
|
* const shardTransport = createOwnerWebSocketTransport({
|
|
722
729
|
* url: "wss://relay.example.com",
|
|
723
730
|
* ownerId: shardOwner.id,
|
|
@@ -753,17 +760,90 @@ export interface Evolu<
|
|
|
753
760
|
owner: ReadonlyOwner | Owner,
|
|
754
761
|
transports?: NonEmptyReadonlyArray<OwnerTransport>,
|
|
755
762
|
) => UnuseOwner;
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Requests a new synchronization round for an active {@link OwnerId}.
|
|
766
|
+
*
|
|
767
|
+
* Reconciles locally stored changes, including writes previously rejected
|
|
768
|
+
* with {@link ProtocolQuotaError}, through the owner's active transports. Call
|
|
769
|
+
* this after the relay provider confirms additional quota is available.
|
|
770
|
+
* Existing connections and {@link Evolu.useOwner} registrations are retained,
|
|
771
|
+
* including registrations shared by multiple instances or tabs.
|
|
772
|
+
*
|
|
773
|
+
* The owner must have an active writable registration in this database.
|
|
774
|
+
* Unregistered or read-only owners are ignored. Requests are skipped while
|
|
775
|
+
* all of the owner's transports are closed; they synchronize when they
|
|
776
|
+
* reopen. Disposal drops locally buffered requests. Calling a disposed
|
|
777
|
+
* instance throws, like other Evolu operations.
|
|
778
|
+
*
|
|
779
|
+
* Returns immediately, without waiting for synchronization to complete.
|
|
780
|
+
* Errors are reported through {@link EvoluErrorDep.evoluError}.
|
|
781
|
+
*
|
|
782
|
+
* ### Example
|
|
783
|
+
*
|
|
784
|
+
* ```ts
|
|
785
|
+
* import { assertType, type Evolu, type OwnerId } from "@evolu/common";
|
|
786
|
+
*
|
|
787
|
+
* // Call after successfully increasing the affected owner's relay quota.
|
|
788
|
+
* const onQuotaIncreased = (evolu: Evolu, ownerId: OwnerId) => {
|
|
789
|
+
* evolu.requestSync(ownerId);
|
|
790
|
+
* };
|
|
791
|
+
*
|
|
792
|
+
* assertType<
|
|
793
|
+
* typeof onQuotaIncreased,
|
|
794
|
+
* (evolu: Evolu, ownerId: OwnerId) => void
|
|
795
|
+
* >();
|
|
796
|
+
* ```
|
|
797
|
+
*/
|
|
798
|
+
readonly requestSync: (ownerId: OwnerId) => void;
|
|
756
799
|
}
|
|
757
800
|
|
|
758
|
-
/**
|
|
801
|
+
/**
|
|
802
|
+
* Function returned by {@link Evolu.useOwner} to stop using an Owner for sync.
|
|
803
|
+
*
|
|
804
|
+
* @group Core
|
|
805
|
+
*/
|
|
759
806
|
export type UnuseOwner = () => void;
|
|
760
807
|
|
|
808
|
+
/**
|
|
809
|
+
* Represents errors that can occur in {@link Evolu}.
|
|
810
|
+
*
|
|
811
|
+
* @group Core
|
|
812
|
+
*/
|
|
813
|
+
export type EvoluError =
|
|
814
|
+
| DecryptWithXChaCha20Poly1305Error
|
|
815
|
+
| OtherBuildRunningError
|
|
816
|
+
| ProtocolError
|
|
817
|
+
| StorageQuotaError
|
|
818
|
+
| UnknownError
|
|
819
|
+
| UnsupportedDbVersionError;
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Dependency wrapper for the shared {@link EvoluError} store.
|
|
823
|
+
*
|
|
824
|
+
* @group Construction
|
|
825
|
+
*/
|
|
761
826
|
export interface EvoluErrorDep {
|
|
762
827
|
/**
|
|
763
828
|
* {@link ReadonlyStore} of {@link EvoluError} shared by all {@link Evolu}
|
|
764
829
|
* instances created from the same {@link createEvoluDeps} result.
|
|
765
830
|
*
|
|
831
|
+
* Starts at `null` and otherwise holds the latest reported error until the
|
|
832
|
+
* first {@link UnsupportedDbVersionError}. That refusal remains for the
|
|
833
|
+
* lifetime of these dependencies, even if a tenant is disposed and recreated.
|
|
834
|
+
* Later errors are still logged but do not replace it or notify this store's
|
|
835
|
+
* subscribers. Fresh dependencies start with a fresh error store. An
|
|
836
|
+
* {@link OtherBuildRunningError} reports a wait, so the store returns to
|
|
837
|
+
* `null` when the wait ends, unless another error replaced it.
|
|
838
|
+
*
|
|
766
839
|
* Subscribe once to show user-facing error messages across all instances.
|
|
840
|
+
* While a refused database's tenant remains alive, the SharedWorker sends the
|
|
841
|
+
* refusal to each tab once, including tabs that connect later, and starts no
|
|
842
|
+
* replacement database workers. After all instances release that tenant and
|
|
843
|
+
* it is disposed when idle, creating another instance retries startup and may
|
|
844
|
+
* send the refusal again. On the web, a refused tab first reloads once
|
|
845
|
+
* instead; see {@link UnsupportedDbVersionError}. Show that blocking message
|
|
846
|
+
* outside any query-loading boundary, so pending queries do not hide it.
|
|
767
847
|
*
|
|
768
848
|
* ### Example
|
|
769
849
|
*
|
|
@@ -771,60 +851,113 @@ export interface EvoluErrorDep {
|
|
|
771
851
|
* import {
|
|
772
852
|
* assertEqual,
|
|
773
853
|
* createStore,
|
|
774
|
-
* Millis,
|
|
775
854
|
* type EvoluError,
|
|
776
855
|
* } from "@evolu/common";
|
|
777
856
|
* import type { EvoluErrorDep } from "@evolu/common/local-first";
|
|
778
857
|
*
|
|
858
|
+
* // The message for the current error, or null for none.
|
|
859
|
+
* const errorMessage = (error: EvoluError | null): string | null => {
|
|
860
|
+
* if (!error) return null;
|
|
861
|
+
* // oxlint-disable-next-line typescript/switch-exhaustiveness-check -- The default handles every other EvoluError.
|
|
862
|
+
* switch (error.type) {
|
|
863
|
+
* case "UnsupportedDbVersionError":
|
|
864
|
+
* return "Your data requires a newer version of this app. Please update it.";
|
|
865
|
+
* case "OtherBuildRunningError":
|
|
866
|
+
* return "This app is open in another tab with a different version. Close that tab to continue.";
|
|
867
|
+
* default:
|
|
868
|
+
* return "Something went wrong. Please try again.";
|
|
869
|
+
* }
|
|
870
|
+
* };
|
|
871
|
+
*
|
|
779
872
|
* // Stand-in for run.deps.evoluError from createEvoluDeps.
|
|
780
873
|
* using evoluError = createStore<EvoluError | null>(null);
|
|
781
874
|
* const deps = { evoluError } satisfies EvoluErrorDep;
|
|
782
|
-
*
|
|
783
|
-
* const
|
|
784
|
-
* displayedMessage = message;
|
|
785
|
-
* };
|
|
875
|
+
* // What the app showed over time; null hides the message.
|
|
876
|
+
* const shown: Array<string | null> = [];
|
|
786
877
|
*
|
|
787
878
|
* deps.evoluError.subscribe(() => {
|
|
788
|
-
*
|
|
789
|
-
* if (!error) return;
|
|
790
|
-
*
|
|
791
|
-
* // oxlint-disable-next-line typescript/switch-exhaustiveness-check -- The default intentionally handles every other EvoluError.
|
|
792
|
-
* switch (error.type) {
|
|
793
|
-
* case "TimestampDriftError":
|
|
794
|
-
* // Show guidance specific to the detected error.
|
|
795
|
-
* showMessage(
|
|
796
|
-
* "Your system clock appears incorrect. Please fix it.",
|
|
797
|
-
* );
|
|
798
|
-
* break;
|
|
799
|
-
* default:
|
|
800
|
-
* // Show a generic user message for other operational errors.
|
|
801
|
-
* showMessage("Something went wrong. Please try again.");
|
|
802
|
-
* }
|
|
879
|
+
* shown.push(errorMessage(deps.evoluError.get()));
|
|
803
880
|
* });
|
|
804
881
|
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
* );
|
|
882
|
+
* // Another version keeps this tab waiting, then the wait ends.
|
|
883
|
+
* deps.evoluError.set({ type: "OtherBuildRunningError" });
|
|
884
|
+
* deps.evoluError.set(null);
|
|
885
|
+
*
|
|
886
|
+
* assertEqual(shown, [
|
|
887
|
+
* "This app is open in another tab with a different version. Close that tab to continue.",
|
|
888
|
+
* null,
|
|
889
|
+
* ]);
|
|
814
890
|
* ```
|
|
815
891
|
*/
|
|
816
892
|
readonly evoluError: ReadonlyStore<EvoluError | null>;
|
|
817
893
|
}
|
|
818
894
|
|
|
895
|
+
/**
|
|
896
|
+
* Dependency wrapper for the shared {@link SyncState} store.
|
|
897
|
+
*
|
|
898
|
+
* @group Construction
|
|
899
|
+
*/
|
|
900
|
+
export interface SyncStateDep {
|
|
901
|
+
/**
|
|
902
|
+
* {@link ReadonlyStore} of the latest {@link SyncState} shared by all
|
|
903
|
+
* {@link Evolu} instances, or null before the shared worker sends its first
|
|
904
|
+
* snapshot. Derive what to show from it, such as one indicator per relay, or
|
|
905
|
+
* use {@link syncStateToOwnerSyncStates} for one state per owner.
|
|
906
|
+
*
|
|
907
|
+
* ### Example
|
|
908
|
+
*
|
|
909
|
+
* ```ts
|
|
910
|
+
* import {
|
|
911
|
+
* assertEqual,
|
|
912
|
+
* createId,
|
|
913
|
+
* createStore,
|
|
914
|
+
* testCreateDeps,
|
|
915
|
+
* } from "@evolu/common";
|
|
916
|
+
* import type {
|
|
917
|
+
* SyncState,
|
|
918
|
+
* SyncStateDep,
|
|
919
|
+
* } from "@evolu/common/local-first";
|
|
920
|
+
*
|
|
921
|
+
* const openRelayLabels = (deps: SyncStateDep): ReadonlyArray<string> =>
|
|
922
|
+
* (deps.syncState.get()?.transports ?? [])
|
|
923
|
+
* .filter(({ readyState }) => readyState === "open")
|
|
924
|
+
* .map(({ label }) => label);
|
|
925
|
+
*
|
|
926
|
+
* using syncState = createStore<SyncState | null>(null);
|
|
927
|
+
* assertEqual(openRelayLabels({ syncState }), []);
|
|
928
|
+
*
|
|
929
|
+
* const deps = testCreateDeps();
|
|
930
|
+
* syncState.set({
|
|
931
|
+
* transports: [
|
|
932
|
+
* {
|
|
933
|
+
* id: createId<"SyncTransport">(deps),
|
|
934
|
+
* label: "wss://relay.example",
|
|
935
|
+
* readyState: "open",
|
|
936
|
+
* openedAt: null,
|
|
937
|
+
* closedAt: null,
|
|
938
|
+
* error: null,
|
|
939
|
+
* },
|
|
940
|
+
* ],
|
|
941
|
+
* tenants: [],
|
|
942
|
+
* });
|
|
943
|
+
* assertEqual(openRelayLabels({ syncState }), ["wss://relay.example"]);
|
|
944
|
+
* ```
|
|
945
|
+
*/
|
|
946
|
+
readonly syncState: ReadonlyStore<SyncState | null>;
|
|
947
|
+
}
|
|
948
|
+
|
|
819
949
|
/**
|
|
820
950
|
* Shared platform dependencies for creating {@link Evolu} instances.
|
|
821
951
|
*
|
|
822
952
|
* Includes platform adapters, the shared {@link EvoluErrorDep.evoluError} store,
|
|
823
953
|
* and disposal for owned resources.
|
|
954
|
+
*
|
|
955
|
+
* @group Construction
|
|
824
956
|
*/
|
|
825
957
|
export type EvoluDeps = EvoluPlatformDeps &
|
|
826
958
|
ConsoleDep &
|
|
827
959
|
EvoluErrorDep &
|
|
960
|
+
SyncStateDep &
|
|
828
961
|
Disposable;
|
|
829
962
|
|
|
830
963
|
/**
|
|
@@ -832,6 +965,8 @@ export type EvoluDeps = EvoluPlatformDeps &
|
|
|
832
965
|
*
|
|
833
966
|
* Provides worker and channel adapters plus optional platform integrations for
|
|
834
967
|
* logging and synchronous UI flush.
|
|
968
|
+
*
|
|
969
|
+
* @group Construction
|
|
835
970
|
*/
|
|
836
971
|
export type EvoluPlatformDeps = CreateDbWorkerDep &
|
|
837
972
|
CreateBroadcastChannelDep &
|
|
@@ -848,36 +983,50 @@ export type EvoluPlatformDeps = CreateDbWorkerDep &
|
|
|
848
983
|
*
|
|
849
984
|
* Call this once per platform and reuse the returned deps when creating
|
|
850
985
|
* multiple Evolu instances. The returned deps object owns long-lived resources
|
|
851
|
-
* such as worker channels and the shared {@link EvoluErrorDep.evoluError}
|
|
852
|
-
*
|
|
986
|
+
* such as worker channels and the shared {@link EvoluErrorDep.evoluError} and
|
|
987
|
+
* {@link SyncStateDep.syncState} stores.
|
|
853
988
|
*
|
|
854
989
|
* Dispose it only during app shutdown.
|
|
990
|
+
*
|
|
991
|
+
* @group Construction
|
|
855
992
|
*/
|
|
856
993
|
export const createEvoluDeps = (deps: EvoluPlatformDeps): EvoluDeps => {
|
|
857
994
|
const { createBroadcastChannel, sharedWorker } = deps;
|
|
858
995
|
const console = deps.console ?? createConsole();
|
|
996
|
+
// Opened when the worker connects.
|
|
997
|
+
let syncStateBroadcastChannel: BroadcastChannel<SyncState> | null = null;
|
|
998
|
+
let tabLeaderElection: Disposable | null = null;
|
|
859
999
|
|
|
860
1000
|
using disposer = new DisposableStack();
|
|
861
1001
|
disposer.use(sharedWorker);
|
|
862
1002
|
const evoluError = disposer.use(createStore<EvoluError | null>(null));
|
|
1003
|
+
const syncState = disposer.use(createStore<SyncState | null>(null));
|
|
1004
|
+
disposer.defer(() => {
|
|
1005
|
+
syncStateBroadcastChannel?.[Symbol.dispose]();
|
|
1006
|
+
});
|
|
863
1007
|
|
|
864
1008
|
const consoleEntryOrErrorBroadcastChannel = disposer.use(
|
|
865
1009
|
createBroadcastChannel<ConsoleEntryOrError>(
|
|
866
1010
|
consoleEntryOrErrorBroadcastChannelName,
|
|
867
1011
|
),
|
|
868
1012
|
);
|
|
1013
|
+
const setEvoluError = (error: EvoluError): void => {
|
|
1014
|
+
if (evoluError.get()?.type === "UnsupportedDbVersionError") return;
|
|
1015
|
+
evoluError.set(error);
|
|
1016
|
+
};
|
|
1017
|
+
|
|
869
1018
|
consoleEntryOrErrorBroadcastChannel.onMessage = (message) => {
|
|
870
1019
|
switch (message.type) {
|
|
871
1020
|
case "ConsoleEntry":
|
|
872
1021
|
console.write(message.entry);
|
|
873
1022
|
// Fallback channel for unexpected errors without EvoluError typing.
|
|
874
1023
|
if (message.entry.method === "error") {
|
|
875
|
-
|
|
1024
|
+
setEvoluError(createUnknownError(message.entry.args));
|
|
876
1025
|
}
|
|
877
1026
|
break;
|
|
878
1027
|
|
|
879
1028
|
case "Error":
|
|
880
|
-
|
|
1029
|
+
setEvoluError(message.error);
|
|
881
1030
|
// Keep typed errors visible in logs as operational failures.
|
|
882
1031
|
console.error(message.error);
|
|
883
1032
|
break;
|
|
@@ -888,23 +1037,69 @@ export const createEvoluDeps = (deps: EvoluPlatformDeps): EvoluDeps => {
|
|
|
888
1037
|
};
|
|
889
1038
|
|
|
890
1039
|
sharedWorker.port.onMessage = (message) => {
|
|
891
|
-
|
|
1040
|
+
switch (message.type) {
|
|
1041
|
+
case "DbWorkerInit":
|
|
1042
|
+
deps.createDbWorker().postMessage(message, [message.port]);
|
|
1043
|
+
break;
|
|
1044
|
+
|
|
1045
|
+
case "Error":
|
|
1046
|
+
// Sent to this tab only: its database refused startup, or its worker
|
|
1047
|
+
// still waits for another build.
|
|
1048
|
+
setEvoluError(message.error);
|
|
1049
|
+
console.error(message.error);
|
|
1050
|
+
break;
|
|
1051
|
+
|
|
1052
|
+
case "Waiting":
|
|
1053
|
+
case "StorageUnavailable":
|
|
1054
|
+
// Platform adapters act on these; see Builds and Storage in the Shared
|
|
1055
|
+
// module.
|
|
1056
|
+
break;
|
|
1057
|
+
|
|
1058
|
+
case "Connected": {
|
|
1059
|
+
assert(!syncStateBroadcastChannel, "The shared worker connects once.");
|
|
1060
|
+
// The wait the error reported is over.
|
|
1061
|
+
if (evoluError.get()?.type === "OtherBuildRunningError") {
|
|
1062
|
+
evoluError.set(null);
|
|
1063
|
+
}
|
|
1064
|
+
// The worker is the channel's only sender, and one sender's messages
|
|
1065
|
+
// arrive in order, so the last one is current.
|
|
1066
|
+
syncStateBroadcastChannel = createBroadcastChannel<SyncState>(
|
|
1067
|
+
message.syncStateChannelName,
|
|
1068
|
+
);
|
|
1069
|
+
syncStateBroadcastChannel.onMessage = (state) => {
|
|
1070
|
+
syncState.set(state);
|
|
1071
|
+
};
|
|
1072
|
+
// Asking only after listening misses no snapshot.
|
|
1073
|
+
sharedWorker.port.postMessage({ type: "RequestSyncState" });
|
|
1074
|
+
// Only this worker's tabs compete, so a tab of another worker never
|
|
1075
|
+
// hosts its DbWorkers.
|
|
1076
|
+
tabLeaderElection = acquireLeaderLockCallback(deps)(
|
|
1077
|
+
`tab-${message.workerId}`,
|
|
1078
|
+
() => {
|
|
1079
|
+
sharedWorker.port.postMessage({
|
|
1080
|
+
type: "AnnounceTabLeader",
|
|
1081
|
+
consoleLevel: console.getLevel(),
|
|
1082
|
+
});
|
|
1083
|
+
},
|
|
1084
|
+
);
|
|
1085
|
+
break;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
default:
|
|
1089
|
+
exhaustiveCheck(message);
|
|
1090
|
+
}
|
|
892
1091
|
};
|
|
893
1092
|
|
|
894
|
-
disposer.
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
type: "AnnounceTabLeader",
|
|
898
|
-
consoleLevel: console.getLevel(),
|
|
899
|
-
});
|
|
900
|
-
}),
|
|
901
|
-
);
|
|
1093
|
+
disposer.defer(() => {
|
|
1094
|
+
tabLeaderElection?.[Symbol.dispose]();
|
|
1095
|
+
});
|
|
902
1096
|
|
|
903
1097
|
return disposable<EvoluDeps>(
|
|
904
1098
|
{
|
|
905
1099
|
...deps,
|
|
906
1100
|
console,
|
|
907
1101
|
evoluError,
|
|
1102
|
+
syncState,
|
|
908
1103
|
},
|
|
909
1104
|
disposer,
|
|
910
1105
|
);
|
|
@@ -913,6 +1108,8 @@ export const createEvoluDeps = (deps: EvoluPlatformDeps): EvoluDeps => {
|
|
|
913
1108
|
/**
|
|
914
1109
|
* Creates an {@link Evolu} instance from {@link EvoluSchema} and
|
|
915
1110
|
* {@link EvoluConfig}.
|
|
1111
|
+
*
|
|
1112
|
+
* @group Construction
|
|
916
1113
|
*/
|
|
917
1114
|
export const createEvolu =
|
|
918
1115
|
<S extends EvoluSchema>(
|
|
@@ -1091,10 +1288,7 @@ export const createEvolu =
|
|
|
1091
1288
|
case "RefreshQueries": {
|
|
1092
1289
|
releaseUnsubscribedLoadingPromises();
|
|
1093
1290
|
|
|
1094
|
-
const queries = new Set<Query>(
|
|
1095
|
-
...loadingPromisesByQuery.keys(),
|
|
1096
|
-
...subscribedQueriesRefCount.keys(),
|
|
1097
|
-
]);
|
|
1291
|
+
const queries = new Set<Query>(subscribedQueriesRefCount.keys());
|
|
1098
1292
|
|
|
1099
1293
|
if (isNonEmptySet(queries)) postMessage({ type: "Query", queries });
|
|
1100
1294
|
break;
|
|
@@ -1311,6 +1505,27 @@ export const createEvolu =
|
|
|
1311
1505
|
listener();
|
|
1312
1506
|
});
|
|
1313
1507
|
|
|
1508
|
+
// Invalidation can happen after loading but before a framework
|
|
1509
|
+
// subscribes. Register first so subsequent invalidations also
|
|
1510
|
+
// refresh this query.
|
|
1511
|
+
const loadingPromise = loadingPromisesByQuery.get(query);
|
|
1512
|
+
if (loadingPromise?.releaseOnResolve) {
|
|
1513
|
+
// Retain the pending promise for its awaiters, but queue a read
|
|
1514
|
+
// after the mutation or sync that invalidated its result.
|
|
1515
|
+
loadingPromise.releaseOnResolve = false;
|
|
1516
|
+
queryBatch.push(query);
|
|
1517
|
+
} else if (!loadingPromise) {
|
|
1518
|
+
const rows = rowsByQueryMapStore.get().get(query);
|
|
1519
|
+
if (rows) {
|
|
1520
|
+
// Queue a read, but keep cached rows available to React use()
|
|
1521
|
+
// so a render before the worker responds does not suspend.
|
|
1522
|
+
void loadQuery(query);
|
|
1523
|
+
const entry = loadingPromisesByQuery.get(query);
|
|
1524
|
+
assertNotUndefined(entry);
|
|
1525
|
+
fulfillLoadingPromise(entry, rows);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1314
1529
|
return () => {
|
|
1315
1530
|
assert(
|
|
1316
1531
|
isSubscribed,
|
|
@@ -1347,6 +1562,13 @@ export const createEvolu =
|
|
|
1347
1562
|
},
|
|
1348
1563
|
|
|
1349
1564
|
useOwner,
|
|
1565
|
+
requestSync: (ownerId) => {
|
|
1566
|
+
// Do not let the forced mutation flush overtake pending owner actions.
|
|
1567
|
+
useOwnerBatch.flushNow();
|
|
1568
|
+
// Queue preceding mutations before the reconciliation request.
|
|
1569
|
+
mutateBatch.flushNow();
|
|
1570
|
+
useOwnerBatch.push({ action: "sync", ownerId });
|
|
1571
|
+
},
|
|
1350
1572
|
},
|
|
1351
1573
|
disposer,
|
|
1352
1574
|
),
|