@evolu/common 6.0.1-preview.30 → 6.0.1-preview.31
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 +14 -3
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +14 -3
- package/dist/src/Evolu/Db.d.ts +3 -0
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.d.ts +13 -4
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +13 -18
- package/dist/src/Evolu/Storage.d.ts +42 -14
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +125 -85
- package/dist/src/Evolu/Sync.d.ts +2 -3
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +27 -8
- package/dist/src/Type.d.ts +3 -2
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +3 -2
- package/package.json +1 -1
- package/src/Array.ts +17 -3
- package/src/Evolu/Db.ts +3 -0
- package/src/Evolu/Owner.ts +15 -29
- package/src/Evolu/Relay.ts +36 -20
- package/src/Evolu/Storage.ts +184 -120
- package/src/Evolu/Sync.ts +57 -11
- package/src/Type.ts +3 -2
package/dist/src/Array.d.ts
CHANGED
|
@@ -20,9 +20,8 @@
|
|
|
20
20
|
* const filtered = filterArray(readonly, (x) => x > 1); // ReadonlyArray<number>
|
|
21
21
|
*
|
|
22
22
|
* // ✅ NonEmptyArray enforces non-emptiness
|
|
23
|
-
* const
|
|
24
|
-
*
|
|
25
|
-
* first(["a"]); // ✅ Works
|
|
23
|
+
* const value = firstInArray(["a", "b"]); // "a"
|
|
24
|
+
* firstInArray([]); // ❌ Compiler error
|
|
26
25
|
* ```
|
|
27
26
|
*
|
|
28
27
|
* @module
|
|
@@ -67,4 +66,16 @@ export declare const filterArray: <T>(array: ReadonlyArray<T>, predicate: (item:
|
|
|
67
66
|
* **Mutates** the original array. Use only with mutable arrays.
|
|
68
67
|
*/
|
|
69
68
|
export declare const shiftArray: <T>(array: NonEmptyArray<T>) => T;
|
|
69
|
+
/**
|
|
70
|
+
* Returns the first element of a non-empty readonly array.
|
|
71
|
+
*
|
|
72
|
+
* Does not mutate the original array.
|
|
73
|
+
*/
|
|
74
|
+
export declare const firstInArray: <T>(array: NonEmptyReadonlyArray<T>) => T;
|
|
75
|
+
/**
|
|
76
|
+
* Returns the last element of a non-empty readonly array.
|
|
77
|
+
*
|
|
78
|
+
* Does not mutate the original array.
|
|
79
|
+
*/
|
|
80
|
+
export declare const lastInArray: <T>(array: NonEmptyReadonlyArray<T>) => T;
|
|
70
81
|
//# sourceMappingURL=Array.d.ts.map
|
package/dist/src/Array.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Array.d.ts","sourceRoot":"","sources":["../../src/Array.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"Array.d.ts","sourceRoot":"","sources":["../../src/Array.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,0CAA0C;AAC1C,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhD,uCAAuC;AACvC,eAAO,MAAM,eAAe,GAAI,CAAC,EAC/B,OAAO,KAAK,CAAC,CAAC,CAAC,KACd,KAAK,IAAI,aAAa,CAAC,CAAC,CAAqB,CAAC;AAEjD,kDAAkD;AAClD,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzE,uCAAuC;AACvC,eAAO,MAAM,uBAAuB,GAAI,CAAC,EACvC,OAAO,aAAa,CAAC,CAAC,CAAC,KACtB,KAAK,IAAI,qBAAqB,CAAC,CAAC,CAAqB,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,EAC7B,MAAM,CAAC,EACP,OAAO,aAAa,CAAC,CAAC,CAAC,KACtB,qBAAqB,CAAC,CAAC,CACwC,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAC9B,MAAM,CAAC,EACP,OAAO,aAAa,CAAC,CAAC,CAAC,KACtB,qBAAqB,CAAC,CAAC,CAAiD,CAAC;AAE5E;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAC3B,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,EAClD,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,GACpC,qBAAqB,CAAC,CAAC,CAAC,CAAC;AAC5B,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAC3B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAClC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,GACpC,aAAa,CAAC,CAAC,CAAC,CAAC;AAQpB;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAC3B,OAAO,aAAa,CAAC,CAAC,CAAC,EACvB,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,KAC7C,aAAa,CAAC,CAAC,CAAgD,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,OAAO,aAAa,CAAC,CAAC,CAAC,KAAG,CAAuB,CAAC;AAEhF;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,EAAE,OAAO,qBAAqB,CAAC,CAAC,CAAC,KAAG,CAAa,CAAC;AAEhF;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,OAAO,qBAAqB,CAAC,CAAC,CAAC,KAAG,CACxC,CAAC"}
|
package/dist/src/Array.js
CHANGED
|
@@ -20,9 +20,8 @@
|
|
|
20
20
|
* const filtered = filterArray(readonly, (x) => x > 1); // ReadonlyArray<number>
|
|
21
21
|
*
|
|
22
22
|
* // ✅ NonEmptyArray enforces non-emptiness
|
|
23
|
-
* const
|
|
24
|
-
*
|
|
25
|
-
* first(["a"]); // ✅ Works
|
|
23
|
+
* const value = firstInArray(["a", "b"]); // "a"
|
|
24
|
+
* firstInArray([]); // ❌ Compiler error
|
|
26
25
|
* ```
|
|
27
26
|
*
|
|
28
27
|
* @module
|
|
@@ -58,3 +57,15 @@ export const filterArray = (array, predicate) => array.filter(predicate);
|
|
|
58
57
|
* **Mutates** the original array. Use only with mutable arrays.
|
|
59
58
|
*/
|
|
60
59
|
export const shiftArray = (array) => array.shift();
|
|
60
|
+
/**
|
|
61
|
+
* Returns the first element of a non-empty readonly array.
|
|
62
|
+
*
|
|
63
|
+
* Does not mutate the original array.
|
|
64
|
+
*/
|
|
65
|
+
export const firstInArray = (array) => array[0];
|
|
66
|
+
/**
|
|
67
|
+
* Returns the last element of a non-empty readonly array.
|
|
68
|
+
*
|
|
69
|
+
* Does not mutate the original array.
|
|
70
|
+
*/
|
|
71
|
+
export const lastInArray = (array) => array[array.length - 1];
|
package/dist/src/Evolu/Db.d.ts
CHANGED
|
@@ -140,6 +140,9 @@ export interface DbConfig extends ConsoleConfig, TimestampConfig {
|
|
|
140
140
|
/**
|
|
141
141
|
* Encryption key for the SQLite database.
|
|
142
142
|
*
|
|
143
|
+
* Note: If an unencrypted SQLite database already exists and you provide an
|
|
144
|
+
* encryptionKey, SQLite will throw an error.
|
|
145
|
+
*
|
|
143
146
|
* @experimental
|
|
144
147
|
*/
|
|
145
148
|
readonly encryptionKey?: EncryptionKey;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAEL,aAAa,EACb,cAAc,EACd,2BAA2B,EAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAEL,qBAAqB,EAIrB,WAAW,EACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAGL,MAAM,EACP,MAAM,cAAc,CAAC;AACtB,OAAO,EAAe,YAAY,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EACL,QAAQ,EAOR,cAAc,EAEf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAmB,MAAM,eAAe,CAAC;AAC/D,OAAO,EAIL,KAAK,EAEN,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,QAAQ,EAGR,cAAc,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAML,SAAS,EACV,MAAM,WAAW,CAAC;AACnB,OAAO,EAEL,eAAe,EACf,cAAc,EAIf,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,QAAS,SAAQ,aAAa,EAAE,eAAe;IAC9D;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsDG;IACH,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC;IAErC;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAE5B
|
|
1
|
+
{"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Db.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NonEmptyReadonlyArray } from "../Array.js";
|
|
2
2
|
import { EncryptionKey, RandomBytesDep } from "../Crypto.js";
|
|
3
3
|
import { Mnemonic, NonNegativeInt } from "../Type.js";
|
|
4
|
+
import { TimestampBytes } from "./Timestamp.js";
|
|
4
5
|
/**
|
|
5
6
|
* The Owner represents ownership of data in Evolu. Every database change is
|
|
6
7
|
* assigned to an owner, enabling sync functionality and access control.
|
|
@@ -276,11 +277,11 @@ export interface BaseOwnerError {
|
|
|
276
277
|
/**
|
|
277
278
|
* Usage data for an {@link OwnerId}.
|
|
278
279
|
*
|
|
279
|
-
* Tracks
|
|
280
|
-
* needed. Used by both relays and clients.
|
|
280
|
+
* Tracks storage usage to enforce quotas if needed, and some other stuff.
|
|
281
281
|
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
282
|
+
* TODO:
|
|
283
|
+
*
|
|
284
|
+
* - Add transferredBytes for billing and monitoring network usage.
|
|
284
285
|
*/
|
|
285
286
|
export interface OwnerUsage {
|
|
286
287
|
/** The {@link Owner} this usage data belongs to. */
|
|
@@ -300,5 +301,13 @@ export interface OwnerUsage {
|
|
|
300
301
|
* monitor efficiency
|
|
301
302
|
*/
|
|
302
303
|
readonly storedBytes: NonNegativeInt;
|
|
304
|
+
/** Tracks the earliest timestamp for timestamp insertion strategies. */
|
|
305
|
+
readonly firstTimestamp: TimestampBytes | null;
|
|
306
|
+
/**
|
|
307
|
+
* Tracks the latest timestamp for timestamp insertion strategies.
|
|
308
|
+
*
|
|
309
|
+
* Free relays can use it to identify inactive accounts for cleanup.
|
|
310
|
+
*/
|
|
311
|
+
readonly lastTimestamp: TimestampBytes | null;
|
|
303
312
|
}
|
|
304
313
|
//# sourceMappingURL=Owner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Owner.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Owner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAEL,aAAa,EAGb,cAAc,EACf,MAAM,cAAc,CAAC;AAEtB,OAAO,EAML,QAAQ,EACR,cAAc,EACf,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"Owner.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Owner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Relay.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Relay.ts"],"names":[],"mappings":"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"}
|
package/dist/src/Evolu/Relay.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { filterArray, isNonEmptyReadonlyArray, mapArray } from "../Array.js";
|
|
1
|
+
import { filterArray, firstInArray, isNonEmptyReadonlyArray, mapArray, } from "../Array.js";
|
|
2
2
|
import { createInstances } from "../Instances.js";
|
|
3
3
|
import { err, ok } from "../Result.js";
|
|
4
4
|
import { sql } from "../Sqlite.js";
|
|
5
5
|
import { createMutex, isAsync } from "../Task.js";
|
|
6
6
|
import { PositiveInt } from "../Type.js";
|
|
7
7
|
import { ownerIdBytesToOwnerId, } from "./Owner.js";
|
|
8
|
-
import { createBaseSqliteStorage, } from "./Storage.js";
|
|
8
|
+
import { createBaseSqliteStorage, getOwnerUsage, getTimestampInsertStrategy, updateOwnerUsage, } from "./Storage.js";
|
|
9
9
|
import { timestampToTimestampBytes } from "./Timestamp.js";
|
|
10
10
|
export const createRelaySqliteStorage = (deps) => (config) => {
|
|
11
11
|
const sqliteStorageBase = createBaseSqliteStorage(deps)(config);
|
|
@@ -83,16 +83,12 @@ export const createRelaySqliteStorage = (deps) => (config) => {
|
|
|
83
83
|
if (!isNonEmptyReadonlyArray(newMessages)) {
|
|
84
84
|
return ok();
|
|
85
85
|
}
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
`);
|
|
91
|
-
if (!storedBytesResult.ok)
|
|
92
|
-
return storedBytesResult;
|
|
93
|
-
const storedBytes = storedBytesResult.value.rows[0]?.storedBytes ?? 0;
|
|
86
|
+
const usage = getOwnerUsage(deps)(ownerIdBytes, firstInArray(newMessages).timestamp);
|
|
87
|
+
if (!usage.ok)
|
|
88
|
+
return usage;
|
|
89
|
+
const { storedBytes } = usage.value;
|
|
94
90
|
const incomingBytes = newMessages.reduce((sum, m) => sum + m.change.length, 0);
|
|
95
|
-
const newStoredBytes = PositiveInt.orThrow(storedBytes + incomingBytes);
|
|
91
|
+
const newStoredBytes = PositiveInt.orThrow((storedBytes ?? 0) + incomingBytes);
|
|
96
92
|
const withinQuotaResult = config.isOwnerWithinQuota(ownerId, newStoredBytes);
|
|
97
93
|
const isWithinQuota = isAsync(withinQuotaResult)
|
|
98
94
|
? await withinQuotaResult
|
|
@@ -100,9 +96,13 @@ export const createRelaySqliteStorage = (deps) => (config) => {
|
|
|
100
96
|
if (!isWithinQuota) {
|
|
101
97
|
return err({ type: "StorageQuotaError", ownerId });
|
|
102
98
|
}
|
|
99
|
+
let { firstTimestamp, lastTimestamp } = usage.value;
|
|
103
100
|
return deps.sqlite.transaction(() => {
|
|
104
101
|
for (const { timestamp, change } of newMessages) {
|
|
105
|
-
|
|
102
|
+
let strategy;
|
|
103
|
+
[strategy, firstTimestamp, lastTimestamp] =
|
|
104
|
+
getTimestampInsertStrategy(timestamp, firstTimestamp, lastTimestamp);
|
|
105
|
+
const insertTimestampResult = sqliteStorageBase.insertTimestamp(ownerIdBytes, timestamp, strategy);
|
|
106
106
|
if (!insertTimestampResult.ok)
|
|
107
107
|
return insertTimestampResult;
|
|
108
108
|
const insertMessage = deps.sqlite.exec(sql `
|
|
@@ -113,12 +113,7 @@ export const createRelaySqliteStorage = (deps) => (config) => {
|
|
|
113
113
|
if (!insertMessage.ok)
|
|
114
114
|
return insertMessage;
|
|
115
115
|
}
|
|
116
|
-
const updateUsage = deps
|
|
117
|
-
insert into evolu_usage ("ownerId", "storedBytes")
|
|
118
|
-
values (${ownerIdBytes}, ${newStoredBytes})
|
|
119
|
-
on conflict (ownerId) do update
|
|
120
|
-
set storedBytes = ${newStoredBytes};
|
|
121
|
-
`);
|
|
116
|
+
const updateUsage = updateOwnerUsage(deps)(ownerIdBytes, newStoredBytes, firstTimestamp, lastTimestamp);
|
|
122
117
|
if (!updateUsage.ok)
|
|
123
118
|
return updateUsage;
|
|
124
119
|
return ok();
|
|
@@ -12,16 +12,16 @@ export interface StorageConfig {
|
|
|
12
12
|
* Callback called before an attempt to write, to check if an {@link OwnerId}
|
|
13
13
|
* has sufficient quota for the write.
|
|
14
14
|
*
|
|
15
|
-
* The callback receives the {@link OwnerId} and the
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* The callback receives the {@link OwnerId} and the total bytes that would be
|
|
16
|
+
* stored after the write (current stored bytes plus incoming bytes), and
|
|
17
|
+
* returns a {@link MaybeAsync} boolean: `true` to allow the write, or `false`
|
|
18
|
+
* to deny it due to quota limits.
|
|
18
19
|
*
|
|
19
20
|
* The callback can be synchronous (for SQLite or in-memory checks) or
|
|
20
21
|
* asynchronous (for calling remote APIs).
|
|
21
22
|
*
|
|
22
|
-
* The callback returns a boolean rather than an error
|
|
23
|
-
*
|
|
24
|
-
* implementation.
|
|
23
|
+
* The callback returns a boolean rather than an error because error handling
|
|
24
|
+
* and logging are the responsibility of the callback implementation.
|
|
25
25
|
*
|
|
26
26
|
* ### Example
|
|
27
27
|
*
|
|
@@ -207,14 +207,8 @@ export type DbChange = typeof DbChange.Type;
|
|
|
207
207
|
* synchronized later.
|
|
208
208
|
*/
|
|
209
209
|
export interface BaseSqliteStorage extends Pick<Storage, "getSize" | "fingerprint" | "fingerprintRanges" | "findLowerBound" | "iterate" | "deleteOwner"> {
|
|
210
|
-
/**
|
|
211
|
-
|
|
212
|
-
*
|
|
213
|
-
* Must be idempotent - inserting the same timestamp multiple times has no
|
|
214
|
-
* effect after the first insertion. This is crucial for sync reliability as
|
|
215
|
-
* messages may be received and processed multiple times.
|
|
216
|
-
*/
|
|
217
|
-
readonly insertTimestamp: (ownerId: OwnerIdBytes, timestamp: TimestampBytes) => Result<void, SqliteError>;
|
|
210
|
+
/** Inserts a timestamp for an owner into the skiplist-based storage. */
|
|
211
|
+
readonly insertTimestamp: (ownerId: OwnerIdBytes, timestamp: TimestampBytes, strategy: StorageInsertTimestampStrategy) => Result<void, SqliteError>;
|
|
218
212
|
/**
|
|
219
213
|
* Efficiently checks which timestamps already exist in the database using a
|
|
220
214
|
* single CTE query instead of N individual queries.
|
|
@@ -228,8 +222,42 @@ export type SqliteStorageDeps = RandomDep & SqliteDep;
|
|
|
228
222
|
export interface CreateBaseSqliteStorageConfig extends StorageConfig {
|
|
229
223
|
onStorageError: (error: SqliteError) => void;
|
|
230
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Creates a {@link BaseSqliteStorage} implementation.
|
|
227
|
+
*
|
|
228
|
+
* # Stateless Design
|
|
229
|
+
*
|
|
230
|
+
* This implementation is fully stateless - it requires no in-memory state
|
|
231
|
+
* between invocations. All necessary metadata (timestamp bounds for insertion
|
|
232
|
+
* strategy optimization) is persisted in the evolu_usage table. This makes
|
|
233
|
+
* Evolu Relay suitable for stateless serverless environments like AWS Lambda,
|
|
234
|
+
* Cloudflare Workers with Durable Objects, and other platforms where memory
|
|
235
|
+
* doesn't persist between requests. While not extensively tested in all these
|
|
236
|
+
* environments yet, the stateless design should work well across them.
|
|
237
|
+
*/
|
|
231
238
|
export declare const createBaseSqliteStorage: (deps: SqliteStorageDeps) => (config: CreateBaseSqliteStorageConfig) => BaseSqliteStorage;
|
|
232
239
|
export declare const createBaseSqliteStorageTables: (deps: SqliteDep) => Result<void, SqliteError>;
|
|
240
|
+
export type StorageInsertTimestampStrategy = "append" | "prepend" | "insert";
|
|
241
|
+
/**
|
|
242
|
+
* Determines the insertion strategy for a timestamp based on its position
|
|
243
|
+
* relative to the current first and last timestamps.
|
|
244
|
+
*
|
|
245
|
+
* Returns a tuple with the strategy and updated timestamp bounds.
|
|
246
|
+
*/
|
|
247
|
+
export declare const getTimestampInsertStrategy: (timestamp: TimestampBytes, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes) => [strategy: StorageInsertTimestampStrategy, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes];
|
|
233
248
|
export declare const timestampBytesToFingerprint: (timestamp: TimestampBytes) => Fingerprint;
|
|
234
249
|
export declare const getTimestampByIndex: (deps: SqliteDep) => (ownerId: OwnerIdBytes, index: NonNegativeInt) => Result<TimestampBytes, SqliteError>;
|
|
250
|
+
/** Retrieves usage information for an owner from the evolu_usage table. */
|
|
251
|
+
export declare const getOwnerUsage: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, initialTimestamp: TimestampBytes) => Result<{
|
|
252
|
+
storedBytes: NonNegativeInt | null;
|
|
253
|
+
firstTimestamp: TimestampBytes;
|
|
254
|
+
lastTimestamp: TimestampBytes;
|
|
255
|
+
}, SqliteError>;
|
|
256
|
+
/**
|
|
257
|
+
* Updates timestamp bounds in evolu_usage table.
|
|
258
|
+
*
|
|
259
|
+
* Used by both relay and client to maintain firstTimestamp/lastTimestamp after
|
|
260
|
+
* processing messages.
|
|
261
|
+
*/
|
|
262
|
+
export declare const updateOwnerUsage: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, storedBytes: PositiveInt, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes) => Result<void, SqliteError>;
|
|
235
263
|
//# sourceMappingURL=Storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Storage.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
2
|
+
import { firstInArray, isNonEmptyReadonlyArray, } from "../Array.js";
|
|
2
3
|
import { assert } from "../Assert.js";
|
|
3
4
|
import { concatBytes } from "../Buffer.js";
|
|
4
5
|
import { decrement } from "../Number.js";
|
|
5
6
|
import { ok } from "../Result.js";
|
|
6
7
|
import { sql, SqliteValue } from "../Sqlite.js";
|
|
7
8
|
import { Id, NonNegativeInt, object, PositiveInt, record, String, } from "../Type.js";
|
|
8
|
-
import { ownerIdBytesToOwnerId, } from "./Owner.js";
|
|
9
9
|
import { orderTimestampBytes } from "./Timestamp.js";
|
|
10
10
|
export const fingerprintSize = NonNegativeInt.orThrow(12);
|
|
11
11
|
/** A fingerprint of an empty range. */
|
|
@@ -25,10 +25,50 @@ export const DbChange = object({
|
|
|
25
25
|
id: Id,
|
|
26
26
|
values: record(String, SqliteValue),
|
|
27
27
|
});
|
|
28
|
+
/**
|
|
29
|
+
* Creates a {@link BaseSqliteStorage} implementation.
|
|
30
|
+
*
|
|
31
|
+
* # Stateless Design
|
|
32
|
+
*
|
|
33
|
+
* This implementation is fully stateless - it requires no in-memory state
|
|
34
|
+
* between invocations. All necessary metadata (timestamp bounds for insertion
|
|
35
|
+
* strategy optimization) is persisted in the evolu_usage table. This makes
|
|
36
|
+
* Evolu Relay suitable for stateless serverless environments like AWS Lambda,
|
|
37
|
+
* Cloudflare Workers with Durable Objects, and other platforms where memory
|
|
38
|
+
* doesn't persist between requests. While not extensively tested in all these
|
|
39
|
+
* environments yet, the stateless design should work well across them.
|
|
40
|
+
*/
|
|
28
41
|
export const createBaseSqliteStorage = (deps) => (config) => {
|
|
29
|
-
// TODO: Use evolu_usage table.
|
|
30
|
-
const ownerStats = new Map();
|
|
31
42
|
return {
|
|
43
|
+
insertTimestamp: (ownerId, timestamp, strategy) => {
|
|
44
|
+
const level = randomSkiplistLevel(deps);
|
|
45
|
+
return insertTimestamp(deps)(ownerId, timestamp, level, strategy);
|
|
46
|
+
},
|
|
47
|
+
getExistingTimestamps: (ownerIdBytes, timestampsBytes) => {
|
|
48
|
+
const concatenatedTimestamps = concatBytes(...timestampsBytes);
|
|
49
|
+
const result = deps.sqlite.exec(sql `
|
|
50
|
+
with recursive
|
|
51
|
+
split_timestamps(timestampBytes, pos) as (
|
|
52
|
+
select
|
|
53
|
+
substr(${concatenatedTimestamps}, 1, 16),
|
|
54
|
+
17 as pos
|
|
55
|
+
union all
|
|
56
|
+
select
|
|
57
|
+
substr(${concatenatedTimestamps}, pos, 16),
|
|
58
|
+
pos + 16
|
|
59
|
+
from split_timestamps
|
|
60
|
+
where pos <= length(${concatenatedTimestamps})
|
|
61
|
+
)
|
|
62
|
+
select s.timestampBytes
|
|
63
|
+
from
|
|
64
|
+
split_timestamps s
|
|
65
|
+
join evolu_timestamp t
|
|
66
|
+
on t.ownerId = ${ownerIdBytes} and s.timestampBytes = t.t;
|
|
67
|
+
`);
|
|
68
|
+
if (!result.ok)
|
|
69
|
+
return result;
|
|
70
|
+
return ok(result.value.rows.map((row) => row.timestampBytes));
|
|
71
|
+
},
|
|
32
72
|
getSize: (ownerId) => {
|
|
33
73
|
const size = getSize(deps)(ownerId);
|
|
34
74
|
if (!size.ok) {
|
|
@@ -114,63 +154,6 @@ export const createBaseSqliteStorage = (deps) => (config) => {
|
|
|
114
154
|
}
|
|
115
155
|
return true;
|
|
116
156
|
},
|
|
117
|
-
insertTimestamp: (ownerId, timestamp) => {
|
|
118
|
-
const ownerIdString = ownerIdBytesToOwnerId(ownerId);
|
|
119
|
-
const level = randomSkiplistLevel(deps);
|
|
120
|
-
let stats = ownerStats.get(ownerIdString);
|
|
121
|
-
if (!stats) {
|
|
122
|
-
const result = deps.sqlite.exec(sql.prepared `
|
|
123
|
-
select min(t) as minT, max(t) as maxT
|
|
124
|
-
from evolu_timestamp
|
|
125
|
-
where ownerId = ${ownerId};
|
|
126
|
-
`);
|
|
127
|
-
if (!result.ok)
|
|
128
|
-
return result;
|
|
129
|
-
stats = {
|
|
130
|
-
minT: result.value.rows[0].minT ?? timestamp,
|
|
131
|
-
maxT: result.value.rows[0].maxT ?? timestamp,
|
|
132
|
-
};
|
|
133
|
-
ownerStats.set(ownerIdString, stats);
|
|
134
|
-
}
|
|
135
|
-
let strategy;
|
|
136
|
-
if (orderTimestampBytes(timestamp, stats.maxT) === 1) {
|
|
137
|
-
strategy = "append";
|
|
138
|
-
stats.maxT = timestamp;
|
|
139
|
-
}
|
|
140
|
-
else if (orderTimestampBytes(timestamp, stats.minT) === -1) {
|
|
141
|
-
strategy = "prepend";
|
|
142
|
-
stats.minT = timestamp;
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
strategy = "insert";
|
|
146
|
-
}
|
|
147
|
-
return insertTimestamp(deps)(ownerId, timestamp, level, strategy);
|
|
148
|
-
},
|
|
149
|
-
getExistingTimestamps: (ownerIdBytes, timestampsBytes) => {
|
|
150
|
-
const concatenatedTimestamps = concatBytes(...timestampsBytes);
|
|
151
|
-
const result = deps.sqlite.exec(sql `
|
|
152
|
-
with recursive
|
|
153
|
-
split_timestamps(timestampBytes, pos) as (
|
|
154
|
-
select
|
|
155
|
-
substr(${concatenatedTimestamps}, 1, 16),
|
|
156
|
-
17 as pos
|
|
157
|
-
union all
|
|
158
|
-
select
|
|
159
|
-
substr(${concatenatedTimestamps}, pos, 16),
|
|
160
|
-
pos + 16
|
|
161
|
-
from split_timestamps
|
|
162
|
-
where pos <= length(${concatenatedTimestamps})
|
|
163
|
-
)
|
|
164
|
-
select s.timestampBytes
|
|
165
|
-
from
|
|
166
|
-
split_timestamps s
|
|
167
|
-
join evolu_timestamp t
|
|
168
|
-
on t.ownerId = ${ownerIdBytes} and s.timestampBytes = t.t;
|
|
169
|
-
`);
|
|
170
|
-
if (!result.ok)
|
|
171
|
-
return result;
|
|
172
|
-
return ok(result.value.rows.map((row) => row.timestampBytes));
|
|
173
|
-
},
|
|
174
157
|
};
|
|
175
158
|
};
|
|
176
159
|
const assertBeginEnd = (begin, end) => {
|
|
@@ -192,13 +175,7 @@ export const createBaseSqliteStorageTables = (deps) => {
|
|
|
192
175
|
* - `t` – TimestampBytes
|
|
193
176
|
* - `h1`/`h2` – 12-byte fingerprint split into two integers for fast XOR
|
|
194
177
|
* - `c` – incremental count
|
|
195
|
-
* - `l` – Skiplist level (1 to
|
|
196
|
-
*
|
|
197
|
-
* For scaling or isolation, sharding is possible—each owner can have a
|
|
198
|
-
* separate SQLite database.
|
|
199
|
-
*
|
|
200
|
-
* Maybe we could use an integer surrogate key for ownerId, but it's fast
|
|
201
|
-
* enough even without it.
|
|
178
|
+
* - `l` – Skiplist level (1 to 10)
|
|
202
179
|
*/
|
|
203
180
|
sql `
|
|
204
181
|
create table evolu_timestamp (
|
|
@@ -229,20 +206,15 @@ export const createBaseSqliteStorageTables = (deps) => {
|
|
|
229
206
|
*
|
|
230
207
|
* - `ownerId` – OwnerIdBytes (primary key)
|
|
231
208
|
* - `storedBytes` – total bytes stored in database
|
|
232
|
-
* - `
|
|
233
|
-
* - `
|
|
234
|
-
* - `firstTimestamp` – TODO: Decide how to use (nullable)
|
|
235
|
-
* - `lastTimestamp` – TODO: Decide how to use (nullable)
|
|
209
|
+
* - `firstTimestamp` – for timestamp insertion strategies
|
|
210
|
+
* - `lastTimestamp` – for timestamp insertion strategies
|
|
236
211
|
*/
|
|
237
212
|
sql `
|
|
238
213
|
create table evolu_usage (
|
|
239
214
|
"ownerId" blob primary key,
|
|
240
|
-
"storedBytes" integer not null
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
-- "sentBytes" integer not null,
|
|
244
|
-
-- "firstTimestamp" blob,
|
|
245
|
-
-- "lastTimestamp" blob
|
|
215
|
+
"storedBytes" integer not null,
|
|
216
|
+
"firstTimestamp" blob,
|
|
217
|
+
"lastTimestamp" blob
|
|
246
218
|
)
|
|
247
219
|
strict;
|
|
248
220
|
`,
|
|
@@ -253,14 +225,35 @@ export const createBaseSqliteStorageTables = (deps) => {
|
|
|
253
225
|
}
|
|
254
226
|
return ok();
|
|
255
227
|
};
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Determines the insertion strategy for a timestamp based on its position
|
|
230
|
+
* relative to the current first and last timestamps.
|
|
231
|
+
*
|
|
232
|
+
* Returns a tuple with the strategy and updated timestamp bounds.
|
|
233
|
+
*/
|
|
234
|
+
export const getTimestampInsertStrategy = (timestamp, firstTimestamp, lastTimestamp) => {
|
|
235
|
+
if (orderTimestampBytes(timestamp, lastTimestamp) === 1) {
|
|
236
|
+
return ["append", firstTimestamp, timestamp];
|
|
237
|
+
}
|
|
238
|
+
if (orderTimestampBytes(timestamp, firstTimestamp) === -1) {
|
|
239
|
+
return ["prepend", timestamp, lastTimestamp];
|
|
240
|
+
}
|
|
241
|
+
return ["insert", firstTimestamp, lastTimestamp];
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* AFAIK, we can't do both insert and update in one query, and that's probably
|
|
245
|
+
* why append is 2x faster than insert. Prepend also has to update parents, but
|
|
246
|
+
* it's constantly fast. Insert degrades for reversed (yet LIMIT X magically
|
|
247
|
+
* fixes that) but it's OK for append.
|
|
248
|
+
*
|
|
249
|
+
* Note: SQL operations are idempotent (using `on conflict do nothing` and
|
|
250
|
+
* `changes() > 0`), but this is no longer required here since we use
|
|
251
|
+
* {@link BaseSqliteStorage.getExistingTimestamps} to filter out duplicates
|
|
252
|
+
* before insertion, which we need for quota checks anyway.
|
|
253
|
+
*
|
|
254
|
+
* TODO: Remove idempotency (`on conflict do nothing` and `changes() > 0`) since
|
|
255
|
+
* duplicates are now filtered before insertion.
|
|
256
|
+
*/
|
|
264
257
|
const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
265
258
|
const [h1, h2] = fingerprintToSqliteFingerprint(timestampBytesToFingerprint(timestamp));
|
|
266
259
|
let queries = [];
|
|
@@ -1167,3 +1160,50 @@ export const getTimestampByIndex = (deps) => (ownerId, index) => {
|
|
|
1167
1160
|
return result;
|
|
1168
1161
|
return ok(result.value.rows[0].pt);
|
|
1169
1162
|
};
|
|
1163
|
+
/** Retrieves usage information for an owner from the evolu_usage table. */
|
|
1164
|
+
export const getOwnerUsage = (deps) => (ownerIdBytes, initialTimestamp) => {
|
|
1165
|
+
const result = deps.sqlite.exec(sql `
|
|
1166
|
+
select storedBytes, firstTimestamp, lastTimestamp
|
|
1167
|
+
from evolu_usage
|
|
1168
|
+
where ownerId = ${ownerIdBytes};
|
|
1169
|
+
`);
|
|
1170
|
+
if (!result.ok)
|
|
1171
|
+
return result;
|
|
1172
|
+
if (!isNonEmptyReadonlyArray(result.value.rows)) {
|
|
1173
|
+
return ok({
|
|
1174
|
+
storedBytes: null,
|
|
1175
|
+
firstTimestamp: initialTimestamp,
|
|
1176
|
+
lastTimestamp: initialTimestamp,
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
const row = firstInArray(result.value.rows);
|
|
1180
|
+
assert(row.firstTimestamp, "not null");
|
|
1181
|
+
assert(row.lastTimestamp, "not null");
|
|
1182
|
+
return ok({
|
|
1183
|
+
storedBytes: row.storedBytes,
|
|
1184
|
+
firstTimestamp: row.firstTimestamp,
|
|
1185
|
+
lastTimestamp: row.lastTimestamp,
|
|
1186
|
+
});
|
|
1187
|
+
};
|
|
1188
|
+
/**
|
|
1189
|
+
* Updates timestamp bounds in evolu_usage table.
|
|
1190
|
+
*
|
|
1191
|
+
* Used by both relay and client to maintain firstTimestamp/lastTimestamp after
|
|
1192
|
+
* processing messages.
|
|
1193
|
+
*/
|
|
1194
|
+
export const updateOwnerUsage = (deps) => (ownerIdBytes, storedBytes, firstTimestamp, lastTimestamp) => {
|
|
1195
|
+
const result = deps.sqlite.exec(sql `
|
|
1196
|
+
insert into evolu_usage
|
|
1197
|
+
("ownerId", "storedBytes", "firstTimestamp", "lastTimestamp")
|
|
1198
|
+
values
|
|
1199
|
+
(${ownerIdBytes}, ${storedBytes}, ${firstTimestamp}, ${lastTimestamp})
|
|
1200
|
+
on conflict (ownerId) do update
|
|
1201
|
+
set
|
|
1202
|
+
storedBytes = ${storedBytes},
|
|
1203
|
+
firstTimestamp = ${firstTimestamp},
|
|
1204
|
+
lastTimestamp = ${lastTimestamp};
|
|
1205
|
+
`);
|
|
1206
|
+
if (!result.ok)
|
|
1207
|
+
return result;
|
|
1208
|
+
return ok();
|
|
1209
|
+
};
|
package/dist/src/Evolu/Sync.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { PostMessageDep } from "./Db.js";
|
|
|
11
11
|
import { AppOwner, OwnerEncryptionKey, OwnerId, OwnerIdBytes, OwnerTransport, OwnerWriteKey } from "./Owner.js";
|
|
12
12
|
import { ProtocolError, ProtocolInvalidDataError, ProtocolTimestampMismatchError } from "./Protocol.js";
|
|
13
13
|
import { MutationChange } from "./Schema.js";
|
|
14
|
-
import { BaseSqliteStorage, CrdtMessage, Storage } from "./Storage.js";
|
|
14
|
+
import { BaseSqliteStorage, CrdtMessage, Storage, StorageInsertTimestampStrategy } from "./Storage.js";
|
|
15
15
|
import { Millis, Timestamp, TimestampConfigDep, TimestampCounterOverflowError, TimestampDriftError, TimestampTimeOutOfRangeError } from "./Timestamp.js";
|
|
16
16
|
export interface Sync extends Disposable {
|
|
17
17
|
/**
|
|
@@ -68,8 +68,7 @@ export interface ClientStorageDep {
|
|
|
68
68
|
readonly storage: ClientStorage;
|
|
69
69
|
}
|
|
70
70
|
export declare const applyLocalOnlyChange: (deps: SqliteDep & TimeDep) => (change: MutationChange) => Result<void, SqliteError>;
|
|
71
|
-
export declare const
|
|
72
|
-
export declare const applyMessageToTimestampAndHistoryTables: (deps: ClientStorageDep & SqliteDep) => (ownerId: OwnerIdBytes, message: CrdtMessage) => Result<void, SqliteError>;
|
|
71
|
+
export declare const applyMessageToTimestampAndHistoryTables: (deps: ClientStorageDep & SqliteDep) => (ownerId: OwnerIdBytes, message: CrdtMessage, strategy: StorageInsertTimestampStrategy) => Result<void, SqliteError>;
|
|
73
72
|
/**
|
|
74
73
|
* TODO: Rework for the new owners API.
|
|
75
74
|
*
|