@evolu/common 8.11.0 → 8.12.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/Bytes.d.ts +39 -2
- package/dist/src/Bytes.d.ts.map +1 -1
- package/dist/src/Bytes.js +50 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/local-first/Evolu.d.ts +77 -3
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +79 -3
- package/dist/src/local-first/Protocol.d.ts +12 -3
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +44 -22
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +4 -2
- package/dist/src/local-first/Schema.d.ts +11 -2
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Storage.d.ts +3 -3
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +4 -2
- package/package.json +1 -1
- package/src/Bytes.test.ts +27 -0
- package/src/Bytes.ts +58 -2
- package/src/index.ts +6 -1
- package/src/local-first/Evolu.test.ts +308 -1
- package/src/local-first/Evolu.ts +176 -5
- package/src/local-first/Protocol.test.ts +17 -0
- package/src/local-first/Protocol.ts +60 -38
- package/src/local-first/Relay.ts +4 -2
- package/src/local-first/Schema.ts +13 -2
- package/src/local-first/Storage.ts +6 -4
|
@@ -8,7 +8,7 @@ import { assert } from "../Assert.js";
|
|
|
8
8
|
import { err, ok } from "../Result.js";
|
|
9
9
|
import { sql } from "../Sqlite.js";
|
|
10
10
|
import { createMutexByKey } from "../Task.js";
|
|
11
|
-
import { Name,
|
|
11
|
+
import { Name, NonNegativeInt, uint8ArrayToBase64Url } from "../Type.js";
|
|
12
12
|
import { isPromiseLike } from "../Types.js";
|
|
13
13
|
import { OwnerId, ownerIdBytesToOwnerId,
|
|
14
14
|
// OwnerTransport,
|
|
@@ -70,7 +70,9 @@ export const createRelaySqliteStorage = (deps) => (config) => {
|
|
|
70
70
|
}
|
|
71
71
|
const usage = readOwnerUsageOrDefault(deps)(ownerIdBytes, firstInArray(newMessages).timestamp);
|
|
72
72
|
const incomingBytes = newMessages.reduce((sum, m) => sum + m.change.length, 0);
|
|
73
|
-
|
|
73
|
+
// A sum of lengths can be zero. Throwing here would panic the
|
|
74
|
+
// relay's shared Run.
|
|
75
|
+
const newStoredBytes = NonNegativeInt.orThrow((usage.storedBytes ?? 0) + incomingBytes);
|
|
74
76
|
const quotaResult = config.isOwnerWithinQuota(ownerId, newStoredBytes);
|
|
75
77
|
const isWithinQuota = isPromiseLike(quotaResult)
|
|
76
78
|
? await quotaResult
|
|
@@ -373,8 +373,17 @@ export type MutationKind = "insert" | "update" | "upsert";
|
|
|
373
373
|
* inadvertently generate a large volume of CRDT messages. Each mutation
|
|
374
374
|
* produces exactly one {@link CrdtMessage} containing all provided columns.
|
|
375
375
|
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
376
|
+
* Each mutation must fit within {@link maxMutationSize}. Give every column a
|
|
377
|
+
* Type with a maximum length, such as {@link NonEmptyTrimmedString1000} or
|
|
378
|
+
* `maxLength(100_000)(Uint8Array)`, so that a table's largest values add up to
|
|
379
|
+
* less than the limit and input that is too large is rejected where it enters
|
|
380
|
+
* the app. A larger mutation throws before anything is saved, so the code after
|
|
381
|
+
* it does not run. Check unbounded input with {@link Evolu.getMutationSize}.
|
|
382
|
+
* Large binary data, such as images or videos, does not belong in a single
|
|
383
|
+
* mutation; a chunked API for it is planned.
|
|
384
|
+
*
|
|
385
|
+
* Binary values are copied when the mutation is made, so later changes to a
|
|
386
|
+
* `Uint8Array` do not change what is saved.
|
|
378
387
|
*
|
|
379
388
|
* - **insert**: all non-nullable columns required, nullable columns optional,
|
|
380
389
|
* `id` omitted (auto-generated)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../../../src/local-first/Schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAML,aAAa,EACb,KAAK,SAAS,EAGd,KAAK,kBAAkB,EAEvB,KAAK,YAAY,EACjB,WAAW,EACZ,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAGL,OAAO,EAEP,KAAK,EAAE,EAEP,OAAO,EACP,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../../../src/local-first/Schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ import type { DecryptWithXChaCha20Poly1305Error } from "../Crypto.ts";
|
|
|
9
9
|
import type { RandomDep } from "../Random.ts";
|
|
10
10
|
import type { SqliteDep } from "../Sqlite.ts";
|
|
11
11
|
import type { Task } from "../Task.ts";
|
|
12
|
-
import { Boolean, Id, type InferType, NonNegativeInt, Null, type ObjectType,
|
|
12
|
+
import { Boolean, Id, type InferType, NonNegativeInt, Null, type ObjectType, String, type Typed, type TypeError, type UnionType } from "../Type.ts";
|
|
13
13
|
import type { Awaitable } from "../Types.ts";
|
|
14
14
|
import type { OwnerError, OwnerIdBytes } from "./Owner.ts";
|
|
15
15
|
import { OwnerId, OwnerWriteKey } from "./Owner.ts";
|
|
@@ -71,7 +71,7 @@ export interface StorageConfig {
|
|
|
71
71
|
* );
|
|
72
72
|
* ```
|
|
73
73
|
*/
|
|
74
|
-
readonly isOwnerWithinQuota: (ownerId: OwnerId, requiredBytes:
|
|
74
|
+
readonly isOwnerWithinQuota: (ownerId: OwnerId, requiredBytes: NonNegativeInt) => Awaitable<boolean>;
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
77
|
* Replica storage used by Evolu's synchronization protocol.
|
|
@@ -465,5 +465,5 @@ export declare const readOwnerUsageOrDefault: (deps: SqliteDep) => (ownerIdBytes
|
|
|
465
465
|
*
|
|
466
466
|
* @group SQLite
|
|
467
467
|
*/
|
|
468
|
-
export declare const updateOwnerUsage: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, storedBytes:
|
|
468
|
+
export declare const updateOwnerUsage: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, storedBytes: NonNegativeInt, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes) => void;
|
|
469
469
|
//# sourceMappingURL=Storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/local-first/Storage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,cAAc,CAAC;AAEtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EACL,OAAO,EAEP,EAAE,EACF,KAAK,SAAS,EAEd,cAAc,EACd,IAAI,EAGJ,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/local-first/Storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
7
7
|
import { firstInArray, isNonEmptyArray } from "../Array.js";
|
|
8
8
|
import { assert, assertNonNullable } from "../Assert.js";
|
|
9
|
-
import {
|
|
9
|
+
import { concatByteArrays } from "../Bytes.js";
|
|
10
10
|
import { decrement } from "../Number.js";
|
|
11
11
|
import { err, ok } from "../Result.js";
|
|
12
12
|
import { sql, SqliteValue } from "../Sqlite.js";
|
|
@@ -116,7 +116,9 @@ export const createBaseSqliteStorage = (deps) => ({
|
|
|
116
116
|
return insertTimestamp(deps)(ownerId, timestamp, level, strategy);
|
|
117
117
|
},
|
|
118
118
|
getExistingTimestamps: (ownerIdBytes, timestampsBytes) => {
|
|
119
|
-
|
|
119
|
+
// A batch can hold hundreds of thousands of timestamps, too many to spread
|
|
120
|
+
// into concatBytes.
|
|
121
|
+
const concatenatedTimestamps = concatByteArrays(timestampsBytes);
|
|
120
122
|
const result = deps.sqlite.exec(sql `
|
|
121
123
|
with recursive
|
|
122
124
|
split_timestamps(timestampBytes, pos) as (
|
package/package.json
CHANGED
package/src/Bytes.test.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
ByteSizeLiteralTiB,
|
|
24
24
|
byteSizeToByteLength,
|
|
25
25
|
type ByteSize,
|
|
26
|
+
concatByteArrays,
|
|
26
27
|
createBuffer,
|
|
27
28
|
createRunLengthEncoder,
|
|
28
29
|
decodeFlags,
|
|
@@ -96,6 +97,32 @@ describe("BufferError", () => {
|
|
|
96
97
|
});
|
|
97
98
|
});
|
|
98
99
|
|
|
100
|
+
describe("concatByteArrays", () => {
|
|
101
|
+
it("copies arrays into one", () => {
|
|
102
|
+
const first = new Uint8Array([1]);
|
|
103
|
+
const result = concatByteArrays([
|
|
104
|
+
first,
|
|
105
|
+
new Uint8Array(),
|
|
106
|
+
new Uint8Array([2, 3]),
|
|
107
|
+
]);
|
|
108
|
+
assertEqual(result, new Uint8Array([1, 2, 3]));
|
|
109
|
+
assertEqual(concatByteArrays([]), new Uint8Array());
|
|
110
|
+
|
|
111
|
+
first[0] = 9;
|
|
112
|
+
assertEqual(result[0], 1);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("copies more arrays than a call can spread", () => {
|
|
116
|
+
const arrays = Array.from(
|
|
117
|
+
{ length: 200_000 },
|
|
118
|
+
(_, index) => new Uint8Array([index % 256]),
|
|
119
|
+
);
|
|
120
|
+
const result = concatByteArrays(arrays);
|
|
121
|
+
assertEqual(result.length, 200_000);
|
|
122
|
+
assertEqual(result[199_999], 199_999 % 256);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
99
126
|
describe("Buffer", () => {
|
|
100
127
|
it("extends, shifts, and resets bytes", () => {
|
|
101
128
|
const buffer = createBuffer();
|
package/src/Bytes.ts
CHANGED
|
@@ -20,7 +20,11 @@
|
|
|
20
20
|
* @module
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
bytesToUtf8,
|
|
25
|
+
concatBytes as nobleConcatBytes,
|
|
26
|
+
utf8ToBytes,
|
|
27
|
+
} from "@noble/ciphers/utils.js";
|
|
24
28
|
import { assert } from "./Assert.ts";
|
|
25
29
|
import { err, ok, type Result } from "./Result.ts";
|
|
26
30
|
import { safelyStringifyUnknownValue } from "./String.ts";
|
|
@@ -44,9 +48,61 @@ import {
|
|
|
44
48
|
union,
|
|
45
49
|
type UnionError,
|
|
46
50
|
} from "./Type.ts";
|
|
47
|
-
export { bytesToHex,
|
|
51
|
+
export { bytesToHex, hexToBytes } from "@noble/ciphers/utils.js";
|
|
48
52
|
export { bytesToUtf8, utf8ToBytes };
|
|
49
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Copies several Uint8Arrays into one.
|
|
56
|
+
*
|
|
57
|
+
* Each array is a separate argument, so it is meant for a few arrays. Spreading
|
|
58
|
+
* many arrays into it overflows the call stack; use {@link concatByteArrays}
|
|
59
|
+
* when their count is not bounded.
|
|
60
|
+
*
|
|
61
|
+
* ### Example
|
|
62
|
+
*
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { assertEqual, concatBytes } from "@evolu/common";
|
|
65
|
+
*
|
|
66
|
+
* assertEqual(
|
|
67
|
+
* concatBytes(new Uint8Array([1]), new Uint8Array([2, 3])),
|
|
68
|
+
* new Uint8Array([1, 2, 3]),
|
|
69
|
+
* );
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export const concatBytes: typeof nobleConcatBytes = nobleConcatBytes;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Copies an array of Uint8Arrays into one.
|
|
76
|
+
*
|
|
77
|
+
* Unlike {@link concatBytes}, it takes the arrays as one array, so it works for
|
|
78
|
+
* any number of them, such as the timestamps of a batch received from a peer.
|
|
79
|
+
*
|
|
80
|
+
* ### Example
|
|
81
|
+
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* import { assertEqual, concatByteArrays } from "@evolu/common";
|
|
84
|
+
*
|
|
85
|
+
* assertEqual(
|
|
86
|
+
* concatByteArrays([new Uint8Array([1]), new Uint8Array([2, 3])]),
|
|
87
|
+
* new Uint8Array([1, 2, 3]),
|
|
88
|
+
* );
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export const concatByteArrays = (
|
|
92
|
+
arrays: ReadonlyArray<Uint8Array>,
|
|
93
|
+
): Uint8Array => {
|
|
94
|
+
let length = 0;
|
|
95
|
+
for (const array of arrays) length += array.length;
|
|
96
|
+
|
|
97
|
+
const result = new Uint8Array(length);
|
|
98
|
+
let offset = 0;
|
|
99
|
+
for (const array of arrays) {
|
|
100
|
+
result.set(array, offset);
|
|
101
|
+
offset += array.length;
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
};
|
|
105
|
+
|
|
50
106
|
/**
|
|
51
107
|
* Custom error for {@link Buffer}-related failures like premature end of data.
|
|
52
108
|
* Provides better stack traces for debugging binary protocol issues.
|
package/src/index.ts
CHANGED
|
@@ -52,7 +52,12 @@ export * from "./Worker.ts";
|
|
|
52
52
|
|
|
53
53
|
// Local-first essentials.
|
|
54
54
|
export type { UnsupportedDbVersionError } from "./local-first/Db.ts";
|
|
55
|
-
export {
|
|
55
|
+
export {
|
|
56
|
+
AppName,
|
|
57
|
+
createEvolu,
|
|
58
|
+
maxMutationSize,
|
|
59
|
+
testAppName,
|
|
60
|
+
} from "./local-first/Evolu.ts";
|
|
56
61
|
export type {
|
|
57
62
|
AppNameError,
|
|
58
63
|
Evolu,
|
|
@@ -13,8 +13,10 @@ import {
|
|
|
13
13
|
assertThrowsInstanceOf,
|
|
14
14
|
assertTrue,
|
|
15
15
|
} from "../Assert.ts";
|
|
16
|
+
import * as fc from "fast-check";
|
|
16
17
|
import { describe, it } from "node:test";
|
|
17
18
|
import type { Brand } from "../Brand.ts";
|
|
19
|
+
import { createBuffer } from "../Bytes.ts";
|
|
18
20
|
import type { ConsoleEntry, TestConsole } from "../Console.ts";
|
|
19
21
|
import { testCreateConsole } from "../Console.ts";
|
|
20
22
|
import { constVoid } from "../Function.ts";
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
createEvoluDeps,
|
|
26
28
|
type Evolu,
|
|
27
29
|
type EvoluConfig,
|
|
30
|
+
maxMutationSize,
|
|
28
31
|
testAppName,
|
|
29
32
|
type EvoluPlatformDeps,
|
|
30
33
|
} from "./Evolu.ts";
|
|
@@ -34,6 +37,17 @@ import {
|
|
|
34
37
|
createSharedOwner,
|
|
35
38
|
testAppOwner,
|
|
36
39
|
} from "./Owner.ts";
|
|
40
|
+
import {
|
|
41
|
+
createProtocolBroadcastMessagesFromCrdtMessages,
|
|
42
|
+
createProtocolMessageBuffer,
|
|
43
|
+
createProtocolMessageFromCrdtMessages,
|
|
44
|
+
createTimestampsBuffer,
|
|
45
|
+
encodeAndEncryptDbChange,
|
|
46
|
+
encodeDbChange,
|
|
47
|
+
MessageType,
|
|
48
|
+
ProtocolErrorCode,
|
|
49
|
+
ProtocolMessageRangesMaxSize,
|
|
50
|
+
} from "./Protocol.ts";
|
|
37
51
|
import { createQueryBuilder } from "./Schema.ts";
|
|
38
52
|
import {
|
|
39
53
|
consoleEntryOrErrorBroadcastChannelName,
|
|
@@ -56,19 +70,24 @@ import {
|
|
|
56
70
|
import { installPolyfills } from "../Polyfills.ts";
|
|
57
71
|
import { err, ok } from "../Result.ts";
|
|
58
72
|
import { SqliteBoolean } from "../Sqlite.ts";
|
|
59
|
-
import { explicitAbortReason, testCreateRun } from "../Task.ts";
|
|
73
|
+
import { explicitAbortReason, testCreateDeps, testCreateRun } from "../Task.ts";
|
|
60
74
|
import { testCreateId } from "../Test.ts";
|
|
61
75
|
import {
|
|
62
76
|
assertType,
|
|
63
77
|
createIdFromString,
|
|
78
|
+
FiniteNumber,
|
|
64
79
|
id,
|
|
65
80
|
NonEmptyTrimmedString100,
|
|
66
81
|
nullOr,
|
|
67
82
|
PositiveInt,
|
|
83
|
+
String as StringType,
|
|
68
84
|
testName,
|
|
69
85
|
Name,
|
|
86
|
+
Uint8Array as Uint8ArrayType,
|
|
70
87
|
} from "../Type.ts";
|
|
71
88
|
import type { ExtractTyped } from "../Type.ts";
|
|
89
|
+
import { DbChange } from "./Storage.ts";
|
|
90
|
+
import { createTimestamp, type NodeId } from "./Timestamp.ts";
|
|
72
91
|
import {
|
|
73
92
|
testCreateBroadcastChannel,
|
|
74
93
|
testCreateMessageChannel,
|
|
@@ -2447,6 +2466,294 @@ describe("Evolu", () => {
|
|
|
2447
2466
|
});
|
|
2448
2467
|
});
|
|
2449
2468
|
|
|
2469
|
+
describe("mutation size", () => {
|
|
2470
|
+
const NoteId = id("Note");
|
|
2471
|
+
const note = {
|
|
2472
|
+
id: NoteId,
|
|
2473
|
+
body: nullOr(StringType),
|
|
2474
|
+
data: nullOr(Uint8ArrayType),
|
|
2475
|
+
count: nullOr(FiniteNumber),
|
|
2476
|
+
};
|
|
2477
|
+
const SizeSchema = { note, _note: note };
|
|
2478
|
+
|
|
2479
|
+
const createSizeEvolu = createEvolu(SizeSchema, {
|
|
2480
|
+
appName: testAppName,
|
|
2481
|
+
appOwner: testAppOwner,
|
|
2482
|
+
transports: [],
|
|
2483
|
+
});
|
|
2484
|
+
|
|
2485
|
+
const noteId = NoteId.orThrow(createIdFromString("note"));
|
|
2486
|
+
|
|
2487
|
+
const getChanges = (inputs: ReadonlyArray<EvoluInput>) =>
|
|
2488
|
+
inputs.flatMap((input) => (input.type === "Mutate" ? input.changes : []));
|
|
2489
|
+
|
|
2490
|
+
const tooLargeMessage = (size: number) =>
|
|
2491
|
+
`The mutation of table 'note' is ${size} bytes, over maxMutationSize (640000). Bound the Types of its columns or check it with evolu.getMutationSize.`;
|
|
2492
|
+
|
|
2493
|
+
// The length of a blob that makes a note exactly maxMutationSize. Its
|
|
2494
|
+
// length varint takes three bytes, two more than an empty blob's.
|
|
2495
|
+
const getDataAtLimitLength = (evolu: Evolu<typeof SizeSchema>) =>
|
|
2496
|
+
maxMutationSize -
|
|
2497
|
+
evolu.getMutationSize("note", { data: new Uint8Array() }) -
|
|
2498
|
+
2;
|
|
2499
|
+
|
|
2500
|
+
it("getMutationSize measures the change as it is encoded", async () => {
|
|
2501
|
+
await using setup = await setupRunWithEvoluDeps();
|
|
2502
|
+
const evolu = await setup.run.ok(createSizeEvolu);
|
|
2503
|
+
|
|
2504
|
+
const values = {
|
|
2505
|
+
body: "Milk",
|
|
2506
|
+
data: new Uint8Array(10),
|
|
2507
|
+
count: FiniteNumber.orThrow(0.1),
|
|
2508
|
+
};
|
|
2509
|
+
const buffer = createBuffer();
|
|
2510
|
+
encodeDbChange(buffer, {
|
|
2511
|
+
timestamp: createTimestamp(),
|
|
2512
|
+
change: DbChange.orThrow({
|
|
2513
|
+
table: "note",
|
|
2514
|
+
id: noteId,
|
|
2515
|
+
values,
|
|
2516
|
+
isInsert: false,
|
|
2517
|
+
isDelete: true,
|
|
2518
|
+
}),
|
|
2519
|
+
});
|
|
2520
|
+
assertEqual(evolu.getMutationSize("note", values), buffer.getLength());
|
|
2521
|
+
|
|
2522
|
+
// `id` and `isDeleted` are not columns.
|
|
2523
|
+
assertEqual(
|
|
2524
|
+
evolu.getMutationSize("note", { id: noteId, isDeleted: 1 }),
|
|
2525
|
+
evolu.getMutationSize("note", {}),
|
|
2526
|
+
);
|
|
2527
|
+
|
|
2528
|
+
// @ts-expect-error Only the schema's tables can be measured.
|
|
2529
|
+
evolu.getMutationSize("unknown", {});
|
|
2530
|
+
// @ts-expect-error Values must match their column's Type.
|
|
2531
|
+
evolu.getMutationSize("note", { body: 1 });
|
|
2532
|
+
});
|
|
2533
|
+
|
|
2534
|
+
it("encodes no string in more than three bytes per code unit", async () => {
|
|
2535
|
+
await using setup = await setupRunWithEvoluDeps();
|
|
2536
|
+
const evolu = await setup.run.ok(createSizeEvolu);
|
|
2537
|
+
|
|
2538
|
+
const assertAtMostThreeBytesPerCodeUnit = (value: string) => {
|
|
2539
|
+
// A string also takes a length varint of up to three bytes, besides
|
|
2540
|
+
// the type tag that null takes too.
|
|
2541
|
+
assertTrue(
|
|
2542
|
+
evolu.getMutationSize("note", { body: value }) -
|
|
2543
|
+
evolu.getMutationSize("note", { body: null }) <=
|
|
2544
|
+
3 * value.length + 3,
|
|
2545
|
+
);
|
|
2546
|
+
};
|
|
2547
|
+
|
|
2548
|
+
for (const value of [
|
|
2549
|
+
"",
|
|
2550
|
+
"€".repeat(200_000),
|
|
2551
|
+
"😀".repeat(1_000),
|
|
2552
|
+
"\uD800".repeat(1_000),
|
|
2553
|
+
// JSON takes exactly three bytes per code unit here.
|
|
2554
|
+
"0.1",
|
|
2555
|
+
`[${"0.1,".repeat(999)}0.1]`,
|
|
2556
|
+
"[".repeat(1_001) + "]".repeat(1_001),
|
|
2557
|
+
noteId,
|
|
2558
|
+
"2024-01-01T00:00:00.000Z",
|
|
2559
|
+
]) {
|
|
2560
|
+
assertAtMostThreeBytesPerCodeUnit(value);
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
fc.assert(
|
|
2564
|
+
fc.property(
|
|
2565
|
+
fc.oneof(fc.string(), fc.string({ unit: "binary" }), fc.json()),
|
|
2566
|
+
assertAtMostThreeBytesPerCodeUnit,
|
|
2567
|
+
),
|
|
2568
|
+
{ numRuns: 2_000 },
|
|
2569
|
+
);
|
|
2570
|
+
});
|
|
2571
|
+
|
|
2572
|
+
it("rejects a mutation over maxMutationSize before batching it", async () => {
|
|
2573
|
+
await using setup = await setupRunWithEvoluDeps();
|
|
2574
|
+
const { run, evoluInputs, postEvoluOutput } = setup;
|
|
2575
|
+
const evolu = await run.ok(createSizeEvolu);
|
|
2576
|
+
|
|
2577
|
+
const dataAtLimitLength = getDataAtLimitLength(evolu);
|
|
2578
|
+
const atLimit = { data: new Uint8Array(dataAtLimitLength) };
|
|
2579
|
+
assertEqual(evolu.getMutationSize("note", atLimit), maxMutationSize);
|
|
2580
|
+
const completed: Array<string> = [];
|
|
2581
|
+
|
|
2582
|
+
const { id } = evolu.insert("note", atLimit, {
|
|
2583
|
+
onComplete: () => {
|
|
2584
|
+
completed.push("at limit");
|
|
2585
|
+
},
|
|
2586
|
+
});
|
|
2587
|
+
const error = assertThrowsInstanceOf(
|
|
2588
|
+
() =>
|
|
2589
|
+
evolu.insert(
|
|
2590
|
+
"note",
|
|
2591
|
+
{ data: new Uint8Array(dataAtLimitLength + 1) },
|
|
2592
|
+
{
|
|
2593
|
+
onComplete: () => {
|
|
2594
|
+
completed.push("over limit");
|
|
2595
|
+
},
|
|
2596
|
+
},
|
|
2597
|
+
),
|
|
2598
|
+
Error,
|
|
2599
|
+
);
|
|
2600
|
+
assertEqual(error.message, tooLargeMessage(maxMutationSize + 1));
|
|
2601
|
+
|
|
2602
|
+
await testWaitForWorkerMessage();
|
|
2603
|
+
|
|
2604
|
+
// The mutation made before the rejected one is sent as usual.
|
|
2605
|
+
assertEqual(
|
|
2606
|
+
getChanges(evoluInputs).map((change) => change.id),
|
|
2607
|
+
[id],
|
|
2608
|
+
);
|
|
2609
|
+
const mutate = evoluInputs.find((input) => input.type === "Mutate");
|
|
2610
|
+
assertNotUndefined(mutate);
|
|
2611
|
+
postEvoluOutput({
|
|
2612
|
+
type: "OnPatchesByQuery",
|
|
2613
|
+
patchesByQuery: new Map(),
|
|
2614
|
+
onCompleteIds: mutate.onCompleteIds,
|
|
2615
|
+
});
|
|
2616
|
+
await testWaitForWorkerMessage();
|
|
2617
|
+
assertEqual(completed, ["at limit"]);
|
|
2618
|
+
});
|
|
2619
|
+
|
|
2620
|
+
it("lets plain text use the whole limit at a byte per character", async () => {
|
|
2621
|
+
await using setup = await setupRunWithEvoluDeps();
|
|
2622
|
+
const { run, evoluInputs } = setup;
|
|
2623
|
+
const evolu = await run.ok(createSizeEvolu);
|
|
2624
|
+
|
|
2625
|
+
// Spaces keep the text from being encoded as Base64Url, which is smaller.
|
|
2626
|
+
// A long text's length varint takes two more bytes than a short one's.
|
|
2627
|
+
const length =
|
|
2628
|
+
maxMutationSize - evolu.getMutationSize("note", { body: " " }) - 1;
|
|
2629
|
+
assertEqual(
|
|
2630
|
+
evolu.getMutationSize("note", { body: " ".repeat(length) }),
|
|
2631
|
+
maxMutationSize,
|
|
2632
|
+
);
|
|
2633
|
+
assertTrue(length > 639_900);
|
|
2634
|
+
|
|
2635
|
+
evolu.update("note", { id: noteId, body: " ".repeat(length) });
|
|
2636
|
+
const error = assertThrowsInstanceOf(
|
|
2637
|
+
() =>
|
|
2638
|
+
evolu.update("note", { id: noteId, body: " ".repeat(length + 1) }),
|
|
2639
|
+
Error,
|
|
2640
|
+
);
|
|
2641
|
+
assertEqual(error.message, tooLargeMessage(maxMutationSize + 1));
|
|
2642
|
+
|
|
2643
|
+
await testWaitForWorkerMessage();
|
|
2644
|
+
assertLength(getChanges(evoluInputs), 1);
|
|
2645
|
+
});
|
|
2646
|
+
|
|
2647
|
+
it("saves a copy of each binary value", async () => {
|
|
2648
|
+
await using setup = await setupRunWithEvoluDeps();
|
|
2649
|
+
const { run, evoluInputs } = setup;
|
|
2650
|
+
const evolu = await run.ok(createSizeEvolu);
|
|
2651
|
+
|
|
2652
|
+
const resizable = new ArrayBuffer(3, { maxByteLength: 8 });
|
|
2653
|
+
const growing = new Uint8Array(resizable);
|
|
2654
|
+
growing.set([1, 2, 3]);
|
|
2655
|
+
const shared = new Uint8Array(new SharedArrayBuffer(3));
|
|
2656
|
+
shared.set([4, 5, 6]);
|
|
2657
|
+
const nodeBuffer = Buffer.from([7, 8, 9]);
|
|
2658
|
+
|
|
2659
|
+
evolu.insert("note", { data: growing });
|
|
2660
|
+
evolu.insert("note", { data: shared });
|
|
2661
|
+
evolu.insert("note", { data: nodeBuffer });
|
|
2662
|
+
|
|
2663
|
+
// Changes made before the batch is sent do not reach it.
|
|
2664
|
+
resizable.resize(8);
|
|
2665
|
+
growing.fill(0);
|
|
2666
|
+
shared.fill(0);
|
|
2667
|
+
nodeBuffer.fill(0);
|
|
2668
|
+
|
|
2669
|
+
await testWaitForWorkerMessage();
|
|
2670
|
+
|
|
2671
|
+
const saved = getChanges(evoluInputs).map((change) => change.values.data);
|
|
2672
|
+
assertEqual(saved, [
|
|
2673
|
+
new Uint8Array([1, 2, 3]),
|
|
2674
|
+
new Uint8Array([4, 5, 6]),
|
|
2675
|
+
new Uint8Array([7, 8, 9]),
|
|
2676
|
+
]);
|
|
2677
|
+
for (const data of saved) {
|
|
2678
|
+
assert(data instanceof Uint8Array, "Expected a Uint8Array.");
|
|
2679
|
+
assertSame(Object.getPrototypeOf(data), Uint8Array.prototype);
|
|
2680
|
+
assertFalse(data.buffer instanceof SharedArrayBuffer);
|
|
2681
|
+
assertFalse((data.buffer as ArrayBuffer).resizable);
|
|
2682
|
+
}
|
|
2683
|
+
});
|
|
2684
|
+
|
|
2685
|
+
it("does not limit local-only tables but copies their binary values", async () => {
|
|
2686
|
+
await using setup = await setupRunWithEvoluDeps();
|
|
2687
|
+
const { run, evoluInputs } = setup;
|
|
2688
|
+
const evolu = await run.ok(createSizeEvolu);
|
|
2689
|
+
|
|
2690
|
+
const data = new Uint8Array(maxMutationSize + 1);
|
|
2691
|
+
evolu.insert("_note", { data });
|
|
2692
|
+
data[0] = 1;
|
|
2693
|
+
|
|
2694
|
+
await testWaitForWorkerMessage();
|
|
2695
|
+
|
|
2696
|
+
const [change] = getChanges(evoluInputs);
|
|
2697
|
+
assertNotUndefined(change);
|
|
2698
|
+
const saved = change.values.data;
|
|
2699
|
+
assert(saved instanceof Uint8Array, "Expected a Uint8Array.");
|
|
2700
|
+
assertNotSame(saved, data);
|
|
2701
|
+
assertEqual(saved.byteLength, maxMutationSize + 1);
|
|
2702
|
+
assertEqual(saved[0], 0);
|
|
2703
|
+
});
|
|
2704
|
+
|
|
2705
|
+
it("fits a change at the limit into one message next to the largest ranges section", async () => {
|
|
2706
|
+
await using setup = await setupRunWithEvoluDeps();
|
|
2707
|
+
const evolu = await setup.run.ok(createSizeEvolu);
|
|
2708
|
+
const deps = testCreateDeps();
|
|
2709
|
+
const values = { data: new Uint8Array(getDataAtLimitLength(evolu)) };
|
|
2710
|
+
assertEqual(evolu.getMutationSize("note", values), maxMutationSize);
|
|
2711
|
+
|
|
2712
|
+
const message = {
|
|
2713
|
+
timestamp: createTimestamp(),
|
|
2714
|
+
change: DbChange.orThrow({
|
|
2715
|
+
table: "note",
|
|
2716
|
+
id: noteId,
|
|
2717
|
+
values,
|
|
2718
|
+
isInsert: true,
|
|
2719
|
+
isDelete: null,
|
|
2720
|
+
}),
|
|
2721
|
+
};
|
|
2722
|
+
|
|
2723
|
+
// A request and a broadcast assert that the change fits.
|
|
2724
|
+
createProtocolMessageFromCrdtMessages(deps)(testAppOwner, [message]);
|
|
2725
|
+
createProtocolBroadcastMessagesFromCrdtMessages(deps)(testAppOwner, [
|
|
2726
|
+
message,
|
|
2727
|
+
]);
|
|
2728
|
+
|
|
2729
|
+
// A relay's response can pair it with the largest ranges section.
|
|
2730
|
+
const rangesMaxSize = ProtocolMessageRangesMaxSize.orThrow(100_000);
|
|
2731
|
+
const timestamps = createTimestampsBuffer();
|
|
2732
|
+
for (let index = 1; timestamps.getLength() < 99_900; index++) {
|
|
2733
|
+
timestamps.add(
|
|
2734
|
+
createTimestamp({
|
|
2735
|
+
millis: createTimestamp().millis,
|
|
2736
|
+
nodeId: index.toString(16).padStart(16, "0") as NodeId,
|
|
2737
|
+
}),
|
|
2738
|
+
);
|
|
2739
|
+
}
|
|
2740
|
+
const response = createProtocolMessageBuffer(testAppOwner.id, {
|
|
2741
|
+
messageType: MessageType.Response,
|
|
2742
|
+
errorCode: ProtocolErrorCode.NoError,
|
|
2743
|
+
rangesMaxSize,
|
|
2744
|
+
});
|
|
2745
|
+
assertTrue(
|
|
2746
|
+
response.canAddTimestampsRangeAndMessage(timestamps, {
|
|
2747
|
+
timestamp: message.timestamp,
|
|
2748
|
+
change: encodeAndEncryptDbChange(deps)(
|
|
2749
|
+
message,
|
|
2750
|
+
testAppOwner.encryptionKey,
|
|
2751
|
+
),
|
|
2752
|
+
}),
|
|
2753
|
+
);
|
|
2754
|
+
});
|
|
2755
|
+
});
|
|
2756
|
+
|
|
2450
2757
|
describe("exportDatabase", () => {
|
|
2451
2758
|
it("throws when OnExport arrives without pending export", async () => {
|
|
2452
2759
|
const channels: Array<{
|