@evolu/common 8.4.0 → 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/Array.d.ts +24 -0
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +27 -1
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +1 -0
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +4 -1
- package/dist/src/Lookup.js +1 -0
- package/dist/src/Platform.js +1 -0
- package/dist/src/Relation.js +3 -3
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +9 -3
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +3 -5
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +9 -7
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +3 -1
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +1 -1
- package/dist/src/Task.d.ts +122 -60
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +40 -53
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +1 -0
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +9 -2
- package/dist/src/Type.d.ts +146 -9
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +10 -7
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +8 -0
- package/dist/src/Worker.js +1 -1
- package/dist/src/local-first/Db.js +3 -2
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +1 -0
- package/dist/src/local-first/Owner.d.ts +8 -7
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +4 -4
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +30 -24
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +2 -1
- package/dist/src/local-first/Relay.js +1 -1
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +4 -2
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +1 -1
- package/package.json +1 -1
- package/src/Array.ts +28 -1
- package/src/Assert.ts +1 -0
- package/src/LeakDetector.ts +4 -3
- package/src/Lookup.ts +1 -0
- package/src/Platform.ts +1 -0
- package/src/Relation.ts +3 -3
- package/src/Resource.ts +9 -3
- package/src/Result.ts +5 -5
- package/src/Sqlite.ts +9 -7
- package/src/Store.ts +3 -1
- package/src/String.ts +1 -1
- package/src/Task.ts +133 -82
- package/src/Test.ts +1 -0
- package/src/Time.ts +9 -2
- package/src/Type.ts +173 -29
- package/src/WebSocket.ts +8 -0
- package/src/Worker.ts +1 -1
- package/src/local-first/Db.ts +2 -1
- package/src/local-first/Evolu.ts +1 -0
- package/src/local-first/Owner.ts +8 -7
- package/src/local-first/Protocol.ts +40 -30
- package/src/local-first/Query.ts +6 -3
- package/src/local-first/Relay.ts +1 -1
- package/src/local-first/Shared.ts +4 -2
- package/src/local-first/Timestamp.ts +1 -1
|
@@ -178,7 +178,11 @@
|
|
|
178
178
|
*/
|
|
179
179
|
|
|
180
180
|
import { Packr } from "msgpackr";
|
|
181
|
-
import {
|
|
181
|
+
import {
|
|
182
|
+
createMutableArray,
|
|
183
|
+
isNonEmptyArray,
|
|
184
|
+
type NonEmptyReadonlyArray,
|
|
185
|
+
} from "../Array.ts";
|
|
182
186
|
import { assert, assertNonNullable } from "../Assert.ts";
|
|
183
187
|
import type { Brand } from "../Brand.ts";
|
|
184
188
|
import {
|
|
@@ -692,11 +696,16 @@ export const createProtocolMessageBuffer = (
|
|
|
692
696
|
* approach, contributions are welcome.
|
|
693
697
|
*/
|
|
694
698
|
const safeMargins = {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
699
|
+
// bytes: range type + possible increased count varint
|
|
700
|
+
remainingRange: fingerprintSize + 10,
|
|
701
|
+
// bytes: max millis + max count + NodeId
|
|
702
|
+
timestamp: 30,
|
|
703
|
+
// bytes: maximum encoded DbChange length varint
|
|
704
|
+
dbChangeLength: 8,
|
|
705
|
+
// bytes: worst case is around 650 bytes
|
|
706
|
+
splitRange: 800,
|
|
707
|
+
// bytes: range type + its upperBound + possible increased count varint
|
|
708
|
+
timestampsRange: 50,
|
|
700
709
|
};
|
|
701
710
|
|
|
702
711
|
const addMessageSafeMargin =
|
|
@@ -972,7 +981,7 @@ export const applyProtocolMessageAsClient =
|
|
|
972
981
|
);
|
|
973
982
|
|
|
974
983
|
if (messageType === MessageType.Response) {
|
|
975
|
-
const errorCode = input.shift()
|
|
984
|
+
const errorCode = input.shift();
|
|
976
985
|
if (errorCode !== ProtocolErrorCode.NoError) {
|
|
977
986
|
switch (errorCode) {
|
|
978
987
|
case ProtocolErrorCode.WriteKeyError:
|
|
@@ -1318,7 +1327,7 @@ const decodeMessages = (
|
|
|
1318
1327
|
): ReadonlyArray<EncryptedCrdtMessage> => {
|
|
1319
1328
|
const timestamps = decodeTimestamps(buffer);
|
|
1320
1329
|
|
|
1321
|
-
const messages =
|
|
1330
|
+
const messages = createMutableArray<EncryptedCrdtMessage>(timestamps.length);
|
|
1322
1331
|
for (let i = 0; i < timestamps.length; i++) {
|
|
1323
1332
|
const timestamp = timestamps[i];
|
|
1324
1333
|
const changeLength = decodeLength(buffer);
|
|
@@ -1441,21 +1450,19 @@ const sync =
|
|
|
1441
1450
|
|
|
1442
1451
|
if (eqArrayNumber(range.fingerprint, ourFingerprint)) {
|
|
1443
1452
|
skipRange(range);
|
|
1453
|
+
} else if (output.canSplitRange()) {
|
|
1454
|
+
coalesceSkipsBeforeAdd();
|
|
1455
|
+
splitRange(deps)(
|
|
1456
|
+
ownerIdBytes,
|
|
1457
|
+
lower,
|
|
1458
|
+
upper,
|
|
1459
|
+
currentUpperBound,
|
|
1460
|
+
output,
|
|
1461
|
+
);
|
|
1444
1462
|
} else {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
ownerIdBytes,
|
|
1449
|
-
lower,
|
|
1450
|
-
upper,
|
|
1451
|
-
currentUpperBound,
|
|
1452
|
-
output,
|
|
1453
|
-
);
|
|
1454
|
-
} else {
|
|
1455
|
-
return addFingerprintForRemainingRange(upper)
|
|
1456
|
-
? ok(true)
|
|
1457
|
-
: err(ProtocolErrorCode.SyncError);
|
|
1458
|
-
}
|
|
1463
|
+
return addFingerprintForRemainingRange(upper)
|
|
1464
|
+
? ok(true)
|
|
1465
|
+
: err(ProtocolErrorCode.SyncError);
|
|
1459
1466
|
}
|
|
1460
1467
|
break;
|
|
1461
1468
|
}
|
|
@@ -1635,7 +1642,7 @@ const decodeRanges = (buffer: Buffer): ReadonlyArray<Range> => {
|
|
|
1635
1642
|
const timestampsCount = NonNegativeInt.orThrow(rangesCount - 1);
|
|
1636
1643
|
const timestamps = decodeTimestamps(buffer, timestampsCount);
|
|
1637
1644
|
|
|
1638
|
-
const rangeTypes =
|
|
1645
|
+
const rangeTypes = createMutableArray<RangeType>(rangesCount);
|
|
1639
1646
|
|
|
1640
1647
|
for (let i = 0; i < rangesCount; i++) {
|
|
1641
1648
|
const rangeType = decodeNonNegativeInt(buffer);
|
|
@@ -1650,7 +1657,7 @@ const decodeRanges = (buffer: Buffer): ReadonlyArray<Range> => {
|
|
|
1650
1657
|
}
|
|
1651
1658
|
}
|
|
1652
1659
|
|
|
1653
|
-
const ranges =
|
|
1660
|
+
const ranges = createMutableArray<Range>(rangesCount);
|
|
1654
1661
|
|
|
1655
1662
|
for (let i = 0; i < rangesCount; i++) {
|
|
1656
1663
|
const upperBound =
|
|
@@ -1700,7 +1707,7 @@ const decodeTimestamps = (
|
|
|
1700
1707
|
|
|
1701
1708
|
let previousMillis = 0 as Millis;
|
|
1702
1709
|
|
|
1703
|
-
const millises =
|
|
1710
|
+
const millises = createMutableArray<Millis>(length);
|
|
1704
1711
|
for (let i = 0; i < length; i++) {
|
|
1705
1712
|
const deltaMillis = decodeNonNegativeInt(buffer);
|
|
1706
1713
|
const millis = Millis.fromUnknown(previousMillis + deltaMillis);
|
|
@@ -1717,7 +1724,7 @@ const decodeTimestamps = (
|
|
|
1717
1724
|
|
|
1718
1725
|
const nodeIds = decodeRle(buffer, length, (): NodeId => decodeNodeId(buffer));
|
|
1719
1726
|
|
|
1720
|
-
const timestamps =
|
|
1727
|
+
const timestamps = createMutableArray<Timestamp>(length);
|
|
1721
1728
|
for (let i = 0; i < length; i++) {
|
|
1722
1729
|
timestamps[i] = {
|
|
1723
1730
|
millis: millises[i],
|
|
@@ -1734,7 +1741,7 @@ export const decodeRle = <T>(
|
|
|
1734
1741
|
length: NonNegativeInt,
|
|
1735
1742
|
decodeValue: () => T,
|
|
1736
1743
|
): ReadonlyArray<T> => {
|
|
1737
|
-
const values =
|
|
1744
|
+
const values = createMutableArray<T>(length);
|
|
1738
1745
|
let index = 0;
|
|
1739
1746
|
while (index < length) {
|
|
1740
1747
|
const value = decodeValue();
|
|
@@ -1851,7 +1858,7 @@ export const decodeFlags = (
|
|
|
1851
1858
|
): ReadonlyArray<boolean> => {
|
|
1852
1859
|
const byte = buffer.shift();
|
|
1853
1860
|
const length = globalThis.Math.min(count, 8);
|
|
1854
|
-
const flags =
|
|
1861
|
+
const flags = createMutableArray<boolean>(length);
|
|
1855
1862
|
for (let i = 0; i < length; i++) {
|
|
1856
1863
|
flags[i] = (byte & (1 << i)) !== 0;
|
|
1857
1864
|
}
|
|
@@ -2088,7 +2095,8 @@ export const ProtocolValueType = {
|
|
|
2088
2095
|
NonNegativeInt: /*#__PURE__*/ NonNegativeInt.orThrow(30),
|
|
2089
2096
|
|
|
2090
2097
|
// String optimizations
|
|
2091
|
-
|
|
2098
|
+
// 1 byte vs 2 bytes (50% reduction)
|
|
2099
|
+
EmptyString: /*#__PURE__*/ NonNegativeInt.orThrow(31),
|
|
2092
2100
|
Base64Url: /*#__PURE__*/ NonNegativeInt.orThrow(32),
|
|
2093
2101
|
Id: /*#__PURE__*/ NonNegativeInt.orThrow(33),
|
|
2094
2102
|
Json: /*#__PURE__*/ NonNegativeInt.orThrow(34),
|
|
@@ -2097,7 +2105,8 @@ export const ProtocolValueType = {
|
|
|
2097
2105
|
// encoded with fixed length - 8 bytes
|
|
2098
2106
|
// encode as NonNegativeInt - 6 bytes (additional 25% reduction)
|
|
2099
2107
|
DateIsoWithNonNegativeTime: /*#__PURE__*/ NonNegativeInt.orThrow(35),
|
|
2100
|
-
|
|
2108
|
+
// 9 bytes
|
|
2109
|
+
DateIsoWithNegativeTime: /*#__PURE__*/ NonNegativeInt.orThrow(36),
|
|
2101
2110
|
|
|
2102
2111
|
// TODO: Operations (from 40)
|
|
2103
2112
|
// Increment, Decrement, Patch, whatever.
|
|
@@ -2109,6 +2118,7 @@ export const encodeSqliteValue = (buffer: Buffer, value: SqliteValue): void => {
|
|
|
2109
2118
|
return;
|
|
2110
2119
|
}
|
|
2111
2120
|
|
|
2121
|
+
// oxlint-disable-next-line typescript/switch-exhaustiveness-check -- The remaining SqliteValue is Uint8Array and is encoded after the switch.
|
|
2112
2122
|
switch (typeof value) {
|
|
2113
2123
|
case "string": {
|
|
2114
2124
|
if (value === "") {
|
package/src/local-first/Query.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
TableNode,
|
|
22
22
|
ValueNode,
|
|
23
23
|
} from "kysely";
|
|
24
|
+
import { createMutableArray } from "../Array.ts";
|
|
24
25
|
import type { Brand } from "../Brand.ts";
|
|
25
26
|
import { createRandomBytes } from "../Crypto.ts";
|
|
26
27
|
import type { ReadonlyRecord } from "../Object.ts";
|
|
@@ -106,8 +107,10 @@ export type InferRow<T extends Query> =
|
|
|
106
107
|
export interface Row {
|
|
107
108
|
readonly [key: string]:
|
|
108
109
|
| SqliteValue
|
|
109
|
-
|
|
110
|
-
|
|
|
110
|
+
// for evoluJsonObjectFrom
|
|
111
|
+
| Row
|
|
112
|
+
// for evoluJsonArrayFrom
|
|
113
|
+
| ReadonlyArray<Row>;
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
/**
|
|
@@ -455,7 +458,7 @@ const getSqliteJsonObjectArgs = (
|
|
|
455
458
|
export const parseSqliteJsonArray = <T>(
|
|
456
459
|
arr: ReadonlyArray<T>,
|
|
457
460
|
): ReadonlyArray<T> => {
|
|
458
|
-
const result =
|
|
461
|
+
const result = createMutableArray<T>(arr.length);
|
|
459
462
|
for (let i = 0; i < arr.length; ++i) {
|
|
460
463
|
result[i] = parse(arr[i]) as T;
|
|
461
464
|
}
|
package/src/local-first/Relay.ts
CHANGED
|
@@ -305,7 +305,7 @@ export const createRelaySqliteStorage =
|
|
|
305
305
|
where "ownerId" = ${ownerId} and "timestamp" = ${timestamp};
|
|
306
306
|
`);
|
|
307
307
|
|
|
308
|
-
const row = result.rows
|
|
308
|
+
const row = result.rows.at(0);
|
|
309
309
|
assert(row, "Every timestamp must have a change");
|
|
310
310
|
return row.change;
|
|
311
311
|
},
|
|
@@ -324,7 +324,9 @@ export const initSharedWorker =
|
|
|
324
324
|
const tenantLease = await run.ok(
|
|
325
325
|
unabortable(tenantsByName.acquire(message)),
|
|
326
326
|
);
|
|
327
|
-
tenantLease.resource.addInstance(message,
|
|
327
|
+
tenantLease.resource.addInstance(message, () => {
|
|
328
|
+
tenantLease.release();
|
|
329
|
+
});
|
|
328
330
|
return ok();
|
|
329
331
|
});
|
|
330
332
|
break;
|
|
@@ -479,7 +481,7 @@ const createEvoluTenant =
|
|
|
479
481
|
new Map<EvoluInstanceId, EvoluInstance>(),
|
|
480
482
|
async (instancesById) => {
|
|
481
483
|
await using disposer = new AsyncDisposableStack();
|
|
482
|
-
for (const instance of
|
|
484
|
+
for (const instance of instancesById.values()) {
|
|
483
485
|
disposer.use(instance);
|
|
484
486
|
}
|
|
485
487
|
},
|
|
@@ -93,7 +93,7 @@ export const maxCounter = 65535 as Counter;
|
|
|
93
93
|
* yet they will think they are synced. This is extremely rare and can be
|
|
94
94
|
* resolved by resetting one device to generate a new NodeId.
|
|
95
95
|
*/
|
|
96
|
-
export const NodeId = /*#__PURE__*/ regex("NodeId", /^[a-f0-9]{16}$/)(String);
|
|
96
|
+
export const NodeId = /*#__PURE__*/ regex("NodeId", /^[a-f0-9]{16}$/u)(String);
|
|
97
97
|
export type NodeId = typeof NodeId.Output;
|
|
98
98
|
|
|
99
99
|
export const minNodeId = "0000000000000000" as NodeId;
|