@evolu/common 6.0.1-preview.0 → 6.0.1-preview.1
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/Config.d.ts +1 -1
- package/dist/src/Evolu/Config.js +1 -1
- package/dist/src/Evolu/Owner.d.ts +6 -1
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +5 -0
- package/dist/src/Evolu/Protocol.d.ts +56 -28
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +62 -29
- package/package.json +1 -1
- package/src/Evolu/Config.ts +2 -2
- package/src/Evolu/Owner.ts +7 -1
- package/src/Evolu/Protocol.ts +74 -34
package/dist/src/Evolu/Config.js
CHANGED
|
@@ -2,7 +2,7 @@ import { getOrThrow } from "../Result.js";
|
|
|
2
2
|
import { SimpleName } from "../Type.js";
|
|
3
3
|
export const defaultConfig = {
|
|
4
4
|
name: getOrThrow(SimpleName.fromParent("Evolu")),
|
|
5
|
-
syncUrl: "https://
|
|
5
|
+
syncUrl: "https://free.evoluhq.com",
|
|
6
6
|
reloadUrl: "/",
|
|
7
7
|
maxDrift: 5 * 60 * 1000,
|
|
8
8
|
enableLogging: false,
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO:
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
1
6
|
import { CreateMnemonicDep, CreateRandomBytesDep, EncryptionKey, MnemonicSeed } from "../Crypto.js";
|
|
2
7
|
import { NanoIdLibDep } from "../NanoId.js";
|
|
3
8
|
import { TimeDep } from "../Time.js";
|
|
@@ -17,7 +22,7 @@ import { TimestampString } from "./Timestamp.js";
|
|
|
17
22
|
* {@link SharedReadonlyOwner}, each with specific roles and properties detailed
|
|
18
23
|
* in their respective definitions.
|
|
19
24
|
*
|
|
20
|
-
* Public-key cryptography isn’t included here as it belongs to
|
|
25
|
+
* Public-key cryptography isn’t included here as it belongs to app and varies
|
|
21
26
|
* by use case. An Evolu app without collaboration doesn’t need it, while a
|
|
22
27
|
* Nostr-like app can leverage Nostr NIPs, or a super-safe app can use
|
|
23
28
|
* post-quantum cryptography.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Owner.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Owner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Owner.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Owner.ts"],"names":[],"mappings":"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"}
|
package/dist/src/Evolu/Owner.js
CHANGED
|
@@ -7,13 +7,11 @@
|
|
|
7
7
|
* relays with each other.
|
|
8
8
|
*
|
|
9
9
|
* Evolu Protocol is designed for SQLite but can be extended to any database. It
|
|
10
|
-
* implements [Range-Based Set
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Negentropy but uses different encoding and also provides data transfer and
|
|
16
|
-
* ownership.
|
|
10
|
+
* implements [Range-Based Set
|
|
11
|
+
* Reconciliation](https://arxiv.org/abs/2212.13567). To learn how RBSR works,
|
|
12
|
+
* check [Negentropy](https://logperiodic.com/rbsr.html). Evolu Protocol is
|
|
13
|
+
* similar to Negentropy but uses different encoding and also provides data
|
|
14
|
+
* transfer and ownership.
|
|
17
15
|
*
|
|
18
16
|
* ### Message Structure
|
|
19
17
|
*
|
|
@@ -31,7 +29,7 @@
|
|
|
31
29
|
* | - {@link NonNegativeInt} | Number of ranges. |
|
|
32
30
|
* | - {@link Range} | |
|
|
33
31
|
*
|
|
34
|
-
* Every protocol message belongs to an
|
|
32
|
+
* Every protocol message belongs to an {@link Owner}.
|
|
35
33
|
*
|
|
36
34
|
* ### Synchronization
|
|
37
35
|
*
|
|
@@ -48,21 +46,34 @@
|
|
|
48
46
|
* Both **Messages** and **Ranges** are optional, allowing each side to send,
|
|
49
47
|
* sync, or only subscribe data as needed.
|
|
50
48
|
*
|
|
51
|
-
* When the initiator sends data, the {@link WriteKey} is
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
49
|
+
* When the initiator sends data, the {@link WriteKey} is required in Messages as
|
|
50
|
+
* a secure token proving the initiator can write changes. The non-initiator
|
|
51
|
+
* responds without a {@link WriteKey}, since the initiator’s request already
|
|
52
|
+
* signals it wants data. If the non-initiator detects an issue, it sends an
|
|
53
|
+
* error code via the `Error` field in the header back to the initiator. In
|
|
54
|
+
* relay-to-relay or P2P sync, both sides may require the {@link WriteKey}
|
|
55
|
+
* depending on who is the initiator.
|
|
56
|
+
*
|
|
57
|
+
* ### Protocol Errors
|
|
58
|
+
*
|
|
59
|
+
* The protocol uses error codes in the header to signal issues:
|
|
60
|
+
*
|
|
61
|
+
* - {@link ProtocolWriteKeyError}: The provided WriteKey is invalid or missing.
|
|
62
|
+
* - {@link ProtocolWriteError}: A write operation failed (e.g., due to storage
|
|
63
|
+
* limits or billing).
|
|
64
|
+
* - {@link ProtocolSyncError}: A generic or unexpected synchronization failure
|
|
65
|
+
* occurred.
|
|
66
|
+
* - {@link ProtocolUnsupportedVersionError}: Protocol version mismatch.
|
|
67
|
+
* - {@link ProtocolInvalidDataError}: The message is malformed or corrupted.
|
|
68
|
+
*
|
|
69
|
+
* All protocol errors except `ProtocolInvalidDataError` include the `ownerId`
|
|
70
|
+
* to allow clients to associate errors with the correct owner.
|
|
60
71
|
*
|
|
61
72
|
* ### Message Size Limit
|
|
62
73
|
*
|
|
63
74
|
* The protocol enforces a strict maximum size for all messages, defined by
|
|
64
|
-
* {@link maxProtocolMessageSize}. This ensures every
|
|
65
|
-
* than or equal to this limit, eliminating the need for applications to
|
|
75
|
+
* {@link maxProtocolMessageSize}. This ensures every {@link ProtocolMessage} is
|
|
76
|
+
* less than or equal to this limit, eliminating the need for applications to
|
|
66
77
|
* fragment and reconstruct messages during transmission.
|
|
67
78
|
*
|
|
68
79
|
* ### Why Binary?
|
|
@@ -88,11 +99,24 @@
|
|
|
88
99
|
*
|
|
89
100
|
* ### Versioning
|
|
90
101
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
102
|
+
* Evolu Protocol uses explicit versioning to ensure compatibility between
|
|
103
|
+
* clients and relays (or peers). Each protocol message begins with a version
|
|
104
|
+
* number and an `ownerId` in its header.
|
|
105
|
+
*
|
|
106
|
+
* **How version negotiation works:**
|
|
107
|
+
*
|
|
108
|
+
* - The initiator (usually a client) sends a `ProtocolMessage` that includes its
|
|
109
|
+
* protocol version and the `ownerId`.
|
|
110
|
+
* - The non-initiator (usually a relay or peer) checks the version.
|
|
111
|
+
*
|
|
112
|
+
* - If the versions match, synchronization proceeds as normal.
|
|
113
|
+
* - If the versions do not match, the non-initiator responds with a message
|
|
114
|
+
* containing **its own protocol version and the same `ownerId`**.
|
|
115
|
+
* - The initiator can then detect the version mismatch for that specific owner
|
|
116
|
+
* and handle it appropriately (e.g., prompt for an update or halt sync).
|
|
117
|
+
*
|
|
118
|
+
* Version negotiation is per-owner, allowing Evolu Protocol to evolve safely
|
|
119
|
+
* over time and provide clear feedback about version mismatches.
|
|
96
120
|
*
|
|
97
121
|
* @module
|
|
98
122
|
*/
|
|
@@ -228,11 +252,15 @@ export interface TimestampsRangeWithTimestampsBuffer extends BaseRange {
|
|
|
228
252
|
}
|
|
229
253
|
export type Range = SkipRange | FingerprintRange | TimestampsRange;
|
|
230
254
|
export type ProtocolError = ProtocolUnsupportedVersionError | ProtocolInvalidDataError | ProtocolWriteKeyError | ProtocolWriteError | ProtocolSyncError;
|
|
255
|
+
/** Base interface for all protocol errors. */
|
|
256
|
+
export interface ProtocolErrorBase {
|
|
257
|
+
readonly ownerId: OwnerId;
|
|
258
|
+
}
|
|
231
259
|
/**
|
|
232
260
|
* Represents a version mismatch in the Evolu Protocol. Occurs when the
|
|
233
261
|
* initiator and non-initiator are using incompatible protocol versions.
|
|
234
262
|
*/
|
|
235
|
-
export interface ProtocolUnsupportedVersionError {
|
|
263
|
+
export interface ProtocolUnsupportedVersionError extends ProtocolErrorBase {
|
|
236
264
|
readonly type: "ProtocolUnsupportedVersionError";
|
|
237
265
|
readonly unsupportedVersion: NonNegativeInt;
|
|
238
266
|
/** Indicates which side is obsolete and should update. */
|
|
@@ -245,21 +273,21 @@ export interface ProtocolInvalidDataError {
|
|
|
245
273
|
readonly error: unknown;
|
|
246
274
|
}
|
|
247
275
|
/** Error when a {@link WriteKey} is invalid, missing, or fails validation. */
|
|
248
|
-
export interface ProtocolWriteKeyError {
|
|
276
|
+
export interface ProtocolWriteKeyError extends ProtocolErrorBase {
|
|
249
277
|
readonly type: "ProtocolWriteKeyError";
|
|
250
278
|
}
|
|
251
279
|
/**
|
|
252
280
|
* Error when a write fails due to storage limits or billing requirements.
|
|
253
281
|
* Indicates the need to expand capacity or resolve payment issues.
|
|
254
282
|
*/
|
|
255
|
-
export interface ProtocolWriteError {
|
|
283
|
+
export interface ProtocolWriteError extends ProtocolErrorBase {
|
|
256
284
|
readonly type: "ProtocolWriteError";
|
|
257
285
|
}
|
|
258
286
|
/**
|
|
259
287
|
* Error indicating a synchronization failure during the protocol exchange. Used
|
|
260
288
|
* for unexpected or generic sync errors not covered by other error types.
|
|
261
289
|
*/
|
|
262
|
-
export interface ProtocolSyncError {
|
|
290
|
+
export interface ProtocolSyncError extends ProtocolErrorBase {
|
|
263
291
|
readonly type: "ProtocolSyncError";
|
|
264
292
|
}
|
|
265
293
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Protocol.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"Protocol.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,13 +7,11 @@
|
|
|
7
7
|
* relays with each other.
|
|
8
8
|
*
|
|
9
9
|
* Evolu Protocol is designed for SQLite but can be extended to any database. It
|
|
10
|
-
* implements [Range-Based Set
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Negentropy but uses different encoding and also provides data transfer and
|
|
16
|
-
* ownership.
|
|
10
|
+
* implements [Range-Based Set
|
|
11
|
+
* Reconciliation](https://arxiv.org/abs/2212.13567). To learn how RBSR works,
|
|
12
|
+
* check [Negentropy](https://logperiodic.com/rbsr.html). Evolu Protocol is
|
|
13
|
+
* similar to Negentropy but uses different encoding and also provides data
|
|
14
|
+
* transfer and ownership.
|
|
17
15
|
*
|
|
18
16
|
* ### Message Structure
|
|
19
17
|
*
|
|
@@ -31,7 +29,7 @@
|
|
|
31
29
|
* | - {@link NonNegativeInt} | Number of ranges. |
|
|
32
30
|
* | - {@link Range} | |
|
|
33
31
|
*
|
|
34
|
-
* Every protocol message belongs to an
|
|
32
|
+
* Every protocol message belongs to an {@link Owner}.
|
|
35
33
|
*
|
|
36
34
|
* ### Synchronization
|
|
37
35
|
*
|
|
@@ -48,21 +46,34 @@
|
|
|
48
46
|
* Both **Messages** and **Ranges** are optional, allowing each side to send,
|
|
49
47
|
* sync, or only subscribe data as needed.
|
|
50
48
|
*
|
|
51
|
-
* When the initiator sends data, the {@link WriteKey} is
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
49
|
+
* When the initiator sends data, the {@link WriteKey} is required in Messages as
|
|
50
|
+
* a secure token proving the initiator can write changes. The non-initiator
|
|
51
|
+
* responds without a {@link WriteKey}, since the initiator’s request already
|
|
52
|
+
* signals it wants data. If the non-initiator detects an issue, it sends an
|
|
53
|
+
* error code via the `Error` field in the header back to the initiator. In
|
|
54
|
+
* relay-to-relay or P2P sync, both sides may require the {@link WriteKey}
|
|
55
|
+
* depending on who is the initiator.
|
|
56
|
+
*
|
|
57
|
+
* ### Protocol Errors
|
|
58
|
+
*
|
|
59
|
+
* The protocol uses error codes in the header to signal issues:
|
|
60
|
+
*
|
|
61
|
+
* - {@link ProtocolWriteKeyError}: The provided WriteKey is invalid or missing.
|
|
62
|
+
* - {@link ProtocolWriteError}: A write operation failed (e.g., due to storage
|
|
63
|
+
* limits or billing).
|
|
64
|
+
* - {@link ProtocolSyncError}: A generic or unexpected synchronization failure
|
|
65
|
+
* occurred.
|
|
66
|
+
* - {@link ProtocolUnsupportedVersionError}: Protocol version mismatch.
|
|
67
|
+
* - {@link ProtocolInvalidDataError}: The message is malformed or corrupted.
|
|
68
|
+
*
|
|
69
|
+
* All protocol errors except `ProtocolInvalidDataError` include the `ownerId`
|
|
70
|
+
* to allow clients to associate errors with the correct owner.
|
|
60
71
|
*
|
|
61
72
|
* ### Message Size Limit
|
|
62
73
|
*
|
|
63
74
|
* The protocol enforces a strict maximum size for all messages, defined by
|
|
64
|
-
* {@link maxProtocolMessageSize}. This ensures every
|
|
65
|
-
* than or equal to this limit, eliminating the need for applications to
|
|
75
|
+
* {@link maxProtocolMessageSize}. This ensures every {@link ProtocolMessage} is
|
|
76
|
+
* less than or equal to this limit, eliminating the need for applications to
|
|
66
77
|
* fragment and reconstruct messages during transmission.
|
|
67
78
|
*
|
|
68
79
|
* ### Why Binary?
|
|
@@ -88,11 +99,24 @@
|
|
|
88
99
|
*
|
|
89
100
|
* ### Versioning
|
|
90
101
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
102
|
+
* Evolu Protocol uses explicit versioning to ensure compatibility between
|
|
103
|
+
* clients and relays (or peers). Each protocol message begins with a version
|
|
104
|
+
* number and an `ownerId` in its header.
|
|
105
|
+
*
|
|
106
|
+
* **How version negotiation works:**
|
|
107
|
+
*
|
|
108
|
+
* - The initiator (usually a client) sends a `ProtocolMessage` that includes its
|
|
109
|
+
* protocol version and the `ownerId`.
|
|
110
|
+
* - The non-initiator (usually a relay or peer) checks the version.
|
|
111
|
+
*
|
|
112
|
+
* - If the versions match, synchronization proceeds as normal.
|
|
113
|
+
* - If the versions do not match, the non-initiator responds with a message
|
|
114
|
+
* containing **its own protocol version and the same `ownerId`**.
|
|
115
|
+
* - The initiator can then detect the version mismatch for that specific owner
|
|
116
|
+
* and handle it appropriately (e.g., prompt for an update or halt sync).
|
|
117
|
+
*
|
|
118
|
+
* Version negotiation is per-owner, allowing Evolu Protocol to evolve safely
|
|
119
|
+
* over time and provide clear feedback about version mismatches.
|
|
96
120
|
*
|
|
97
121
|
* @module
|
|
98
122
|
*/
|
|
@@ -386,15 +410,15 @@ const createRunLengthEncoder = (encodeValue) => {
|
|
|
386
410
|
};
|
|
387
411
|
};
|
|
388
412
|
export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteKey, version = protocolVersion, totalMaxSize, rangesMaxSize, } = {}) => tryDecodeProtocolData(inputMessage, (input) => {
|
|
389
|
-
const requestedVersion =
|
|
413
|
+
const [requestedVersion, ownerId] = decodeVersionAndOwner(input);
|
|
390
414
|
if (requestedVersion !== version) {
|
|
391
415
|
return err({
|
|
392
416
|
type: "ProtocolUnsupportedVersionError",
|
|
393
417
|
unsupportedVersion: requestedVersion,
|
|
394
418
|
isInitiator: version < requestedVersion,
|
|
419
|
+
ownerId,
|
|
395
420
|
});
|
|
396
421
|
}
|
|
397
|
-
const ownerId = decodeOwnerId(input);
|
|
398
422
|
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
399
423
|
const errorCode = input.shift();
|
|
400
424
|
if (errorCode !== ProtocolErrorCode.NoError) {
|
|
@@ -402,14 +426,17 @@ export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteK
|
|
|
402
426
|
case ProtocolErrorCode.WriteKeyError:
|
|
403
427
|
return err({
|
|
404
428
|
type: "ProtocolWriteKeyError",
|
|
429
|
+
ownerId,
|
|
405
430
|
});
|
|
406
431
|
case ProtocolErrorCode.WriteError:
|
|
407
432
|
return err({
|
|
408
433
|
type: "ProtocolWriteError",
|
|
434
|
+
ownerId,
|
|
409
435
|
});
|
|
410
436
|
case ProtocolErrorCode.SyncError:
|
|
411
437
|
return err({
|
|
412
438
|
type: "ProtocolSyncError",
|
|
439
|
+
ownerId,
|
|
413
440
|
});
|
|
414
441
|
default:
|
|
415
442
|
throw new ProtocolDecodeError(`Invalid ProtocolErrorCode: ${errorCode}`);
|
|
@@ -436,14 +463,15 @@ export const applyProtocolMessageAsRelay = (deps) => (inputMessage, { subscribe,
|
|
|
436
463
|
/** For testing purposes only; should not be used in production. */
|
|
437
464
|
version = protocolVersion) => tryDecodeProtocolData(inputMessage, (input) => {
|
|
438
465
|
const requestedVersion = decodeNonNegativeInt(input);
|
|
466
|
+
const ownerId = decodeOwnerId(input);
|
|
467
|
+
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
439
468
|
if (requestedVersion !== version) {
|
|
440
|
-
// Non-initiator responds with its version.
|
|
469
|
+
// Non-initiator responds with its version and ownerId.
|
|
441
470
|
const output = createBuffer();
|
|
442
471
|
encodeNonNegativeInt(output, version);
|
|
472
|
+
output.extend(binaryOwnerId);
|
|
443
473
|
return ok(output.unwrap());
|
|
444
474
|
}
|
|
445
|
-
const ownerId = decodeOwnerId(input);
|
|
446
|
-
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
447
475
|
subscribe?.(ownerId);
|
|
448
476
|
const messages = decodeMessages(input);
|
|
449
477
|
if (isNonEmptyReadonlyArray(messages)) {
|
|
@@ -490,6 +518,11 @@ const tryDecodeProtocolData = (data, callback) => {
|
|
|
490
518
|
throw error;
|
|
491
519
|
}
|
|
492
520
|
};
|
|
521
|
+
const decodeVersionAndOwner = (input) => {
|
|
522
|
+
const version = decodeNonNegativeInt(input);
|
|
523
|
+
const ownerId = decodeOwnerId(input);
|
|
524
|
+
return [version, ownerId];
|
|
525
|
+
};
|
|
493
526
|
/**
|
|
494
527
|
* Error thrown for internal protocol validation failures, such as invalid data
|
|
495
528
|
* or type errors.
|
package/package.json
CHANGED
package/src/Evolu/Config.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface Config extends ConsoleConfig {
|
|
|
22
22
|
/**
|
|
23
23
|
* URL for Evolu sync and backup server.
|
|
24
24
|
*
|
|
25
|
-
* The default value is `https://
|
|
25
|
+
* The default value is `https://free.evoluhq.com`.
|
|
26
26
|
*/
|
|
27
27
|
readonly syncUrl: string;
|
|
28
28
|
|
|
@@ -76,7 +76,7 @@ export interface ConfigDep {
|
|
|
76
76
|
|
|
77
77
|
export const defaultConfig: Config = {
|
|
78
78
|
name: getOrThrow(SimpleName.fromParent("Evolu")),
|
|
79
|
-
syncUrl: "https://
|
|
79
|
+
syncUrl: "https://free.evoluhq.com",
|
|
80
80
|
reloadUrl: "/",
|
|
81
81
|
maxDrift: 5 * 60 * 1000,
|
|
82
82
|
enableLogging: false,
|
package/src/Evolu/Owner.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO:
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { assert } from "../Assert.js";
|
|
2
8
|
import {
|
|
3
9
|
createEncryptionKey,
|
|
@@ -50,7 +56,7 @@ import {
|
|
|
50
56
|
* {@link SharedReadonlyOwner}, each with specific roles and properties detailed
|
|
51
57
|
* in their respective definitions.
|
|
52
58
|
*
|
|
53
|
-
* Public-key cryptography isn’t included here as it belongs to
|
|
59
|
+
* Public-key cryptography isn’t included here as it belongs to app and varies
|
|
54
60
|
* by use case. An Evolu app without collaboration doesn’t need it, while a
|
|
55
61
|
* Nostr-like app can leverage Nostr NIPs, or a super-safe app can use
|
|
56
62
|
* post-quantum cryptography.
|
package/src/Evolu/Protocol.ts
CHANGED
|
@@ -7,13 +7,11 @@
|
|
|
7
7
|
* relays with each other.
|
|
8
8
|
*
|
|
9
9
|
* Evolu Protocol is designed for SQLite but can be extended to any database. It
|
|
10
|
-
* implements [Range-Based Set
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Negentropy but uses different encoding and also provides data transfer and
|
|
16
|
-
* ownership.
|
|
10
|
+
* implements [Range-Based Set
|
|
11
|
+
* Reconciliation](https://arxiv.org/abs/2212.13567). To learn how RBSR works,
|
|
12
|
+
* check [Negentropy](https://logperiodic.com/rbsr.html). Evolu Protocol is
|
|
13
|
+
* similar to Negentropy but uses different encoding and also provides data
|
|
14
|
+
* transfer and ownership.
|
|
17
15
|
*
|
|
18
16
|
* ### Message Structure
|
|
19
17
|
*
|
|
@@ -31,7 +29,7 @@
|
|
|
31
29
|
* | - {@link NonNegativeInt} | Number of ranges. |
|
|
32
30
|
* | - {@link Range} | |
|
|
33
31
|
*
|
|
34
|
-
* Every protocol message belongs to an
|
|
32
|
+
* Every protocol message belongs to an {@link Owner}.
|
|
35
33
|
*
|
|
36
34
|
* ### Synchronization
|
|
37
35
|
*
|
|
@@ -48,21 +46,34 @@
|
|
|
48
46
|
* Both **Messages** and **Ranges** are optional, allowing each side to send,
|
|
49
47
|
* sync, or only subscribe data as needed.
|
|
50
48
|
*
|
|
51
|
-
* When the initiator sends data, the {@link WriteKey} is
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
49
|
+
* When the initiator sends data, the {@link WriteKey} is required in Messages as
|
|
50
|
+
* a secure token proving the initiator can write changes. The non-initiator
|
|
51
|
+
* responds without a {@link WriteKey}, since the initiator’s request already
|
|
52
|
+
* signals it wants data. If the non-initiator detects an issue, it sends an
|
|
53
|
+
* error code via the `Error` field in the header back to the initiator. In
|
|
54
|
+
* relay-to-relay or P2P sync, both sides may require the {@link WriteKey}
|
|
55
|
+
* depending on who is the initiator.
|
|
56
|
+
*
|
|
57
|
+
* ### Protocol Errors
|
|
58
|
+
*
|
|
59
|
+
* The protocol uses error codes in the header to signal issues:
|
|
60
|
+
*
|
|
61
|
+
* - {@link ProtocolWriteKeyError}: The provided WriteKey is invalid or missing.
|
|
62
|
+
* - {@link ProtocolWriteError}: A write operation failed (e.g., due to storage
|
|
63
|
+
* limits or billing).
|
|
64
|
+
* - {@link ProtocolSyncError}: A generic or unexpected synchronization failure
|
|
65
|
+
* occurred.
|
|
66
|
+
* - {@link ProtocolUnsupportedVersionError}: Protocol version mismatch.
|
|
67
|
+
* - {@link ProtocolInvalidDataError}: The message is malformed or corrupted.
|
|
68
|
+
*
|
|
69
|
+
* All protocol errors except `ProtocolInvalidDataError` include the `ownerId`
|
|
70
|
+
* to allow clients to associate errors with the correct owner.
|
|
60
71
|
*
|
|
61
72
|
* ### Message Size Limit
|
|
62
73
|
*
|
|
63
74
|
* The protocol enforces a strict maximum size for all messages, defined by
|
|
64
|
-
* {@link maxProtocolMessageSize}. This ensures every
|
|
65
|
-
* than or equal to this limit, eliminating the need for applications to
|
|
75
|
+
* {@link maxProtocolMessageSize}. This ensures every {@link ProtocolMessage} is
|
|
76
|
+
* less than or equal to this limit, eliminating the need for applications to
|
|
66
77
|
* fragment and reconstruct messages during transmission.
|
|
67
78
|
*
|
|
68
79
|
* ### Why Binary?
|
|
@@ -88,11 +99,24 @@
|
|
|
88
99
|
*
|
|
89
100
|
* ### Versioning
|
|
90
101
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
102
|
+
* Evolu Protocol uses explicit versioning to ensure compatibility between
|
|
103
|
+
* clients and relays (or peers). Each protocol message begins with a version
|
|
104
|
+
* number and an `ownerId` in its header.
|
|
105
|
+
*
|
|
106
|
+
* **How version negotiation works:**
|
|
107
|
+
*
|
|
108
|
+
* - The initiator (usually a client) sends a `ProtocolMessage` that includes its
|
|
109
|
+
* protocol version and the `ownerId`.
|
|
110
|
+
* - The non-initiator (usually a relay or peer) checks the version.
|
|
111
|
+
*
|
|
112
|
+
* - If the versions match, synchronization proceeds as normal.
|
|
113
|
+
* - If the versions do not match, the non-initiator responds with a message
|
|
114
|
+
* containing **its own protocol version and the same `ownerId`**.
|
|
115
|
+
* - The initiator can then detect the version mismatch for that specific owner
|
|
116
|
+
* and handle it appropriately (e.g., prompt for an update or halt sync).
|
|
117
|
+
*
|
|
118
|
+
* Version negotiation is per-owner, allowing Evolu Protocol to evolve safely
|
|
119
|
+
* over time and provide clear feedback about version mismatches.
|
|
96
120
|
*
|
|
97
121
|
* @module
|
|
98
122
|
*/
|
|
@@ -137,6 +161,8 @@ import {
|
|
|
137
161
|
} from "../Type.js";
|
|
138
162
|
import { Brand, Predicate } from "../Types.js";
|
|
139
163
|
import {
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
165
|
+
Owner,
|
|
140
166
|
OwnerId,
|
|
141
167
|
OwnerWithWriteAccess,
|
|
142
168
|
WriteKey,
|
|
@@ -340,11 +366,16 @@ export type ProtocolError =
|
|
|
340
366
|
| ProtocolWriteError
|
|
341
367
|
| ProtocolSyncError;
|
|
342
368
|
|
|
369
|
+
/** Base interface for all protocol errors. */
|
|
370
|
+
export interface ProtocolErrorBase {
|
|
371
|
+
readonly ownerId: OwnerId;
|
|
372
|
+
}
|
|
373
|
+
|
|
343
374
|
/**
|
|
344
375
|
* Represents a version mismatch in the Evolu Protocol. Occurs when the
|
|
345
376
|
* initiator and non-initiator are using incompatible protocol versions.
|
|
346
377
|
*/
|
|
347
|
-
export interface ProtocolUnsupportedVersionError {
|
|
378
|
+
export interface ProtocolUnsupportedVersionError extends ProtocolErrorBase {
|
|
348
379
|
readonly type: "ProtocolUnsupportedVersionError";
|
|
349
380
|
readonly unsupportedVersion: NonNegativeInt;
|
|
350
381
|
/** Indicates which side is obsolete and should update. */
|
|
@@ -359,7 +390,7 @@ export interface ProtocolInvalidDataError {
|
|
|
359
390
|
}
|
|
360
391
|
|
|
361
392
|
/** Error when a {@link WriteKey} is invalid, missing, or fails validation. */
|
|
362
|
-
export interface ProtocolWriteKeyError {
|
|
393
|
+
export interface ProtocolWriteKeyError extends ProtocolErrorBase {
|
|
363
394
|
readonly type: "ProtocolWriteKeyError";
|
|
364
395
|
}
|
|
365
396
|
|
|
@@ -367,7 +398,7 @@ export interface ProtocolWriteKeyError {
|
|
|
367
398
|
* Error when a write fails due to storage limits or billing requirements.
|
|
368
399
|
* Indicates the need to expand capacity or resolve payment issues.
|
|
369
400
|
*/
|
|
370
|
-
export interface ProtocolWriteError {
|
|
401
|
+
export interface ProtocolWriteError extends ProtocolErrorBase {
|
|
371
402
|
readonly type: "ProtocolWriteError";
|
|
372
403
|
}
|
|
373
404
|
|
|
@@ -375,7 +406,7 @@ export interface ProtocolWriteError {
|
|
|
375
406
|
* Error indicating a synchronization failure during the protocol exchange. Used
|
|
376
407
|
* for unexpected or generic sync errors not covered by other error types.
|
|
377
408
|
*/
|
|
378
|
-
export interface ProtocolSyncError {
|
|
409
|
+
export interface ProtocolSyncError extends ProtocolErrorBase {
|
|
379
410
|
readonly type: "ProtocolSyncError";
|
|
380
411
|
}
|
|
381
412
|
|
|
@@ -794,17 +825,17 @@ export const applyProtocolMessageAsClient =
|
|
|
794
825
|
tryDecodeProtocolData<ProtocolMessage | null, ProtocolError>(
|
|
795
826
|
inputMessage,
|
|
796
827
|
(input) => {
|
|
797
|
-
const requestedVersion =
|
|
828
|
+
const [requestedVersion, ownerId] = decodeVersionAndOwner(input);
|
|
798
829
|
|
|
799
830
|
if (requestedVersion !== version) {
|
|
800
831
|
return err<ProtocolUnsupportedVersionError>({
|
|
801
832
|
type: "ProtocolUnsupportedVersionError",
|
|
802
833
|
unsupportedVersion: requestedVersion,
|
|
803
834
|
isInitiator: version < requestedVersion,
|
|
835
|
+
ownerId,
|
|
804
836
|
});
|
|
805
837
|
}
|
|
806
838
|
|
|
807
|
-
const ownerId = decodeOwnerId(input);
|
|
808
839
|
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
809
840
|
|
|
810
841
|
const errorCode = input.shift() as ProtocolErrorCode;
|
|
@@ -813,14 +844,17 @@ export const applyProtocolMessageAsClient =
|
|
|
813
844
|
case ProtocolErrorCode.WriteKeyError:
|
|
814
845
|
return err<ProtocolWriteKeyError>({
|
|
815
846
|
type: "ProtocolWriteKeyError",
|
|
847
|
+
ownerId,
|
|
816
848
|
});
|
|
817
849
|
case ProtocolErrorCode.WriteError:
|
|
818
850
|
return err<ProtocolWriteError>({
|
|
819
851
|
type: "ProtocolWriteError",
|
|
852
|
+
ownerId,
|
|
820
853
|
});
|
|
821
854
|
case ProtocolErrorCode.SyncError:
|
|
822
855
|
return err<ProtocolSyncError>({
|
|
823
856
|
type: "ProtocolSyncError",
|
|
857
|
+
ownerId,
|
|
824
858
|
});
|
|
825
859
|
default:
|
|
826
860
|
throw new ProtocolDecodeError(
|
|
@@ -878,17 +912,17 @@ export const applyProtocolMessageAsRelay =
|
|
|
878
912
|
): Result<ProtocolMessage | null, ProtocolInvalidDataError> =>
|
|
879
913
|
tryDecodeProtocolData(inputMessage, (input) => {
|
|
880
914
|
const requestedVersion = decodeNonNegativeInt(input);
|
|
915
|
+
const ownerId = decodeOwnerId(input);
|
|
916
|
+
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
881
917
|
|
|
882
918
|
if (requestedVersion !== version) {
|
|
883
|
-
// Non-initiator responds with its version.
|
|
919
|
+
// Non-initiator responds with its version and ownerId.
|
|
884
920
|
const output = createBuffer();
|
|
885
921
|
encodeNonNegativeInt(output, version);
|
|
922
|
+
output.extend(binaryOwnerId);
|
|
886
923
|
return ok(output.unwrap() as ProtocolMessage);
|
|
887
924
|
}
|
|
888
925
|
|
|
889
|
-
const ownerId = decodeOwnerId(input);
|
|
890
|
-
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
891
|
-
|
|
892
926
|
subscribe?.(ownerId);
|
|
893
927
|
|
|
894
928
|
const messages = decodeMessages(input);
|
|
@@ -960,6 +994,12 @@ const tryDecodeProtocolData = <T, E>(
|
|
|
960
994
|
}
|
|
961
995
|
};
|
|
962
996
|
|
|
997
|
+
const decodeVersionAndOwner = (input: Buffer): [NonNegativeInt, OwnerId] => {
|
|
998
|
+
const version = decodeNonNegativeInt(input);
|
|
999
|
+
const ownerId = decodeOwnerId(input);
|
|
1000
|
+
return [version, ownerId];
|
|
1001
|
+
};
|
|
1002
|
+
|
|
963
1003
|
/**
|
|
964
1004
|
* Error thrown for internal protocol validation failures, such as invalid data
|
|
965
1005
|
* or type errors.
|