@evolu/common 8.10.0 → 8.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/Bytes.d.ts +39 -2
- package/dist/src/Bytes.d.ts.map +1 -1
- package/dist/src/Bytes.js +50 -2
- package/dist/src/Config.d.ts +22 -22
- package/dist/src/Config.d.ts.map +1 -1
- package/dist/src/Console.d.ts +62 -7
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +20 -4
- package/dist/src/Crypto.d.ts +76 -4
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +55 -4
- package/dist/src/Error.d.ts +45 -0
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +69 -0
- package/dist/src/Fs.d.ts +92 -18
- package/dist/src/Fs.d.ts.map +1 -1
- package/dist/src/Fs.js +2 -0
- package/dist/src/Identicon.d.ts +2 -2
- package/dist/src/Identicon.js +2 -2
- package/dist/src/LeakDetector.d.ts +22 -3
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +12 -2
- package/dist/src/LockManager.d.ts +8 -0
- package/dist/src/LockManager.d.ts.map +1 -1
- package/dist/src/LockManager.js +6 -0
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +5 -0
- package/dist/src/Platform.d.ts +47 -7
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +24 -5
- package/dist/src/Random.d.ts +25 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +14 -2
- package/dist/src/Resource.d.ts +156 -1
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +201 -72
- package/dist/src/Schedule.d.ts +11 -10
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +1 -1
- package/dist/src/Sqlite.d.ts +132 -16
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +63 -9
- package/dist/src/Task.d.ts +15 -4
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +41 -15
- package/dist/src/Test.d.ts +9 -0
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +4 -0
- package/dist/src/Time.d.ts +106 -9
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +55 -4
- package/dist/src/Type.d.ts +1455 -1310
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +1274 -517
- package/dist/src/WebSocket.d.ts +164 -13
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +133 -24
- package/dist/src/Worker.d.ts +90 -8
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +28 -2
- package/dist/src/index.d.ts +6 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -3
- package/dist/src/local-first/Db.d.ts +52 -3
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +412 -137
- package/dist/src/local-first/Evolu.d.ts +412 -213
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +181 -18
- package/dist/src/local-first/Owner.d.ts +13 -30
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +13 -30
- package/dist/src/local-first/Protocol.d.ts +106 -19
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +162 -60
- package/dist/src/local-first/Query.d.ts +8 -15
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +4 -2
- package/dist/src/local-first/Schema.d.ts +346 -23
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +214 -17
- package/dist/src/local-first/Shared.d.ts +537 -22
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +1437 -234
- package/dist/src/local-first/Storage.d.ts +195 -17
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +85 -22
- package/dist/src/local-first/Timestamp.d.ts +392 -41
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +403 -81
- package/dist/src/local-first/index.d.ts +0 -1
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +0 -1
- package/package.json +1 -1
- package/src/Assert.test.ts +2 -5
- package/src/Bytes.test.ts +27 -0
- package/src/Bytes.ts +58 -2
- package/src/Config.test.ts +2 -6
- package/src/Config.ts +133 -133
- package/src/Console.ts +62 -7
- package/src/Crypto.ts +76 -4
- package/src/Eq.test.ts +2 -3
- package/src/Error.test.ts +76 -3
- package/src/Error.ts +71 -0
- package/src/Fs.ts +92 -18
- package/src/Identicon.ts +2 -2
- package/src/LeakDetector.ts +22 -3
- package/src/LockManager.ts +8 -0
- package/src/Object.test.ts +27 -12
- package/src/Object.ts +5 -0
- package/src/Platform.ts +50 -8
- package/src/Random.ts +25 -2
- package/src/Resource.test.ts +837 -0
- package/src/Resource.ts +235 -15
- package/src/Schedule.test.ts +50 -12
- package/src/Schedule.ts +24 -14
- package/src/Sqlite.ts +137 -17
- package/src/Task.test.ts +189 -8
- package/src/Task.ts +56 -17
- package/src/Test.ts +9 -0
- package/src/Time.ts +106 -9
- package/src/Type.test.ts +946 -1028
- package/src/Type.ts +4195 -3136
- package/src/Types.test.ts +4 -14
- package/src/WebSocket.ts +313 -40
- package/src/Worker.ts +90 -8
- package/src/index.ts +20 -6
- package/src/local-first/Db.ts +644 -339
- package/src/local-first/Evolu.test.ts +994 -22
- package/src/local-first/Evolu.ts +625 -232
- package/src/local-first/Owner.ts +13 -30
- package/src/local-first/Protocol.test.ts +634 -10
- package/src/local-first/Protocol.ts +255 -109
- package/src/local-first/Query.ts +8 -15
- package/src/local-first/Relay.ts +4 -2
- package/src/local-first/Schema.test.ts +143 -0
- package/src/local-first/Schema.ts +376 -26
- package/src/local-first/Shared.test.ts +7731 -559
- package/src/local-first/Shared.ts +2036 -267
- package/src/local-first/Storage.ts +224 -36
- package/src/local-first/Timestamp.test.ts +344 -70
- package/src/local-first/Timestamp.ts +434 -118
- package/src/local-first/index.ts +0 -1
- package/dist/src/local-first/Error.d.ts +0 -12
- package/dist/src/local-first/Error.d.ts.map +0 -1
- package/dist/src/local-first/Error.js +0 -6
- package/dist/src/local-first/LocalAuth.d.ts +0 -150
- package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
- package/dist/src/local-first/LocalAuth.js +0 -179
- package/src/local-first/Error.ts +0 -17
- package/src/local-first/LocalAuth.ts +0 -457
|
@@ -103,9 +103,9 @@
|
|
|
103
103
|
* fit within the limit, the protocol automatically continues synchronization in
|
|
104
104
|
* subsequent rounds using range-based reconciliation.
|
|
105
105
|
*
|
|
106
|
-
*
|
|
107
|
-
* message
|
|
108
|
-
*
|
|
106
|
+
* Each mutation is limited to {@link maxMutationSize}, so every change fits one
|
|
107
|
+
* message of {@link defaultProtocolMessageMaxSize} next to the largest ranges
|
|
108
|
+
* section.
|
|
109
109
|
*
|
|
110
110
|
* ## Why Binary?
|
|
111
111
|
*
|
|
@@ -176,14 +176,13 @@
|
|
|
176
176
|
import { type NonEmptyReadonlyArray } from "../Array.ts";
|
|
177
177
|
import type { Brand } from "../Brand.ts";
|
|
178
178
|
import { type Buffer } from "../Bytes.ts";
|
|
179
|
-
import type { ConsoleDep } from "../Console.ts";
|
|
180
179
|
import { type DecryptWithXChaCha20Poly1305Error, EncryptionKey, type RandomBytesDep } from "../Crypto.ts";
|
|
181
180
|
import { type Result } from "../Result.ts";
|
|
182
181
|
import type { SqliteValue } from "../Sqlite.ts";
|
|
183
182
|
import { type Task } from "../Task.ts";
|
|
184
183
|
import { NonNegativeInt, PositiveInt, type Typed } from "../Type.ts";
|
|
185
184
|
import { type Owner, type OwnerError, OwnerId, OwnerWriteKey } from "./Owner.ts";
|
|
186
|
-
import { type BaseRange, type CrdtMessage, DbChange, type EncryptedCrdtMessage, type EncryptedDbChange, type FingerprintRange, RangeType, type SkipRange, type StorageDep } from "./Storage.ts";
|
|
185
|
+
import { type BaseRange, type CrdtMessage, DbChange, type EncryptedCrdtMessage, type EncryptedDbChange, type FingerprintRange, RangeType, type SkipRange, type StorageDep, type StorageWriteMessagesError } from "./Storage.ts";
|
|
187
186
|
import { Timestamp } from "./Timestamp.ts";
|
|
188
187
|
/**
|
|
189
188
|
* Protocol message maximum size.
|
|
@@ -243,17 +242,26 @@ export declare const MessageType: {
|
|
|
243
242
|
readonly Broadcast: 2;
|
|
244
243
|
};
|
|
245
244
|
export type MessageType = (typeof MessageType)[keyof typeof MessageType];
|
|
246
|
-
/**
|
|
245
|
+
/**
|
|
246
|
+
* The routing prefix of a {@link ProtocolMessage}.
|
|
247
|
+
*
|
|
248
|
+
* The version and the {@link OwnerId} begin every message of every protocol
|
|
249
|
+
* version. The message type follows them only in messages of this peer's
|
|
250
|
+
* version; the layout after the prefix is unknown for any other version.
|
|
251
|
+
*/
|
|
247
252
|
export interface ProtocolHeader extends Typed<"ProtocolHeader"> {
|
|
248
|
-
readonly version:
|
|
253
|
+
readonly version: NonNegativeInt;
|
|
249
254
|
readonly ownerId: OwnerId;
|
|
250
|
-
|
|
255
|
+
/** Absent when `version` differs from {@link protocolVersion}. */
|
|
256
|
+
readonly messageType?: MessageType;
|
|
251
257
|
}
|
|
252
258
|
/**
|
|
253
|
-
* Parses the
|
|
259
|
+
* Parses the {@link ProtocolHeader} a transport needs to route a message.
|
|
254
260
|
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
261
|
+
* Parsing succeeds for versions this peer cannot process, because a
|
|
262
|
+
* non-initiator answers a version mismatch with only its version and the owner
|
|
263
|
+
* ID. Rejecting that reply here would drop it before
|
|
264
|
+
* {@link applyProtocolMessageAsClient} can report {@link ProtocolVersionError}.
|
|
257
265
|
*/
|
|
258
266
|
export declare const parseProtocolHeader: (inputMessage: Uint8Array) => Result<ProtocolHeader, ProtocolInvalidDataError>;
|
|
259
267
|
export declare const SubscriptionFlags: {
|
|
@@ -311,8 +319,9 @@ export interface ProtocolWriteError extends OwnerError, Typed<"ProtocolWriteErro
|
|
|
311
319
|
* still sync normally.
|
|
312
320
|
*
|
|
313
321
|
* Clients should prompt the user to contact the relay provider or upgrade their
|
|
314
|
-
* plan. Quota monitoring and management is the relay provider's
|
|
315
|
-
*
|
|
322
|
+
* plan. Quota monitoring and management is the relay provider's responsibility.
|
|
323
|
+
* After additional quota is available, call {@link Evolu.requestSync} with the
|
|
324
|
+
* affected owner's ID to retry locally stored changes.
|
|
316
325
|
*/
|
|
317
326
|
export interface ProtocolQuotaError extends OwnerError, Typed<"ProtocolQuotaError"> {
|
|
318
327
|
}
|
|
@@ -339,8 +348,48 @@ export interface ProtocolTimestampMismatchError extends Typed<"ProtocolTimestamp
|
|
|
339
348
|
* unidirectional and stateless transports.
|
|
340
349
|
*/
|
|
341
350
|
export declare const createProtocolMessageFromCrdtMessages: (deps: RandomBytesDep) => (owner: Owner, messages: NonEmptyReadonlyArray<CrdtMessage>, maxSize?: ProtocolMessageMaxSize) => ProtocolMessage;
|
|
351
|
+
/**
|
|
352
|
+
* Creates size-limited broadcast {@link ProtocolMessage}s containing every
|
|
353
|
+
* supplied {@link CrdtMessage}.
|
|
354
|
+
*
|
|
355
|
+
* Broadcasts contain no synchronization ranges or write key. Unlike
|
|
356
|
+
* {@link createProtocolMessageFromCrdtMessages}, this function splits all
|
|
357
|
+
* messages into complete frames rather than relying on later synchronization
|
|
358
|
+
* rounds to deliver messages that do not fit. Each individual message must fit
|
|
359
|
+
* the configured size limit.
|
|
360
|
+
*
|
|
361
|
+
* ### Example
|
|
362
|
+
*
|
|
363
|
+
* ```ts
|
|
364
|
+
* import {
|
|
365
|
+
* assertSame,
|
|
366
|
+
* createId,
|
|
367
|
+
* getOrThrow,
|
|
368
|
+
* testCreateDeps,
|
|
369
|
+
* } from "@evolu/common";
|
|
370
|
+
* import {
|
|
371
|
+
* createProtocolBroadcastMessagesFromCrdtMessages,
|
|
372
|
+
* MessageType,
|
|
373
|
+
* parseProtocolHeader,
|
|
374
|
+
* testAppOwner,
|
|
375
|
+
* testCreateCrdtMessage,
|
|
376
|
+
* } from "@evolu/common/local-first";
|
|
377
|
+
*
|
|
378
|
+
* const deps = testCreateDeps();
|
|
379
|
+
* const broadcasts = createProtocolBroadcastMessagesFromCrdtMessages(deps)(
|
|
380
|
+
* testAppOwner,
|
|
381
|
+
* [testCreateCrdtMessage(createId(deps), 1, "Ada")],
|
|
382
|
+
* );
|
|
383
|
+
* assertSame(broadcasts.length, 1);
|
|
384
|
+
* assertSame(
|
|
385
|
+
* getOrThrow(parseProtocolHeader(broadcasts[0])).messageType,
|
|
386
|
+
* MessageType.Broadcast,
|
|
387
|
+
* );
|
|
388
|
+
* ```
|
|
389
|
+
*/
|
|
390
|
+
export declare const createProtocolBroadcastMessagesFromCrdtMessages: (deps: RandomBytesDep) => (owner: Owner, messages: NonEmptyReadonlyArray<CrdtMessage>, maxSize?: ProtocolMessageMaxSize) => NonEmptyReadonlyArray<ProtocolMessage>;
|
|
342
391
|
/** Creates a {@link ProtocolMessage} for sync. */
|
|
343
|
-
export declare const createProtocolMessageForSync: (deps: StorageDep
|
|
392
|
+
export declare const createProtocolMessageForSync: (deps: StorageDep) => (ownerId: OwnerId, subscriptionFlag?: SubscriptionFlag) => ProtocolMessage;
|
|
344
393
|
export declare const createProtocolMessageForUnsubscribe: (ownerId: OwnerId) => ProtocolMessage;
|
|
345
394
|
/**
|
|
346
395
|
* Mutable builder for constructing {@link ProtocolMessage} respecting size
|
|
@@ -388,18 +437,47 @@ export interface ApplyProtocolMessageAsClientOptions {
|
|
|
388
437
|
version?: NonNegativeInt;
|
|
389
438
|
}
|
|
390
439
|
/**
|
|
391
|
-
* Result
|
|
392
|
-
*
|
|
440
|
+
* Result of {@link applyProtocolMessageAsClient}: a continuation for the
|
|
441
|
+
* non-initiator.
|
|
393
442
|
*/
|
|
394
443
|
export interface ApplyProtocolMessageAsClientResponse extends Typed<"Response"> {
|
|
395
444
|
readonly message: ProtocolMessage;
|
|
445
|
+
/** The uploaded messages, if any, for delivery to other local databases. */
|
|
446
|
+
readonly broadcast?: ProtocolMessage;
|
|
396
447
|
}
|
|
397
|
-
|
|
448
|
+
/**
|
|
449
|
+
* Result of {@link applyProtocolMessageAsClient}: the response needed nothing
|
|
450
|
+
* more, because its ranges all matched or it carried none.
|
|
451
|
+
*/
|
|
452
|
+
export interface ApplyProtocolMessageAsClientConverged extends Typed<"Converged"> {
|
|
398
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* Result of {@link applyProtocolMessageAsClient}: a Broadcast message whose
|
|
456
|
+
* messages were written.
|
|
457
|
+
*/
|
|
399
458
|
export interface ApplyProtocolMessageAsClientBroadcast extends Typed<"Broadcast"> {
|
|
400
459
|
}
|
|
401
|
-
|
|
402
|
-
|
|
460
|
+
/**
|
|
461
|
+
* Result of {@link applyProtocolMessageAsClient}: the messages were written, but
|
|
462
|
+
* without a write key no ranges are reconciled.
|
|
463
|
+
*/
|
|
464
|
+
export interface ApplyProtocolMessageAsClientReadonly extends Typed<"Readonly"> {
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Result of {@link applyProtocolMessageAsClient}: the protocol logged an
|
|
468
|
+
* exception thrown by calling the storage's `writeMessages` (`Write`) or a
|
|
469
|
+
* failed range reconciliation (`Sync`) to the console. An exception while the
|
|
470
|
+
* returned Task runs, as the built-in storages throw, is a defect that aborts
|
|
471
|
+
* the Run instead. Expected write rejections return the original
|
|
472
|
+
* {@link StorageWriteMessagesError} through {@link Result}. Reconciliation can
|
|
473
|
+
* fail after messages have been committed; that failure does not roll back the
|
|
474
|
+
* write.
|
|
475
|
+
*/
|
|
476
|
+
export interface ApplyProtocolMessageAsClientFailed extends Typed<"Failed"> {
|
|
477
|
+
readonly cause: "Write" | "Sync";
|
|
478
|
+
}
|
|
479
|
+
export type ApplyProtocolMessageAsClientResult = ApplyProtocolMessageAsClientResponse | ApplyProtocolMessageAsClientConverged | ApplyProtocolMessageAsClientBroadcast | ApplyProtocolMessageAsClientReadonly | ApplyProtocolMessageAsClientFailed;
|
|
480
|
+
export declare const applyProtocolMessageAsClient: (inputMessage: Uint8Array, options?: ApplyProtocolMessageAsClientOptions) => Task<ApplyProtocolMessageAsClientResult, ProtocolError | StorageWriteMessagesError, StorageDep>;
|
|
403
481
|
export interface ApplyProtocolMessageAsRelayOptions {
|
|
404
482
|
/** To subscribe an owner for broadcasting. */
|
|
405
483
|
subscribe?: (ownerId: OwnerId) => void;
|
|
@@ -434,6 +512,15 @@ version?: NonNegativeInt) => Task<ApplyProtocolMessageAsRelayResult, ProtocolInv
|
|
|
434
512
|
* data.
|
|
435
513
|
*/
|
|
436
514
|
export declare const encodeAndEncryptDbChange: (deps: RandomBytesDep) => (message: CrdtMessage, key: EncryptionKey) => EncryptedDbChange;
|
|
515
|
+
/**
|
|
516
|
+
* Encodes a {@link CrdtMessage} as {@link encodeAndEncryptDbChange} does before
|
|
517
|
+
* padding and encryption.
|
|
518
|
+
*
|
|
519
|
+
* {@link Evolu.getMutationSize} measures mutations with this encoding, so
|
|
520
|
+
* {@link maxMutationSize} limits exactly what is encoded, and every change
|
|
521
|
+
* within it fits one protocol message.
|
|
522
|
+
*/
|
|
523
|
+
export declare const encodeDbChange: (buffer: Buffer, message: CrdtMessage) => void;
|
|
437
524
|
/**
|
|
438
525
|
* Decrypts and decodes an {@link EncryptedCrdtMessage} using the provided
|
|
439
526
|
* owner's encryption key. Verifies that the embedded timestamp matches the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../../src/local-first/Protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgKG;AAEH;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../../src/local-first/Protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -103,9 +103,9 @@
|
|
|
103
103
|
* fit within the limit, the protocol automatically continues synchronization in
|
|
104
104
|
* subsequent rounds using range-based reconciliation.
|
|
105
105
|
*
|
|
106
|
-
*
|
|
107
|
-
* message
|
|
108
|
-
*
|
|
106
|
+
* Each mutation is limited to {@link maxMutationSize}, so every change fits one
|
|
107
|
+
* message of {@link defaultProtocolMessageMaxSize} next to the largest ranges
|
|
108
|
+
* section.
|
|
109
109
|
*
|
|
110
110
|
* ## Why Binary?
|
|
111
111
|
*
|
|
@@ -174,8 +174,8 @@
|
|
|
174
174
|
* indicators) to be forwarded by relays without storage overhead.
|
|
175
175
|
*/
|
|
176
176
|
import { createMutableArray, isNonEmptyArray, } from "../Array.js";
|
|
177
|
-
import { assert, assertNonNullable, assertNotUndefined, assertSame, } from "../Assert.js";
|
|
178
|
-
import { createBuffer, createRunLengthEncoder, decodeFlags, decodeJsonValue, decodeLength, decodeNonNegativeInt, decodeNumber, decodeRle, decodeString, encodeFlags, encodeJsonValue, encodeLength, encodeNonNegativeInt, encodeNumber, encodeString, } from "../Bytes.js";
|
|
177
|
+
import { assert, assertNonEmptyArray, assertNonNullable, assertNotUndefined, assertSame, } from "../Assert.js";
|
|
178
|
+
import { BufferError, createBuffer, createRunLengthEncoder, decodeFlags, decodeJsonValue, decodeLength, decodeNonNegativeInt, decodeNumber, decodeRle, decodeString, encodeFlags, encodeJsonValue, encodeLength, encodeNonNegativeInt, encodeNumber, encodeString, } from "../Bytes.js";
|
|
179
179
|
import { createPadmePadding, decryptWithXChaCha20Poly1305, EncryptionKey, encryptWithXChaCha20Poly1305, Entropy24, XChaCha20Poly1305Ciphertext, xChaCha20Poly1305NonceLength, } from "../Crypto.js";
|
|
180
180
|
import { eqArrayNumber } from "../Eq.js";
|
|
181
181
|
import { computeBalancedBuckets } from "../Number.js";
|
|
@@ -243,10 +243,12 @@ export const MessageType = {
|
|
|
243
243
|
Broadcast: 2,
|
|
244
244
|
};
|
|
245
245
|
/**
|
|
246
|
-
* Parses the
|
|
246
|
+
* Parses the {@link ProtocolHeader} a transport needs to route a message.
|
|
247
247
|
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
248
|
+
* Parsing succeeds for versions this peer cannot process, because a
|
|
249
|
+
* non-initiator answers a version mismatch with only its version and the owner
|
|
250
|
+
* ID. Rejecting that reply here would drop it before
|
|
251
|
+
* {@link applyProtocolMessageAsClient} can report {@link ProtocolVersionError}.
|
|
250
252
|
*/
|
|
251
253
|
export const parseProtocolHeader = (inputMessage) => {
|
|
252
254
|
try {
|
|
@@ -330,6 +332,71 @@ export const createProtocolMessageFromCrdtMessages = (deps) => (owner, messages,
|
|
|
330
332
|
}
|
|
331
333
|
return buffer.unwrap();
|
|
332
334
|
};
|
|
335
|
+
/**
|
|
336
|
+
* Creates size-limited broadcast {@link ProtocolMessage}s containing every
|
|
337
|
+
* supplied {@link CrdtMessage}.
|
|
338
|
+
*
|
|
339
|
+
* Broadcasts contain no synchronization ranges or write key. Unlike
|
|
340
|
+
* {@link createProtocolMessageFromCrdtMessages}, this function splits all
|
|
341
|
+
* messages into complete frames rather than relying on later synchronization
|
|
342
|
+
* rounds to deliver messages that do not fit. Each individual message must fit
|
|
343
|
+
* the configured size limit.
|
|
344
|
+
*
|
|
345
|
+
* ### Example
|
|
346
|
+
*
|
|
347
|
+
* ```ts
|
|
348
|
+
* import {
|
|
349
|
+
* assertSame,
|
|
350
|
+
* createId,
|
|
351
|
+
* getOrThrow,
|
|
352
|
+
* testCreateDeps,
|
|
353
|
+
* } from "@evolu/common";
|
|
354
|
+
* import {
|
|
355
|
+
* createProtocolBroadcastMessagesFromCrdtMessages,
|
|
356
|
+
* MessageType,
|
|
357
|
+
* parseProtocolHeader,
|
|
358
|
+
* testAppOwner,
|
|
359
|
+
* testCreateCrdtMessage,
|
|
360
|
+
* } from "@evolu/common/local-first";
|
|
361
|
+
*
|
|
362
|
+
* const deps = testCreateDeps();
|
|
363
|
+
* const broadcasts = createProtocolBroadcastMessagesFromCrdtMessages(deps)(
|
|
364
|
+
* testAppOwner,
|
|
365
|
+
* [testCreateCrdtMessage(createId(deps), 1, "Ada")],
|
|
366
|
+
* );
|
|
367
|
+
* assertSame(broadcasts.length, 1);
|
|
368
|
+
* assertSame(
|
|
369
|
+
* getOrThrow(parseProtocolHeader(broadcasts[0])).messageType,
|
|
370
|
+
* MessageType.Broadcast,
|
|
371
|
+
* );
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
export const createProtocolBroadcastMessagesFromCrdtMessages = (deps) => (owner, messages, maxSize = defaultProtocolMessageMaxSize) => {
|
|
375
|
+
const broadcasts = [];
|
|
376
|
+
let buffer = createProtocolMessageBuffer(owner.id, {
|
|
377
|
+
messageType: MessageType.Broadcast,
|
|
378
|
+
totalMaxSize: maxSize,
|
|
379
|
+
});
|
|
380
|
+
for (const message of messages) {
|
|
381
|
+
const encryptedMessage = {
|
|
382
|
+
timestamp: message.timestamp,
|
|
383
|
+
change: encodeAndEncryptDbChange(deps)(message, owner.encryptionKey),
|
|
384
|
+
};
|
|
385
|
+
if (!buffer.canAddMessage(encryptedMessage)) {
|
|
386
|
+
const nextBuffer = createProtocolMessageBuffer(owner.id, {
|
|
387
|
+
messageType: MessageType.Broadcast,
|
|
388
|
+
totalMaxSize: maxSize,
|
|
389
|
+
});
|
|
390
|
+
assert(nextBuffer.canAddMessage(encryptedMessage), "the message is too big");
|
|
391
|
+
broadcasts.push(buffer.unwrap());
|
|
392
|
+
buffer = nextBuffer;
|
|
393
|
+
}
|
|
394
|
+
buffer.addMessage(encryptedMessage);
|
|
395
|
+
}
|
|
396
|
+
broadcasts.push(buffer.unwrap());
|
|
397
|
+
assertNonEmptyArray(broadcasts);
|
|
398
|
+
return broadcasts;
|
|
399
|
+
};
|
|
333
400
|
/** Creates a {@link ProtocolMessage} for sync. */
|
|
334
401
|
export const createProtocolMessageForSync = (deps) => (ownerId, subscriptionFlag) => {
|
|
335
402
|
const buffer = createProtocolMessageBuffer(ownerId, {
|
|
@@ -576,17 +643,19 @@ export const applyProtocolMessageAsClient = (inputMessage, options = {}) => asyn
|
|
|
576
643
|
if (isNonEmptyArray(messages)) {
|
|
577
644
|
try {
|
|
578
645
|
const result = await run(storage.writeMessages(ownerIdBytes, messages));
|
|
579
|
-
// Quota errors are handled by Storage; protocol just stops syncing.
|
|
580
646
|
if (!result.ok)
|
|
581
|
-
return
|
|
647
|
+
return result;
|
|
582
648
|
}
|
|
583
649
|
catch (error) {
|
|
584
650
|
if (AbortError.is(error))
|
|
585
651
|
throw error;
|
|
586
652
|
run.deps.console.error(error);
|
|
587
|
-
return ok({ type: "
|
|
653
|
+
return ok({ type: "Failed", cause: "Write" });
|
|
588
654
|
}
|
|
589
655
|
}
|
|
656
|
+
if (messageType === MessageType.Broadcast) {
|
|
657
|
+
return ok({ type: "Broadcast" });
|
|
658
|
+
}
|
|
590
659
|
// Now: No writeKey, no sync.
|
|
591
660
|
// TODO: Allow to sync SharedReadonlyOwner
|
|
592
661
|
// Without local changes, writeKey will not be required.
|
|
@@ -594,26 +663,34 @@ export const applyProtocolMessageAsClient = (inputMessage, options = {}) => asyn
|
|
|
594
663
|
// the sync will stop.
|
|
595
664
|
const writeKey = options.writeKey;
|
|
596
665
|
if (writeKey == null) {
|
|
597
|
-
return ok({ type: "
|
|
598
|
-
}
|
|
599
|
-
if (messageType === MessageType.Broadcast) {
|
|
600
|
-
return ok({ type: "Broadcast" });
|
|
666
|
+
return ok({ type: "Readonly" });
|
|
601
667
|
}
|
|
602
668
|
const ranges = decodeRanges(input);
|
|
603
669
|
if (!isNonEmptyArray(ranges)) {
|
|
604
|
-
return ok({ type: "
|
|
670
|
+
return ok({ type: "Converged" });
|
|
605
671
|
}
|
|
606
672
|
const output = createProtocolMessageBuffer(ownerId, {
|
|
607
673
|
messageType: MessageType.Request,
|
|
608
674
|
writeKey,
|
|
609
675
|
rangesMaxSize: options.rangesMaxSize,
|
|
610
676
|
});
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
677
|
+
let broadcast;
|
|
678
|
+
const result = sync(run.deps)(ranges, output, ownerIdBytes, (message) => {
|
|
679
|
+
broadcast ??= createProtocolMessageBuffer(ownerId, {
|
|
680
|
+
messageType: MessageType.Broadcast,
|
|
681
|
+
});
|
|
682
|
+
broadcast.addMessage(message);
|
|
683
|
+
});
|
|
684
|
+
// A failure was logged by sync.
|
|
685
|
+
if (!result.ok)
|
|
686
|
+
return ok({ type: "Failed", cause: "Sync" });
|
|
687
|
+
if (!result.value)
|
|
688
|
+
return ok({ type: "Converged" });
|
|
689
|
+
return ok({
|
|
690
|
+
type: "Response",
|
|
691
|
+
message: output.unwrap(),
|
|
692
|
+
...(broadcast && { broadcast: broadcast.unwrap() }),
|
|
693
|
+
});
|
|
617
694
|
}
|
|
618
695
|
catch (error) {
|
|
619
696
|
if (AbortError.is(error))
|
|
@@ -687,9 +764,14 @@ version = protocolVersion) => async (run) => {
|
|
|
687
764
|
try {
|
|
688
765
|
const result = await run(storage.writeMessages(ownerIdBytes, messages));
|
|
689
766
|
if (!result.ok) {
|
|
767
|
+
const isQuotaError = result.error.type === "StorageQuotaError";
|
|
768
|
+
if (!isQuotaError)
|
|
769
|
+
run.deps.console.error(result.error);
|
|
690
770
|
const message = createProtocolMessageBuffer(ownerId, {
|
|
691
771
|
messageType: MessageType.Response,
|
|
692
|
-
errorCode:
|
|
772
|
+
errorCode: isQuotaError
|
|
773
|
+
? ProtocolErrorCode.QuotaError
|
|
774
|
+
: ProtocolErrorCode.WriteError,
|
|
693
775
|
}).unwrap();
|
|
694
776
|
return ok({ type: "Response", message });
|
|
695
777
|
}
|
|
@@ -778,7 +860,7 @@ const decodeVersionAndOwner = (input) => {
|
|
|
778
860
|
const parseProtocolHeaderFromBuffer = (input) => {
|
|
779
861
|
const [version, ownerId] = decodeVersionAndOwner(input);
|
|
780
862
|
if (version !== protocolVersion) {
|
|
781
|
-
|
|
863
|
+
return { type: "ProtocolHeader", version, ownerId };
|
|
782
864
|
}
|
|
783
865
|
const messageTypeValue = input.shift();
|
|
784
866
|
let messageType;
|
|
@@ -795,12 +877,7 @@ const parseProtocolHeaderFromBuffer = (input) => {
|
|
|
795
877
|
default:
|
|
796
878
|
throw new ProtocolDecodeError("Invalid MessageType");
|
|
797
879
|
}
|
|
798
|
-
return {
|
|
799
|
-
type: "ProtocolHeader",
|
|
800
|
-
version: 1,
|
|
801
|
-
ownerId,
|
|
802
|
-
messageType,
|
|
803
|
-
};
|
|
880
|
+
return { type: "ProtocolHeader", version, ownerId, messageType };
|
|
804
881
|
};
|
|
805
882
|
/**
|
|
806
883
|
* Error thrown for internal protocol validation failures, such as invalid data
|
|
@@ -824,7 +901,7 @@ const decodeMessages = (buffer) => {
|
|
|
824
901
|
}
|
|
825
902
|
return messages;
|
|
826
903
|
};
|
|
827
|
-
const sync = (deps) => (ranges, output, ownerIdBytes) => {
|
|
904
|
+
const sync = (deps) => (ranges, output, ownerIdBytes, onMessage) => {
|
|
828
905
|
const outputInitialSize = output.getSize();
|
|
829
906
|
let storageSize;
|
|
830
907
|
try {
|
|
@@ -911,7 +988,15 @@ const sync = (deps) => (ranges, output, ownerIdBytes) => {
|
|
|
911
988
|
}
|
|
912
989
|
else if (output.canSplitRange()) {
|
|
913
990
|
coalesceSkipsBeforeAdd();
|
|
914
|
-
|
|
991
|
+
try {
|
|
992
|
+
splitRange(deps)(ownerIdBytes, lower, upper, currentUpperBound, output);
|
|
993
|
+
}
|
|
994
|
+
catch (error) {
|
|
995
|
+
if (AbortError.is(error))
|
|
996
|
+
throw error;
|
|
997
|
+
deps.console.error(error);
|
|
998
|
+
return err(ProtocolErrorCode.SyncError);
|
|
999
|
+
}
|
|
915
1000
|
}
|
|
916
1001
|
else {
|
|
917
1002
|
return addFingerprintForRemainingRange(upper)
|
|
@@ -954,8 +1039,10 @@ const sync = (deps) => (ranges, output, ownerIdBytes) => {
|
|
|
954
1039
|
return false;
|
|
955
1040
|
}
|
|
956
1041
|
ourTimestamps.add(timestampBinary);
|
|
957
|
-
if (message)
|
|
1042
|
+
if (message) {
|
|
958
1043
|
output.addMessage(message);
|
|
1044
|
+
onMessage?.(message);
|
|
1045
|
+
}
|
|
959
1046
|
return true;
|
|
960
1047
|
});
|
|
961
1048
|
}
|
|
@@ -1007,7 +1094,7 @@ const splitRange = (deps) => (ownerId, lower, upper, upperBound, buffer) => {
|
|
|
1007
1094
|
upperBound,
|
|
1008
1095
|
timestamps: createTimestampsBuffer(),
|
|
1009
1096
|
};
|
|
1010
|
-
deps.storage.iterate(ownerId,
|
|
1097
|
+
deps.storage.iterate(ownerId, lower, upper, (timestamp) => {
|
|
1011
1098
|
range.timestamps.add(timestampBytesToTimestamp(timestamp));
|
|
1012
1099
|
return true;
|
|
1013
1100
|
});
|
|
@@ -1021,14 +1108,7 @@ const splitRange = (deps) => (ownerId, lower, upper, upperBound, buffer) => {
|
|
|
1021
1108
|
lower,
|
|
1022
1109
|
...buckets.value.map((b) => NonNegativeInt.orThrow(b + lower)),
|
|
1023
1110
|
];
|
|
1024
|
-
|
|
1025
|
-
try {
|
|
1026
|
-
fingerprintRanges = deps.storage.fingerprintRanges(ownerId, fingerprintRangesBuckets, upperBound);
|
|
1027
|
-
}
|
|
1028
|
-
catch (error) {
|
|
1029
|
-
deps.console.error(error);
|
|
1030
|
-
return;
|
|
1031
|
-
}
|
|
1111
|
+
const fingerprintRanges = deps.storage.fingerprintRanges(ownerId, fingerprintRangesBuckets, upperBound);
|
|
1032
1112
|
const rangesToUse = lower > 0 ? fingerprintRanges.slice(1) : fingerprintRanges;
|
|
1033
1113
|
for (const range of rangesToUse) {
|
|
1034
1114
|
buffer.addRange(range);
|
|
@@ -1130,6 +1210,25 @@ const decodeId = (buffer) => {
|
|
|
1130
1210
|
*/
|
|
1131
1211
|
export const encodeAndEncryptDbChange = (deps) => (message, key) => {
|
|
1132
1212
|
const buffer = createBuffer();
|
|
1213
|
+
encodeDbChange(buffer, message);
|
|
1214
|
+
// Add PADMÉ padding (ignored during decoding)
|
|
1215
|
+
buffer.extend(createPadmePadding(buffer.getLength()));
|
|
1216
|
+
const [ciphertext, nonce] = encryptWithXChaCha20Poly1305(deps)(buffer.unwrap(), key);
|
|
1217
|
+
buffer.reset();
|
|
1218
|
+
buffer.extend(nonce);
|
|
1219
|
+
encodeLength(buffer, ciphertext);
|
|
1220
|
+
buffer.extend(ciphertext);
|
|
1221
|
+
return buffer.unwrap();
|
|
1222
|
+
};
|
|
1223
|
+
/**
|
|
1224
|
+
* Encodes a {@link CrdtMessage} as {@link encodeAndEncryptDbChange} does before
|
|
1225
|
+
* padding and encryption.
|
|
1226
|
+
*
|
|
1227
|
+
* {@link Evolu.getMutationSize} measures mutations with this encoding, so
|
|
1228
|
+
* {@link maxMutationSize} limits exactly what is encoded, and every change
|
|
1229
|
+
* within it fits one protocol message.
|
|
1230
|
+
*/
|
|
1231
|
+
export const encodeDbChange = (buffer, message) => {
|
|
1133
1232
|
encodeNonNegativeInt(buffer, protocolVersion);
|
|
1134
1233
|
// Encode the timestamp to prevent tampering (e.g., a malicious relay
|
|
1135
1234
|
// assigning this EncryptedDbChange to a different EncryptedCrdtMessage)
|
|
@@ -1149,14 +1248,6 @@ export const encodeAndEncryptDbChange = (deps) => (message, key) => {
|
|
|
1149
1248
|
encodeString(buffer, column);
|
|
1150
1249
|
encodeSqliteValue(buffer, value);
|
|
1151
1250
|
}
|
|
1152
|
-
// Add PADMÉ padding (ignored during decoding)
|
|
1153
|
-
buffer.extend(createPadmePadding(buffer.getLength()));
|
|
1154
|
-
const [ciphertext, nonce] = encryptWithXChaCha20Poly1305(deps)(buffer.unwrap(), key);
|
|
1155
|
-
buffer.reset();
|
|
1156
|
-
buffer.extend(nonce);
|
|
1157
|
-
encodeLength(buffer, ciphertext);
|
|
1158
|
-
buffer.extend(ciphertext);
|
|
1159
|
-
return buffer.unwrap();
|
|
1160
1251
|
};
|
|
1161
1252
|
/**
|
|
1162
1253
|
* Decrypts and decodes an {@link EncryptedCrdtMessage} using the provided
|
|
@@ -1288,22 +1379,33 @@ export const encodeSqliteValue = (buffer, value) => {
|
|
|
1288
1379
|
return;
|
|
1289
1380
|
}
|
|
1290
1381
|
const json = Json.from.parent(value);
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
// which would cause data corruption if we don't verify round-trip safety.
|
|
1294
|
-
if (json.ok && JSON.stringify(jsonToJsonValue(json.value)) === value) {
|
|
1382
|
+
if (json.ok) {
|
|
1383
|
+
const jsonValue = jsonToJsonValue(json.value);
|
|
1295
1384
|
jsonBuffer.reset();
|
|
1296
1385
|
try {
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1386
|
+
// Encoding first rejects nesting deeper than decoding allows, before
|
|
1387
|
+
// the recursive JSON.stringify below could overflow the stack. Such
|
|
1388
|
+
// a value is encoded as a plain string.
|
|
1389
|
+
encodeJsonValue(jsonBuffer, jsonValue);
|
|
1390
|
+
// Only encode as Json if it survives JSON.parse/JSON.stringify
|
|
1391
|
+
// round-trip. Some valid JSON strings like "-0E0" get normalized to
|
|
1392
|
+
// "0" during parsing, which would cause data corruption if we don't
|
|
1393
|
+
// verify round-trip safety.
|
|
1394
|
+
if (JSON.stringify(jsonValue) === value) {
|
|
1395
|
+
const jsonBytes = jsonBuffer.unwrap();
|
|
1396
|
+
encodeNonNegativeInt(buffer, ProtocolValueType.Json);
|
|
1397
|
+
encodeLength(buffer, jsonBytes);
|
|
1398
|
+
buffer.extend(jsonBytes);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
catch (error) {
|
|
1403
|
+
if (!(error instanceof BufferError))
|
|
1404
|
+
throw error;
|
|
1302
1405
|
}
|
|
1303
1406
|
finally {
|
|
1304
1407
|
jsonBuffer.reset();
|
|
1305
1408
|
}
|
|
1306
|
-
return;
|
|
1307
1409
|
}
|
|
1308
1410
|
const base64Url = Base64Url.from.parent(value);
|
|
1309
1411
|
if (base64Url.ok) {
|
|
@@ -18,25 +18,22 @@ export type { NotNull as KyselyNotNull } from "kysely";
|
|
|
18
18
|
* import {
|
|
19
19
|
* assertType,
|
|
20
20
|
* createQueryBuilder,
|
|
21
|
-
*
|
|
21
|
+
* testEvoluSchema,
|
|
22
|
+
* type TestEvoluSchema,
|
|
22
23
|
* NonEmptyTrimmedString100,
|
|
23
24
|
* type Query,
|
|
25
|
+
* type TestTodoId,
|
|
24
26
|
* } from "@evolu/common";
|
|
25
27
|
*
|
|
26
|
-
* const
|
|
27
|
-
* type TodoId = typeof TodoId.Output;
|
|
28
|
-
* const Schema = {
|
|
29
|
-
* todo: { id: TodoId, title: NonEmptyTrimmedString100 },
|
|
30
|
-
* };
|
|
31
|
-
* const createQuery = createQueryBuilder(Schema);
|
|
28
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
32
29
|
* const allTodos = createQuery((db) => db.selectFrom("todo").selectAll());
|
|
33
30
|
*
|
|
34
31
|
* type AllTodosRow = typeof allTodos.Row;
|
|
35
32
|
* assertType<
|
|
36
|
-
* typeof allTodos extends Query<
|
|
33
|
+
* typeof allTodos extends Query<TestEvoluSchema> ? true : false,
|
|
37
34
|
* true
|
|
38
35
|
* >();
|
|
39
|
-
* assertType<AllTodosRow["id"],
|
|
36
|
+
* assertType<AllTodosRow["id"], TestTodoId>();
|
|
40
37
|
* assertType<AllTodosRow["title"], NonEmptyTrimmedString100 | null>();
|
|
41
38
|
* ```
|
|
42
39
|
*/
|
|
@@ -52,15 +49,11 @@ export type Query<S extends EvoluSchema = EvoluSchema, R extends Row = Row> = Sq
|
|
|
52
49
|
* import {
|
|
53
50
|
* assertType,
|
|
54
51
|
* createQueryBuilder,
|
|
55
|
-
*
|
|
52
|
+
* testEvoluSchema,
|
|
56
53
|
* type InferRow,
|
|
57
|
-
* NonEmptyTrimmedString100,
|
|
58
54
|
* } from "@evolu/common";
|
|
59
55
|
*
|
|
60
|
-
* const
|
|
61
|
-
* todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
|
|
62
|
-
* };
|
|
63
|
-
* const createQuery = createQueryBuilder(Schema);
|
|
56
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
64
57
|
* const allTodos = createQuery((db) =>
|
|
65
58
|
* db.selectFrom("todo").selectAll(),
|
|
66
59
|
* );
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../../../src/local-first/Query.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,UAAU,EACV,QAAQ,IAAI,cAAc,EAC1B,UAAU,EACV,eAAe,EAChB,MAAM,QAAQ,CAAC;AAYhB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,GAAG,IAAI,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC1C,YAAY,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEvD
|
|
1
|
+
{"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../../../src/local-first/Query.ts"],"names":[],"mappings":"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"}
|