@evolu/common 6.0.1-preview.13 → 6.0.1-preview.14
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/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +10 -7
- package/dist/src/Evolu/Protocol.d.ts +20 -5
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +32 -8
- package/dist/src/Evolu/Timestamp.d.ts +6 -0
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +7 -0
- package/package.json +3 -2
- package/src/Evolu/Db.ts +10 -7
- package/src/Evolu/Protocol.ts +90 -38
- package/src/Evolu/Timestamp.ts +8 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Db.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EAEpB,2BAA2B,EAE5B,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAa,GAAG,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAM,MAAM,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAEL,qBAAqB,EAErB,OAAO,EAEP,SAAS,EACT,WAAW,EAEX,SAAS,EAEV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAS,EAAE,EAAE,QAAQ,EAAU,UAAU,EAAU,MAAM,YAAY,CAAC;AAC7E,OAAO,EAEL,MAAM,EAEP,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAe,YAAY,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EACL,QAAQ,EAGR,QAAQ,EAET,MAAM,YAAY,CAAC;AACpB,OAAO,EAKL,aAAa,EACb,WAAW,EAGX,QAAQ,EAKR,aAAa,EAEb,OAAO,EAER,MAAM,eAAe,CAAC;AACvB,OAAO,EAIL,KAAK,EAEN,MAAM,YAAY,CAAC;AACpB,OAAO,EAGL,iBAAiB,EAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAuB,MAAM,WAAW,CAAC;AAC/D,OAAO,
|
|
1
|
+
{"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Db.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EAEpB,2BAA2B,EAE5B,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAa,GAAG,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAM,MAAM,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAEL,qBAAqB,EAErB,OAAO,EAEP,SAAS,EACT,WAAW,EAEX,SAAS,EAEV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAS,EAAE,EAAE,QAAQ,EAAU,UAAU,EAAU,MAAM,YAAY,CAAC;AAC7E,OAAO,EAEL,MAAM,EAEP,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAe,YAAY,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EACL,QAAQ,EAGR,QAAQ,EAET,MAAM,YAAY,CAAC;AACpB,OAAO,EAKL,aAAa,EACb,WAAW,EAGX,QAAQ,EAKR,aAAa,EAEb,OAAO,EAER,MAAM,eAAe,CAAC;AACvB,OAAO,EAIL,KAAK,EAEN,MAAM,YAAY,CAAC;AACpB,OAAO,EAGL,iBAAiB,EAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAuB,MAAM,WAAW,CAAC;AAC/D,OAAO,EAQL,cAAc,EAKf,MAAM,gBAAgB,CAAC;AAExB,eAAO,MAAM,OAAO;;;EAGlB,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAE1C,eAAO,MAAM,OAAO;;;EAAwC,CAAC;AAC7D,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAE1C,eAAO,MAAM,QAAQ;;;;;;;;;EAGnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAE5C,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;AAE7D,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,QAAQ,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CACzC;AAED,MAAM,MAAM,aAAa,GACrB;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAC/C,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAClD,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAClD,QAAQ,CAAC,iBAAiB,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;CAClD,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC,KAAK,CAAC,CAAC;CAChD,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;QAC5B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;KAC7B,CAAC;CACH,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC;CACnC,CAAC;AAEN,MAAM,MAAM,cAAc,GACtB;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;CAC1B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EACV,WAAW,GACX,iBAAiB,GACjB,cAAc,GACd,aAAa,GACb,2BAA2B,CAAC;CACjC,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAC9C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;CACnD,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;CACrB,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B,CAAC;AAEN,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,GACtD,aAAa,GACb,UAAU,GACV,OAAO,GACP,SAAS,GACT,YAAY,GACZ,iBAAiB,GACjB,oBAAoB,CAAC;AAkBvB,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;CACrC;AAMD,eAAO,MAAM,yBAAyB,GACpC,cAAc,oBAAoB,KACjC,QA0SF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,GACrB,MAAM,SAAS,MACf,iBAAwB;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,KAAG,MAAM,CAC7D,QAAQ,EACR,WAAW,CA4DZ,CAAC;AAKJ,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;KAChC,CAAC,CAAC;CACJ;AAGD,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,UAmB/C,CAAC;AAiEF,eAAO,MAAM,cAAc,GACzB,WAAW,MAAM,EACjB,SAAS,aAAa,CAAC,MAAM,CAAC,KAC7B,OAcW,CAAC;AAyPf,eAAO,MAAM,uCAAuC,GACjD,MAAM,SAAS,GAAG,gBAAgB,MAClC,SAAS,aAAa,EAAE,SAAS,WAAW,KAAG,MAAM,CAAC,IAAI,EAAE,WAAW,CA0BvE,CAAC;AA6FJ,MAAM,WAAW,aAAc,SAAQ,iBAAiB,EAAE,OAAO;CAAG;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;CACjC"}
|
package/dist/src/Evolu/Db.js
CHANGED
|
@@ -14,7 +14,7 @@ import { createAppOwner, createOwnerRow, } from "./Owner.js";
|
|
|
14
14
|
import { applyProtocolMessageAsClient, Base64Url256, binaryIdToId, createProtocolMessageForSync, createProtocolMessageFromCrdtMessages, decryptAndDecodeDbChange, encodeAndEncryptDbChange, idToBinaryId, ownerIdToBinaryOwnerId, protocolVersion, } from "./Protocol.js";
|
|
15
15
|
import { createQueryRowsCache, deserializeQuery, emptyRows, } from "./Query.js";
|
|
16
16
|
import { createSqliteStorageBase, } from "./Storage.js";
|
|
17
|
-
import { receiveTimestamp, sendTimestamp, timestampStringToTimestamp, timestampToBinaryTimestamp, timestampToTimestampString, } from "./Timestamp.js";
|
|
17
|
+
import { binaryTimestampToTimestamp, receiveTimestamp, sendTimestamp, timestampStringToTimestamp, timestampToBinaryTimestamp, timestampToTimestampString, } from "./Timestamp.js";
|
|
18
18
|
export const DbTable = object({
|
|
19
19
|
name: Base64Url256,
|
|
20
20
|
columns: array(Base64Url256),
|
|
@@ -676,7 +676,7 @@ const createClientStorage = (deps) => (options) => {
|
|
|
676
676
|
const owner = deps.ownerRowRef.get();
|
|
677
677
|
const decodedAndDecryptedMessages = [];
|
|
678
678
|
for (const message of messages) {
|
|
679
|
-
const dbChange = decryptAndDecodeDbChange(deps)(message
|
|
679
|
+
const dbChange = decryptAndDecodeDbChange(deps)(message, owner.encryptionKey);
|
|
680
680
|
if (!dbChange.ok) {
|
|
681
681
|
deps.postMessage({
|
|
682
682
|
type: "onError",
|
|
@@ -728,13 +728,16 @@ const createClientStorage = (deps) => (options) => {
|
|
|
728
728
|
assert(eqArrayNumber(r.id, id), "All rows must have the same Id");
|
|
729
729
|
values[r.column] = r.value;
|
|
730
730
|
}
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
731
|
+
const message = {
|
|
732
|
+
timestamp: binaryTimestampToTimestamp(timestamp),
|
|
733
|
+
change: {
|
|
734
|
+
table: rows[0].table,
|
|
735
|
+
id: binaryIdToId(rows[0].id),
|
|
736
|
+
values,
|
|
737
|
+
},
|
|
735
738
|
};
|
|
736
739
|
const { encryptionKey } = deps.ownerRowRef.get();
|
|
737
|
-
return encodeAndEncryptDbChange(deps)(
|
|
740
|
+
return encodeAndEncryptDbChange(deps)(message, encryptionKey);
|
|
738
741
|
},
|
|
739
742
|
};
|
|
740
743
|
return ok(storage);
|
|
@@ -279,7 +279,7 @@ export interface TimestampsRangeWithTimestampsBuffer extends BaseRange {
|
|
|
279
279
|
readonly timestamps: TimestampsBuffer;
|
|
280
280
|
}
|
|
281
281
|
export type Range = SkipRange | FingerprintRange | TimestampsRange;
|
|
282
|
-
export type ProtocolError = ProtocolUnsupportedVersionError | ProtocolInvalidDataError | ProtocolWriteKeyError | ProtocolWriteError | ProtocolSyncError;
|
|
282
|
+
export type ProtocolError = ProtocolUnsupportedVersionError | ProtocolInvalidDataError | ProtocolWriteKeyError | ProtocolWriteError | ProtocolSyncError | ProtocolTimestampMismatchError;
|
|
283
283
|
/** Base interface for all protocol errors. */
|
|
284
284
|
export interface ProtocolErrorBase {
|
|
285
285
|
readonly ownerId: OwnerId;
|
|
@@ -318,6 +318,16 @@ export interface ProtocolWriteError extends ProtocolErrorBase {
|
|
|
318
318
|
export interface ProtocolSyncError extends ProtocolErrorBase {
|
|
319
319
|
readonly type: "ProtocolSyncError";
|
|
320
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* Error when embedded timestamp doesn't match expected timestamp in
|
|
323
|
+
* EncryptedDbChange. Indicates potential tampering or corruption of CRDT
|
|
324
|
+
* messages.
|
|
325
|
+
*/
|
|
326
|
+
export interface ProtocolTimestampMismatchError {
|
|
327
|
+
readonly type: "ProtocolTimestampMismatchError";
|
|
328
|
+
readonly expected: Timestamp;
|
|
329
|
+
readonly embedded: Timestamp;
|
|
330
|
+
}
|
|
321
331
|
/**
|
|
322
332
|
* Creates a {@link ProtocolMessage} from CRDT messages.
|
|
323
333
|
*
|
|
@@ -412,13 +422,18 @@ export declare const binaryTimestampToFingerprint: (timestamp: BinaryTimestamp)
|
|
|
412
422
|
/**
|
|
413
423
|
* Encodes and encrypts a {@link DbChange} using the provided owner's encryption
|
|
414
424
|
* key. Returns an encrypted binary representation as {@link EncryptedDbChange}.
|
|
425
|
+
*
|
|
426
|
+
* The format includes the protocol version for backward compatibility and the
|
|
427
|
+
* timestamp for tamper-proof verification that the timestamp matches the change
|
|
428
|
+
* data.
|
|
415
429
|
*/
|
|
416
|
-
export declare const encodeAndEncryptDbChange: (deps: SymmetricCryptoDep) => (
|
|
430
|
+
export declare const encodeAndEncryptDbChange: (deps: SymmetricCryptoDep) => (message: CrdtMessage, key: EncryptionKey) => EncryptedDbChange;
|
|
417
431
|
/**
|
|
418
|
-
* Decrypts and decodes an {@link
|
|
419
|
-
* encryption key.
|
|
432
|
+
* Decrypts and decodes an {@link EncryptedCrdtMessage} using the provided
|
|
433
|
+
* owner's encryption key. Verifies that the embedded timestamp matches the
|
|
434
|
+
* expected timestamp to ensure message integrity.
|
|
420
435
|
*/
|
|
421
|
-
export declare const decryptAndDecodeDbChange: (deps: SymmetricCryptoDep) => (
|
|
436
|
+
export declare const decryptAndDecodeDbChange: (deps: SymmetricCryptoDep) => (message: EncryptedCrdtMessage, key: EncryptionKey) => Result<DbChange, SymmetricCryptoDecryptError | ProtocolInvalidDataError | ProtocolTimestampMismatchError>;
|
|
422
437
|
/**
|
|
423
438
|
* Encodes a non-negative integer into a variable-length integer format. It's
|
|
424
439
|
* more efficient than encoding via {@link encodeNumber}.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2IG;AAIH,OAAO,EAA2B,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE7E,OAAO,EACL,MAAM,EAMP,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,aAAa,EAEb,2BAA2B,EAC3B,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAItB,OAAO,EAAW,MAAM,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAIL,EAAE,EAIF,MAAM,EACN,cAAc,EAGd,WAAW,EAEZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAC;AAC/C,OAAO,EAEL,OAAO,EACP,oBAAoB,EACpB,QAAQ,EAET,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,
|
|
1
|
+
{"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -151,7 +151,7 @@ import { err, ok } from "../Result.js";
|
|
|
151
151
|
import { SqliteValue } from "../Sqlite.js";
|
|
152
152
|
import { Base64Url, base64UrlAlphabet, DateIsoString, Id, idTypeValueLength, JsonValueFromString, maxLength, NonNegativeInt, Number, object, record, } from "../Type.js";
|
|
153
153
|
import { writeKeyLength, } from "./Owner.js";
|
|
154
|
-
import { binaryTimestampToTimestamp, Counter, Millis, timestampToBinaryTimestamp, } from "./Timestamp.js";
|
|
154
|
+
import { binaryTimestampLength, binaryTimestampToTimestamp, Counter, eqTimestamp, Millis, timestampToBinaryTimestamp, } from "./Timestamp.js";
|
|
155
155
|
/** Maximum size of the entire protocol message in bytes. */
|
|
156
156
|
export const maxProtocolMessageSize = 1_000_000;
|
|
157
157
|
/** Maximum size of the ranges in bytes. */
|
|
@@ -210,7 +210,7 @@ export const createProtocolMessageFromCrdtMessages = (deps) => (owner, messages,
|
|
|
210
210
|
});
|
|
211
211
|
let notAllMessagesSent = false;
|
|
212
212
|
for (const message of messages) {
|
|
213
|
-
const change = encodeAndEncryptDbChange(deps)(message
|
|
213
|
+
const change = encodeAndEncryptDbChange(deps)(message, owner.encryptionKey);
|
|
214
214
|
const encryptedCrdtMessage = { timestamp: message.timestamp, change };
|
|
215
215
|
if (buffer.canAddMessage(encryptedCrdtMessage)) {
|
|
216
216
|
buffer.addMessage(encryptedCrdtMessage);
|
|
@@ -901,7 +901,7 @@ const decodeTimestamps = (buffer, length) => {
|
|
|
901
901
|
const deltaMillis = decodeNonNegativeInt(buffer);
|
|
902
902
|
const millis = Millis.from(previousMillis + deltaMillis);
|
|
903
903
|
if (!millis.ok)
|
|
904
|
-
throw new
|
|
904
|
+
throw new ProtocolDecodeError(millis.error.type);
|
|
905
905
|
millises.push(millis.value);
|
|
906
906
|
previousMillis = millis.value;
|
|
907
907
|
}
|
|
@@ -910,7 +910,7 @@ const decodeTimestamps = (buffer, length) => {
|
|
|
910
910
|
while (counterIndex < length) {
|
|
911
911
|
const counter = Counter.from(decodeNonNegativeInt(buffer));
|
|
912
912
|
if (!counter.ok)
|
|
913
|
-
throw new
|
|
913
|
+
throw new ProtocolDecodeError(counter.error.type);
|
|
914
914
|
const runLength = decodeNonNegativeInt(buffer);
|
|
915
915
|
for (let i = 0; i < runLength; i++) {
|
|
916
916
|
counters.push(counter.value);
|
|
@@ -1025,9 +1025,19 @@ export const binaryTimestampToFingerprint = (timestamp) => {
|
|
|
1025
1025
|
/**
|
|
1026
1026
|
* Encodes and encrypts a {@link DbChange} using the provided owner's encryption
|
|
1027
1027
|
* key. Returns an encrypted binary representation as {@link EncryptedDbChange}.
|
|
1028
|
+
*
|
|
1029
|
+
* The format includes the protocol version for backward compatibility and the
|
|
1030
|
+
* timestamp for tamper-proof verification that the timestamp matches the change
|
|
1031
|
+
* data.
|
|
1028
1032
|
*/
|
|
1029
|
-
export const encodeAndEncryptDbChange = (deps) => (
|
|
1033
|
+
export const encodeAndEncryptDbChange = (deps) => (message, key) => {
|
|
1034
|
+
const change = message.change;
|
|
1030
1035
|
const buffer = createBuffer();
|
|
1036
|
+
// Encode protocol version first for backward compatibility
|
|
1037
|
+
encodeNonNegativeInt(buffer, protocolVersion);
|
|
1038
|
+
// Encode the timestamp (after version) for tamper verification
|
|
1039
|
+
const binaryTimestamp = timestampToBinaryTimestamp(message.timestamp);
|
|
1040
|
+
buffer.extend(binaryTimestamp);
|
|
1031
1041
|
encodeBase64Url256(buffer, change.table);
|
|
1032
1042
|
buffer.extend(idToBinaryId(change.id));
|
|
1033
1043
|
const entries = objectToEntries(change.values).map(([column, value]) => {
|
|
@@ -1049,10 +1059,11 @@ export const encodeAndEncryptDbChange = (deps) => (change, key) => {
|
|
|
1049
1059
|
return buffer.unwrap();
|
|
1050
1060
|
};
|
|
1051
1061
|
/**
|
|
1052
|
-
* Decrypts and decodes an {@link
|
|
1053
|
-
* encryption key.
|
|
1062
|
+
* Decrypts and decodes an {@link EncryptedCrdtMessage} using the provided
|
|
1063
|
+
* owner's encryption key. Verifies that the embedded timestamp matches the
|
|
1064
|
+
* expected timestamp to ensure message integrity.
|
|
1054
1065
|
*/
|
|
1055
|
-
export const decryptAndDecodeDbChange = (deps) => (
|
|
1066
|
+
export const decryptAndDecodeDbChange = (deps) => (message, key) => tryDecodeProtocolData(message.change, (buffer) => {
|
|
1056
1067
|
const nonce = buffer.shiftN(deps.symmetricCrypto.nonceLength);
|
|
1057
1068
|
const ciphertextLength = decodeLength(buffer);
|
|
1058
1069
|
const ciphertext = buffer.shiftN(ciphertextLength);
|
|
@@ -1061,6 +1072,19 @@ export const decryptAndDecodeDbChange = (deps) => (change, key) => tryDecodeProt
|
|
|
1061
1072
|
return plaintextBytes;
|
|
1062
1073
|
buffer.reset();
|
|
1063
1074
|
buffer.extend(plaintextBytes.value);
|
|
1075
|
+
// Decode version (for future compatibility, no validation needed for now)
|
|
1076
|
+
decodeNonNegativeInt(buffer);
|
|
1077
|
+
// Decode and verify the embedded timestamp
|
|
1078
|
+
const embeddedBinaryTimestamp = buffer.shiftN(binaryTimestampLength);
|
|
1079
|
+
const embeddedTimestamp = binaryTimestampToTimestamp(embeddedBinaryTimestamp);
|
|
1080
|
+
// Verify timestamp integrity
|
|
1081
|
+
if (!eqTimestamp(embeddedTimestamp, message.timestamp)) {
|
|
1082
|
+
return err({
|
|
1083
|
+
type: "ProtocolTimestampMismatchError",
|
|
1084
|
+
expected: message.timestamp,
|
|
1085
|
+
embedded: embeddedTimestamp,
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1064
1088
|
const table = decodeBase64Url256WithLength(buffer);
|
|
1065
1089
|
const id = decodeId(buffer);
|
|
1066
1090
|
const length = decodeLength(buffer);
|
|
@@ -89,6 +89,12 @@ export declare const Timestamp: import("../Type.js").ObjectType<{
|
|
|
89
89
|
nodeId: import("../Type.js").BrandType<import("../Type.js").Type<"String", string, string, import("../Type.js").StringError, string, import("../Type.js").StringError>, "NodeId", import("../Type.js").RegexError<"NodeId">, import("../Type.js").StringError>;
|
|
90
90
|
}>;
|
|
91
91
|
export type Timestamp = typeof Timestamp.Type;
|
|
92
|
+
/** Equality function for comparing {@link Timestamp}. */
|
|
93
|
+
export declare const eqTimestamp: import("../Eq.js").Eq<{
|
|
94
|
+
readonly millis: number & Brand<"Int"> & Brand<"NonNegative"> & Brand<`LessThanOrEqualTo${number}`> & Brand<"Millis">;
|
|
95
|
+
readonly counter: number & Brand<"Int"> & Brand<"NonNegative"> & Brand<"LessThanOrEqualTo65535"> & Brand<"Counter">;
|
|
96
|
+
readonly nodeId: string & Brand<"NodeId">;
|
|
97
|
+
}>;
|
|
92
98
|
export declare const createTimestamp: ({ millis, counter, nodeId, }?: Partial<Timestamp>) => Timestamp;
|
|
93
99
|
export declare const createInitialTimestamp: (deps: NanoIdLibDep) => Timestamp;
|
|
94
100
|
/** TimestampString is a sortable string version of {@link Timestamp}. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Timestamp.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Timestamp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Timestamp.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Timestamp.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert } from "../Assert.js";
|
|
2
|
+
import { createEqObject, eqNumber, eqString } from "../Eq.js";
|
|
2
3
|
import { increment } from "../Number.js";
|
|
3
4
|
import { orderUint8Array } from "../Order.js";
|
|
4
5
|
import { err, getOrThrow, ok } from "../Result.js";
|
|
@@ -58,6 +59,12 @@ export const Timestamp = object({
|
|
|
58
59
|
counter: Counter,
|
|
59
60
|
nodeId: NodeId,
|
|
60
61
|
});
|
|
62
|
+
/** Equality function for comparing {@link Timestamp}. */
|
|
63
|
+
export const eqTimestamp = createEqObject({
|
|
64
|
+
millis: eqNumber,
|
|
65
|
+
counter: eqNumber,
|
|
66
|
+
nodeId: eqString,
|
|
67
|
+
});
|
|
61
68
|
export const createTimestamp = ({ millis = minMillis, counter = minCounter, nodeId = minNodeId, } = {}) => ({ millis, counter, nodeId });
|
|
62
69
|
const hexAlphabet = "0123456789abcdef";
|
|
63
70
|
export const createInitialTimestamp = (deps) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolu/common",
|
|
3
|
-
"version": "6.0.1-preview.
|
|
3
|
+
"version": "6.0.1-preview.14",
|
|
4
4
|
"description": "TypeScript library and local-first framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"evolu",
|
|
@@ -71,7 +71,8 @@
|
|
|
71
71
|
"scripts": {
|
|
72
72
|
"dev": "tsc --watch",
|
|
73
73
|
"build": "shx rm -rf dist ./README.md && tsc && shx cp ../../README.md ./",
|
|
74
|
-
"test": "vitest --disableConsoleIntercept",
|
|
74
|
+
"test": "vitest run --disableConsoleIntercept",
|
|
75
|
+
"test:watch": "vitest --disableConsoleIntercept",
|
|
75
76
|
"clean": "shx rm -rf .turbo node_modules dist",
|
|
76
77
|
"format": "prettier --write \"src/*.{ts,tsx,md}\"",
|
|
77
78
|
"bench": "vitest bench"
|
package/src/Evolu/Db.ts
CHANGED
|
@@ -82,6 +82,7 @@ import {
|
|
|
82
82
|
} from "./Storage.js";
|
|
83
83
|
import { CreateSyncDep, SyncConfig, SyncDep } from "./Sync.js";
|
|
84
84
|
import {
|
|
85
|
+
binaryTimestampToTimestamp,
|
|
85
86
|
receiveTimestamp,
|
|
86
87
|
sendTimestamp,
|
|
87
88
|
Timestamp,
|
|
@@ -1114,7 +1115,7 @@ const createClientStorage =
|
|
|
1114
1115
|
|
|
1115
1116
|
for (const message of messages) {
|
|
1116
1117
|
const dbChange = decryptAndDecodeDbChange(deps)(
|
|
1117
|
-
message
|
|
1118
|
+
message,
|
|
1118
1119
|
owner.encryptionKey,
|
|
1119
1120
|
);
|
|
1120
1121
|
|
|
@@ -1189,15 +1190,17 @@ const createClientStorage =
|
|
|
1189
1190
|
values[r.column] = r.value;
|
|
1190
1191
|
}
|
|
1191
1192
|
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1193
|
+
const message: CrdtMessage = {
|
|
1194
|
+
timestamp: binaryTimestampToTimestamp(timestamp),
|
|
1195
|
+
change: {
|
|
1196
|
+
table: rows[0].table,
|
|
1197
|
+
id: binaryIdToId(rows[0].id),
|
|
1198
|
+
values,
|
|
1199
|
+
},
|
|
1196
1200
|
};
|
|
1197
|
-
|
|
1198
1201
|
const { encryptionKey } = deps.ownerRowRef.get();
|
|
1199
1202
|
|
|
1200
|
-
return encodeAndEncryptDbChange(deps)(
|
|
1203
|
+
return encodeAndEncryptDbChange(deps)(message, encryptionKey);
|
|
1201
1204
|
},
|
|
1202
1205
|
};
|
|
1203
1206
|
|
package/src/Evolu/Protocol.ts
CHANGED
|
@@ -188,8 +188,10 @@ import {
|
|
|
188
188
|
} from "./Owner.js";
|
|
189
189
|
import {
|
|
190
190
|
BinaryTimestamp,
|
|
191
|
+
binaryTimestampLength,
|
|
191
192
|
binaryTimestampToTimestamp,
|
|
192
193
|
Counter,
|
|
194
|
+
eqTimestamp,
|
|
193
195
|
Millis,
|
|
194
196
|
NodeId,
|
|
195
197
|
Timestamp,
|
|
@@ -399,7 +401,8 @@ export type ProtocolError =
|
|
|
399
401
|
| ProtocolInvalidDataError
|
|
400
402
|
| ProtocolWriteKeyError
|
|
401
403
|
| ProtocolWriteError
|
|
402
|
-
| ProtocolSyncError
|
|
404
|
+
| ProtocolSyncError
|
|
405
|
+
| ProtocolTimestampMismatchError;
|
|
403
406
|
|
|
404
407
|
/** Base interface for all protocol errors. */
|
|
405
408
|
export interface ProtocolErrorBase {
|
|
@@ -445,6 +448,17 @@ export interface ProtocolSyncError extends ProtocolErrorBase {
|
|
|
445
448
|
readonly type: "ProtocolSyncError";
|
|
446
449
|
}
|
|
447
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Error when embedded timestamp doesn't match expected timestamp in
|
|
453
|
+
* EncryptedDbChange. Indicates potential tampering or corruption of CRDT
|
|
454
|
+
* messages.
|
|
455
|
+
*/
|
|
456
|
+
export interface ProtocolTimestampMismatchError {
|
|
457
|
+
readonly type: "ProtocolTimestampMismatchError";
|
|
458
|
+
readonly expected: Timestamp;
|
|
459
|
+
readonly embedded: Timestamp;
|
|
460
|
+
}
|
|
461
|
+
|
|
448
462
|
/**
|
|
449
463
|
* Creates a {@link ProtocolMessage} from CRDT messages.
|
|
450
464
|
*
|
|
@@ -469,7 +483,7 @@ export const createProtocolMessageFromCrdtMessages =
|
|
|
469
483
|
|
|
470
484
|
for (const message of messages) {
|
|
471
485
|
const change = encodeAndEncryptDbChange(deps)(
|
|
472
|
-
message
|
|
486
|
+
message,
|
|
473
487
|
owner.encryptionKey,
|
|
474
488
|
);
|
|
475
489
|
const encryptedCrdtMessage = { timestamp: message.timestamp, change };
|
|
@@ -1484,7 +1498,7 @@ const decodeTimestamps = (
|
|
|
1484
1498
|
for (let i = 0; i < length; i++) {
|
|
1485
1499
|
const deltaMillis = decodeNonNegativeInt(buffer);
|
|
1486
1500
|
const millis = Millis.from(previousMillis + deltaMillis);
|
|
1487
|
-
if (!millis.ok) throw new
|
|
1501
|
+
if (!millis.ok) throw new ProtocolDecodeError(millis.error.type);
|
|
1488
1502
|
millises.push(millis.value);
|
|
1489
1503
|
previousMillis = millis.value;
|
|
1490
1504
|
}
|
|
@@ -1493,7 +1507,7 @@ const decodeTimestamps = (
|
|
|
1493
1507
|
let counterIndex = 0;
|
|
1494
1508
|
while (counterIndex < length) {
|
|
1495
1509
|
const counter = Counter.from(decodeNonNegativeInt(buffer));
|
|
1496
|
-
if (!counter.ok) throw new
|
|
1510
|
+
if (!counter.ok) throw new ProtocolDecodeError(counter.error.type);
|
|
1497
1511
|
const runLength = decodeNonNegativeInt(buffer);
|
|
1498
1512
|
for (let i = 0; i < runLength; i++) {
|
|
1499
1513
|
counters.push(counter.value);
|
|
@@ -1659,12 +1673,24 @@ export const binaryTimestampToFingerprint = (
|
|
|
1659
1673
|
/**
|
|
1660
1674
|
* Encodes and encrypts a {@link DbChange} using the provided owner's encryption
|
|
1661
1675
|
* key. Returns an encrypted binary representation as {@link EncryptedDbChange}.
|
|
1676
|
+
*
|
|
1677
|
+
* The format includes the protocol version for backward compatibility and the
|
|
1678
|
+
* timestamp for tamper-proof verification that the timestamp matches the change
|
|
1679
|
+
* data.
|
|
1662
1680
|
*/
|
|
1663
1681
|
export const encodeAndEncryptDbChange =
|
|
1664
1682
|
(deps: SymmetricCryptoDep) =>
|
|
1665
|
-
(
|
|
1683
|
+
(message: CrdtMessage, key: EncryptionKey): EncryptedDbChange => {
|
|
1684
|
+
const change = message.change;
|
|
1666
1685
|
const buffer = createBuffer();
|
|
1667
1686
|
|
|
1687
|
+
// Encode protocol version first for backward compatibility
|
|
1688
|
+
encodeNonNegativeInt(buffer, protocolVersion);
|
|
1689
|
+
|
|
1690
|
+
// Encode the timestamp (after version) for tamper verification
|
|
1691
|
+
const binaryTimestamp = timestampToBinaryTimestamp(message.timestamp);
|
|
1692
|
+
buffer.extend(binaryTimestamp);
|
|
1693
|
+
|
|
1668
1694
|
encodeBase64Url256(buffer, change.table);
|
|
1669
1695
|
|
|
1670
1696
|
buffer.extend(idToBinaryId(change.id));
|
|
@@ -1700,50 +1726,76 @@ export const encodeAndEncryptDbChange =
|
|
|
1700
1726
|
};
|
|
1701
1727
|
|
|
1702
1728
|
/**
|
|
1703
|
-
* Decrypts and decodes an {@link
|
|
1704
|
-
* encryption key.
|
|
1729
|
+
* Decrypts and decodes an {@link EncryptedCrdtMessage} using the provided
|
|
1730
|
+
* owner's encryption key. Verifies that the embedded timestamp matches the
|
|
1731
|
+
* expected timestamp to ensure message integrity.
|
|
1705
1732
|
*/
|
|
1706
1733
|
export const decryptAndDecodeDbChange =
|
|
1707
1734
|
(deps: SymmetricCryptoDep) =>
|
|
1708
1735
|
(
|
|
1709
|
-
|
|
1736
|
+
message: EncryptedCrdtMessage,
|
|
1710
1737
|
key: EncryptionKey,
|
|
1711
|
-
): Result<
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1738
|
+
): Result<
|
|
1739
|
+
DbChange,
|
|
1740
|
+
| SymmetricCryptoDecryptError
|
|
1741
|
+
| ProtocolInvalidDataError
|
|
1742
|
+
| ProtocolTimestampMismatchError
|
|
1743
|
+
> =>
|
|
1744
|
+
tryDecodeProtocolData<
|
|
1745
|
+
DbChange,
|
|
1746
|
+
SymmetricCryptoDecryptError | ProtocolTimestampMismatchError
|
|
1747
|
+
>(message.change, (buffer) => {
|
|
1748
|
+
const nonce = buffer.shiftN(deps.symmetricCrypto.nonceLength);
|
|
1749
|
+
|
|
1750
|
+
const ciphertextLength = decodeLength(buffer);
|
|
1751
|
+
const ciphertext = buffer.shiftN(ciphertextLength);
|
|
1752
|
+
|
|
1753
|
+
const plaintextBytes = deps.symmetricCrypto.decrypt(
|
|
1754
|
+
ciphertext,
|
|
1755
|
+
key,
|
|
1756
|
+
nonce,
|
|
1757
|
+
);
|
|
1758
|
+
if (!plaintextBytes.ok) return plaintextBytes;
|
|
1726
1759
|
|
|
1727
|
-
|
|
1728
|
-
|
|
1760
|
+
buffer.reset();
|
|
1761
|
+
buffer.extend(plaintextBytes.value);
|
|
1729
1762
|
|
|
1730
|
-
|
|
1731
|
-
|
|
1763
|
+
// Decode version (for future compatibility, no validation needed for now)
|
|
1764
|
+
decodeNonNegativeInt(buffer);
|
|
1732
1765
|
|
|
1733
|
-
|
|
1734
|
-
|
|
1766
|
+
// Decode and verify the embedded timestamp
|
|
1767
|
+
const embeddedBinaryTimestamp = buffer.shiftN(
|
|
1768
|
+
binaryTimestampLength,
|
|
1769
|
+
) as BinaryTimestamp;
|
|
1770
|
+
const embeddedTimestamp = binaryTimestampToTimestamp(
|
|
1771
|
+
embeddedBinaryTimestamp,
|
|
1772
|
+
);
|
|
1735
1773
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1774
|
+
// Verify timestamp integrity
|
|
1775
|
+
if (!eqTimestamp(embeddedTimestamp, message.timestamp)) {
|
|
1776
|
+
return err<ProtocolTimestampMismatchError>({
|
|
1777
|
+
type: "ProtocolTimestampMismatchError",
|
|
1778
|
+
expected: message.timestamp,
|
|
1779
|
+
embedded: embeddedTimestamp,
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1741
1782
|
|
|
1742
|
-
|
|
1783
|
+
const table = decodeBase64Url256WithLength(buffer);
|
|
1784
|
+
const id = decodeId(buffer);
|
|
1743
1785
|
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1786
|
+
const length = decodeLength(buffer);
|
|
1787
|
+
const values = Object.create(null) as Record<string, SqliteValue>;
|
|
1788
|
+
|
|
1789
|
+
for (let i = 0; i < length; i++) {
|
|
1790
|
+
const column = decodeBase64Url256WithLength(buffer);
|
|
1791
|
+
const value = decodeSqliteValue(buffer);
|
|
1792
|
+
values[column] = value;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
const dbChange = { table, id, values };
|
|
1796
|
+
|
|
1797
|
+
return ok(dbChange);
|
|
1798
|
+
});
|
|
1747
1799
|
|
|
1748
1800
|
/**
|
|
1749
1801
|
* Encodes a non-negative integer into a variable-length integer format. It's
|
package/src/Evolu/Timestamp.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert } from "../Assert.js";
|
|
2
|
+
import { createEqObject, eqNumber, eqString } from "../Eq.js";
|
|
2
3
|
import { NanoIdLibDep } from "../NanoId.js";
|
|
3
4
|
import { increment } from "../Number.js";
|
|
4
5
|
import { Order, orderUint8Array } from "../Order.js";
|
|
@@ -124,6 +125,13 @@ export const Timestamp = object({
|
|
|
124
125
|
});
|
|
125
126
|
export type Timestamp = typeof Timestamp.Type;
|
|
126
127
|
|
|
128
|
+
/** Equality function for comparing {@link Timestamp}. */
|
|
129
|
+
export const eqTimestamp = createEqObject<Timestamp>({
|
|
130
|
+
millis: eqNumber,
|
|
131
|
+
counter: eqNumber,
|
|
132
|
+
nodeId: eqString,
|
|
133
|
+
});
|
|
134
|
+
|
|
127
135
|
export const createTimestamp = ({
|
|
128
136
|
millis = minMillis,
|
|
129
137
|
counter = minCounter,
|