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