@evolu/common 6.0.1-preview.1 → 6.0.1-preview.11
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/Assert.d.ts +6 -3
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +6 -3
- package/dist/src/Crypto.d.ts +11 -0
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Evolu/Config.d.ts +17 -4
- package/dist/src/Evolu/Config.d.ts.map +1 -1
- package/dist/src/Evolu/Config.js +1 -1
- package/dist/src/Evolu/Db.d.ts +21 -13
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +81 -64
- package/dist/src/Evolu/Evolu.d.ts +6 -6
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +19 -24
- package/dist/src/Evolu/Protocol.d.ts +79 -37
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +170 -58
- package/dist/src/Evolu/Relay.d.ts +3 -1
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +39 -3
- package/dist/src/Evolu/Schema.d.ts +24 -40
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.js +13 -72
- package/dist/src/Evolu/Storage.d.ts +1 -0
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +10 -0
- package/dist/src/Type.d.ts +42 -2
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +58 -1
- package/package.json +3 -3
- package/src/Assert.ts +6 -3
- package/src/Crypto.ts +13 -0
- package/src/Evolu/Config.ts +19 -5
- package/src/Evolu/Db.ts +92 -76
- package/src/Evolu/Evolu.ts +38 -35
- package/src/Evolu/Protocol.ts +209 -89
- package/src/Evolu/Relay.ts +45 -4
- package/src/Evolu/Schema.ts +102 -130
- package/src/Evolu/Storage.ts +12 -0
- package/src/Type.ts +71 -3
|
@@ -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
|
-
* |
|
|
24
|
-
* |
|
|
25
|
-
* | - {@link
|
|
26
|
-
* | - {@link
|
|
27
|
-
* | -
|
|
28
|
-
* |
|
|
29
|
-
* |
|
|
30
|
-
* | - {@link
|
|
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
|
-
*
|
|
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,13 +142,14 @@ 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 {
|
|
145
|
+
import { 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";
|
|
131
149
|
import { objectToEntries } from "../Object.js";
|
|
132
150
|
import { err, ok } from "../Result.js";
|
|
133
|
-
import {
|
|
151
|
+
import { SqliteValue } from "../Sqlite.js";
|
|
152
|
+
import { Base64Url, base64UrlAlphabet, DateIsoString, Id, idTypeValueLength, JsonValueFromString, maxLength, NonNegativeInt, Number, object, record, } from "../Type.js";
|
|
134
153
|
import { writeKeyLength, } from "./Owner.js";
|
|
135
154
|
import { binaryTimestampToTimestamp, Counter, Millis, timestampToBinaryTimestamp, } from "./Timestamp.js";
|
|
136
155
|
/** Maximum size of the entire protocol message in bytes. */
|
|
@@ -148,6 +167,25 @@ export const ProtocolErrorCode = {
|
|
|
148
167
|
/** A code for {@link ProtocolSyncError}. */
|
|
149
168
|
SyncError: 3,
|
|
150
169
|
};
|
|
170
|
+
export const WriteKeyMode = {
|
|
171
|
+
None: 0,
|
|
172
|
+
Single: 1,
|
|
173
|
+
Rotation: 2,
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Base64Url string with maximum length of 256 characters. Encoding strings as
|
|
177
|
+
* Base64UrlString saves up to 25% in size compared to regular strings.
|
|
178
|
+
*/
|
|
179
|
+
export const Base64Url256 = maxLength(256)(Base64Url);
|
|
180
|
+
/**
|
|
181
|
+
* A DbChange is a change to a table row. Together with a unique
|
|
182
|
+
* {@link Timestamp}, it forms a {@link CrdtMessage}.
|
|
183
|
+
*/
|
|
184
|
+
export const DbChange = object({
|
|
185
|
+
table: Base64Url256,
|
|
186
|
+
id: Id,
|
|
187
|
+
values: record(Base64Url256, SqliteValue),
|
|
188
|
+
});
|
|
151
189
|
export const RangeType = {
|
|
152
190
|
Fingerprint: 1,
|
|
153
191
|
Skip: 0,
|
|
@@ -166,6 +204,7 @@ export const zeroFingerprint = new Uint8Array(fingerprintSize);
|
|
|
166
204
|
*/
|
|
167
205
|
export const createProtocolMessageFromCrdtMessages = (deps) => (owner, messages, maxSize) => {
|
|
168
206
|
const buffer = createProtocolMessageBuffer(owner.id, {
|
|
207
|
+
type: "initiator",
|
|
169
208
|
totalMaxSize: maxSize ?? maxProtocolMessageSize,
|
|
170
209
|
writeKey: owner.writeKey,
|
|
171
210
|
});
|
|
@@ -209,7 +248,7 @@ export const createProtocolMessageFromCrdtMessages = (deps) => (owner, messages,
|
|
|
209
248
|
};
|
|
210
249
|
/** Creates a {@link ProtocolMessage} for sync. */
|
|
211
250
|
export const createProtocolMessageForSync = (deps) => (ownerId) => {
|
|
212
|
-
const buffer = createProtocolMessageBuffer(ownerId);
|
|
251
|
+
const buffer = createProtocolMessageBuffer(ownerId, { type: "initiator" });
|
|
213
252
|
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
214
253
|
const size = deps.storage.getSize(binaryOwnerId);
|
|
215
254
|
// Errors are handled by the storage.
|
|
@@ -218,8 +257,16 @@ export const createProtocolMessageForSync = (deps) => (ownerId) => {
|
|
|
218
257
|
splitRange(deps)(binaryOwnerId, 0, size, InfiniteUpperBound, buffer);
|
|
219
258
|
return buffer.unwrap();
|
|
220
259
|
};
|
|
221
|
-
|
|
222
|
-
|
|
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;
|
|
223
270
|
const buffers = {
|
|
224
271
|
header: createBuffer(),
|
|
225
272
|
messages: {
|
|
@@ -234,17 +281,29 @@ export const createProtocolMessageBuffer = (ownerId, options = {}) => {
|
|
|
234
281
|
};
|
|
235
282
|
encodeNonNegativeInt(buffers.header, version);
|
|
236
283
|
buffers.header.extend(ownerIdToBinaryOwnerId(ownerId));
|
|
237
|
-
if (
|
|
238
|
-
|
|
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
|
+
}
|
|
239
301
|
let isLastRangeInfinite = false;
|
|
240
302
|
const isWithinSizeLimits = () => getSize() <= totalMaxSize;
|
|
241
303
|
const getSize = () => (getHeaderAndMessagesSize() + getRangesSize());
|
|
242
304
|
const getHeaderAndMessagesSize = () => buffers.header.getLength() +
|
|
243
305
|
buffers.messages.timestamps.getLength() +
|
|
244
|
-
buffers.messages.dbChanges.getLength()
|
|
245
|
-
(buffers.messages.timestamps.getCount() > 0 && writeKey
|
|
246
|
-
? writeKeyLength
|
|
247
|
-
: 0);
|
|
306
|
+
buffers.messages.dbChanges.getLength();
|
|
248
307
|
const getRangesSize = () => buffers.ranges.timestamps.getCount() > 0
|
|
249
308
|
? buffers.ranges.timestamps.getLength() +
|
|
250
309
|
buffers.ranges.types.getLength() +
|
|
@@ -330,8 +389,6 @@ export const createProtocolMessageBuffer = (ownerId, options = {}) => {
|
|
|
330
389
|
}
|
|
331
390
|
buffers.messages.timestamps.append(buffers.header);
|
|
332
391
|
buffers.header.extend(buffers.messages.dbChanges.unwrap());
|
|
333
|
-
if (buffers.messages.timestamps.getCount() > 0 && writeKey)
|
|
334
|
-
buffers.header.extend(writeKey);
|
|
335
392
|
if (buffers.ranges.timestamps.getCount() > 0) {
|
|
336
393
|
buffers.ranges.timestamps.append(buffers.header);
|
|
337
394
|
buffers.header.extend(buffers.ranges.types.unwrap());
|
|
@@ -419,7 +476,6 @@ export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteK
|
|
|
419
476
|
ownerId,
|
|
420
477
|
});
|
|
421
478
|
}
|
|
422
|
-
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
423
479
|
const errorCode = input.shift();
|
|
424
480
|
if (errorCode !== ProtocolErrorCode.NoError) {
|
|
425
481
|
switch (errorCode) {
|
|
@@ -443,6 +499,7 @@ export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteK
|
|
|
443
499
|
}
|
|
444
500
|
}
|
|
445
501
|
const messages = decodeMessages(input);
|
|
502
|
+
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
446
503
|
if (isNonEmptyReadonlyArray(messages) &&
|
|
447
504
|
!deps.storage.writeMessages(binaryOwnerId, messages)) {
|
|
448
505
|
return ok(null);
|
|
@@ -453,6 +510,7 @@ export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteK
|
|
|
453
510
|
if (writeKey == null)
|
|
454
511
|
return ok(null);
|
|
455
512
|
const output = createProtocolMessageBuffer(ownerId, {
|
|
513
|
+
type: "initiator",
|
|
456
514
|
writeKey,
|
|
457
515
|
totalMaxSize,
|
|
458
516
|
rangesMaxSize,
|
|
@@ -462,8 +520,7 @@ export const applyProtocolMessageAsClient = (deps) => (inputMessage, { getWriteK
|
|
|
462
520
|
export const applyProtocolMessageAsRelay = (deps) => (inputMessage, { subscribe, broadcast, totalMaxSize, rangesMaxSize, } = {},
|
|
463
521
|
/** For testing purposes only; should not be used in production. */
|
|
464
522
|
version = protocolVersion) => tryDecodeProtocolData(inputMessage, (input) => {
|
|
465
|
-
const requestedVersion =
|
|
466
|
-
const ownerId = decodeOwnerId(input);
|
|
523
|
+
const [requestedVersion, ownerId] = decodeVersionAndOwner(input);
|
|
467
524
|
const binaryOwnerId = ownerIdToBinaryOwnerId(ownerId);
|
|
468
525
|
if (requestedVersion !== version) {
|
|
469
526
|
// Non-initiator responds with its version and ownerId.
|
|
@@ -473,26 +530,68 @@ version = protocolVersion) => tryDecodeProtocolData(inputMessage, (input) => {
|
|
|
473
530
|
return ok(output.unwrap());
|
|
474
531
|
}
|
|
475
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
|
+
}
|
|
476
566
|
const messages = decodeMessages(input);
|
|
477
567
|
if (isNonEmptyReadonlyArray(messages)) {
|
|
478
|
-
|
|
479
|
-
const writeKey = input.shiftN(writeKeyLength);
|
|
480
|
-
const writeKeyIsValid = deps.storage.validateWriteKey(binaryOwnerId, writeKey);
|
|
481
|
-
if (!writeKeyIsValid)
|
|
568
|
+
if (!writeKey)
|
|
482
569
|
return ok(createProtocolMessageBuffer(ownerId, {
|
|
570
|
+
type: "non-initiator",
|
|
483
571
|
errorCode: ProtocolErrorCode.WriteKeyError,
|
|
484
572
|
}).unwrap());
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const
|
|
488
|
-
|
|
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());
|
|
489
586
|
}
|
|
490
587
|
if (!deps.storage.writeMessages(binaryOwnerId, messages))
|
|
491
588
|
return ok(createProtocolMessageBuffer(ownerId, {
|
|
589
|
+
type: "non-initiator",
|
|
492
590
|
errorCode: ProtocolErrorCode.WriteError,
|
|
493
591
|
}).unwrap());
|
|
494
592
|
}
|
|
495
593
|
const output = createProtocolMessageBuffer(ownerId, {
|
|
594
|
+
type: "non-initiator",
|
|
496
595
|
errorCode: ProtocolErrorCode.NoError,
|
|
497
596
|
totalMaxSize,
|
|
498
597
|
rangesMaxSize,
|
|
@@ -509,16 +608,18 @@ const tryDecodeProtocolData = (data, callback) => {
|
|
|
509
608
|
return callback(createBuffer(data));
|
|
510
609
|
}
|
|
511
610
|
catch (error) {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
});
|
|
518
|
-
throw error;
|
|
611
|
+
return err({
|
|
612
|
+
type: "ProtocolInvalidDataError",
|
|
613
|
+
data,
|
|
614
|
+
error,
|
|
615
|
+
});
|
|
519
616
|
}
|
|
520
617
|
};
|
|
521
618
|
const decodeVersionAndOwner = (input) => {
|
|
619
|
+
// This structure must never change across protocol versions. The version
|
|
620
|
+
// and owner ID must always be the first two fields in every protocol message
|
|
621
|
+
// to enable version negotiation and owner identification before any other
|
|
622
|
+
// processing occurs.
|
|
522
623
|
const version = decodeNonNegativeInt(input);
|
|
523
624
|
const ownerId = decodeOwnerId(input);
|
|
524
625
|
return [version, ownerId];
|
|
@@ -558,6 +659,7 @@ const sync = (deps) => (role, input, output, ownerId) => {
|
|
|
558
659
|
return ok(null);
|
|
559
660
|
}
|
|
560
661
|
const message = createProtocolMessageBuffer(binaryOwnerId, {
|
|
662
|
+
type: "non-initiator",
|
|
561
663
|
errorCode: ProtocolErrorCode.SyncError,
|
|
562
664
|
});
|
|
563
665
|
return ok(message.unwrap());
|
|
@@ -840,16 +942,6 @@ export const idToBinaryId = (id) => base64Url256ToBytes(id);
|
|
|
840
942
|
export const binaryIdToId = (binaryId) => decodeId(createBuffer(binaryId));
|
|
841
943
|
export const ownerIdToBinaryOwnerId = (ownerId) => base64Url256ToBytes(ownerId);
|
|
842
944
|
export const binaryOwnerIdToOwnerId = (binaryOwnerId) => decodeOwnerId(createBuffer(binaryOwnerId));
|
|
843
|
-
/**
|
|
844
|
-
* Base64Url string with maximum length of 256 characters. Encoding strings as
|
|
845
|
-
* Base64UrlString saves up to 25% in size compared to regular strings.
|
|
846
|
-
*/
|
|
847
|
-
export const Base64Url256 = maxLength(256)(Base64Url);
|
|
848
|
-
/**
|
|
849
|
-
* Alphabet used for Base64Url encoding. This is copied from the `nanoid`
|
|
850
|
-
* library to avoid dependency on a specific version of `nanoid`.
|
|
851
|
-
*/
|
|
852
|
-
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
853
945
|
/**
|
|
854
946
|
* Converts a Base64Url string to a Uint8Array for binary storage. This encoding
|
|
855
947
|
* is more space-efficient than UTF-8 for Base64Url strings.
|
|
@@ -862,7 +954,7 @@ export const base64Url256ToBytes = (string) => {
|
|
|
862
954
|
let bitsInBuffer = 0;
|
|
863
955
|
let byteIndex = 0;
|
|
864
956
|
for (const char of string) {
|
|
865
|
-
const charValue =
|
|
957
|
+
const charValue = base64UrlAlphabet.indexOf(char);
|
|
866
958
|
bitBuffer = (bitBuffer << 6) | charValue;
|
|
867
959
|
bitsInBuffer += 6;
|
|
868
960
|
while (bitsInBuffer >= 8) {
|
|
@@ -887,10 +979,10 @@ export const decodeBase64Url256 = (buffer, stringLength) => {
|
|
|
887
979
|
bitsInBuffer -= 6;
|
|
888
980
|
if (string.length < stringLength) {
|
|
889
981
|
const charValue = (bitBuffer >> bitsInBuffer) & 0x3f;
|
|
890
|
-
if (charValue < 0 || charValue >=
|
|
982
|
+
if (charValue < 0 || charValue >= base64UrlAlphabet.length) {
|
|
891
983
|
throw new ProtocolDecodeError("invalid charValue");
|
|
892
984
|
}
|
|
893
|
-
string +=
|
|
985
|
+
string += base64UrlAlphabet[charValue];
|
|
894
986
|
}
|
|
895
987
|
}
|
|
896
988
|
}
|
|
@@ -1182,3 +1274,23 @@ export const decodeSqliteValue = (buffer) => {
|
|
|
1182
1274
|
throw new ProtocolDecodeError("invalid ProtocolValueType");
|
|
1183
1275
|
}
|
|
1184
1276
|
};
|
|
1277
|
+
/**
|
|
1278
|
+
* Decodes a ProtocolMessage into a readable JSON object for debugging.
|
|
1279
|
+
*
|
|
1280
|
+
* Note: This is a stub for future implementation. It should use:
|
|
1281
|
+
*
|
|
1282
|
+
* - DecodeVersionAndOwner
|
|
1283
|
+
* - DecodeError or decodeWriteKeys (depending on context)
|
|
1284
|
+
* - DecodeMessages
|
|
1285
|
+
* - DecodeRanges
|
|
1286
|
+
*
|
|
1287
|
+
* If you want to help, please contribute to this function.
|
|
1288
|
+
*/
|
|
1289
|
+
export const decodeProtocolMessageToJson = (_protocolMessage, _isInitiator) => {
|
|
1290
|
+
// TODO: Implement using
|
|
1291
|
+
// - decodeVersionAndOwner
|
|
1292
|
+
// -- decodeError or decodeWriteKeys (should be refactored out),
|
|
1293
|
+
// -- decodeMessages, and decodeRanges.
|
|
1294
|
+
// This is a stub for PRs and community contributions.
|
|
1295
|
+
throw new Error("decodeProtocolMessageToJson is not implemented yet.");
|
|
1296
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ConsoleConfig } from "../Console.js";
|
|
2
|
+
import { TimingSafeEqualDep } from "../Crypto.js";
|
|
2
3
|
import { Result } from "../Result.js";
|
|
3
4
|
import { SqliteError } from "../Sqlite.js";
|
|
4
5
|
import { SimpleName } from "../Type.js";
|
|
@@ -9,5 +10,6 @@ export interface Relay extends Disposable {
|
|
|
9
10
|
export interface RelayConfig extends ConsoleConfig {
|
|
10
11
|
readonly name?: SimpleName;
|
|
11
12
|
}
|
|
12
|
-
export
|
|
13
|
+
export type RelaySqliteStorageDeps = SqliteStorageDeps & TimingSafeEqualDep;
|
|
14
|
+
export declare const createRelayStorage: (deps: RelaySqliteStorageDeps) => (options: CreateSqliteStorageBaseOptions) => Result<Storage, SqliteError>;
|
|
13
15
|
//# sourceMappingURL=Relay.d.ts.map
|
|
@@ -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;
|
|
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"}
|
package/dist/src/Evolu/Relay.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isNonEmptyReadonlyArray } from "../Array.js";
|
|
2
|
-
import {
|
|
3
|
-
import { ok } from "../Result.js";
|
|
2
|
+
import { err, ok } from "../Result.js";
|
|
4
3
|
import { sql } from "../Sqlite.js";
|
|
5
4
|
import { createSqliteStorageBase, } from "./Storage.js";
|
|
6
5
|
import { timestampToBinaryTimestamp } from "./Timestamp.js";
|
|
@@ -64,7 +63,20 @@ export const createRelayStorage = (deps) => (options) => {
|
|
|
64
63
|
}
|
|
65
64
|
return true;
|
|
66
65
|
}
|
|
67
|
-
return
|
|
66
|
+
return deps.timingSafeEqual(rows[0].writeKey, writeKey);
|
|
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;
|
|
68
80
|
},
|
|
69
81
|
writeMessages: (ownerId, messages) => {
|
|
70
82
|
const result = deps.sqlite.transaction(() => {
|
|
@@ -105,5 +117,29 @@ export const createRelayStorage = (deps) => (options) => {
|
|
|
105
117
|
}
|
|
106
118
|
return result.value.rows[0]?.change;
|
|
107
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
|
+
},
|
|
108
144
|
});
|
|
109
145
|
};
|
|
@@ -2,7 +2,7 @@ import { Kysely, SelectQueryBuilder } from "kysely";
|
|
|
2
2
|
import { ReadonlyRecord } from "../Object.js";
|
|
3
3
|
import { Result } from "../Result.js";
|
|
4
4
|
import { SqliteBoolean, SqliteQueryOptions, SqliteValue } from "../Sqlite.js";
|
|
5
|
-
import { AnyType, BrandType, InferErrors, InferInput, InferType, MergeObjectTypeErrors, NullableToOptionalProps, ObjectType, OptionalType, Type, TypeError } from "../Type.js";
|
|
5
|
+
import { AnyType, BrandType, IdType, InferErrors, InferInput, InferType, MergeObjectTypeErrors, NullableToOptionalProps, ObjectType, OptionalType, Type, TypeError } from "../Type.js";
|
|
6
6
|
import { Simplify } from "../Types.js";
|
|
7
7
|
import { DbSchema } from "./Db.js";
|
|
8
8
|
import { DbIndexesBuilder } from "./Kysely.js";
|
|
@@ -13,12 +13,6 @@ import { BinaryTimestamp } from "./Timestamp.js";
|
|
|
13
13
|
/**
|
|
14
14
|
* Defines the schema of an Evolu database.
|
|
15
15
|
*
|
|
16
|
-
* - Each top-level key represents a table name.
|
|
17
|
-
* - The value for each table name is a record of column names mapped to their
|
|
18
|
-
* respective data types, defined by {@link Type}.
|
|
19
|
-
* - Each table must include a mandatory `id` column of type {@link Id}.
|
|
20
|
-
* - No table may contain {@link DefaultColumns}.
|
|
21
|
-
*
|
|
22
16
|
* Table schema defines columns that are required for table rows. For not
|
|
23
17
|
* required columns, use {@link nullOr}.
|
|
24
18
|
*
|
|
@@ -50,9 +44,28 @@ import { BinaryTimestamp } from "./Timestamp.js";
|
|
|
50
44
|
* };
|
|
51
45
|
* ```
|
|
52
46
|
*/
|
|
53
|
-
export type EvoluSchema = ReadonlyRecord<string, ReadonlyRecord<string, Type<any, any, any, any, any
|
|
54
|
-
|
|
55
|
-
}
|
|
47
|
+
export type EvoluSchema = ReadonlyRecord<string, ReadonlyRecord<string, Type<any, any, any, any, any>>>;
|
|
48
|
+
/**
|
|
49
|
+
* Validates an {@link EvoluSchema} at compile time, returning the first error
|
|
50
|
+
* found as a readable string literal type. This approach provides much clearer
|
|
51
|
+
* and more actionable TypeScript errors than the default, which are often hard
|
|
52
|
+
* to read.
|
|
53
|
+
*
|
|
54
|
+
* Validates the following schema requirements:
|
|
55
|
+
*
|
|
56
|
+
* 1. All tables must have an 'id' column
|
|
57
|
+
* 2. The 'id' column must be a branded ID type (created with id() function)
|
|
58
|
+
* 3. Tables cannot use default column names (createdAt, updatedAt, isDeleted)
|
|
59
|
+
* 4. All column types must be compatible with SQLite (extend SqliteValue)
|
|
60
|
+
*/
|
|
61
|
+
export type ValidateSchema<S extends EvoluSchema> = ValidateSchemaHasId<S> extends never ? ValidateIdColumnType<S> extends never ? ValidateNoDefaultColumns<S> extends never ? ValidateColumnTypes<S> extends never ? S : ValidateColumnTypes<S> : ValidateNoDefaultColumns<S> : ValidateIdColumnType<S> : ValidateSchemaHasId<S>;
|
|
62
|
+
export type ValidateSchemaHasId<S extends EvoluSchema> = keyof S extends infer TableName ? TableName extends keyof S ? "id" extends keyof S[TableName] ? never : SchemaValidationError<`Table "${TableName & string}" is missing required id column.`> : never : never;
|
|
63
|
+
export type ValidateIdColumnType<S extends EvoluSchema> = keyof S extends infer TableName ? TableName extends keyof S ? "id" extends keyof S[TableName] ? S[TableName]["id"] extends IdType<any> ? never : SchemaValidationError<`Table "${TableName & string}" id column must be a branded ID type (created with id("${TableName & string}")).`> : never : never : never;
|
|
64
|
+
export type ValidateNoDefaultColumns<S extends EvoluSchema> = keyof S extends infer TableName ? TableName extends keyof S ? keyof S[TableName] extends infer ColumnName ? ColumnName extends keyof S[TableName] ? ColumnName extends "createdAt" | "updatedAt" | "isDeleted" ? SchemaValidationError<`Table "${TableName & string}" uses default column name "${ColumnName & string}". Default columns (createdAt, updatedAt, isDeleted) are added automatically.`> : never : never : never : never : never;
|
|
65
|
+
export type ValidateColumnTypes<S extends EvoluSchema> = keyof S extends infer TableName ? TableName extends keyof S ? keyof S[TableName] extends infer ColumnName ? ColumnName extends keyof S[TableName] ? InferType<S[TableName][ColumnName]> extends SqliteValue ? never : SchemaValidationError<`Table "${TableName & string}" column "${ColumnName & string}" type is not compatible with SQLite. Column types must extend SqliteValue (string, number, Uint8Array, or null).`> : never : never : never : never;
|
|
66
|
+
/** Schema validation error that shows clear, readable messages */
|
|
67
|
+
export type SchemaValidationError<Message extends string> = `❌ Schema Error: ${Message}`;
|
|
68
|
+
export declare const evoluSchemaToDbSchema: (schema: EvoluSchema, indexes?: DbIndexesBuilder) => DbSchema;
|
|
56
69
|
export type CreateQuery<S extends EvoluSchema> = <R extends Row>(queryCallback: (db: Pick<Kysely<{
|
|
57
70
|
[Table in keyof S]: {
|
|
58
71
|
readonly [Column in keyof S[Table]]: Column extends "id" | "createdAt" | "updatedAt" ? InferType<S[Table][Column]> : InferType<S[Table][Column]> | null;
|
|
@@ -61,7 +74,7 @@ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(queryCallback:
|
|
|
61
74
|
readonly evolu_history: {
|
|
62
75
|
readonly timestamp: BinaryTimestamp;
|
|
63
76
|
readonly table: keyof S;
|
|
64
|
-
readonly
|
|
77
|
+
readonly id: BinaryId;
|
|
65
78
|
readonly column: string;
|
|
66
79
|
readonly value: SqliteValue;
|
|
67
80
|
};
|
|
@@ -72,35 +85,6 @@ export declare const DefaultColumns: ObjectType<{
|
|
|
72
85
|
isDeleted: import("../Type.js").UnionType<[Type<"Null", null, null, import("../Type.js").NullError, null, import("../Type.js").NullError>, import("../Type.js").TransformType<Type<"Boolean", boolean, boolean, import("../Type.js").BooleanError, boolean, import("../Type.js").BooleanError>, import("../Type.js").UnionType<[import("../Type.js").LiteralType<0>, import("../Type.js").LiteralType<1>]>, never>]>;
|
|
73
86
|
}>;
|
|
74
87
|
export type DefaultColumns = typeof DefaultColumns.Type;
|
|
75
|
-
/**
|
|
76
|
-
* Valid {@link EvoluSchema}.
|
|
77
|
-
*
|
|
78
|
-
* - Table and column names must be Base64Url strings.
|
|
79
|
-
* - Each table must include an `id` column of type {@link Id}.
|
|
80
|
-
* - Default column names (`createdAt`, `updatedAt`, `isDeleted`) are not allowed.
|
|
81
|
-
*/
|
|
82
|
-
export declare const ValidEvoluSchema: BrandType<import("../Type.js").RecordType<"Brand", string & import("../Types.js").Brand<"Base64Url"> & import("../Types.js").Brand<"MaxLength256">, string, import("../Type.js").MaxLengthError<256>, string & import("../Types.js").Brand<"Base64Url">, import("../Type.js").StringError | import("../Type.js").RegexError<"Base64Url">, import("../Type.js").ObjectWithRecordType<{
|
|
83
|
-
id: Type<"EvoluType", AnyType, AnyType, import("../Type.js").EvoluTypeError, AnyType, import("../Type.js").EvoluTypeError>;
|
|
84
|
-
}, "Brand", string & import("../Types.js").Brand<"Base64Url"> & import("../Types.js").Brand<"MaxLength256">, string, import("../Type.js").MaxLengthError<256>, string & import("../Types.js").Brand<"Base64Url">, import("../Type.js").StringError | import("../Type.js").RegexError<"Base64Url">, Type<"Unknown", unknown, unknown, never, unknown, never>>>, "ValidEvoluSchema", ValidEvoluSchemaError, import("../Type.js").RecordError<import("../Type.js").MaxLengthError<256>, import("../Type.js").ObjectWithRecordError<{
|
|
85
|
-
id: import("../Type.js").EvoluTypeError;
|
|
86
|
-
}, import("../Type.js").MaxLengthError<256>, never>> | import("../Type.js").RecordError<import("../Type.js").StringError | import("../Type.js").RegexError<"Base64Url">, import("../Type.js").ObjectWithRecordError<{
|
|
87
|
-
id: import("../Type.js").EvoluTypeError;
|
|
88
|
-
}, import("../Type.js").StringError | import("../Type.js").RegexError<"Base64Url">, never>>>;
|
|
89
|
-
export type ValidEvoluSchema = typeof ValidEvoluSchema.Type;
|
|
90
|
-
export interface ValidEvoluSchemaError extends TypeError<"ValidEvoluSchema"> {
|
|
91
|
-
readonly reason: {
|
|
92
|
-
kind: "DefaultColumnError";
|
|
93
|
-
tableName: string;
|
|
94
|
-
columnName: string;
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Asserts that the given value is {@link ValidEvoluSchema}.
|
|
99
|
-
*
|
|
100
|
-
* Throws an error if the value is not a valid Evolu schema.
|
|
101
|
-
*/
|
|
102
|
-
export declare const assertValidEvoluSchema: (value: unknown) => ValidEvoluSchema;
|
|
103
|
-
export declare const validEvoluSchemaToDbSchema: (validEvoluSchema: ValidEvoluSchema, indexes?: DbIndexesBuilder) => DbSchema;
|
|
104
88
|
export type MutationKind = "insert" | "update" | "upsert";
|
|
105
89
|
export type Mutation<S extends EvoluSchema, Kind extends MutationKind> = <TableName extends keyof S>(table: TableName, props: InferInput<ObjectType<MutationMapping<S[TableName], Kind>>>, options?: MutationOptions) => Result<{
|
|
106
90
|
readonly id: S[TableName]["id"]["Type"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Schema.ts"],"names":[],"mappings":"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"}
|