@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
|
@@ -182,6 +182,7 @@ import {
|
|
|
182
182
|
} from "../Array.ts";
|
|
183
183
|
import {
|
|
184
184
|
assert,
|
|
185
|
+
assertNonEmptyArray,
|
|
185
186
|
assertNonNullable,
|
|
186
187
|
assertNotUndefined,
|
|
187
188
|
assertSame,
|
|
@@ -204,7 +205,7 @@ import {
|
|
|
204
205
|
encodeNonNegativeInt,
|
|
205
206
|
encodeNumber,
|
|
206
207
|
encodeString,
|
|
207
|
-
} from "../
|
|
208
|
+
} from "../Bytes.ts";
|
|
208
209
|
import type { ConsoleDep } from "../Console.ts";
|
|
209
210
|
import {
|
|
210
211
|
createPadmePadding,
|
|
@@ -247,6 +248,7 @@ import {
|
|
|
247
248
|
zeroNonNegativeInt,
|
|
248
249
|
} from "../Type.ts";
|
|
249
250
|
import type { Predicate } from "../Types.ts";
|
|
251
|
+
import type { Evolu } from "./Evolu.ts";
|
|
250
252
|
import {
|
|
251
253
|
type Owner,
|
|
252
254
|
type OwnerError,
|
|
@@ -271,6 +273,7 @@ import {
|
|
|
271
273
|
type RangeUpperBound,
|
|
272
274
|
type SkipRange,
|
|
273
275
|
type StorageDep,
|
|
276
|
+
type StorageWriteMessagesError,
|
|
274
277
|
type TimestampsRange,
|
|
275
278
|
} from "./Storage.ts";
|
|
276
279
|
import {
|
|
@@ -367,18 +370,27 @@ export const MessageType = {
|
|
|
367
370
|
|
|
368
371
|
export type MessageType = (typeof MessageType)[keyof typeof MessageType];
|
|
369
372
|
|
|
370
|
-
/**
|
|
373
|
+
/**
|
|
374
|
+
* The routing prefix of a {@link ProtocolMessage}.
|
|
375
|
+
*
|
|
376
|
+
* The version and the {@link OwnerId} begin every message of every protocol
|
|
377
|
+
* version. The message type follows them only in messages of this peer's
|
|
378
|
+
* version; the layout after the prefix is unknown for any other version.
|
|
379
|
+
*/
|
|
371
380
|
export interface ProtocolHeader extends Typed<"ProtocolHeader"> {
|
|
372
|
-
readonly version:
|
|
381
|
+
readonly version: NonNegativeInt;
|
|
373
382
|
readonly ownerId: OwnerId;
|
|
374
|
-
|
|
383
|
+
/** Absent when `version` differs from {@link protocolVersion}. */
|
|
384
|
+
readonly messageType?: MessageType;
|
|
375
385
|
}
|
|
376
386
|
|
|
377
387
|
/**
|
|
378
|
-
* Parses the
|
|
388
|
+
* Parses the {@link ProtocolHeader} a transport needs to route a message.
|
|
379
389
|
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
390
|
+
* Parsing succeeds for versions this peer cannot process, because a
|
|
391
|
+
* non-initiator answers a version mismatch with only its version and the owner
|
|
392
|
+
* ID. Rejecting that reply here would drop it before
|
|
393
|
+
* {@link applyProtocolMessageAsClient} can report {@link ProtocolVersionError}.
|
|
382
394
|
*/
|
|
383
395
|
export const parseProtocolHeader = (
|
|
384
396
|
inputMessage: Uint8Array,
|
|
@@ -468,8 +480,9 @@ export interface ProtocolWriteError
|
|
|
468
480
|
* still sync normally.
|
|
469
481
|
*
|
|
470
482
|
* Clients should prompt the user to contact the relay provider or upgrade their
|
|
471
|
-
* plan. Quota monitoring and management is the relay provider's
|
|
472
|
-
*
|
|
483
|
+
* plan. Quota monitoring and management is the relay provider's responsibility.
|
|
484
|
+
* After additional quota is available, call {@link Evolu.requestSync} with the
|
|
485
|
+
* affected owner's ID to retry locally stored changes.
|
|
473
486
|
*/
|
|
474
487
|
export interface ProtocolQuotaError
|
|
475
488
|
extends OwnerError, Typed<"ProtocolQuotaError"> {}
|
|
@@ -558,9 +571,87 @@ export const createProtocolMessageFromCrdtMessages =
|
|
|
558
571
|
return buffer.unwrap();
|
|
559
572
|
};
|
|
560
573
|
|
|
574
|
+
/**
|
|
575
|
+
* Creates size-limited broadcast {@link ProtocolMessage}s containing every
|
|
576
|
+
* supplied {@link CrdtMessage}.
|
|
577
|
+
*
|
|
578
|
+
* Broadcasts contain no synchronization ranges or write key. Unlike
|
|
579
|
+
* {@link createProtocolMessageFromCrdtMessages}, this function splits all
|
|
580
|
+
* messages into complete frames rather than relying on later synchronization
|
|
581
|
+
* rounds to deliver messages that do not fit. Each individual message must fit
|
|
582
|
+
* the configured size limit.
|
|
583
|
+
*
|
|
584
|
+
* ### Example
|
|
585
|
+
*
|
|
586
|
+
* ```ts
|
|
587
|
+
* import {
|
|
588
|
+
* assertSame,
|
|
589
|
+
* createId,
|
|
590
|
+
* getOrThrow,
|
|
591
|
+
* testCreateDeps,
|
|
592
|
+
* } from "@evolu/common";
|
|
593
|
+
* import {
|
|
594
|
+
* createProtocolBroadcastMessagesFromCrdtMessages,
|
|
595
|
+
* MessageType,
|
|
596
|
+
* parseProtocolHeader,
|
|
597
|
+
* testAppOwner,
|
|
598
|
+
* testCreateCrdtMessage,
|
|
599
|
+
* } from "@evolu/common/local-first";
|
|
600
|
+
*
|
|
601
|
+
* const deps = testCreateDeps();
|
|
602
|
+
* const broadcasts = createProtocolBroadcastMessagesFromCrdtMessages(deps)(
|
|
603
|
+
* testAppOwner,
|
|
604
|
+
* [testCreateCrdtMessage(createId(deps), 1, "Ada")],
|
|
605
|
+
* );
|
|
606
|
+
* assertSame(broadcasts.length, 1);
|
|
607
|
+
* assertSame(
|
|
608
|
+
* getOrThrow(parseProtocolHeader(broadcasts[0])).messageType,
|
|
609
|
+
* MessageType.Broadcast,
|
|
610
|
+
* );
|
|
611
|
+
* ```
|
|
612
|
+
*/
|
|
613
|
+
export const createProtocolBroadcastMessagesFromCrdtMessages =
|
|
614
|
+
(deps: RandomBytesDep) =>
|
|
615
|
+
(
|
|
616
|
+
owner: Owner,
|
|
617
|
+
messages: NonEmptyReadonlyArray<CrdtMessage>,
|
|
618
|
+
maxSize: ProtocolMessageMaxSize = defaultProtocolMessageMaxSize,
|
|
619
|
+
): NonEmptyReadonlyArray<ProtocolMessage> => {
|
|
620
|
+
const broadcasts: Array<ProtocolMessage> = [];
|
|
621
|
+
let buffer = createProtocolMessageBuffer(owner.id, {
|
|
622
|
+
messageType: MessageType.Broadcast,
|
|
623
|
+
totalMaxSize: maxSize,
|
|
624
|
+
});
|
|
625
|
+
for (const message of messages) {
|
|
626
|
+
const encryptedMessage = {
|
|
627
|
+
timestamp: message.timestamp,
|
|
628
|
+
change: encodeAndEncryptDbChange(deps)(message, owner.encryptionKey),
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
if (!buffer.canAddMessage(encryptedMessage)) {
|
|
632
|
+
const nextBuffer = createProtocolMessageBuffer(owner.id, {
|
|
633
|
+
messageType: MessageType.Broadcast,
|
|
634
|
+
totalMaxSize: maxSize,
|
|
635
|
+
});
|
|
636
|
+
assert(
|
|
637
|
+
nextBuffer.canAddMessage(encryptedMessage),
|
|
638
|
+
"the message is too big",
|
|
639
|
+
);
|
|
640
|
+
broadcasts.push(buffer.unwrap());
|
|
641
|
+
buffer = nextBuffer;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
buffer.addMessage(encryptedMessage);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
broadcasts.push(buffer.unwrap());
|
|
648
|
+
assertNonEmptyArray(broadcasts);
|
|
649
|
+
return broadcasts;
|
|
650
|
+
};
|
|
651
|
+
|
|
561
652
|
/** Creates a {@link ProtocolMessage} for sync. */
|
|
562
653
|
export const createProtocolMessageForSync =
|
|
563
|
-
(deps: StorageDep
|
|
654
|
+
(deps: StorageDep) =>
|
|
564
655
|
(ownerId: OwnerId, subscriptionFlag?: SubscriptionFlag): ProtocolMessage => {
|
|
565
656
|
const buffer = createProtocolMessageBuffer(ownerId, {
|
|
566
657
|
messageType: MessageType.Request,
|
|
@@ -902,21 +993,53 @@ export interface ApplyProtocolMessageAsClientOptions {
|
|
|
902
993
|
}
|
|
903
994
|
|
|
904
995
|
/**
|
|
905
|
-
* Result
|
|
906
|
-
*
|
|
996
|
+
* Result of {@link applyProtocolMessageAsClient}: a continuation for the
|
|
997
|
+
* non-initiator.
|
|
907
998
|
*/
|
|
908
999
|
export interface ApplyProtocolMessageAsClientResponse extends Typed<"Response"> {
|
|
909
1000
|
readonly message: ProtocolMessage;
|
|
1001
|
+
/** The uploaded messages, if any, for delivery to other local databases. */
|
|
1002
|
+
readonly broadcast?: ProtocolMessage;
|
|
910
1003
|
}
|
|
911
1004
|
|
|
912
|
-
|
|
1005
|
+
/**
|
|
1006
|
+
* Result of {@link applyProtocolMessageAsClient}: the response needed nothing
|
|
1007
|
+
* more, because its ranges all matched or it carried none.
|
|
1008
|
+
*/
|
|
1009
|
+
export interface ApplyProtocolMessageAsClientConverged extends Typed<"Converged"> {}
|
|
913
1010
|
|
|
1011
|
+
/**
|
|
1012
|
+
* Result of {@link applyProtocolMessageAsClient}: a Broadcast message whose
|
|
1013
|
+
* messages were written.
|
|
1014
|
+
*/
|
|
914
1015
|
export interface ApplyProtocolMessageAsClientBroadcast extends Typed<"Broadcast"> {}
|
|
915
1016
|
|
|
1017
|
+
/**
|
|
1018
|
+
* Result of {@link applyProtocolMessageAsClient}: the messages were written, but
|
|
1019
|
+
* without a write key no ranges are reconciled.
|
|
1020
|
+
*/
|
|
1021
|
+
export interface ApplyProtocolMessageAsClientReadonly extends Typed<"Readonly"> {}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Result of {@link applyProtocolMessageAsClient}: the protocol logged an
|
|
1025
|
+
* exception thrown by calling the storage's `writeMessages` (`Write`) or a
|
|
1026
|
+
* failed range reconciliation (`Sync`) to the console. An exception while the
|
|
1027
|
+
* returned Task runs, as the built-in storages throw, is a defect that aborts
|
|
1028
|
+
* the Run instead. Expected write rejections return the original
|
|
1029
|
+
* {@link StorageWriteMessagesError} through {@link Result}. Reconciliation can
|
|
1030
|
+
* fail after messages have been committed; that failure does not roll back the
|
|
1031
|
+
* write.
|
|
1032
|
+
*/
|
|
1033
|
+
export interface ApplyProtocolMessageAsClientFailed extends Typed<"Failed"> {
|
|
1034
|
+
readonly cause: "Write" | "Sync";
|
|
1035
|
+
}
|
|
1036
|
+
|
|
916
1037
|
export type ApplyProtocolMessageAsClientResult =
|
|
917
1038
|
| ApplyProtocolMessageAsClientResponse
|
|
918
|
-
|
|
|
919
|
-
| ApplyProtocolMessageAsClientBroadcast
|
|
1039
|
+
| ApplyProtocolMessageAsClientConverged
|
|
1040
|
+
| ApplyProtocolMessageAsClientBroadcast
|
|
1041
|
+
| ApplyProtocolMessageAsClientReadonly
|
|
1042
|
+
| ApplyProtocolMessageAsClientFailed;
|
|
920
1043
|
|
|
921
1044
|
export const applyProtocolMessageAsClient =
|
|
922
1045
|
(
|
|
@@ -924,12 +1047,7 @@ export const applyProtocolMessageAsClient =
|
|
|
924
1047
|
options: ApplyProtocolMessageAsClientOptions = {},
|
|
925
1048
|
): Task<
|
|
926
1049
|
ApplyProtocolMessageAsClientResult,
|
|
927
|
-
|
|
|
928
|
-
| ProtocolSyncError
|
|
929
|
-
| ProtocolVersionError
|
|
930
|
-
| ProtocolWriteError
|
|
931
|
-
| ProtocolWriteKeyError
|
|
932
|
-
| ProtocolQuotaError,
|
|
1050
|
+
ProtocolError | StorageWriteMessagesError,
|
|
933
1051
|
StorageDep
|
|
934
1052
|
> =>
|
|
935
1053
|
async (run) => {
|
|
@@ -995,15 +1113,18 @@ export const applyProtocolMessageAsClient =
|
|
|
995
1113
|
const result = await run(
|
|
996
1114
|
storage.writeMessages(ownerIdBytes, messages),
|
|
997
1115
|
);
|
|
998
|
-
|
|
999
|
-
if (!result.ok) return ok({ type: "NoResponse" });
|
|
1116
|
+
if (!result.ok) return result;
|
|
1000
1117
|
} catch (error) {
|
|
1001
1118
|
if (AbortError.is(error)) throw error;
|
|
1002
1119
|
run.deps.console.error(error);
|
|
1003
|
-
return ok({ type: "
|
|
1120
|
+
return ok({ type: "Failed", cause: "Write" });
|
|
1004
1121
|
}
|
|
1005
1122
|
}
|
|
1006
1123
|
|
|
1124
|
+
if (messageType === MessageType.Broadcast) {
|
|
1125
|
+
return ok({ type: "Broadcast" });
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1007
1128
|
// Now: No writeKey, no sync.
|
|
1008
1129
|
// TODO: Allow to sync SharedReadonlyOwner
|
|
1009
1130
|
// Without local changes, writeKey will not be required.
|
|
@@ -1011,17 +1132,13 @@ export const applyProtocolMessageAsClient =
|
|
|
1011
1132
|
// the sync will stop.
|
|
1012
1133
|
const writeKey = options.writeKey;
|
|
1013
1134
|
if (writeKey == null) {
|
|
1014
|
-
return ok({ type: "
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
if (messageType === MessageType.Broadcast) {
|
|
1018
|
-
return ok({ type: "Broadcast" });
|
|
1135
|
+
return ok({ type: "Readonly" });
|
|
1019
1136
|
}
|
|
1020
1137
|
|
|
1021
1138
|
const ranges = decodeRanges(input);
|
|
1022
1139
|
|
|
1023
1140
|
if (!isNonEmptyArray(ranges)) {
|
|
1024
|
-
return ok({ type: "
|
|
1141
|
+
return ok({ type: "Converged" });
|
|
1025
1142
|
}
|
|
1026
1143
|
|
|
1027
1144
|
const output = createProtocolMessageBuffer(ownerId, {
|
|
@@ -1030,14 +1147,23 @@ export const applyProtocolMessageAsClient =
|
|
|
1030
1147
|
rangesMaxSize: options.rangesMaxSize,
|
|
1031
1148
|
});
|
|
1032
1149
|
|
|
1033
|
-
|
|
1150
|
+
let broadcast: ProtocolMessageBuffer | undefined;
|
|
1151
|
+
const result = sync(run.deps)(ranges, output, ownerIdBytes, (message) => {
|
|
1152
|
+
broadcast ??= createProtocolMessageBuffer(ownerId, {
|
|
1153
|
+
messageType: MessageType.Broadcast,
|
|
1154
|
+
});
|
|
1155
|
+
broadcast.addMessage(message);
|
|
1156
|
+
});
|
|
1034
1157
|
|
|
1035
|
-
//
|
|
1036
|
-
if (!result.ok
|
|
1037
|
-
|
|
1038
|
-
}
|
|
1158
|
+
// A failure was logged by sync.
|
|
1159
|
+
if (!result.ok) return ok({ type: "Failed", cause: "Sync" });
|
|
1160
|
+
if (!result.value) return ok({ type: "Converged" });
|
|
1039
1161
|
|
|
1040
|
-
return ok({
|
|
1162
|
+
return ok({
|
|
1163
|
+
type: "Response",
|
|
1164
|
+
message: output.unwrap(),
|
|
1165
|
+
...(broadcast && { broadcast: broadcast.unwrap() }),
|
|
1166
|
+
});
|
|
1041
1167
|
} catch (error) {
|
|
1042
1168
|
if (AbortError.is(error)) throw error;
|
|
1043
1169
|
return err<ProtocolInvalidDataError>({
|
|
@@ -1159,9 +1285,13 @@ export const applyProtocolMessageAsRelay =
|
|
|
1159
1285
|
);
|
|
1160
1286
|
|
|
1161
1287
|
if (!result.ok) {
|
|
1288
|
+
const isQuotaError = result.error.type === "StorageQuotaError";
|
|
1289
|
+
if (!isQuotaError) run.deps.console.error(result.error);
|
|
1162
1290
|
const message = createProtocolMessageBuffer(ownerId, {
|
|
1163
1291
|
messageType: MessageType.Response,
|
|
1164
|
-
errorCode:
|
|
1292
|
+
errorCode: isQuotaError
|
|
1293
|
+
? ProtocolErrorCode.QuotaError
|
|
1294
|
+
: ProtocolErrorCode.WriteError,
|
|
1165
1295
|
}).unwrap();
|
|
1166
1296
|
return ok({ type: "Response", message });
|
|
1167
1297
|
}
|
|
@@ -1256,7 +1386,7 @@ const parseProtocolHeaderFromBuffer = (input: Buffer): ProtocolHeader => {
|
|
|
1256
1386
|
const [version, ownerId] = decodeVersionAndOwner(input);
|
|
1257
1387
|
|
|
1258
1388
|
if (version !== protocolVersion) {
|
|
1259
|
-
|
|
1389
|
+
return { type: "ProtocolHeader", version, ownerId };
|
|
1260
1390
|
}
|
|
1261
1391
|
|
|
1262
1392
|
const messageTypeValue = input.shift();
|
|
@@ -1276,12 +1406,7 @@ const parseProtocolHeaderFromBuffer = (input: Buffer): ProtocolHeader => {
|
|
|
1276
1406
|
throw new ProtocolDecodeError("Invalid MessageType");
|
|
1277
1407
|
}
|
|
1278
1408
|
|
|
1279
|
-
return {
|
|
1280
|
-
type: "ProtocolHeader",
|
|
1281
|
-
version: 1,
|
|
1282
|
-
ownerId,
|
|
1283
|
-
messageType,
|
|
1284
|
-
};
|
|
1409
|
+
return { type: "ProtocolHeader", version, ownerId, messageType };
|
|
1285
1410
|
};
|
|
1286
1411
|
|
|
1287
1412
|
/**
|
|
@@ -1319,6 +1444,7 @@ const sync =
|
|
|
1319
1444
|
ranges: NonEmptyReadonlyArray<Range>,
|
|
1320
1445
|
output: ProtocolMessageBuffer,
|
|
1321
1446
|
ownerIdBytes: OwnerIdBytes,
|
|
1447
|
+
onMessage?: (message: EncryptedCrdtMessage) => void,
|
|
1322
1448
|
): Result<boolean, typeof ProtocolErrorCode.SyncError> => {
|
|
1323
1449
|
const outputInitialSize = output.getSize();
|
|
1324
1450
|
let storageSize: NonNegativeInt;
|
|
@@ -1427,13 +1553,19 @@ const sync =
|
|
|
1427
1553
|
skipRange(range);
|
|
1428
1554
|
} else if (output.canSplitRange()) {
|
|
1429
1555
|
coalesceSkipsBeforeAdd();
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1556
|
+
try {
|
|
1557
|
+
splitRange(deps)(
|
|
1558
|
+
ownerIdBytes,
|
|
1559
|
+
lower,
|
|
1560
|
+
upper,
|
|
1561
|
+
currentUpperBound,
|
|
1562
|
+
output,
|
|
1563
|
+
);
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
if (AbortError.is(error)) throw error;
|
|
1566
|
+
deps.console.error(error);
|
|
1567
|
+
return err(ProtocolErrorCode.SyncError);
|
|
1568
|
+
}
|
|
1437
1569
|
} else {
|
|
1438
1570
|
return addFingerprintForRemainingRange(upper)
|
|
1439
1571
|
? ok(true)
|
|
@@ -1495,7 +1627,10 @@ const sync =
|
|
|
1495
1627
|
}
|
|
1496
1628
|
|
|
1497
1629
|
ourTimestamps.add(timestampBinary);
|
|
1498
|
-
if (message)
|
|
1630
|
+
if (message) {
|
|
1631
|
+
output.addMessage(message);
|
|
1632
|
+
onMessage?.(message);
|
|
1633
|
+
}
|
|
1499
1634
|
return true;
|
|
1500
1635
|
},
|
|
1501
1636
|
);
|
|
@@ -1547,7 +1682,7 @@ const sync =
|
|
|
1547
1682
|
};
|
|
1548
1683
|
|
|
1549
1684
|
const splitRange =
|
|
1550
|
-
(deps: StorageDep
|
|
1685
|
+
(deps: StorageDep) =>
|
|
1551
1686
|
(
|
|
1552
1687
|
ownerId: OwnerIdBytes,
|
|
1553
1688
|
lower: NonNegativeInt,
|
|
@@ -1565,15 +1700,10 @@ const splitRange =
|
|
|
1565
1700
|
timestamps: createTimestampsBuffer(),
|
|
1566
1701
|
};
|
|
1567
1702
|
|
|
1568
|
-
deps.storage.iterate(
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
(timestamp) => {
|
|
1573
|
-
range.timestamps.add(timestampBytesToTimestamp(timestamp));
|
|
1574
|
-
return true;
|
|
1575
|
-
},
|
|
1576
|
-
);
|
|
1703
|
+
deps.storage.iterate(ownerId, lower, upper, (timestamp) => {
|
|
1704
|
+
range.timestamps.add(timestampBytesToTimestamp(timestamp));
|
|
1705
|
+
return true;
|
|
1706
|
+
});
|
|
1577
1707
|
|
|
1578
1708
|
buffer.addRange(range);
|
|
1579
1709
|
return;
|
|
@@ -1588,17 +1718,11 @@ const splitRange =
|
|
|
1588
1718
|
...buckets.value.map((b) => NonNegativeInt.orThrow(b + lower)),
|
|
1589
1719
|
];
|
|
1590
1720
|
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
upperBound,
|
|
1597
|
-
);
|
|
1598
|
-
} catch (error) {
|
|
1599
|
-
deps.console.error(error);
|
|
1600
|
-
return;
|
|
1601
|
-
}
|
|
1721
|
+
const fingerprintRanges = deps.storage.fingerprintRanges(
|
|
1722
|
+
ownerId,
|
|
1723
|
+
fingerprintRangesBuckets,
|
|
1724
|
+
upperBound,
|
|
1725
|
+
);
|
|
1602
1726
|
|
|
1603
1727
|
const rangesToUse =
|
|
1604
1728
|
lower > 0 ? fingerprintRanges.slice(1) : fingerprintRanges;
|
package/src/local-first/Query.ts
CHANGED
|
@@ -43,25 +43,22 @@ export type { NotNull as KyselyNotNull } from "kysely";
|
|
|
43
43
|
* import {
|
|
44
44
|
* assertType,
|
|
45
45
|
* createQueryBuilder,
|
|
46
|
-
*
|
|
46
|
+
* testEvoluSchema,
|
|
47
|
+
* type TestEvoluSchema,
|
|
47
48
|
* NonEmptyTrimmedString100,
|
|
48
49
|
* type Query,
|
|
50
|
+
* type TestTodoId,
|
|
49
51
|
* } from "@evolu/common";
|
|
50
52
|
*
|
|
51
|
-
* const
|
|
52
|
-
* type TodoId = typeof TodoId.Output;
|
|
53
|
-
* const Schema = {
|
|
54
|
-
* todo: { id: TodoId, title: NonEmptyTrimmedString100 },
|
|
55
|
-
* };
|
|
56
|
-
* const createQuery = createQueryBuilder(Schema);
|
|
53
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
57
54
|
* const allTodos = createQuery((db) => db.selectFrom("todo").selectAll());
|
|
58
55
|
*
|
|
59
56
|
* type AllTodosRow = typeof allTodos.Row;
|
|
60
57
|
* assertType<
|
|
61
|
-
* typeof allTodos extends Query<
|
|
58
|
+
* typeof allTodos extends Query<TestEvoluSchema> ? true : false,
|
|
62
59
|
* true
|
|
63
60
|
* >();
|
|
64
|
-
* assertType<AllTodosRow["id"],
|
|
61
|
+
* assertType<AllTodosRow["id"], TestTodoId>();
|
|
65
62
|
* assertType<AllTodosRow["title"], NonEmptyTrimmedString100 | null>();
|
|
66
63
|
* ```
|
|
67
64
|
*/
|
|
@@ -82,15 +79,11 @@ export type Query<
|
|
|
82
79
|
* import {
|
|
83
80
|
* assertType,
|
|
84
81
|
* createQueryBuilder,
|
|
85
|
-
*
|
|
82
|
+
* testEvoluSchema,
|
|
86
83
|
* type InferRow,
|
|
87
|
-
* NonEmptyTrimmedString100,
|
|
88
84
|
* } from "@evolu/common";
|
|
89
85
|
*
|
|
90
|
-
* const
|
|
91
|
-
* todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
|
|
92
|
-
* };
|
|
93
|
-
* const createQuery = createQueryBuilder(Schema);
|
|
86
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
94
87
|
* const allTodos = createQuery((db) =>
|
|
95
88
|
* db.selectFrom("todo").selectAll(),
|
|
96
89
|
* );
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import {
|
|
3
|
+
assertEqual,
|
|
4
|
+
assertErr,
|
|
5
|
+
assertOk,
|
|
6
|
+
assertSame,
|
|
7
|
+
assertType,
|
|
8
|
+
createEvolu,
|
|
9
|
+
createQueryBuilder,
|
|
10
|
+
type Evolu,
|
|
11
|
+
NonEmptyTrimmedString100,
|
|
12
|
+
object,
|
|
13
|
+
type OwnerIdBytes,
|
|
14
|
+
ownerIdToOwnerIdBytes,
|
|
15
|
+
sqliteQueryStringToSqliteQuery,
|
|
16
|
+
type Task,
|
|
17
|
+
testAppName,
|
|
18
|
+
testAppOwner,
|
|
19
|
+
type TestEvoluSchema,
|
|
20
|
+
testEvoluSchema,
|
|
21
|
+
testLocalOnlyEvoluSchema,
|
|
22
|
+
testOwnerSecret,
|
|
23
|
+
TestProjectId,
|
|
24
|
+
testProjectId,
|
|
25
|
+
TestTodoId,
|
|
26
|
+
testTodoId,
|
|
27
|
+
type TimestampBytes,
|
|
28
|
+
} from "../index.ts";
|
|
29
|
+
import type { EvoluPlatformDeps } from "./Evolu.ts";
|
|
30
|
+
import type { ValidateSchema } from "./Schema.ts";
|
|
31
|
+
|
|
32
|
+
test("history queries select and filter by ownerId bytes", () => {
|
|
33
|
+
const createQuery = createQueryBuilder(testEvoluSchema);
|
|
34
|
+
const query = createQuery((db) =>
|
|
35
|
+
db
|
|
36
|
+
.selectFrom("evolu_history")
|
|
37
|
+
.select(["ownerId", "timestamp"])
|
|
38
|
+
.where("ownerId", "=", ownerIdToOwnerIdBytes(testAppOwner.id)),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
assertType<
|
|
42
|
+
typeof query.Row,
|
|
43
|
+
{ ownerId: OwnerIdBytes; timestamp: TimestampBytes }
|
|
44
|
+
>();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("testEvoluSchema supports project-linked and independent todos", () => {
|
|
48
|
+
assertType<TestEvoluSchema, typeof testEvoluSchema>();
|
|
49
|
+
assertType<ValidateSchema<TestEvoluSchema>, TestEvoluSchema>();
|
|
50
|
+
assertType<typeof testTodoId, TestTodoId>();
|
|
51
|
+
assertType<typeof testProjectId, TestProjectId>();
|
|
52
|
+
assertSame(testEvoluSchema.todo.id, TestTodoId);
|
|
53
|
+
assertSame(testEvoluSchema.project.id, TestProjectId);
|
|
54
|
+
assertEqual(new Set<string>([testTodoId, testProjectId]).size, 2);
|
|
55
|
+
const Todo = object(testEvoluSchema.todo);
|
|
56
|
+
const todo = {
|
|
57
|
+
id: testTodoId,
|
|
58
|
+
title: "Write documentation",
|
|
59
|
+
isCompleted: null,
|
|
60
|
+
projectId: testProjectId,
|
|
61
|
+
};
|
|
62
|
+
assertOk(Todo.fromUnknown(todo), todo);
|
|
63
|
+
const independentTodo = { ...todo, projectId: null };
|
|
64
|
+
assertOk(Todo.fromUnknown(independentTodo), independentTodo);
|
|
65
|
+
assertOk(testEvoluSchema.todo.projectId.from(testProjectId), testProjectId);
|
|
66
|
+
void (() => {
|
|
67
|
+
// @ts-expect-error A todo ID is not a project ID.
|
|
68
|
+
testEvoluSchema.todo.projectId.from(testTodoId);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const createQuery = createQueryBuilder(testEvoluSchema);
|
|
72
|
+
const query = createQuery((db) =>
|
|
73
|
+
db
|
|
74
|
+
.selectFrom("todo")
|
|
75
|
+
.leftJoin("project", "project.id", "todo.projectId")
|
|
76
|
+
.select(["todo.title", "project.name as projectName"]),
|
|
77
|
+
);
|
|
78
|
+
assertType<
|
|
79
|
+
typeof query.Row,
|
|
80
|
+
{
|
|
81
|
+
title: NonEmptyTrimmedString100 | null;
|
|
82
|
+
projectName: NonEmptyTrimmedString100 | null;
|
|
83
|
+
}
|
|
84
|
+
>();
|
|
85
|
+
assertEqual(
|
|
86
|
+
sqliteQueryStringToSqliteQuery(query).sql,
|
|
87
|
+
'select "todo"."title", "project"."name" as "projectName" from "todo" left join "project" on "project"."id" = "todo"."projectId"',
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const createTodos = createEvolu(testEvoluSchema, {
|
|
91
|
+
appName: testAppName,
|
|
92
|
+
appOwner: testAppOwner,
|
|
93
|
+
transports: [],
|
|
94
|
+
});
|
|
95
|
+
assertType<
|
|
96
|
+
typeof createTodos,
|
|
97
|
+
Task<Evolu<TestEvoluSchema>, never, EvoluPlatformDeps>
|
|
98
|
+
>();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("testLocalOnlyEvoluSchema supports keys with or without recovery material", () => {
|
|
102
|
+
assertType<
|
|
103
|
+
ValidateSchema<typeof testLocalOnlyEvoluSchema>,
|
|
104
|
+
typeof testLocalOnlyEvoluSchema
|
|
105
|
+
>();
|
|
106
|
+
const AppOwnerRow = object(testLocalOnlyEvoluSchema._appOwner);
|
|
107
|
+
const recoverableOwner = {
|
|
108
|
+
id: testAppOwner.id,
|
|
109
|
+
encryptionKey: testAppOwner.encryptionKey,
|
|
110
|
+
writeKey: testAppOwner.writeKey,
|
|
111
|
+
secret: testOwnerSecret,
|
|
112
|
+
name: "Personal",
|
|
113
|
+
};
|
|
114
|
+
assertOk(AppOwnerRow.fromUnknown(recoverableOwner), recoverableOwner);
|
|
115
|
+
const keyOnlyOwner = {
|
|
116
|
+
...recoverableOwner,
|
|
117
|
+
secret: null,
|
|
118
|
+
name: null,
|
|
119
|
+
};
|
|
120
|
+
assertOk(AppOwnerRow.fromUnknown(keyOnlyOwner), keyOnlyOwner);
|
|
121
|
+
assertErr(
|
|
122
|
+
AppOwnerRow.fromUnknown({
|
|
123
|
+
...keyOnlyOwner,
|
|
124
|
+
encryptionKey: new Uint8Array(1),
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
assertErr(
|
|
128
|
+
AppOwnerRow.fromUnknown({
|
|
129
|
+
...recoverableOwner,
|
|
130
|
+
secret: new Uint8Array(1),
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const createAccounts = createEvolu(testLocalOnlyEvoluSchema, {
|
|
135
|
+
appName: testAppName,
|
|
136
|
+
appOwner: testAppOwner,
|
|
137
|
+
transports: [],
|
|
138
|
+
});
|
|
139
|
+
assertType<
|
|
140
|
+
typeof createAccounts,
|
|
141
|
+
Task<Evolu<typeof testLocalOnlyEvoluSchema>, never, EvoluPlatformDeps>
|
|
142
|
+
>();
|
|
143
|
+
});
|