@evolu/common 6.0.1-preview.2 → 6.0.1-preview.21
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 +7 -4
- package/dist/src/Brand.d.ts +75 -0
- package/dist/src/Brand.d.ts.map +1 -0
- package/dist/src/Brand.js +1 -0
- package/dist/src/Buffer.d.ts +1 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +1 -1
- package/dist/src/CallbackRegistry.d.ts +53 -0
- package/dist/src/CallbackRegistry.d.ts.map +1 -0
- package/dist/src/CallbackRegistry.js +25 -0
- package/dist/src/Console.d.ts +31 -6
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +72 -9
- package/dist/src/Crypto.d.ts +61 -34
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +29 -42
- package/dist/src/Evolu/Db.d.ts +159 -66
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +300 -705
- package/dist/src/Evolu/Diff.d.ts +3 -3
- package/dist/src/Evolu/Diff.d.ts.map +1 -1
- package/dist/src/Evolu/Diff.js +7 -5
- package/dist/src/Evolu/Evolu.d.ts +200 -115
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +287 -157
- package/dist/src/Evolu/Internal.d.ts +0 -2
- package/dist/src/Evolu/Internal.d.ts.map +1 -1
- package/dist/src/Evolu/Internal.js +0 -2
- package/dist/src/Evolu/LocalAuth.d.ts +144 -0
- package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
- package/dist/src/Evolu/LocalAuth.js +171 -0
- package/dist/src/Evolu/Owner.d.ts +209 -124
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +118 -104
- package/dist/src/Evolu/Platform.d.ts +9 -7
- package/dist/src/Evolu/Platform.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.d.ts +256 -211
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +558 -359
- package/dist/src/Evolu/Public.d.ts +6 -8
- package/dist/src/Evolu/Public.d.ts.map +1 -1
- package/dist/src/Evolu/Public.js +2 -3
- package/dist/src/Evolu/PublicKysely.js +3 -3
- package/dist/src/Evolu/Query.d.ts +2 -1
- package/dist/src/Evolu/Query.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.d.ts +72 -4
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +124 -13
- package/dist/src/Evolu/Schema.d.ts +129 -73
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.js +169 -89
- package/dist/src/Evolu/Storage.d.ts +159 -14
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +42 -32
- package/dist/src/Evolu/Sync.d.ts +68 -13
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +414 -20
- package/dist/src/Evolu/Timestamp.d.ts +35 -27
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +27 -18
- package/dist/src/ManyToManyMap.d.ts +74 -10
- package/dist/src/ManyToManyMap.d.ts.map +1 -1
- package/dist/src/ManyToManyMap.js +41 -6
- package/dist/src/Number.d.ts +2 -1
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Random.d.ts +3 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/RefCountedResourceManager.d.ts +119 -0
- package/dist/src/RefCountedResourceManager.d.ts.map +1 -0
- package/dist/src/RefCountedResourceManager.js +197 -0
- package/dist/src/Result.d.ts +176 -51
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +30 -241
- package/dist/src/Sqlite.d.ts +21 -4
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +50 -8
- package/dist/src/Task.d.ts +511 -0
- package/dist/src/Task.d.ts.map +1 -0
- package/dist/src/Task.js +410 -0
- package/dist/src/Time.d.ts +59 -0
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +87 -4
- package/dist/src/Type.d.ts +474 -327
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +502 -436
- package/dist/src/Types.d.ts +0 -74
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/WebSocket.d.ts +5 -2
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +12 -13
- package/dist/src/Worker.d.ts +39 -11
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +22 -4
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -3
- package/package.json +14 -13
- package/src/Assert.ts +8 -7
- package/src/Brand.ts +75 -0
- package/src/Buffer.ts +1 -1
- package/src/CallbackRegistry.ts +84 -0
- package/src/Console.ts +91 -11
- package/src/Crypto.ts +94 -79
- package/src/Evolu/Db.ts +528 -1027
- package/src/Evolu/Diff.ts +7 -5
- package/src/Evolu/Evolu.ts +682 -329
- package/src/Evolu/Internal.ts +0 -2
- package/src/Evolu/LocalAuth.ts +422 -0
- package/src/Evolu/Owner.ts +282 -227
- package/src/Evolu/Platform.ts +9 -9
- package/src/Evolu/Protocol.ts +788 -640
- package/src/Evolu/Public.ts +7 -14
- package/src/Evolu/PublicKysely.ts +3 -3
- package/src/Evolu/Query.ts +2 -1
- package/src/Evolu/Relay.ts +238 -18
- package/src/Evolu/Schema.ts +391 -191
- package/src/Evolu/Storage.ts +275 -55
- package/src/Evolu/Sync.ts +708 -37
- package/src/Evolu/Timestamp.ts +38 -35
- package/src/ManyToManyMap.ts +127 -24
- package/src/Number.ts +2 -6
- package/src/Random.ts +3 -2
- package/src/RefCountedResourceManager.ts +368 -0
- package/src/Result.ts +183 -53
- package/src/Sqlite.ts +59 -23
- package/src/Task.ts +779 -0
- package/src/Time.ts +168 -4
- package/src/Type.ts +708 -660
- package/src/Types.ts +0 -76
- package/src/WebSocket.ts +23 -17
- package/src/Worker.ts +72 -23
- package/src/index.ts +3 -3
- package/dist/src/Callbacks.d.ts +0 -20
- package/dist/src/Callbacks.d.ts.map +0 -1
- package/dist/src/Callbacks.js +0 -18
- package/dist/src/Evolu/Config.d.ts +0 -69
- package/dist/src/Evolu/Config.d.ts.map +0 -1
- package/dist/src/Evolu/Config.js +0 -9
- package/dist/src/Evolu/Kysely.d.ts +0 -6
- package/dist/src/Evolu/Kysely.d.ts.map +0 -1
- package/dist/src/Evolu/Kysely.js +0 -21
- package/dist/src/NanoId.d.ts +0 -27
- package/dist/src/NanoId.d.ts.map +0 -1
- package/dist/src/NanoId.js +0 -6
- package/dist/src/Promise.d.ts +0 -180
- package/dist/src/Promise.d.ts.map +0 -1
- package/dist/src/Promise.js +0 -176
- package/src/Callbacks.ts +0 -43
- package/src/Evolu/Config.ts +0 -83
- package/src/Evolu/Kysely.ts +0 -38
- package/src/NanoId.ts +0 -39
- package/src/Promise.ts +0 -295
|
@@ -1,10 +1,155 @@
|
|
|
1
|
+
import { NonEmptyReadonlyArray } from "../Array.js";
|
|
2
|
+
import { Brand } from "../Brand.js";
|
|
3
|
+
import { RandomDep } from "../Random.js";
|
|
4
|
+
import { Result } from "../Result.js";
|
|
5
|
+
import { SqliteDep, SqliteError } from "../Sqlite.js";
|
|
6
|
+
import { NonNegativeInt } from "../Type.js";
|
|
7
|
+
import { OwnerIdBytes, OwnerWriteKey } from "./Owner.js";
|
|
8
|
+
import { Timestamp, TimestampBytes } from "./Timestamp.js";
|
|
9
|
+
/**
|
|
10
|
+
* Evolu Storage
|
|
11
|
+
*
|
|
12
|
+
* The protocol using Storage is agnostic to storage implementation details—any
|
|
13
|
+
* storage can be plugged in, as long as it implements this interface.
|
|
14
|
+
* Implementations must handle their own errors; return values only indicate
|
|
15
|
+
* overall success or failure.
|
|
16
|
+
*
|
|
17
|
+
* The Storage API is synchronous because SQLite's synchronous API is the
|
|
18
|
+
* fastest way to use SQLite. Synchronous bindings (like better-sqlite3) call
|
|
19
|
+
* SQLite's C API directly with no context switching between the event loop and
|
|
20
|
+
* native code, and no promise microtasks or await overhead.
|
|
21
|
+
*
|
|
22
|
+
* The only exception is {@link Storage#writeMessages}, which is async to allow
|
|
23
|
+
* for async validation logic before writing to storage. The write operation
|
|
24
|
+
* itself remains synchronous.
|
|
25
|
+
*/
|
|
26
|
+
export interface Storage {
|
|
27
|
+
readonly getSize: (ownerId: OwnerIdBytes) => NonNegativeInt | null;
|
|
28
|
+
readonly fingerprint: (ownerId: OwnerIdBytes, begin: NonNegativeInt, end: NonNegativeInt) => Fingerprint | null;
|
|
29
|
+
/**
|
|
30
|
+
* Computes fingerprints with their upper bounds in one call.
|
|
31
|
+
*
|
|
32
|
+
* This function can be replaced with many fingerprint/findLowerBound calls,
|
|
33
|
+
* but implementations can leverage it for batching and more efficient
|
|
34
|
+
* fingerprint computation.
|
|
35
|
+
*/
|
|
36
|
+
readonly fingerprintRanges: (ownerId: OwnerIdBytes, buckets: ReadonlyArray<NonNegativeInt>, upperBound?: RangeUpperBound) => ReadonlyArray<FingerprintRange> | null;
|
|
37
|
+
readonly findLowerBound: (ownerId: OwnerIdBytes, begin: NonNegativeInt, end: NonNegativeInt, upperBound: RangeUpperBound) => NonNegativeInt | null;
|
|
38
|
+
readonly iterate: (ownerId: OwnerIdBytes, begin: NonNegativeInt, end: NonNegativeInt, callback: (timestamp: TimestampBytes, index: NonNegativeInt) => boolean) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Validates the {@link OwnerWriteKey} for the given {@link Owner}.
|
|
41
|
+
*
|
|
42
|
+
* Returns `true` if the write key is valid, `false` otherwise.
|
|
43
|
+
*/
|
|
44
|
+
readonly validateWriteKey: (ownerId: OwnerIdBytes, writeKey: OwnerWriteKey) => boolean;
|
|
45
|
+
/** Sets the {@link OwnerWriteKey} for the given {@link Owner}. */
|
|
46
|
+
readonly setWriteKey: (ownerId: OwnerIdBytes, writeKey: OwnerWriteKey) => boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Write encrypted {@link CrdtMessage}s to storage.
|
|
49
|
+
*
|
|
50
|
+
* Must use a mutex (per ownerId on Relay) to ensure sequential processing and
|
|
51
|
+
* proper protocol logic handling during sync operations.
|
|
52
|
+
*
|
|
53
|
+
* Returns `true` on success, `false` on failure.
|
|
54
|
+
*/
|
|
55
|
+
readonly writeMessages: (ownerId: OwnerIdBytes, messages: NonEmptyReadonlyArray<EncryptedCrdtMessage>) => Promise<boolean>;
|
|
56
|
+
/** Read encrypted {@link DbChange}s from storage. */
|
|
57
|
+
readonly readDbChange: (ownerId: OwnerIdBytes, timestamp: TimestampBytes) => EncryptedDbChange | null;
|
|
58
|
+
/**
|
|
59
|
+
* Delete all data for the given {@link Owner}.
|
|
60
|
+
*
|
|
61
|
+
* Returns `true` on success, `false` on failure.
|
|
62
|
+
*/
|
|
63
|
+
readonly deleteOwner: (ownerId: OwnerIdBytes) => boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface StorageDep {
|
|
66
|
+
readonly storage: Storage;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A cryptographic hash used for efficiently comparing collections of
|
|
70
|
+
* {@link TimestampBytes}s.
|
|
71
|
+
*
|
|
72
|
+
* It consists of the first {@link fingerprintSize} bytes of the SHA-256 hash of
|
|
73
|
+
* one or more timestamps.
|
|
74
|
+
*/
|
|
75
|
+
export type Fingerprint = Uint8Array & Brand<"Fingerprint">;
|
|
76
|
+
export declare const fingerprintSize: NonNegativeInt;
|
|
77
|
+
/** A fingerprint of an empty range. */
|
|
78
|
+
export declare const zeroFingerprint: Fingerprint;
|
|
79
|
+
export interface BaseRange {
|
|
80
|
+
readonly upperBound: RangeUpperBound;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Union type for Range's upperBound: either a {@link TimestampBytes} or
|
|
84
|
+
* {@link InfiniteUpperBound}.
|
|
85
|
+
*/
|
|
86
|
+
export type RangeUpperBound = TimestampBytes | InfiniteUpperBound;
|
|
87
|
+
export declare const InfiniteUpperBound: unique symbol;
|
|
88
|
+
export type InfiniteUpperBound = typeof InfiniteUpperBound;
|
|
89
|
+
export declare const RangeType: {
|
|
90
|
+
readonly Fingerprint: 1;
|
|
91
|
+
readonly Skip: 0;
|
|
92
|
+
readonly Timestamps: 2;
|
|
93
|
+
};
|
|
94
|
+
export type RangeType = (typeof RangeType)[keyof typeof RangeType];
|
|
95
|
+
export interface SkipRange extends BaseRange {
|
|
96
|
+
readonly type: typeof RangeType.Skip;
|
|
97
|
+
}
|
|
98
|
+
export interface FingerprintRange extends BaseRange {
|
|
99
|
+
readonly type: typeof RangeType.Fingerprint;
|
|
100
|
+
readonly fingerprint: Fingerprint;
|
|
101
|
+
}
|
|
102
|
+
export interface TimestampsRange extends BaseRange {
|
|
103
|
+
readonly type: typeof RangeType.Timestamps;
|
|
104
|
+
readonly timestamps: ReadonlyArray<TimestampBytes>;
|
|
105
|
+
}
|
|
106
|
+
export type Range = SkipRange | FingerprintRange | TimestampsRange;
|
|
107
|
+
/** An encrypted {@link CrdtMessage}. */
|
|
108
|
+
export interface EncryptedCrdtMessage {
|
|
109
|
+
readonly timestamp: Timestamp;
|
|
110
|
+
readonly change: EncryptedDbChange;
|
|
111
|
+
}
|
|
112
|
+
/** Encrypted DbChange */
|
|
113
|
+
export type EncryptedDbChange = Uint8Array & Brand<"EncryptedDbChange">;
|
|
1
114
|
/**
|
|
2
|
-
*
|
|
115
|
+
* A CRDT message combining a unique {@link Timestamp} with a {@link DbChange}.
|
|
116
|
+
*
|
|
117
|
+
* Used in Evolu's sync protocol to replicate data changes across devices. Evolu
|
|
118
|
+
* operates as a durable queue, providing exactly-once delivery guarantees for
|
|
119
|
+
* reliable synchronization across application restarts and network failures.
|
|
120
|
+
*/
|
|
121
|
+
export interface CrdtMessage {
|
|
122
|
+
readonly timestamp: Timestamp;
|
|
123
|
+
readonly change: DbChange;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A DbChange is a change to a table row. Together with a unique
|
|
127
|
+
* {@link Timestamp}, it forms a {@link CrdtMessage}.
|
|
128
|
+
*/
|
|
129
|
+
export declare const DbChange: import("../Type.js").ObjectType<{
|
|
130
|
+
table: import("../Type.js").Type<"String", string, string, import("../Type.js").StringError, string, import("../Type.js").StringError>;
|
|
131
|
+
id: import("../Type.js").BrandType<import("../Type.js").Type<"String", string, string, import("../Type.js").StringError, string, import("../Type.js").StringError>, "Id", import("../Type.js").IdError, import("../Type.js").StringError>;
|
|
132
|
+
values: import("../Type.js").RecordType<"String", string, string, import("../Type.js").StringError, string, import("../Type.js").StringError, import("../Type.js").UnionType<[import("../Type.js").Type<"Null", null, null, import("../Type.js").NullError, null, import("../Type.js").NullError>, import("../Type.js").Type<"String", string, string, import("../Type.js").StringError, string, import("../Type.js").StringError>, import("../Type.js").Type<"Number", number, number, import("../Type.js").NumberError, number, import("../Type.js").NumberError>, import("../Type.js").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.js").Uint8ArrayError, Uint8Array<ArrayBufferLike>, import("../Type.js").Uint8ArrayError>]>>;
|
|
133
|
+
}>;
|
|
134
|
+
export type DbChange = typeof DbChange.Type;
|
|
135
|
+
/**
|
|
136
|
+
* Common interface for both client and relay SQLite storages.
|
|
3
137
|
*
|
|
4
138
|
* Evolu uses a Skiplist, which leverages SQLite indexes. The core logic is
|
|
5
139
|
* implemented in SQL, so it doesn't have to make roundtrips to the DB.
|
|
6
140
|
*
|
|
7
|
-
*
|
|
141
|
+
* While the SQL implementation may look sophisticated, it's conceptually simple
|
|
142
|
+
* and LLMs can explain how it works. The Skiplist data structure is well
|
|
143
|
+
* explained in [this Stack Overflow
|
|
144
|
+
* answer](https://stackoverflow.com/questions/61944198/what-is-a-zip-tree-and-how-does-it-work).
|
|
145
|
+
* The logic resembles [Negentropy's C++
|
|
146
|
+
* storage](https://github.com/hoytech/negentropy), except we use a Skiplist to
|
|
147
|
+
* leverage SQLite indexes, which makes the code simpler.
|
|
148
|
+
*
|
|
149
|
+
* Note: A paid review by the SQLite team is planned, as they use the same
|
|
150
|
+
* algorithm for their rsync tool.
|
|
151
|
+
*
|
|
152
|
+
* The ideal storage for a Relay should use an architecture like
|
|
8
153
|
* [strfry](https://github.com/hoytech/strfry) (a KV storage), but with Skiplist
|
|
9
154
|
* to ensure that insertion order doesn't matter (local-first apps can often
|
|
10
155
|
* write in the past.)
|
|
@@ -19,31 +164,31 @@
|
|
|
19
164
|
* each other, if necessary. One relay should handle hundreds of thousands of
|
|
20
165
|
* users, and when it goes down, nothing happens, because it will be
|
|
21
166
|
* synchronized later.
|
|
22
|
-
*
|
|
23
|
-
* @module
|
|
24
167
|
*/
|
|
25
|
-
import { RandomDep } from "../Random.js";
|
|
26
|
-
import { Result } from "../Result.js";
|
|
27
|
-
import { SqliteDep, SqliteError } from "../Sqlite.js";
|
|
28
|
-
import { NonNegativeInt } from "../Type.js";
|
|
29
|
-
import { BinaryOwnerId, Storage } from "./Protocol.js";
|
|
30
|
-
import { BinaryTimestamp } from "./Timestamp.js";
|
|
31
|
-
/** Common interface for both client and relay SQLite storages. */
|
|
32
168
|
export interface SqliteStorageBase {
|
|
33
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Inserts a timestamp for an owner into the skiplist-based storage.
|
|
171
|
+
*
|
|
172
|
+
* Must be idempotent - inserting the same timestamp multiple times has no
|
|
173
|
+
* effect after the first insertion. This is crucial for sync reliability as
|
|
174
|
+
* messages may be received and processed multiple times.
|
|
175
|
+
*/
|
|
176
|
+
readonly insertTimestamp: (ownerId: OwnerIdBytes, timestamp: TimestampBytes) => Result<void, SqliteError>;
|
|
34
177
|
readonly getSize: Storage["getSize"];
|
|
35
178
|
readonly fingerprint: Storage["fingerprint"];
|
|
36
179
|
readonly fingerprintRanges: Storage["fingerprintRanges"];
|
|
37
180
|
readonly findLowerBound: Storage["findLowerBound"];
|
|
38
181
|
readonly iterate: Storage["iterate"];
|
|
182
|
+
readonly deleteOwner: Storage["deleteOwner"];
|
|
39
183
|
}
|
|
40
184
|
export interface SqliteStorageBaseDep {
|
|
41
185
|
readonly storage: SqliteStorageBase;
|
|
42
186
|
}
|
|
43
|
-
export type SqliteStorageDeps =
|
|
187
|
+
export type SqliteStorageDeps = RandomDep & SqliteDep;
|
|
44
188
|
export interface CreateSqliteStorageBaseOptions {
|
|
45
189
|
onStorageError: (error: SqliteError) => void;
|
|
46
190
|
}
|
|
47
191
|
export declare const createSqliteStorageBase: (deps: SqliteStorageDeps) => (options: CreateSqliteStorageBaseOptions) => Result<SqliteStorageBase, SqliteError>;
|
|
48
|
-
export declare const
|
|
192
|
+
export declare const timestampBytesToFingerprint: (timestamp: TimestampBytes) => Fingerprint;
|
|
193
|
+
export declare const getTimestampByIndex: (deps: SqliteDep) => (ownerId: OwnerIdBytes, index: NonNegativeInt) => Result<TimestampBytes, SqliteError>;
|
|
49
194
|
//# sourceMappingURL=Storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,33 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
* Evolu Storage for SQLite
|
|
3
|
-
*
|
|
4
|
-
* Evolu uses a Skiplist, which leverages SQLite indexes. The core logic is
|
|
5
|
-
* implemented in SQL, so it doesn't have to make roundtrips to the DB.
|
|
6
|
-
*
|
|
7
|
-
* The ideal storage for a Relay should use a similar architecture to
|
|
8
|
-
* [strfry](https://github.com/hoytech/strfry) (a KV storage), but with Skiplist
|
|
9
|
-
* to ensure that insertion order doesn't matter (local-first apps can often
|
|
10
|
-
* write in the past.)
|
|
11
|
-
*
|
|
12
|
-
* The ideal client implementation should probably use the SQLite extension
|
|
13
|
-
* instead of SQL or even a KV storage, when such a thing for browsers/native
|
|
14
|
-
* will exist and will be faster than SQLite.
|
|
15
|
-
*
|
|
16
|
-
* # Scaling
|
|
17
|
-
*
|
|
18
|
-
* The load can be distributed by deploying multiple relays, synchronized with
|
|
19
|
-
* each other, if necessary. One relay should handle hundreds of thousands of
|
|
20
|
-
* users, and when it goes down, nothing happens, because it will be
|
|
21
|
-
* synchronized later.
|
|
22
|
-
*
|
|
23
|
-
* @module
|
|
24
|
-
*/
|
|
1
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
25
2
|
import { assert } from "../Assert.js";
|
|
26
3
|
import { decrement } from "../Number.js";
|
|
27
4
|
import { ok } from "../Result.js";
|
|
28
|
-
import { sql } from "../Sqlite.js";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
5
|
+
import { sql, SqliteValue } from "../Sqlite.js";
|
|
6
|
+
import { Id, object, record, String, } from "../Type.js";
|
|
7
|
+
import { ownerIdBytesToOwnerId, } from "./Owner.js";
|
|
8
|
+
import { orderTimestampBytes } from "./Timestamp.js";
|
|
9
|
+
export const fingerprintSize = 12;
|
|
10
|
+
/** A fingerprint of an empty range. */
|
|
11
|
+
export const zeroFingerprint = new Uint8Array(fingerprintSize);
|
|
12
|
+
export const InfiniteUpperBound = Symbol("InfiniteUpperBound");
|
|
13
|
+
export const RangeType = {
|
|
14
|
+
Fingerprint: 1,
|
|
15
|
+
Skip: 0,
|
|
16
|
+
Timestamps: 2,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A DbChange is a change to a table row. Together with a unique
|
|
20
|
+
* {@link Timestamp}, it forms a {@link CrdtMessage}.
|
|
21
|
+
*/
|
|
22
|
+
export const DbChange = object({
|
|
23
|
+
table: String,
|
|
24
|
+
id: Id,
|
|
25
|
+
values: record(String, SqliteValue),
|
|
26
|
+
});
|
|
31
27
|
export const createSqliteStorageBase = (deps) => (options) => {
|
|
32
28
|
const createTablesResult = createTables(deps);
|
|
33
29
|
if (!createTablesResult.ok)
|
|
@@ -35,7 +31,7 @@ export const createSqliteStorageBase = (deps) => (options) => {
|
|
|
35
31
|
const ownerStats = new Map();
|
|
36
32
|
return ok({
|
|
37
33
|
insertTimestamp: (ownerId, timestamp) => {
|
|
38
|
-
const ownerIdString =
|
|
34
|
+
const ownerIdString = ownerIdBytesToOwnerId(ownerId);
|
|
39
35
|
const level = randomSkiplistLevel(deps);
|
|
40
36
|
let stats = ownerStats.get(ownerIdString);
|
|
41
37
|
if (!stats) {
|
|
@@ -53,11 +49,11 @@ export const createSqliteStorageBase = (deps) => (options) => {
|
|
|
53
49
|
ownerStats.set(ownerIdString, stats);
|
|
54
50
|
}
|
|
55
51
|
let strategy;
|
|
56
|
-
if (
|
|
52
|
+
if (orderTimestampBytes(timestamp, stats.maxT) === 1) {
|
|
57
53
|
strategy = "append";
|
|
58
54
|
stats.maxT = timestamp;
|
|
59
55
|
}
|
|
60
|
-
else if (
|
|
56
|
+
else if (orderTimestampBytes(timestamp, stats.minT) === -1) {
|
|
61
57
|
strategy = "prepend";
|
|
62
58
|
stats.minT = timestamp;
|
|
63
59
|
}
|
|
@@ -141,6 +137,16 @@ export const createSqliteStorageBase = (deps) => (options) => {
|
|
|
141
137
|
return;
|
|
142
138
|
}
|
|
143
139
|
},
|
|
140
|
+
deleteOwner: (ownerId) => {
|
|
141
|
+
const result = deps.sqlite.exec(sql `
|
|
142
|
+
delete from evolu_timestamp where ownerId = ${ownerId};
|
|
143
|
+
`);
|
|
144
|
+
if (!result.ok) {
|
|
145
|
+
options.onStorageError(result.error);
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
},
|
|
144
150
|
});
|
|
145
151
|
};
|
|
146
152
|
const assertBeginEnd = (begin, end) => {
|
|
@@ -159,7 +165,7 @@ const createTables = (deps) => {
|
|
|
159
165
|
*
|
|
160
166
|
* Columns:
|
|
161
167
|
*
|
|
162
|
-
* - `t` –
|
|
168
|
+
* - `t` – TimestampBytes
|
|
163
169
|
* - `h1`/`h2` – 12-byte fingerprint split into two integers for fast XOR
|
|
164
170
|
* - `c` – incremental count
|
|
165
171
|
* - `l` – Skiplist level (1 to 32)
|
|
@@ -208,7 +214,7 @@ const createTables = (deps) => {
|
|
|
208
214
|
// they will prepend. They are always sorted in ascending order by the
|
|
209
215
|
// Protocol.
|
|
210
216
|
const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
211
|
-
const [h1, h2] = fingerprintToSqliteFingerprint(
|
|
217
|
+
const [h1, h2] = fingerprintToSqliteFingerprint(timestampBytesToFingerprint(timestamp));
|
|
212
218
|
let queries = [];
|
|
213
219
|
switch (strategy) {
|
|
214
220
|
case "append":
|
|
@@ -717,6 +723,10 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
717
723
|
}
|
|
718
724
|
return ok();
|
|
719
725
|
};
|
|
726
|
+
export const timestampBytesToFingerprint = (timestamp) => {
|
|
727
|
+
const hash = sha256(timestamp).slice(0, fingerprintSize);
|
|
728
|
+
return hash;
|
|
729
|
+
};
|
|
720
730
|
/**
|
|
721
731
|
* Generates a random skiplist level in the range [1, skiplistMaxLevel].
|
|
722
732
|
* Probabilistic approach avoids the need for explicit tree balancing.
|
package/dist/src/Evolu/Sync.d.ts
CHANGED
|
@@ -1,23 +1,78 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { NonEmptyReadonlyArray } from "../Array.js";
|
|
2
|
+
import { ConsoleDep } from "../Console.js";
|
|
3
|
+
import { RandomBytesDep, SymmetricCryptoDecryptError, SymmetricCryptoDep } from "../Crypto.js";
|
|
4
|
+
import { TransferableError } from "../Error.js";
|
|
5
|
+
import { RandomDep } from "../Random.js";
|
|
6
|
+
import { Result } from "../Result.js";
|
|
7
|
+
import { SqliteDep, SqliteError } from "../Sqlite.js";
|
|
8
|
+
import { TimeDep } from "../Time.js";
|
|
9
|
+
import { CreateWebSocketDep } from "../WebSocket.js";
|
|
10
|
+
import type { PostMessageDep } from "./Db.js";
|
|
11
|
+
import { AppOwner, OwnerEncryptionKey, OwnerId, OwnerIdBytes, OwnerWriteKey, TransportConfig } from "./Owner.js";
|
|
12
|
+
import { ProtocolError, ProtocolInvalidDataError, ProtocolTimestampMismatchError } from "./Protocol.js";
|
|
13
|
+
import { MutationChange } from "./Schema.js";
|
|
14
|
+
import { CrdtMessage, SqliteStorageBase, Storage } from "./Storage.js";
|
|
15
|
+
import { Millis, Timestamp, TimestampConfigDep, TimestampCounterOverflowError, TimestampDriftError, TimestampTimeOutOfRangeError } from "./Timestamp.js";
|
|
16
|
+
export interface Sync extends Disposable {
|
|
17
|
+
/**
|
|
18
|
+
* Assigns or removes an owner to/from transports with reference counting.
|
|
19
|
+
*
|
|
20
|
+
* Owners are only "active" if assigned to at least one transport. Uses
|
|
21
|
+
* `owner.transports` or falls back to config transports. Multiple calls
|
|
22
|
+
* increment/decrement reference counts (useful for React Hooks).
|
|
23
|
+
*/
|
|
24
|
+
readonly useOwner: (use: boolean, owner: SyncOwner) => void;
|
|
25
|
+
readonly applyChanges: (changes: NonEmptyReadonlyArray<MutationChange>) => Result<void, SqliteError | TimestampCounterOverflowError | TimestampDriftError | TimestampTimeOutOfRangeError>;
|
|
6
26
|
}
|
|
7
27
|
export interface SyncDep {
|
|
8
28
|
readonly sync: Sync;
|
|
9
29
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Represents an owner for sync operations. This is a unified interface that
|
|
32
|
+
* abstracts over the specific owner types ({@link ShardOwner},
|
|
33
|
+
* {@link SharedOwner}, {@link SharedReadonlyOwner}) for the sync layer.
|
|
34
|
+
*
|
|
35
|
+
* The sync layer only needs the essential data for synchronization and doesn't
|
|
36
|
+
* need to distinguish between different owner types.
|
|
37
|
+
*/
|
|
38
|
+
export interface SyncOwner {
|
|
39
|
+
readonly id: OwnerId;
|
|
40
|
+
readonly encryptionKey: OwnerEncryptionKey;
|
|
41
|
+
/** Optional for read-only owners like {@link SharedReadonlyOwner}. */
|
|
42
|
+
readonly writeKey?: OwnerWriteKey;
|
|
43
|
+
readonly transports?: ReadonlyArray<TransportConfig>;
|
|
44
|
+
}
|
|
45
|
+
export interface SyncConfig {
|
|
46
|
+
readonly appOwner: AppOwner;
|
|
47
|
+
readonly transports: ReadonlyArray<TransportConfig>;
|
|
48
|
+
/**
|
|
49
|
+
* Delay in milliseconds before disposing unused WebSocket connections.
|
|
50
|
+
* Defaults to 100ms.
|
|
51
|
+
*/
|
|
52
|
+
readonly disposalDelayMs?: number;
|
|
53
|
+
readonly onError: (error: ProtocolError | ProtocolInvalidDataError | ProtocolTimestampMismatchError | SqliteError | SymmetricCryptoDecryptError | TimestampCounterOverflowError | TimestampDriftError | TimestampTimeOutOfRangeError | TransferableError) => void;
|
|
54
|
+
readonly onReceive: () => void;
|
|
13
55
|
}
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
readonly
|
|
17
|
-
readonly onMessage: (message: Uint8Array, send: Sync["send"]) => void;
|
|
56
|
+
export declare const createSync: (deps: ClockDep & ConsoleDep & CreateWebSocketDep & PostMessageDep & RandomBytesDep & RandomDep & SqliteDep & SymmetricCryptoDep & TimeDep & TimestampConfigDep) => (config: SyncConfig) => Result<Sync, SqliteError>;
|
|
57
|
+
export interface ClockDep {
|
|
58
|
+
readonly clock: Clock;
|
|
18
59
|
}
|
|
19
|
-
export
|
|
60
|
+
export interface Clock {
|
|
61
|
+
readonly get: () => Timestamp;
|
|
62
|
+
readonly save: (timestamp: Timestamp) => Result<void, SqliteError>;
|
|
63
|
+
}
|
|
64
|
+
export declare const createClock: (deps: RandomBytesDep & SqliteDep) => (initialTimestamp?: Timestamp) => Clock;
|
|
65
|
+
export interface ClientStorage extends SqliteStorageBase, Storage {
|
|
66
|
+
}
|
|
67
|
+
export interface ClientStorageDep {
|
|
68
|
+
readonly storage: ClientStorage;
|
|
69
|
+
}
|
|
70
|
+
export declare const applyLocalOnlyChange: (deps: SqliteDep & TimeDep) => (change: MutationChange) => Result<void, SqliteError>;
|
|
71
|
+
export declare const applyMessages: (deps: ClientStorageDep & ClockDep & RandomDep & SqliteDep) => (ownerId: OwnerId, messages: ReadonlyArray<CrdtMessage>) => Result<void, SqliteError>;
|
|
72
|
+
export declare const applyMessageToTimestampAndHistoryTables: (deps: ClientStorageDep & SqliteDep) => (ownerId: OwnerIdBytes, message: CrdtMessage) => Result<void, SqliteError>;
|
|
20
73
|
/**
|
|
74
|
+
* TODO: Rework for the new owners API.
|
|
75
|
+
*
|
|
21
76
|
* The possible states of a synchronization process. The `SyncState` can be one
|
|
22
77
|
* of the following:
|
|
23
78
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sync.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"Sync.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Sync.ts"],"names":[],"mappings":"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"}
|