@evolu/common 6.0.1-preview.5 → 6.0.1-preview.7

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.
@@ -16,9 +16,12 @@
16
16
  * assert(true, "true is not true"); // no-op
17
17
  * assert(false, "true is not true"); // throws Error
18
18
  *
19
- * const size = fooSize - buffer.getLength();
20
- * // Ensure required type
21
- * assert(PositiveInt.is(size), "size is not positive int");
19
+ * const length = buffer.getLength();
20
+ * // We know length is logically non-negative, but TypeScript doesn't
21
+ * assert(
22
+ * NonNegativeInt.is(length),
23
+ * "buffer length should be non-negative",
24
+ * );
22
25
  * ```
23
26
  */
24
27
  export declare const assert: (condition: unknown, message: string) => asserts condition;
@@ -1 +1 @@
1
- {"version":3,"file":"Assert.d.ts","sourceRoot":"","sources":["../../src/Assert.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,MAAM,EAAE,CACnB,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,SAIZ,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,EAClC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,EACb,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAKlC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,2BAA2B,EAAE,CAAC,CAAC,EAC1C,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAK3C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,CAO3E"}
1
+ {"version":3,"file":"Assert.d.ts","sourceRoot":"","sources":["../../src/Assert.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,MAAM,EAAE,CACnB,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,SAIZ,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,EAClC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,EACb,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAKlC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,2BAA2B,EAAE,CAAC,CAAC,EAC1C,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAK3C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,CAO3E"}
@@ -16,9 +16,12 @@
16
16
  * assert(true, "true is not true"); // no-op
17
17
  * assert(false, "true is not true"); // throws Error
18
18
  *
19
- * const size = fooSize - buffer.getLength();
20
- * // Ensure required type
21
- * assert(PositiveInt.is(size), "size is not positive int");
19
+ * const length = buffer.getLength();
20
+ * // We know length is logically non-negative, but TypeScript doesn't
21
+ * assert(
22
+ * NonNegativeInt.is(length),
23
+ * "buffer length should be non-negative",
24
+ * );
22
25
  * ```
23
26
  */
24
27
  export const assert = (condition, message) => {
@@ -720,6 +720,7 @@ const createClientStorage = (deps) => (options) => {
720
720
  const storage = {
721
721
  ...sqliteStorageBase.value,
722
722
  validateWriteKey: constFalse,
723
+ setWriteKey: constFalse,
723
724
  writeMessages: (_ownerId, messages) => {
724
725
  // TODO: Get owner by _ownerId when we support more.
725
726
  const owner = deps.ownerRowRef.get();
@@ -15,21 +15,39 @@
15
15
  *
16
16
  * ### Message Structure
17
17
  *
18
- * | Field | Notes |
19
- * | :----------------------------- | :------------------------- |
20
- * | **Header** | |
21
- * | - {@link protocolVersion} | |
22
- * | - {@link OwnerId} | |
23
- * | - {@link ProtocolErrorCode} | In non-initiator response. |
24
- * | **Messages** | |
25
- * | - {@link NonNegativeInt} | A number of messages. |
26
- * | - {@link EncryptedCrdtMessage} | |
27
- * | - {@link WriteKey} | In initiator request. |
28
- * | **Ranges** | |
29
- * | - {@link NonNegativeInt} | Number of ranges. |
30
- * | - {@link Range} | |
31
- *
32
- * Every protocol message belongs to an {@link Owner}.
18
+ * | Field | Notes |
19
+ * | :----------------------------- | :------------------------ |
20
+ * | **Header** | |
21
+ * | - {@link protocolVersion} | |
22
+ * | - {@link OwnerId} | {@link Owner} |
23
+ * | **Initiator** | |
24
+ * | - {@link WriteKeyMode} | |
25
+ * | - {@link WriteKey} | If WriteKeyMode >= 1 |
26
+ * | - {@link WriteKey} | If WriteKeyMode = 2 (new) |
27
+ * | **Non-initiator** | |
28
+ * | - {@link ProtocolErrorCode} | |
29
+ * | **Messages** | |
30
+ * | - {@link NonNegativeInt} | A number of messages. |
31
+ * | - {@link EncryptedCrdtMessage} | |
32
+ * | **Ranges** | |
33
+ * | - {@link NonNegativeInt} | Number of ranges. |
34
+ * | - {@link Range} | |
35
+ *
36
+ * ### WriteKey Validation
37
+ *
38
+ * The initiator sends WriteKeyMode and optionally one or two WriteKeys. One key
39
+ * for write operations and two for key rotation (current and new). Note that
40
+ * it's ok to not send any key if initiator is going to be synced with readonly
41
+ * owner. The non-initiator validates them immediately after parsing the
42
+ * initiator header, before processing any messages or ranges.
43
+ *
44
+ * ### WriteKey Rotation
45
+ *
46
+ * When initiator's {@link WriteKeyMode} is `Rotation`, two WriteKeys are
47
+ * present:
48
+ *
49
+ * 1. Current WriteKey (for validation)
50
+ * 2. New WriteKey (to be stored)
33
51
  *
34
52
  * ### Synchronization
35
53
  *
@@ -147,6 +165,11 @@ export declare const ProtocolErrorCode: {
147
165
  readonly SyncError: 3;
148
166
  };
149
167
  type ProtocolErrorCode = (typeof ProtocolErrorCode)[keyof typeof ProtocolErrorCode];
168
+ export declare const WriteKeyMode: {
169
+ readonly None: 0;
170
+ readonly Single: 1;
171
+ readonly Rotation: 2;
172
+ };
150
173
  /**
151
174
  * Evolu Protocol Storage
152
175
  *
@@ -168,17 +191,16 @@ export interface Storage {
168
191
  readonly fingerprintRanges: (ownerId: BinaryOwnerId, buckets: ReadonlyArray<NonNegativeInt>, upperBound?: RangeUpperBound) => ReadonlyArray<FingerprintRange> | null;
169
192
  readonly findLowerBound: (ownerId: BinaryOwnerId, begin: NonNegativeInt, end: NonNegativeInt, upperBound: RangeUpperBound) => NonNegativeInt | null;
170
193
  readonly iterate: (ownerId: BinaryOwnerId, begin: NonNegativeInt, end: NonNegativeInt, callback: (timestamp: BinaryTimestamp, index: NonNegativeInt) => boolean) => void;
171
- /**
172
- * Authorizes the initiator's {@link WriteKey} for the given
173
- * {@link BinaryOwnerId}.
174
- *
175
- * For a client that does not expect foreign writes, return `false`.
176
- */
194
+ /** Validates the {@link WriteKey} for the given {@link Owner}. */
177
195
  readonly validateWriteKey: (ownerId: BinaryOwnerId, writeKey: WriteKey) => boolean;
196
+ /** Sets the {@link WriteKey} for the given {@link Owner}. */
197
+ readonly setWriteKey: (ownerId: BinaryOwnerId, writeKey: WriteKey) => boolean;
178
198
  /** Write encrypted {@link CrdtMessage}s to storage. */
179
199
  readonly writeMessages: (ownerId: BinaryOwnerId, messages: NonEmptyReadonlyArray<EncryptedCrdtMessage>) => boolean;
180
200
  /** Read encrypted {@link DbChange}s from storage. */
181
201
  readonly readDbChange: (ownerId: BinaryOwnerId, timestamp: BinaryTimestamp) => EncryptedDbChange | null;
202
+ /** Delete all data for the given {@link Owner}. */
203
+ readonly deleteOwner: (ownerId: BinaryOwnerId) => boolean;
182
204
  }
183
205
  export interface StorageDep {
184
206
  readonly storage: Storage;
@@ -306,6 +328,8 @@ export interface ProtocolSyncError extends ProtocolErrorBase {
306
328
  export declare const createProtocolMessageFromCrdtMessages: (deps: SymmetricCryptoDep & CreateRandomBytesDep) => (owner: OwnerWithWriteAccess, messages: NonEmptyReadonlyArray<CrdtMessage>, maxSize?: PositiveInt) => ProtocolMessage;
307
329
  /** Creates a {@link ProtocolMessage} for sync. */
308
330
  export declare const createProtocolMessageForSync: (deps: StorageDep) => (ownerId: OwnerId) => ProtocolMessage | null;
331
+ /** Creates a ProtocolMessage for {@link WriteKey} rotation. */
332
+ export declare const createProtocolMessageForWriteKeyRotation: (ownerId: OwnerId, currentWriteKey: WriteKey, newWriteKey: WriteKey) => ProtocolMessage;
309
333
  /**
310
334
  * Mutable builder for constructing {@link ProtocolMessage} respecting size
311
335
  * limits.
@@ -319,13 +343,18 @@ export interface ProtocolMessageBuffer {
319
343
  readonly unwrap: () => ProtocolMessage;
320
344
  readonly getSize: () => PositiveInt;
321
345
  }
322
- export declare const createProtocolMessageBuffer: (ownerId: OwnerId, options?: {
323
- readonly errorCode?: ProtocolErrorCode;
324
- readonly writeKey?: WriteKey;
346
+ export declare const createProtocolMessageBuffer: (ownerId: OwnerId, options: {
325
347
  readonly totalMaxSize?: PositiveInt | undefined;
326
348
  readonly rangesMaxSize?: PositiveInt | undefined;
327
349
  readonly version?: NonNegativeInt;
328
- }) => ProtocolMessageBuffer;
350
+ } & ({
351
+ readonly type: "initiator";
352
+ /** Single key or [current, new] for rotation. */
353
+ readonly writeKey?: WriteKey | readonly [WriteKey, WriteKey];
354
+ } | {
355
+ readonly type: "non-initiator";
356
+ readonly errorCode: ProtocolErrorCode;
357
+ })) => ProtocolMessageBuffer;
329
358
  export interface TimestampsBuffer {
330
359
  readonly add: (timestamp: Timestamp) => void;
331
360
  readonly addInfinite: () => void;
@@ -425,5 +454,18 @@ export declare const ProtocolValueType: {
425
454
  };
426
455
  export declare const encodeSqliteValue: (buffer: Buffer, value: SqliteValue) => void;
427
456
  export declare const decodeSqliteValue: (buffer: Buffer) => SqliteValue;
457
+ /**
458
+ * Decodes a ProtocolMessage into a readable JSON object for debugging.
459
+ *
460
+ * Note: This is a stub for future implementation. It should use:
461
+ *
462
+ * - DecodeVersionAndOwner
463
+ * - DecodeError or decodeWriteKeys (depending on context)
464
+ * - DecodeMessages
465
+ * - DecodeRanges
466
+ *
467
+ * If you want to help, please contribute to this function.
468
+ */
469
+ export declare const decodeProtocolMessageToJson: (_protocolMessage: ProtocolMessage, _isInitiator: boolean) => unknown;
428
470
  export {};
429
471
  //# sourceMappingURL=Protocol.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyHG;AAIH,OAAO,EAA2B,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE7E,OAAO,EACL,MAAM,EAQP,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,EAGL,EAAE,EAIF,MAAM,EACN,cAAc,EAGd,WAAW,EAEZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAC;AAC/C,OAAO,EAGL,OAAO,EACP,oBAAoB,EACpB,QAAQ,EAET,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EAIf,MAAM,EACN,SAAS,EAEV,MAAM,gBAAgB,CAAC;AAExB,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,EAAgB,WAAW,CAAC;AAE/D,2CAA2C;AAC3C,eAAO,MAAM,4BAA4B,EAAa,WAAW,CAAC;AAElE,8BAA8B;AAC9B,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAAC;AAEpE,8BAA8B;AAC9B,eAAO,MAAM,eAAe,EAAQ,cAAc,CAAC;AAEnD,eAAO,MAAM,iBAAiB;;IAE5B,gDAAgD;;IAEhD,6CAA6C;;IAE7C,4CAA4C;;CAEpC,CAAC;AAEX,KAAK,iBAAiB,GACpB,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,GAAG,IAAI,CAAC;IAEpE,QAAQ,CAAC,WAAW,EAAE,CACpB,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,cAAc,KAChB,WAAW,GAAG,IAAI,CAAC;IAExB;;;;;;OAMG;IACH,QAAQ,CAAC,iBAAiB,EAAE,CAC1B,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,EACtC,UAAU,CAAC,EAAE,eAAe,KACzB,aAAa,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;IAE5C,QAAQ,CAAC,cAAc,EAAE,CACvB,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,cAAc,EACnB,UAAU,EAAE,eAAe,KACxB,cAAc,GAAG,IAAI,CAAC;IAE3B,QAAQ,CAAC,OAAO,EAAE,CAChB,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,CAAC,SAAS,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,KAAK,OAAO,KACrE,IAAI,CAAC;IAEV;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,EAAE,CACzB,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,KACf,OAAO,CAAC;IAEb,uDAAuD;IACvD,QAAQ,CAAC,aAAa,EAAE,CACtB,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,qBAAqB,CAAC,oBAAoB,CAAC,KAClD,OAAO,CAAC;IAEb,qDAAqD;IACrD,QAAQ,CAAC,YAAY,EAAE,CACrB,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,eAAe,KACvB,iBAAiB,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,wCAAwC;AACxC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;CACpC;AAED,yBAAyB;AACzB,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,KAAK,CAAC,mBAAmB,CAAC,CAAC;AAExE;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,4UAA4B,CAAC;AACtD,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;EAInB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAE5C,eAAO,MAAM,SAAS;;;;CAIZ,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAEnE,eAAO,MAAM,kBAAkB,eAA+B,CAAC;AAC/D,MAAM,MAAM,kBAAkB,GAAG,OAAO,kBAAkB,CAAC;AAE3D;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAEnE,UAAU,SAAS;IACjB,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;CACtC;AAED,MAAM,WAAW,SAAU,SAAQ,SAAS;IAC1C,QAAQ,CAAC,IAAI,EAAE,OAAO,SAAS,CAAC,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,QAAQ,CAAC,IAAI,EAAE,OAAO,SAAS,CAAC,WAAW,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;CACnC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;AAE5D,eAAO,MAAM,eAAe,EAAS,cAAc,CAAC;AAEpD,uCAAuC;AACvC,eAAO,MAAM,eAAe,EAAsC,WAAW,CAAC;AAE9E,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAChD,QAAQ,CAAC,IAAI,EAAE,OAAO,SAAS,CAAC,UAAU,CAAC;IAC3C,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,mCAAoC,SAAQ,SAAS;IACpE,QAAQ,CAAC,IAAI,EAAE,OAAO,SAAS,CAAC,UAAU,CAAC;IAC3C,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;CACvC;AAED,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAEnE,MAAM,MAAM,aAAa,GACrB,+BAA+B,GAC/B,wBAAwB,GACxB,qBAAqB,GACrB,kBAAkB,GAClB,iBAAiB,CAAC;AAEtB,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,+BAAgC,SAAQ,iBAAiB;IACxE,QAAQ,CAAC,IAAI,EAAE,iCAAiC,CAAC;IACjD,QAAQ,CAAC,kBAAkB,EAAE,cAAc,CAAC;IAC5C,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,8EAA8E;AAC9E,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;CACpC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,qCAAqC,GAC/C,MAAM,kBAAkB,GAAG,oBAAoB,MAE9C,OAAO,oBAAoB,EAC3B,UAAU,qBAAqB,CAAC,WAAW,CAAC,EAC5C,UAAU,WAAW,KACpB,eAmDF,CAAC;AAEJ,kDAAkD;AAClD,eAAO,MAAM,4BAA4B,GACtC,MAAM,UAAU,MAChB,SAAS,OAAO,KAAG,eAAe,GAAG,IAiBrC,CAAC;AAEJ;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC;IAEnE,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAE7D,QAAQ,CAAC,aAAa,EAAE,MAAM,OAAO,CAAC;IAEtC,QAAQ,CAAC,+BAA+B,EAAE,CACxC,UAAU,EAAE,gBAAgB,EAC5B,OAAO,EAAE,oBAAoB,GAAG,IAAI,KACjC,OAAO,CAAC;IAEb,QAAQ,CAAC,QAAQ,EAAE,CACjB,KAAK,EAAE,SAAS,GAAG,gBAAgB,GAAG,mCAAmC,KACtE,IAAI,CAAC;IAEV,QAAQ,CAAC,MAAM,EAAE,MAAM,eAAe,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,WAAW,CAAC;CACrC;AAED,eAAO,MAAM,2BAA2B,GACtC,SAAS,OAAO,EAChB,UAAS;IACP,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,aAAa,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;CAC9B,KACL,qBAwKF,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,cAAc,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED,eAAO,MAAM,sBAAsB,QAAO,gBAwDzC,CAAC;AAoCF,MAAM,WAAW,mCAAmC;IAClD,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,IAAI,CAAC;IAEpD,mEAAmE;IACnE,OAAO,CAAC,EAAE,cAAc,CAAC;IAEzB,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,aAAa,CAAC,EAAE,WAAW,CAAC;CAC7B;AAED,eAAO,MAAM,4BAA4B,GACtC,MAAM,UAAU,MAEf,cAAc,UAAU,EACxB,yDAKG,mCAAwC,KAC1C,MAAM,CAAC,eAAe,GAAG,IAAI,EAAE,aAAa,CA+D5C,CAAC;AAEN,MAAM,WAAW,kCAAkC;IACjD,8CAA8C;IAC9C,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAEvC,0DAA0D;IAC1D,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAEjE,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,aAAa,CAAC,EAAE,WAAW,CAAC;CAC7B;AAED,eAAO,MAAM,2BAA2B,GACrC,MAAM,UAAU,MAEf,cAAc,UAAU,EACxB,yDAKG,kCAAuC;AAC1C,mEAAmE;AACnE,sDAAyB,KACxB,MAAM,CAAC,eAAe,GAAG,IAAI,EAAE,wBAAwB,CA4DtD,CAAC;AA6bP,2CAA2C;AAC3C,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;AAEtD,eAAO,MAAM,cAAc,EAAS,cAAc,CAAC;AAEnD,eAAO,MAAM,YAAY,GAAI,IAAI,EAAE,KAAG,QACD,CAAC;AAEtC,eAAO,MAAM,YAAY,GAAI,UAAU,QAAQ,KAAG,EAChB,CAAC;AAEnC,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC;AAEhE,eAAO,MAAM,sBAAsB,GAAI,SAAS,OAAO,KAAG,aACX,CAAC;AAEhD,eAAO,MAAM,sBAAsB,GAAI,eAAe,aAAa,KAAG,OAC1B,CAAC;AAE7C;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC;AASvE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,mBAAmB,KAC1B,UAAU,CAAC,UAwBb,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,MAAM,EACd,cAAc,MAAM,KACnB,mBA4BF,CAAC;AAQF;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,EAAE,QAAQ,MAAM,KAAG,IAE7D,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,KAAG,MAkB7C,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,WAAW,eAAe,KACzB,WAGF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAClC,MAAM,kBAAkB,MACxB,QAAQ,QAAQ,EAAE,KAAK,aAAa,KAAG,iBAmCvC,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAClC,MAAM,kBAAkB,MAEvB,QAAQ,iBAAiB,EACzB,KAAK,aAAa,KACjB,MAAM,CAAC,QAAQ,EAAE,2BAA2B,GAAG,wBAAwB,CAmCvE,CAAC;AAEN;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,MAAM,EACd,KAAK,cAAc,KAClB,IAoBF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,KAAG,cAiBrD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,EAAE,OAAO,SAAS,CAAC,GAAG,CAAC,KAAG,IAEpE,CAAC;AAEF,eAAO,MAAM,YAAY,WAvBoB,MAAM,KAAG,cAuBN,CAAC;AAEjD,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,IAI5D,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,KAAG,MAI7C,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,EAAE,QAAQ,MAAM,KAAG,IAE7D,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,KAAG,MAG7C,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,MAAM,EACd,QAAQ,mBAAmB,KAC1B,IAGF,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAI,QAAQ,MAAM,KAAG,YAG7D,CAAC;AAMF,eAAO,MAAM,iBAAiB;qBAId,cAAc;qBACd,cAAc;mBAChB,cAAc;qBACZ,cAAc;iBAIlB,cAAc;2BACJ,cAAc;6BACZ,cAAc;mBACxB,cAAc;yCAKQ,cAAc;sCACjB,cAAc;CAIrC,CAAC;AAEX,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,EAAE,OAAO,WAAW,KAAG,IAwEtE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,KAAG,WA8ClD,CAAC"}
1
+ {"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Protocol.ts"],"names":[],"mappings":"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"}
@@ -15,21 +15,39 @@
15
15
  *
16
16
  * ### Message Structure
17
17
  *
18
- * | Field | Notes |
19
- * | :----------------------------- | :------------------------- |
20
- * | **Header** | |
21
- * | - {@link protocolVersion} | |
22
- * | - {@link OwnerId} | |
23
- * | - {@link ProtocolErrorCode} | In non-initiator response. |
24
- * | **Messages** | |
25
- * | - {@link NonNegativeInt} | A number of messages. |
26
- * | - {@link EncryptedCrdtMessage} | |
27
- * | - {@link WriteKey} | In initiator request. |
28
- * | **Ranges** | |
29
- * | - {@link NonNegativeInt} | Number of ranges. |
30
- * | - {@link Range} | |
18
+ * | Field | Notes |
19
+ * | :----------------------------- | :------------------------ |
20
+ * | **Header** | |
21
+ * | - {@link protocolVersion} | |
22
+ * | - {@link OwnerId} | {@link Owner} |
23
+ * | **Initiator** | |
24
+ * | - {@link WriteKeyMode} | |
25
+ * | - {@link WriteKey} | If WriteKeyMode >= 1 |
26
+ * | - {@link WriteKey} | If WriteKeyMode = 2 (new) |
27
+ * | **Non-initiator** | |
28
+ * | - {@link ProtocolErrorCode} | |
29
+ * | **Messages** | |
30
+ * | - {@link NonNegativeInt} | A number of messages. |
31
+ * | - {@link EncryptedCrdtMessage} | |
32
+ * | **Ranges** | |
33
+ * | - {@link NonNegativeInt} | Number of ranges. |
34
+ * | - {@link Range} | |
31
35
  *
32
- * Every protocol message belongs to an {@link Owner}.
36
+ * ### WriteKey Validation
37
+ *
38
+ * The initiator sends WriteKeyMode and optionally one or two WriteKeys. One key
39
+ * for write operations and two for key rotation (current and new). Note that
40
+ * it's ok to not send any key if initiator is going to be synced with readonly
41
+ * owner. The non-initiator validates them immediately after parsing the
42
+ * initiator header, before processing any messages or ranges.
43
+ *
44
+ * ### WriteKey Rotation
45
+ *
46
+ * When initiator's {@link WriteKeyMode} is `Rotation`, two WriteKeys are
47
+ * present:
48
+ *
49
+ * 1. Current WriteKey (for validation)
50
+ * 2. New WriteKey (to be stored)
33
51
  *
34
52
  * ### Synchronization
35
53
  *
@@ -124,7 +142,7 @@ import { sha256 } from "@noble/hashes/sha2";
124
142
  import { pack, unpack, unpackMultiple } from "msgpackr";
125
143
  import { isNonEmptyReadonlyArray } from "../Array.js";
126
144
  import { assert } from "../Assert.js";
127
- import { BufferError, bytesToHex, bytesToUtf8, concatBytes, createBuffer, hexToBytes, utf8ToBytes, } from "../Buffer.js";
145
+ import { BufferError, bytesToHex, bytesToUtf8, createBuffer, hexToBytes, utf8ToBytes, } from "../Buffer.js";
128
146
  import { padmePaddingLength, } from "../Crypto.js";
129
147
  import { eqArrayNumber } from "../Eq.js";
130
148
  import { computeBalancedBuckets } from "../Number.js";
@@ -149,6 +167,11 @@ export const ProtocolErrorCode = {
149
167
  /** A code for {@link ProtocolSyncError}. */
150
168
  SyncError: 3,
151
169
  };
170
+ export const WriteKeyMode = {
171
+ None: 0,
172
+ Single: 1,
173
+ Rotation: 2,
174
+ };
152
175
  /**
153
176
  * Base64Url string with maximum length of 256 characters. Encoding strings as
154
177
  * Base64UrlString saves up to 25% in size compared to regular strings.
@@ -181,6 +204,7 @@ export const zeroFingerprint = new Uint8Array(fingerprintSize);
181
204
  */
182
205
  export const createProtocolMessageFromCrdtMessages = (deps) => (owner, messages, maxSize) => {
183
206
  const buffer = createProtocolMessageBuffer(owner.id, {
207
+ type: "initiator",
184
208
  totalMaxSize: maxSize ?? maxProtocolMessageSize,
185
209
  writeKey: owner.writeKey,
186
210
  });
@@ -224,7 +248,7 @@ export const createProtocolMessageFromCrdtMessages = (deps) => (owner, messages,
224
248
  };
225
249
  /** Creates a {@link ProtocolMessage} for sync. */
226
250
  export const createProtocolMessageForSync = (deps) => (ownerId) => {
227
- const buffer = createProtocolMessageBuffer(ownerId);
251
+ const buffer = createProtocolMessageBuffer(ownerId, { type: "initiator" });
228
252
  const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
229
253
  const size = deps.storage.getSize(binaryOwnerId);
230
254
  // Errors are handled by the storage.
@@ -233,8 +257,16 @@ export const createProtocolMessageForSync = (deps) => (ownerId) => {
233
257
  splitRange(deps)(binaryOwnerId, 0, size, InfiniteUpperBound, buffer);
234
258
  return buffer.unwrap();
235
259
  };
236
- export const createProtocolMessageBuffer = (ownerId, options = {}) => {
237
- const { errorCode, writeKey, totalMaxSize = maxProtocolMessageSize, rangesMaxSize = maxProtocolMessageRangesSize, version = protocolVersion, } = options;
260
+ /** Creates a ProtocolMessage for {@link WriteKey} rotation. */
261
+ export const createProtocolMessageForWriteKeyRotation = (ownerId, currentWriteKey, newWriteKey) => {
262
+ const buffer = createProtocolMessageBuffer(ownerId, {
263
+ type: "initiator",
264
+ writeKey: [currentWriteKey, newWriteKey],
265
+ });
266
+ return buffer.unwrap();
267
+ };
268
+ export const createProtocolMessageBuffer = (ownerId, options) => {
269
+ const { totalMaxSize = maxProtocolMessageSize, rangesMaxSize = maxProtocolMessageRangesSize, version = protocolVersion, } = options;
238
270
  const buffers = {
239
271
  header: createBuffer(),
240
272
  messages: {
@@ -249,17 +281,29 @@ export const createProtocolMessageBuffer = (ownerId, options = {}) => {
249
281
  };
250
282
  encodeNonNegativeInt(buffers.header, version);
251
283
  buffers.header.extend(ownerIdToBinaryOwnerId(ownerId));
252
- if (errorCode != null)
253
- buffers.header.extend([errorCode]);
284
+ if (options.type === "initiator") {
285
+ if (!options.writeKey) {
286
+ buffers.header.extend([WriteKeyMode.None]);
287
+ }
288
+ else if (!Array.isArray(options.writeKey)) {
289
+ buffers.header.extend([WriteKeyMode.Single]);
290
+ buffers.header.extend(options.writeKey);
291
+ }
292
+ else {
293
+ buffers.header.extend([WriteKeyMode.Rotation]);
294
+ buffers.header.extend(options.writeKey[0]); // current
295
+ buffers.header.extend(options.writeKey[1]); // new
296
+ }
297
+ }
298
+ else {
299
+ buffers.header.extend([options.errorCode]);
300
+ }
254
301
  let isLastRangeInfinite = false;
255
302
  const isWithinSizeLimits = () => getSize() <= totalMaxSize;
256
303
  const getSize = () => (getHeaderAndMessagesSize() + getRangesSize());
257
304
  const getHeaderAndMessagesSize = () => buffers.header.getLength() +
258
305
  buffers.messages.timestamps.getLength() +
259
- buffers.messages.dbChanges.getLength() +
260
- (buffers.messages.timestamps.getCount() > 0 && writeKey
261
- ? writeKeyLength
262
- : 0);
306
+ buffers.messages.dbChanges.getLength();
263
307
  const getRangesSize = () => buffers.ranges.timestamps.getCount() > 0
264
308
  ? buffers.ranges.timestamps.getLength() +
265
309
  buffers.ranges.types.getLength() +
@@ -345,8 +389,6 @@ export const createProtocolMessageBuffer = (ownerId, options = {}) => {
345
389
  }
346
390
  buffers.messages.timestamps.append(buffers.header);
347
391
  buffers.header.extend(buffers.messages.dbChanges.unwrap());
348
- if (buffers.messages.timestamps.getCount() > 0 && writeKey)
349
- buffers.header.extend(writeKey);
350
392
  if (buffers.ranges.timestamps.getCount() > 0) {
351
393
  buffers.ranges.timestamps.append(buffers.header);
352
394
  buffers.header.extend(buffers.ranges.types.unwrap());
@@ -434,7 +476,6 @@ export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteK
434
476
  ownerId,
435
477
  });
436
478
  }
437
- const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
438
479
  const errorCode = input.shift();
439
480
  if (errorCode !== ProtocolErrorCode.NoError) {
440
481
  switch (errorCode) {
@@ -458,6 +499,7 @@ export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteK
458
499
  }
459
500
  }
460
501
  const messages = decodeMessages(input);
502
+ const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
461
503
  if (isNonEmptyReadonlyArray(messages) &&
462
504
  !deps.storage.writeMessages(binaryOwnerId, messages)) {
463
505
  return ok(null);
@@ -468,6 +510,7 @@ export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteK
468
510
  if (writeKey == null)
469
511
  return ok(null);
470
512
  const output = createProtocolMessageBuffer(ownerId, {
513
+ type: "initiator",
471
514
  writeKey,
472
515
  totalMaxSize,
473
516
  rangesMaxSize,
@@ -477,8 +520,7 @@ export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteK
477
520
  export const applyProtocolMessageAsRelay = (deps) => (inputMessage, { subscribe, broadcast, totalMaxSize, rangesMaxSize, } = {},
478
521
  /** For testing purposes only; should not be used in production. */
479
522
  version = protocolVersion) => tryDecodeProtocolData(inputMessage, (input) => {
480
- const requestedVersion = decodeNonNegativeInt(input);
481
- const ownerId = decodeOwnerId(input);
523
+ const [requestedVersion, ownerId] = decodeVersionAndOwner(input);
482
524
  const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
483
525
  if (requestedVersion !== version) {
484
526
  // Non-initiator responds with its version and ownerId.
@@ -488,26 +530,68 @@ version = protocolVersion) => tryDecodeProtocolData(inputMessage, (input) => {
488
530
  return ok(output.unwrap());
489
531
  }
490
532
  subscribe?.(ownerId);
533
+ const writeKeyMode = input.shift();
534
+ let writeKey;
535
+ let newWriteKey;
536
+ if (writeKeyMode !== WriteKeyMode.None) {
537
+ writeKey = input.shiftN(writeKeyLength);
538
+ switch (writeKeyMode) {
539
+ case WriteKeyMode.Single:
540
+ break;
541
+ case WriteKeyMode.Rotation:
542
+ newWriteKey = input.shiftN(writeKeyLength);
543
+ break;
544
+ default:
545
+ throw new ProtocolDecodeError(`Invalid WriteKeyMode: ${writeKeyMode}`);
546
+ }
547
+ }
548
+ if (writeKey) {
549
+ const isValid = deps.storage.validateWriteKey(binaryOwnerId, writeKey);
550
+ if (!isValid) {
551
+ return ok(createProtocolMessageBuffer(ownerId, {
552
+ type: "non-initiator",
553
+ errorCode: ProtocolErrorCode.WriteKeyError,
554
+ }).unwrap());
555
+ }
556
+ if (newWriteKey) {
557
+ const rotationSuccess = deps.storage.setWriteKey(binaryOwnerId, newWriteKey);
558
+ if (!rotationSuccess) {
559
+ return ok(createProtocolMessageBuffer(ownerId, {
560
+ type: "non-initiator",
561
+ errorCode: ProtocolErrorCode.WriteError,
562
+ }).unwrap());
563
+ }
564
+ }
565
+ }
491
566
  const messages = decodeMessages(input);
492
567
  if (isNonEmptyReadonlyArray(messages)) {
493
- const messagesEnd = inputMessage.length - input.getLength();
494
- const writeKey = input.shiftN(writeKeyLength);
495
- const writeKeyIsValid = deps.storage.validateWriteKey(binaryOwnerId, writeKey);
496
- if (!writeKeyIsValid)
568
+ if (!writeKey)
497
569
  return ok(createProtocolMessageBuffer(ownerId, {
570
+ type: "non-initiator",
498
571
  errorCode: ProtocolErrorCode.WriteKeyError,
499
572
  }).unwrap());
500
- if (broadcast) {
501
- // Instead of encoding a new protocol message, we reuse the inputMessage.
502
- const broadcastMessage = concatBytes(inputMessage.slice(0, 17), new Uint8Array([ProtocolErrorCode.NoError]), inputMessage.slice(17, messagesEnd));
503
- broadcast(ownerId, broadcastMessage);
573
+ // Only broadcast if there's no ranges.
574
+ if (broadcast && input.getLength() === 0) {
575
+ const broadcastBuffer = createProtocolMessageBuffer(ownerId, {
576
+ type: "non-initiator",
577
+ errorCode: ProtocolErrorCode.NoError,
578
+ totalMaxSize,
579
+ rangesMaxSize,
580
+ version,
581
+ });
582
+ for (const message of messages) {
583
+ broadcastBuffer.addMessage(message);
584
+ }
585
+ broadcast(ownerId, broadcastBuffer.unwrap());
504
586
  }
505
587
  if (!deps.storage.writeMessages(binaryOwnerId, messages))
506
588
  return ok(createProtocolMessageBuffer(ownerId, {
589
+ type: "non-initiator",
507
590
  errorCode: ProtocolErrorCode.WriteError,
508
591
  }).unwrap());
509
592
  }
510
593
  const output = createProtocolMessageBuffer(ownerId, {
594
+ type: "non-initiator",
511
595
  errorCode: ProtocolErrorCode.NoError,
512
596
  totalMaxSize,
513
597
  rangesMaxSize,
@@ -534,6 +618,10 @@ const tryDecodeProtocolData = (data, callback) => {
534
618
  }
535
619
  };
536
620
  const decodeVersionAndOwner = (input) => {
621
+ // This structure must never change across protocol versions. The version
622
+ // and owner ID must always be the first two fields in every protocol message
623
+ // to enable version negotiation and owner identification before any other
624
+ // processing occurs.
537
625
  const version = decodeNonNegativeInt(input);
538
626
  const ownerId = decodeOwnerId(input);
539
627
  return [version, ownerId];
@@ -573,6 +661,7 @@ const sync = (deps) => (role, input, output, ownerId) => {
573
661
  return ok(null);
574
662
  }
575
663
  const message = createProtocolMessageBuffer(binaryOwnerId, {
664
+ type: "non-initiator",
576
665
  errorCode: ProtocolErrorCode.SyncError,
577
666
  });
578
667
  return ok(message.unwrap());
@@ -1192,3 +1281,23 @@ export const decodeSqliteValue = (buffer) => {
1192
1281
  throw new ProtocolDecodeError("invalid ProtocolValueType");
1193
1282
  }
1194
1283
  };
1284
+ /**
1285
+ * Decodes a ProtocolMessage into a readable JSON object for debugging.
1286
+ *
1287
+ * Note: This is a stub for future implementation. It should use:
1288
+ *
1289
+ * - DecodeVersionAndOwner
1290
+ * - DecodeError or decodeWriteKeys (depending on context)
1291
+ * - DecodeMessages
1292
+ * - DecodeRanges
1293
+ *
1294
+ * If you want to help, please contribute to this function.
1295
+ */
1296
+ export const decodeProtocolMessageToJson = (_protocolMessage, _isInitiator) => {
1297
+ // TODO: Implement using
1298
+ // - decodeVersionAndOwner
1299
+ // -- decodeError or decodeWriteKeys (should be refactored out),
1300
+ // -- decodeMessages, and decodeRanges.
1301
+ // This is a stub for PRs and community contributions.
1302
+ throw new Error("decodeProtocolMessageToJson is not implemented yet.");
1303
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Relay.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAM,MAAM,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAO,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAqB,OAAO,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAEL,8BAA8B,EAC9B,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAGtB,MAAM,WAAW,KAAM,SAAQ,UAAU;CAAG;AAE5C,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAE5E,eAAO,MAAM,kBAAkB,GAC5B,MAAM,sBAAsB,MAC5B,SAAS,8BAA8B,KAAG,MAAM,CAAC,OAAO,EAAE,WAAW,CAsHrE,CAAC"}
1
+ {"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Relay.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAW,MAAM,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAO,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAqB,OAAO,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAEL,8BAA8B,EAC9B,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAGtB,MAAM,WAAW,KAAM,SAAQ,UAAU;CAAG;AAE5C,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAE5E,eAAO,MAAM,kBAAkB,GAC5B,MAAM,sBAAsB,MAC5B,SAAS,8BAA8B,KAAG,MAAM,CAAC,OAAO,EAAE,WAAW,CA6JrE,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { isNonEmptyReadonlyArray } from "../Array.js";
2
- import { ok } from "../Result.js";
2
+ import { err, ok } from "../Result.js";
3
3
  import { sql } from "../Sqlite.js";
4
4
  import { createSqliteStorageBase, } from "./Storage.js";
5
5
  import { timestampToBinaryTimestamp } from "./Timestamp.js";
@@ -65,6 +65,19 @@ export const createRelayStorage = (deps) => (options) => {
65
65
  }
66
66
  return deps.timingSafeEqual(rows[0].writeKey, writeKey);
67
67
  },
68
+ setWriteKey: (ownerId, writeKey) => {
69
+ const upsertWriteKey = deps.sqlite.exec(sql `
70
+ insert into evolu_writeKey (ownerId, writeKey)
71
+ values (${ownerId}, ${writeKey})
72
+ on conflict (ownerId) do update
73
+ set writeKey = excluded.writeKey;
74
+ `);
75
+ if (!upsertWriteKey.ok) {
76
+ options.onStorageError(upsertWriteKey.error);
77
+ return false;
78
+ }
79
+ return true;
80
+ },
68
81
  writeMessages: (ownerId, messages) => {
69
82
  const result = deps.sqlite.transaction(() => {
70
83
  for (const message of messages) {
@@ -104,5 +117,29 @@ export const createRelayStorage = (deps) => (options) => {
104
117
  }
105
118
  return result.value.rows[0]?.change;
106
119
  },
120
+ deleteOwner: (ownerId) => {
121
+ const result = deps.sqlite.transaction(() => {
122
+ const del1 = deps.sqlite.exec(sql `
123
+ delete from evolu_writeKey where ownerId = ${ownerId};
124
+ `);
125
+ if (!del1.ok)
126
+ return del1;
127
+ const del2 = deps.sqlite.exec(sql `
128
+ delete from evolu_message where ownerId = ${ownerId};
129
+ `);
130
+ if (!del2.ok)
131
+ return del2;
132
+ const del3 = sqliteStorageBase.value.deleteOwner(ownerId);
133
+ if (!del3)
134
+ return err(null);
135
+ return ok();
136
+ });
137
+ if (!result.ok) {
138
+ if (result.error)
139
+ options.onStorageError(result.error);
140
+ return false;
141
+ }
142
+ return true;
143
+ },
107
144
  });
108
145
  };
@@ -36,6 +36,7 @@ export interface SqliteStorageBase {
36
36
  readonly fingerprintRanges: Storage["fingerprintRanges"];
37
37
  readonly findLowerBound: Storage["findLowerBound"];
38
38
  readonly iterate: Storage["iterate"];
39
+ readonly deleteOwner: Storage["deleteOwner"];
39
40
  }
40
41
  export interface SqliteStorageBaseDep {
41
42
  readonly storage: SqliteStorageBase;
@@ -1 +1 @@
1
- {"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAM,MAAM,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAO,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAe,cAAc,EAAe,MAAM,YAAY,CAAC;AAGtE,OAAO,EACL,aAAa,EAQb,OAAO,EAER,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAwB,MAAM,gBAAgB,CAAC;AAEvE,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,eAAe,EAAE,CACxB,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,eAAe,KACvB,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAE/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7C,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACzD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;CACrC;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtD,MAAM,WAAW,8BAA8B;IAC7C,cAAc,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CAC9C;AAED,eAAO,MAAM,uBAAuB,GACjC,MAAM,iBAAiB,MAEtB,SAAS,8BAA8B,KACtC,MAAM,CAAC,iBAAiB,EAAE,WAAW,CAyIvC,CAAC;AA09BJ,eAAO,MAAM,mBAAmB,GAC7B,MAAM,SAAS,MAEd,SAAS,aAAa,EACtB,OAAO,cAAc,KACpB,MAAM,CAAC,eAAe,EAAE,WAAW,CA6ErC,CAAC"}
1
+ {"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAM,MAAM,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAO,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAe,cAAc,EAAe,MAAM,YAAY,CAAC;AAGtE,OAAO,EACL,aAAa,EAQb,OAAO,EAER,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAwB,MAAM,gBAAgB,CAAC;AAEvE,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,eAAe,EAAE,CACxB,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,eAAe,KACvB,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAE/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7C,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACzD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;CACrC;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtD,MAAM,WAAW,8BAA8B;IAC7C,cAAc,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CAC9C;AAED,eAAO,MAAM,uBAAuB,GACjC,MAAM,iBAAiB,MAEtB,SAAS,8BAA8B,KACtC,MAAM,CAAC,iBAAiB,EAAE,WAAW,CAoJvC,CAAC;AA09BJ,eAAO,MAAM,mBAAmB,GAC7B,MAAM,SAAS,MAEd,SAAS,aAAa,EACtB,OAAO,cAAc,KACpB,MAAM,CAAC,eAAe,EAAE,WAAW,CA6ErC,CAAC"}
@@ -141,6 +141,16 @@ export const createSqliteStorageBase = (deps) => (options) => {
141
141
  return;
142
142
  }
143
143
  },
144
+ deleteOwner: (ownerId) => {
145
+ const result = deps.sqlite.exec(sql `
146
+ delete from evolu_timestamp where ownerId = ${ownerId};
147
+ `);
148
+ if (!result.ok) {
149
+ options.onStorageError(result.error);
150
+ return false;
151
+ }
152
+ return true;
153
+ },
144
154
  });
145
155
  };
146
156
  const assertBeginEnd = (begin, end) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evolu/common",
3
- "version": "6.0.1-preview.5",
3
+ "version": "6.0.1-preview.7",
4
4
  "description": "TypeScript library and local-first framework",
5
5
  "keywords": [
6
6
  "evolu",
package/src/Assert.ts CHANGED
@@ -32,9 +32,12 @@ import type { Type } from "./Type.js";
32
32
  * assert(true, "true is not true"); // no-op
33
33
  * assert(false, "true is not true"); // throws Error
34
34
  *
35
- * const size = fooSize - buffer.getLength();
36
- * // Ensure required type
37
- * assert(PositiveInt.is(size), "size is not positive int");
35
+ * const length = buffer.getLength();
36
+ * // We know length is logically non-negative, but TypeScript doesn't
37
+ * assert(
38
+ * NonNegativeInt.is(length),
39
+ * "buffer length should be non-negative",
40
+ * );
38
41
  * ```
39
42
  */
40
43
  export const assert: (
package/src/Evolu/Db.ts CHANGED
@@ -1190,6 +1190,7 @@ const createClientStorage =
1190
1190
  ...sqliteStorageBase.value,
1191
1191
 
1192
1192
  validateWriteKey: constFalse,
1193
+ setWriteKey: constFalse,
1193
1194
 
1194
1195
  writeMessages: (_ownerId, messages) => {
1195
1196
  // TODO: Get owner by _ownerId when we support more.
@@ -15,21 +15,39 @@
15
15
  *
16
16
  * ### Message Structure
17
17
  *
18
- * | Field | Notes |
19
- * | :----------------------------- | :------------------------- |
20
- * | **Header** | |
21
- * | - {@link protocolVersion} | |
22
- * | - {@link OwnerId} | |
23
- * | - {@link ProtocolErrorCode} | In non-initiator response. |
24
- * | **Messages** | |
25
- * | - {@link NonNegativeInt} | A number of messages. |
26
- * | - {@link EncryptedCrdtMessage} | |
27
- * | - {@link WriteKey} | In initiator request. |
28
- * | **Ranges** | |
29
- * | - {@link NonNegativeInt} | Number of ranges. |
30
- * | - {@link Range} | |
18
+ * | Field | Notes |
19
+ * | :----------------------------- | :------------------------ |
20
+ * | **Header** | |
21
+ * | - {@link protocolVersion} | |
22
+ * | - {@link OwnerId} | {@link Owner} |
23
+ * | **Initiator** | |
24
+ * | - {@link WriteKeyMode} | |
25
+ * | - {@link WriteKey} | If WriteKeyMode >= 1 |
26
+ * | - {@link WriteKey} | If WriteKeyMode = 2 (new) |
27
+ * | **Non-initiator** | |
28
+ * | - {@link ProtocolErrorCode} | |
29
+ * | **Messages** | |
30
+ * | - {@link NonNegativeInt} | A number of messages. |
31
+ * | - {@link EncryptedCrdtMessage} | |
32
+ * | **Ranges** | |
33
+ * | - {@link NonNegativeInt} | Number of ranges. |
34
+ * | - {@link Range} | |
31
35
  *
32
- * Every protocol message belongs to an {@link Owner}.
36
+ * ### WriteKey Validation
37
+ *
38
+ * The initiator sends WriteKeyMode and optionally one or two WriteKeys. One key
39
+ * for write operations and two for key rotation (current and new). Note that
40
+ * it's ok to not send any key if initiator is going to be synced with readonly
41
+ * owner. The non-initiator validates them immediately after parsing the
42
+ * initiator header, before processing any messages or ranges.
43
+ *
44
+ * ### WriteKey Rotation
45
+ *
46
+ * When initiator's {@link WriteKeyMode} is `Rotation`, two WriteKeys are
47
+ * present:
48
+ *
49
+ * 1. Current WriteKey (for validation)
50
+ * 2. New WriteKey (to be stored)
33
51
  *
34
52
  * ### Synchronization
35
53
  *
@@ -130,7 +148,6 @@ import {
130
148
  BufferError,
131
149
  bytesToHex,
132
150
  bytesToUtf8,
133
- concatBytes,
134
151
  createBuffer,
135
152
  hexToBytes,
136
153
  utf8ToBytes,
@@ -163,7 +180,6 @@ import {
163
180
  } from "../Type.js";
164
181
  import { Brand, Predicate } from "../Types.js";
165
182
  import {
166
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
167
183
  Owner,
168
184
  OwnerId,
169
185
  OwnerWithWriteAccess,
@@ -205,6 +221,14 @@ export const ProtocolErrorCode = {
205
221
  type ProtocolErrorCode =
206
222
  (typeof ProtocolErrorCode)[keyof typeof ProtocolErrorCode];
207
223
 
224
+ export const WriteKeyMode = {
225
+ None: 0,
226
+ Single: 1,
227
+ Rotation: 2,
228
+ } as const;
229
+
230
+ type WriteKeyMode = (typeof WriteKeyMode)[keyof typeof WriteKeyMode];
231
+
208
232
  /**
209
233
  * Evolu Protocol Storage
210
234
  *
@@ -249,17 +273,15 @@ export interface Storage {
249
273
  callback: (timestamp: BinaryTimestamp, index: NonNegativeInt) => boolean,
250
274
  ) => void;
251
275
 
252
- /**
253
- * Authorizes the initiator's {@link WriteKey} for the given
254
- * {@link BinaryOwnerId}.
255
- *
256
- * For a client that does not expect foreign writes, return `false`.
257
- */
276
+ /** Validates the {@link WriteKey} for the given {@link Owner}. */
258
277
  readonly validateWriteKey: (
259
278
  ownerId: BinaryOwnerId,
260
279
  writeKey: WriteKey,
261
280
  ) => boolean;
262
281
 
282
+ /** Sets the {@link WriteKey} for the given {@link Owner}. */
283
+ readonly setWriteKey: (ownerId: BinaryOwnerId, writeKey: WriteKey) => boolean;
284
+
263
285
  /** Write encrypted {@link CrdtMessage}s to storage. */
264
286
  readonly writeMessages: (
265
287
  ownerId: BinaryOwnerId,
@@ -271,6 +293,9 @@ export interface Storage {
271
293
  ownerId: BinaryOwnerId,
272
294
  timestamp: BinaryTimestamp,
273
295
  ) => EncryptedDbChange | null;
296
+
297
+ /** Delete all data for the given {@link Owner}. */
298
+ readonly deleteOwner: (ownerId: BinaryOwnerId) => boolean;
274
299
  }
275
300
 
276
301
  export interface StorageDep {
@@ -435,6 +460,7 @@ export const createProtocolMessageFromCrdtMessages =
435
460
  maxSize?: PositiveInt,
436
461
  ): ProtocolMessage => {
437
462
  const buffer = createProtocolMessageBuffer(owner.id, {
463
+ type: "initiator",
438
464
  totalMaxSize: maxSize ?? maxProtocolMessageSize,
439
465
  writeKey: owner.writeKey,
440
466
  });
@@ -490,7 +516,7 @@ export const createProtocolMessageFromCrdtMessages =
490
516
  export const createProtocolMessageForSync =
491
517
  (deps: StorageDep) =>
492
518
  (ownerId: OwnerId): ProtocolMessage | null => {
493
- const buffer = createProtocolMessageBuffer(ownerId);
519
+ const buffer = createProtocolMessageBuffer(ownerId, { type: "initiator" });
494
520
  const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
495
521
 
496
522
  const size = deps.storage.getSize(binaryOwnerId);
@@ -508,6 +534,19 @@ export const createProtocolMessageForSync =
508
534
  return buffer.unwrap();
509
535
  };
510
536
 
537
+ /** Creates a ProtocolMessage for {@link WriteKey} rotation. */
538
+ export const createProtocolMessageForWriteKeyRotation = (
539
+ ownerId: OwnerId,
540
+ currentWriteKey: WriteKey,
541
+ newWriteKey: WriteKey,
542
+ ): ProtocolMessage => {
543
+ const buffer = createProtocolMessageBuffer(ownerId, {
544
+ type: "initiator",
545
+ writeKey: [currentWriteKey, newWriteKey],
546
+ });
547
+ return buffer.unwrap();
548
+ };
549
+
511
550
  /**
512
551
  * Mutable builder for constructing {@link ProtocolMessage} respecting size
513
552
  * limits.
@@ -535,16 +574,22 @@ export interface ProtocolMessageBuffer {
535
574
  export const createProtocolMessageBuffer = (
536
575
  ownerId: OwnerId,
537
576
  options: {
538
- readonly errorCode?: ProtocolErrorCode;
539
- readonly writeKey?: WriteKey;
540
577
  readonly totalMaxSize?: PositiveInt | undefined;
541
578
  readonly rangesMaxSize?: PositiveInt | undefined;
542
579
  readonly version?: NonNegativeInt;
543
- } = {},
580
+ } & (
581
+ | {
582
+ readonly type: "initiator";
583
+ /** Single key or [current, new] for rotation. */
584
+ readonly writeKey?: WriteKey | readonly [WriteKey, WriteKey];
585
+ }
586
+ | {
587
+ readonly type: "non-initiator";
588
+ readonly errorCode: ProtocolErrorCode;
589
+ }
590
+ ),
544
591
  ): ProtocolMessageBuffer => {
545
592
  const {
546
- errorCode,
547
- writeKey,
548
593
  totalMaxSize = maxProtocolMessageSize,
549
594
  rangesMaxSize = maxProtocolMessageRangesSize,
550
595
  version = protocolVersion,
@@ -565,7 +610,21 @@ export const createProtocolMessageBuffer = (
565
610
 
566
611
  encodeNonNegativeInt(buffers.header, version);
567
612
  buffers.header.extend(ownerIdToBinaryOwnerId(ownerId));
568
- if (errorCode != null) buffers.header.extend([errorCode]);
613
+
614
+ if (options.type === "initiator") {
615
+ if (!options.writeKey) {
616
+ buffers.header.extend([WriteKeyMode.None]);
617
+ } else if (!Array.isArray(options.writeKey)) {
618
+ buffers.header.extend([WriteKeyMode.Single]);
619
+ buffers.header.extend(options.writeKey as WriteKey);
620
+ } else {
621
+ buffers.header.extend([WriteKeyMode.Rotation]);
622
+ buffers.header.extend(options.writeKey[0] as WriteKey); // current
623
+ buffers.header.extend(options.writeKey[1] as WriteKey); // new
624
+ }
625
+ } else {
626
+ buffers.header.extend([options.errorCode]);
627
+ }
569
628
 
570
629
  let isLastRangeInfinite = false;
571
630
 
@@ -577,10 +636,7 @@ export const createProtocolMessageBuffer = (
577
636
  const getHeaderAndMessagesSize = () =>
578
637
  buffers.header.getLength() +
579
638
  buffers.messages.timestamps.getLength() +
580
- buffers.messages.dbChanges.getLength() +
581
- (buffers.messages.timestamps.getCount() > 0 && writeKey
582
- ? writeKeyLength
583
- : 0);
639
+ buffers.messages.dbChanges.getLength();
584
640
 
585
641
  const getRangesSize = () =>
586
642
  buffers.ranges.timestamps.getCount() > 0
@@ -695,8 +751,6 @@ export const createProtocolMessageBuffer = (
695
751
 
696
752
  buffers.messages.timestamps.append(buffers.header);
697
753
  buffers.header.extend(buffers.messages.dbChanges.unwrap());
698
- if (buffers.messages.timestamps.getCount() > 0 && writeKey)
699
- buffers.header.extend(writeKey);
700
754
 
701
755
  if (buffers.ranges.timestamps.getCount() > 0) {
702
756
  buffers.ranges.timestamps.append(buffers.header);
@@ -846,8 +900,6 @@ export const applyProtocolMessageAsClient =
846
900
  });
847
901
  }
848
902
 
849
- const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
850
-
851
903
  const errorCode = input.shift() as ProtocolErrorCode;
852
904
  if (errorCode !== ProtocolErrorCode.NoError) {
853
905
  switch (errorCode) {
@@ -874,6 +926,7 @@ export const applyProtocolMessageAsClient =
874
926
  }
875
927
 
876
928
  const messages = decodeMessages(input);
929
+ const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
877
930
 
878
931
  if (
879
932
  isNonEmptyReadonlyArray(messages) &&
@@ -887,6 +940,7 @@ export const applyProtocolMessageAsClient =
887
940
  if (writeKey == null) return ok(null);
888
941
 
889
942
  const output = createProtocolMessageBuffer(ownerId, {
943
+ type: "initiator",
890
944
  writeKey,
891
945
  totalMaxSize,
892
946
  rangesMaxSize,
@@ -921,8 +975,7 @@ export const applyProtocolMessageAsRelay =
921
975
  version = protocolVersion,
922
976
  ): Result<ProtocolMessage | null, ProtocolInvalidDataError> =>
923
977
  tryDecodeProtocolData(inputMessage, (input) => {
924
- const requestedVersion = decodeNonNegativeInt(input);
925
- const ownerId = decodeOwnerId(input);
978
+ const [requestedVersion, ownerId] = decodeVersionAndOwner(input);
926
979
  const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
927
980
 
928
981
  if (requestedVersion !== version) {
@@ -935,44 +988,89 @@ export const applyProtocolMessageAsRelay =
935
988
 
936
989
  subscribe?.(ownerId);
937
990
 
938
- const messages = decodeMessages(input);
991
+ const writeKeyMode = input.shift() as WriteKeyMode;
992
+ let writeKey: WriteKey | undefined;
993
+ let newWriteKey: WriteKey | undefined;
994
+
995
+ if (writeKeyMode !== WriteKeyMode.None) {
996
+ writeKey = input.shiftN(writeKeyLength) as WriteKey;
997
+ switch (writeKeyMode) {
998
+ case WriteKeyMode.Single:
999
+ break;
1000
+ case WriteKeyMode.Rotation:
1001
+ newWriteKey = input.shiftN(writeKeyLength) as WriteKey;
1002
+ break;
1003
+ default:
1004
+ throw new ProtocolDecodeError(
1005
+ `Invalid WriteKeyMode: ${writeKeyMode}`,
1006
+ );
1007
+ }
1008
+ }
939
1009
 
940
- if (isNonEmptyReadonlyArray(messages)) {
941
- const messagesEnd = inputMessage.length - input.getLength();
942
- const writeKey = input.shiftN(writeKeyLength) as WriteKey;
1010
+ if (writeKey) {
1011
+ const isValid = deps.storage.validateWriteKey(binaryOwnerId, writeKey);
1012
+ if (!isValid) {
1013
+ return ok(
1014
+ createProtocolMessageBuffer(ownerId, {
1015
+ type: "non-initiator",
1016
+ errorCode: ProtocolErrorCode.WriteKeyError,
1017
+ }).unwrap(),
1018
+ );
1019
+ }
943
1020
 
944
- const writeKeyIsValid = deps.storage.validateWriteKey(
945
- binaryOwnerId,
946
- writeKey,
947
- );
1021
+ if (newWriteKey) {
1022
+ const rotationSuccess = deps.storage.setWriteKey(
1023
+ binaryOwnerId,
1024
+ newWriteKey,
1025
+ );
1026
+ if (!rotationSuccess) {
1027
+ return ok(
1028
+ createProtocolMessageBuffer(ownerId, {
1029
+ type: "non-initiator",
1030
+ errorCode: ProtocolErrorCode.WriteError,
1031
+ }).unwrap(),
1032
+ );
1033
+ }
1034
+ }
1035
+ }
948
1036
 
949
- if (!writeKeyIsValid)
1037
+ const messages = decodeMessages(input);
1038
+
1039
+ if (isNonEmptyReadonlyArray(messages)) {
1040
+ if (!writeKey)
950
1041
  return ok(
951
1042
  createProtocolMessageBuffer(ownerId, {
1043
+ type: "non-initiator",
952
1044
  errorCode: ProtocolErrorCode.WriteKeyError,
953
1045
  }).unwrap(),
954
1046
  );
955
1047
 
956
- if (broadcast) {
957
- // Instead of encoding a new protocol message, we reuse the inputMessage.
958
- const broadcastMessage = concatBytes(
959
- inputMessage.slice(0, 17),
960
- new Uint8Array([ProtocolErrorCode.NoError]),
961
- inputMessage.slice(17, messagesEnd),
962
- ) as ProtocolMessage;
963
-
964
- broadcast(ownerId, broadcastMessage);
1048
+ // Only broadcast if there's no ranges.
1049
+ if (broadcast && input.getLength() === 0) {
1050
+ const broadcastBuffer = createProtocolMessageBuffer(ownerId, {
1051
+ type: "non-initiator",
1052
+ errorCode: ProtocolErrorCode.NoError,
1053
+ totalMaxSize,
1054
+ rangesMaxSize,
1055
+ version,
1056
+ });
1057
+ for (const message of messages) {
1058
+ broadcastBuffer.addMessage(message);
1059
+ }
1060
+ broadcast(ownerId, broadcastBuffer.unwrap());
965
1061
  }
966
1062
 
967
1063
  if (!deps.storage.writeMessages(binaryOwnerId, messages))
968
1064
  return ok(
969
1065
  createProtocolMessageBuffer(ownerId, {
1066
+ type: "non-initiator",
970
1067
  errorCode: ProtocolErrorCode.WriteError,
971
1068
  }).unwrap(),
972
1069
  );
973
1070
  }
974
1071
 
975
1072
  const output = createProtocolMessageBuffer(ownerId, {
1073
+ type: "non-initiator",
976
1074
  errorCode: ProtocolErrorCode.NoError,
977
1075
  totalMaxSize,
978
1076
  rangesMaxSize,
@@ -1005,6 +1103,10 @@ const tryDecodeProtocolData = <T, E>(
1005
1103
  };
1006
1104
 
1007
1105
  const decodeVersionAndOwner = (input: Buffer): [NonNegativeInt, OwnerId] => {
1106
+ // This structure must never change across protocol versions. The version
1107
+ // and owner ID must always be the first two fields in every protocol message
1108
+ // to enable version negotiation and owner identification before any other
1109
+ // processing occurs.
1008
1110
  const version = decodeNonNegativeInt(input);
1009
1111
  const ownerId = decodeOwnerId(input);
1010
1112
  return [version, ownerId];
@@ -1062,6 +1164,7 @@ const sync =
1062
1164
  return ok(null);
1063
1165
  }
1064
1166
  const message = createProtocolMessageBuffer(binaryOwnerId, {
1167
+ type: "non-initiator",
1065
1168
  errorCode: ProtocolErrorCode.SyncError,
1066
1169
  });
1067
1170
  return ok(message.unwrap());
@@ -1898,3 +2001,27 @@ export const decodeSqliteValue = (buffer: Buffer): SqliteValue => {
1898
2001
  throw new ProtocolDecodeError("invalid ProtocolValueType");
1899
2002
  }
1900
2003
  };
2004
+
2005
+ /**
2006
+ * Decodes a ProtocolMessage into a readable JSON object for debugging.
2007
+ *
2008
+ * Note: This is a stub for future implementation. It should use:
2009
+ *
2010
+ * - DecodeVersionAndOwner
2011
+ * - DecodeError or decodeWriteKeys (depending on context)
2012
+ * - DecodeMessages
2013
+ * - DecodeRanges
2014
+ *
2015
+ * If you want to help, please contribute to this function.
2016
+ */
2017
+ export const decodeProtocolMessageToJson = (
2018
+ _protocolMessage: ProtocolMessage,
2019
+ _isInitiator: boolean,
2020
+ ): unknown => {
2021
+ // TODO: Implement using
2022
+ // - decodeVersionAndOwner
2023
+ // -- decodeError or decodeWriteKeys (should be refactored out),
2024
+ // -- decodeMessages, and decodeRanges.
2025
+ // This is a stub for PRs and community contributions.
2026
+ throw new Error("decodeProtocolMessageToJson is not implemented yet.");
2027
+ };
@@ -1,7 +1,7 @@
1
1
  import { isNonEmptyReadonlyArray } from "../Array.js";
2
2
  import { ConsoleConfig } from "../Console.js";
3
3
  import { TimingSafeEqualDep } from "../Crypto.js";
4
- import { ok, Result } from "../Result.js";
4
+ import { err, ok, Result } from "../Result.js";
5
5
  import { sql, SqliteError } from "../Sqlite.js";
6
6
  import { SimpleName } from "../Type.js";
7
7
  import { OwnerId, WriteKey } from "./Owner.js";
@@ -92,6 +92,21 @@ export const createRelayStorage =
92
92
  return deps.timingSafeEqual(rows[0].writeKey, writeKey);
93
93
  },
94
94
 
95
+ setWriteKey: (ownerId, writeKey) => {
96
+ const upsertWriteKey = deps.sqlite.exec(sql`
97
+ insert into evolu_writeKey (ownerId, writeKey)
98
+ values (${ownerId}, ${writeKey})
99
+ on conflict (ownerId) do update
100
+ set writeKey = excluded.writeKey;
101
+ `);
102
+ if (!upsertWriteKey.ok) {
103
+ options.onStorageError(upsertWriteKey.error);
104
+ return false;
105
+ }
106
+
107
+ return true;
108
+ },
109
+
95
110
  writeMessages: (ownerId, messages) => {
96
111
  const result = deps.sqlite.transaction(() => {
97
112
  for (const message of messages) {
@@ -140,5 +155,29 @@ export const createRelayStorage =
140
155
 
141
156
  return result.value.rows[0]?.change;
142
157
  },
158
+
159
+ deleteOwner: (ownerId) => {
160
+ const result = deps.sqlite.transaction(() => {
161
+ const del1 = deps.sqlite.exec(sql`
162
+ delete from evolu_writeKey where ownerId = ${ownerId};
163
+ `);
164
+ if (!del1.ok) return del1;
165
+
166
+ const del2 = deps.sqlite.exec(sql`
167
+ delete from evolu_message where ownerId = ${ownerId};
168
+ `);
169
+ if (!del2.ok) return del2;
170
+
171
+ const del3 = sqliteStorageBase.value.deleteOwner(ownerId);
172
+ if (!del3) return err(null);
173
+
174
+ return ok();
175
+ });
176
+ if (!result.ok) {
177
+ if (result.error) options.onStorageError(result.error);
178
+ return false;
179
+ }
180
+ return true;
181
+ },
143
182
  });
144
183
  };
@@ -57,6 +57,7 @@ export interface SqliteStorageBase {
57
57
  readonly fingerprintRanges: Storage["fingerprintRanges"];
58
58
  readonly findLowerBound: Storage["findLowerBound"];
59
59
  readonly iterate: Storage["iterate"];
60
+ readonly deleteOwner: Storage["deleteOwner"];
60
61
  }
61
62
 
62
63
  export interface SqliteStorageBaseDep {
@@ -209,6 +210,17 @@ export const createSqliteStorageBase =
209
210
  if (!callback(result.value.rows[i].t, index)) return;
210
211
  }
211
212
  },
213
+
214
+ deleteOwner: (ownerId) => {
215
+ const result = deps.sqlite.exec(sql`
216
+ delete from evolu_timestamp where ownerId = ${ownerId};
217
+ `);
218
+ if (!result.ok) {
219
+ options.onStorageError(result.error);
220
+ return false;
221
+ }
222
+ return true;
223
+ },
212
224
  });
213
225
  };
214
226