@evolu/common 8.10.0 → 8.11.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/Config.d.ts +22 -22
- package/dist/src/Config.d.ts.map +1 -1
- package/dist/src/Console.d.ts +62 -7
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +20 -4
- package/dist/src/Crypto.d.ts +76 -4
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +55 -4
- package/dist/src/Error.d.ts +45 -0
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +69 -0
- package/dist/src/Fs.d.ts +92 -18
- package/dist/src/Fs.d.ts.map +1 -1
- package/dist/src/Fs.js +2 -0
- package/dist/src/Identicon.d.ts +2 -2
- package/dist/src/Identicon.js +2 -2
- package/dist/src/LeakDetector.d.ts +22 -3
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +12 -2
- package/dist/src/LockManager.d.ts +8 -0
- package/dist/src/LockManager.d.ts.map +1 -1
- package/dist/src/LockManager.js +6 -0
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +5 -0
- package/dist/src/Platform.d.ts +47 -7
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +24 -5
- package/dist/src/Random.d.ts +25 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +14 -2
- package/dist/src/Resource.d.ts +156 -1
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +201 -72
- package/dist/src/Schedule.d.ts +11 -10
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +1 -1
- package/dist/src/Sqlite.d.ts +132 -16
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +63 -9
- package/dist/src/Task.d.ts +15 -4
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +41 -15
- package/dist/src/Test.d.ts +9 -0
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +4 -0
- package/dist/src/Time.d.ts +106 -9
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +55 -4
- package/dist/src/Type.d.ts +1455 -1310
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +1274 -517
- package/dist/src/WebSocket.d.ts +164 -13
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +133 -24
- package/dist/src/Worker.d.ts +90 -8
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +28 -2
- package/dist/src/index.d.ts +6 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -3
- package/dist/src/local-first/Db.d.ts +52 -3
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +412 -137
- package/dist/src/local-first/Evolu.d.ts +336 -211
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +102 -15
- package/dist/src/local-first/Owner.d.ts +13 -30
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +13 -30
- package/dist/src/local-first/Protocol.d.ts +94 -16
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +118 -38
- package/dist/src/local-first/Query.d.ts +8 -15
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Schema.d.ts +335 -21
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +214 -17
- package/dist/src/local-first/Shared.d.ts +537 -22
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +1437 -234
- package/dist/src/local-first/Storage.d.ts +192 -14
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +81 -20
- package/dist/src/local-first/Timestamp.d.ts +392 -41
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +403 -81
- package/dist/src/local-first/index.d.ts +0 -1
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +0 -1
- package/package.json +1 -1
- package/src/Assert.test.ts +2 -5
- package/src/Config.test.ts +2 -6
- package/src/Config.ts +133 -133
- package/src/Console.ts +62 -7
- package/src/Crypto.ts +76 -4
- package/src/Eq.test.ts +2 -3
- package/src/Error.test.ts +76 -3
- package/src/Error.ts +71 -0
- package/src/Fs.ts +92 -18
- package/src/Identicon.ts +2 -2
- package/src/LeakDetector.ts +22 -3
- package/src/LockManager.ts +8 -0
- package/src/Object.test.ts +27 -12
- package/src/Object.ts +5 -0
- package/src/Platform.ts +50 -8
- package/src/Random.ts +25 -2
- package/src/Resource.test.ts +837 -0
- package/src/Resource.ts +235 -15
- package/src/Schedule.test.ts +50 -12
- package/src/Schedule.ts +24 -14
- package/src/Sqlite.ts +137 -17
- package/src/Task.test.ts +189 -8
- package/src/Task.ts +56 -17
- package/src/Test.ts +9 -0
- package/src/Time.ts +106 -9
- package/src/Type.test.ts +946 -1028
- package/src/Type.ts +4195 -3136
- package/src/Types.test.ts +4 -14
- package/src/WebSocket.ts +313 -40
- package/src/Worker.ts +90 -8
- package/src/index.ts +15 -6
- package/src/local-first/Db.ts +644 -339
- package/src/local-first/Evolu.test.ts +686 -21
- package/src/local-first/Evolu.ts +450 -228
- package/src/local-first/Owner.ts +13 -30
- package/src/local-first/Protocol.test.ts +617 -10
- package/src/local-first/Protocol.ts +196 -72
- package/src/local-first/Query.ts +8 -15
- package/src/local-first/Schema.test.ts +143 -0
- package/src/local-first/Schema.ts +363 -24
- package/src/local-first/Shared.test.ts +7731 -559
- package/src/local-first/Shared.ts +2036 -267
- package/src/local-first/Storage.ts +218 -32
- package/src/local-first/Timestamp.test.ts +344 -70
- package/src/local-first/Timestamp.ts +434 -118
- package/src/local-first/index.ts +0 -1
- package/dist/src/local-first/Error.d.ts +0 -12
- package/dist/src/local-first/Error.d.ts.map +0 -1
- package/dist/src/local-first/Error.js +0 -6
- package/dist/src/local-first/LocalAuth.d.ts +0 -150
- package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
- package/dist/src/local-first/LocalAuth.js +0 -179
- package/src/local-first/Error.ts +0 -17
- package/src/local-first/LocalAuth.ts +0 -457
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { NonEmptyReadonlyArray } from "../Array.ts";
|
|
7
7
|
import type { Brand } from "../Brand.ts";
|
|
8
|
+
import type { DecryptWithXChaCha20Poly1305Error } from "../Crypto.ts";
|
|
8
9
|
import type { RandomDep } from "../Random.ts";
|
|
9
10
|
import type { SqliteDep } from "../Sqlite.ts";
|
|
10
11
|
import type { Task } from "../Task.ts";
|
|
@@ -12,7 +13,13 @@ import { Boolean, Id, type InferType, NonNegativeInt, Null, type ObjectType, Pos
|
|
|
12
13
|
import type { Awaitable } from "../Types.ts";
|
|
13
14
|
import type { OwnerError, OwnerIdBytes } from "./Owner.ts";
|
|
14
15
|
import { OwnerId, OwnerWriteKey } from "./Owner.ts";
|
|
16
|
+
import type { ProtocolInvalidDataError, ProtocolTimestampMismatchError } from "./Protocol.ts";
|
|
15
17
|
import { Timestamp, TimestampBytes } from "./Timestamp.ts";
|
|
18
|
+
/**
|
|
19
|
+
* Configuration for {@link Storage}, such as quota checks.
|
|
20
|
+
*
|
|
21
|
+
* @group Core
|
|
22
|
+
*/
|
|
16
23
|
export interface StorageConfig {
|
|
17
24
|
/**
|
|
18
25
|
* Callback called before an attempt to write, to check if an {@link OwnerId}
|
|
@@ -67,12 +74,18 @@ export interface StorageConfig {
|
|
|
67
74
|
readonly isOwnerWithinQuota: (ownerId: OwnerId, requiredBytes: PositiveInt) => Awaitable<boolean>;
|
|
68
75
|
}
|
|
69
76
|
/**
|
|
70
|
-
* Evolu
|
|
77
|
+
* Replica storage used by Evolu's synchronization protocol.
|
|
78
|
+
*
|
|
79
|
+
* Protocol owns message framing, set reconciliation, and sync continuation.
|
|
80
|
+
* Storage owns batch acceptance, persistence, and the timestamp and fingerprint
|
|
81
|
+
* queries used by reconciliation. Protocol functions accept any implementation
|
|
82
|
+
* that satisfies this contract.
|
|
71
83
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
84
|
+
* {@link Storage.writeMessages} returns the {@link StorageWriteMessagesError}
|
|
85
|
+
* that made it store none of a batch. Implementations return expected write
|
|
86
|
+
* rejections without reporting them; the caller owns reporting. The client
|
|
87
|
+
* protocol forwards these errors unchanged, while the relay protocol maps them
|
|
88
|
+
* to wire error codes.
|
|
76
89
|
*
|
|
77
90
|
* The Storage API is synchronous because SQLite's synchronous API is the
|
|
78
91
|
* fastest way to use SQLite. Synchronous bindings (like better-sqlite3) call
|
|
@@ -82,6 +95,8 @@ export interface StorageConfig {
|
|
|
82
95
|
* The only exception is {@link Storage.writeMessages}, which is async to allow
|
|
83
96
|
* for async validation logic before writing to storage. The write operation
|
|
84
97
|
* itself remains synchronous.
|
|
98
|
+
*
|
|
99
|
+
* @group Core
|
|
85
100
|
*/
|
|
86
101
|
export interface Storage {
|
|
87
102
|
readonly getSize: (ownerId: OwnerIdBytes) => NonNegativeInt;
|
|
@@ -107,66 +122,157 @@ export interface Storage {
|
|
|
107
122
|
/**
|
|
108
123
|
* Write encrypted {@link CrdtMessage}s to storage.
|
|
109
124
|
*
|
|
125
|
+
* Stores none of the messages and returns the cause when the batch cannot be
|
|
126
|
+
* accepted.
|
|
127
|
+
*
|
|
110
128
|
* Must use a mutex per ownerId to ensure sequential processing and proper
|
|
111
129
|
* protocol logic handling during sync operations.
|
|
112
130
|
*/
|
|
113
|
-
readonly writeMessages: (ownerIdBytes: OwnerIdBytes, messages: NonEmptyReadonlyArray<EncryptedCrdtMessage>) => Task<void,
|
|
131
|
+
readonly writeMessages: (ownerIdBytes: OwnerIdBytes, messages: NonEmptyReadonlyArray<EncryptedCrdtMessage>) => Task<void, StorageWriteMessagesError>;
|
|
114
132
|
/** Read encrypted {@link DbChange}s from storage. */
|
|
115
133
|
readonly readDbChange: (ownerId: OwnerIdBytes, timestamp: TimestampBytes) => EncryptedDbChange;
|
|
116
134
|
/** Delete all data for the given {@link Owner}. */
|
|
117
135
|
readonly deleteOwner: (ownerId: OwnerIdBytes) => void;
|
|
118
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Dependency wrapper for {@link Storage}.
|
|
139
|
+
*
|
|
140
|
+
* @group Core
|
|
141
|
+
*/
|
|
119
142
|
export interface StorageDep {
|
|
120
143
|
readonly storage: Storage;
|
|
121
144
|
}
|
|
122
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Error when storage or billing quota is exceeded.
|
|
147
|
+
*
|
|
148
|
+
* @group Core
|
|
149
|
+
*/
|
|
123
150
|
export interface StorageQuotaError extends OwnerError, Typed<"StorageQuotaError"> {
|
|
124
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Expected reasons why {@link Storage.writeMessages} stored none of a batch.
|
|
154
|
+
*
|
|
155
|
+
* Each implementation returns the members that apply to it. The built-in relay
|
|
156
|
+
* storage currently stores opaque encrypted messages and rejects batches over
|
|
157
|
+
* quota. The built-in client storage decrypts and validates incoming messages
|
|
158
|
+
* before updating its clock and database tables. The contract permits quota
|
|
159
|
+
* checks on either side.
|
|
160
|
+
*
|
|
161
|
+
* @group Core
|
|
162
|
+
*/
|
|
163
|
+
export type StorageWriteMessagesError = DecryptWithXChaCha20Poly1305Error | ProtocolInvalidDataError | ProtocolTimestampMismatchError | StorageQuotaError;
|
|
125
164
|
/**
|
|
126
165
|
* A cryptographic hash used for efficiently comparing collections of
|
|
127
166
|
* {@link TimestampBytes}es.
|
|
128
167
|
*
|
|
129
168
|
* It consists of the first {@link fingerprintSize} bytes of the SHA-256 hash of
|
|
130
169
|
* one or more timestamps.
|
|
170
|
+
*
|
|
171
|
+
* @group Ranges
|
|
131
172
|
*/
|
|
132
173
|
export type Fingerprint = Uint8Array & Brand<"Fingerprint">;
|
|
174
|
+
/**
|
|
175
|
+
* Number of leading SHA-256 bytes that form a {@link Fingerprint}.
|
|
176
|
+
*
|
|
177
|
+
* @group Ranges
|
|
178
|
+
*/
|
|
133
179
|
export declare const fingerprintSize: number & Brand<"NonNaN"> & Brand<"Finite"> & Brand<"Int"> & Brand<"NonNegative">;
|
|
134
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* A fingerprint of an empty range.
|
|
182
|
+
*
|
|
183
|
+
* @group Ranges
|
|
184
|
+
*/
|
|
135
185
|
export declare const zeroFingerprint: Fingerprint;
|
|
186
|
+
/**
|
|
187
|
+
* Common shape of every {@link Range}.
|
|
188
|
+
*
|
|
189
|
+
* @group Ranges
|
|
190
|
+
*/
|
|
136
191
|
export interface BaseRange {
|
|
137
192
|
readonly upperBound: RangeUpperBound;
|
|
138
193
|
}
|
|
139
194
|
/**
|
|
140
195
|
* Union type for Range's upperBound: either a {@link TimestampBytes} or
|
|
141
196
|
* {@link InfiniteUpperBound}.
|
|
197
|
+
*
|
|
198
|
+
* @group Ranges
|
|
142
199
|
*/
|
|
143
200
|
export type RangeUpperBound = TimestampBytes | InfiniteUpperBound;
|
|
201
|
+
/**
|
|
202
|
+
* Sentinel {@link RangeUpperBound} for a range without an upper limit.
|
|
203
|
+
*
|
|
204
|
+
* @group Ranges
|
|
205
|
+
*/
|
|
144
206
|
export declare const InfiniteUpperBound: unique symbol;
|
|
207
|
+
/**
|
|
208
|
+
* Type of the {@link InfiniteUpperBound} sentinel.
|
|
209
|
+
*
|
|
210
|
+
* @group Ranges
|
|
211
|
+
*/
|
|
145
212
|
export type InfiniteUpperBound = typeof InfiniteUpperBound;
|
|
213
|
+
/**
|
|
214
|
+
* Numeric tags discriminating {@link Range} variants.
|
|
215
|
+
*
|
|
216
|
+
* @group Ranges
|
|
217
|
+
*/
|
|
146
218
|
export declare const RangeType: {
|
|
147
219
|
readonly Fingerprint: 1;
|
|
148
220
|
readonly Skip: 0;
|
|
149
221
|
readonly Timestamps: 2;
|
|
150
222
|
};
|
|
223
|
+
/**
|
|
224
|
+
* Numeric tag of one {@link Range} variant.
|
|
225
|
+
*
|
|
226
|
+
* @group Ranges
|
|
227
|
+
*/
|
|
151
228
|
export type RangeType = (typeof RangeType)[keyof typeof RangeType];
|
|
229
|
+
/**
|
|
230
|
+
* Range with nothing to reconcile.
|
|
231
|
+
*
|
|
232
|
+
* @group Ranges
|
|
233
|
+
*/
|
|
152
234
|
export interface SkipRange extends BaseRange {
|
|
153
235
|
readonly type: typeof RangeType.Skip;
|
|
154
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Range summarized by a {@link Fingerprint} for comparison.
|
|
239
|
+
*
|
|
240
|
+
* @group Ranges
|
|
241
|
+
*/
|
|
155
242
|
export interface FingerprintRange extends BaseRange {
|
|
156
243
|
readonly type: typeof RangeType.Fingerprint;
|
|
157
244
|
readonly fingerprint: Fingerprint;
|
|
158
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Range listing its {@link TimestampBytes} explicitly.
|
|
248
|
+
*
|
|
249
|
+
* @group Ranges
|
|
250
|
+
*/
|
|
159
251
|
export interface TimestampsRange extends BaseRange {
|
|
160
252
|
readonly type: typeof RangeType.Timestamps;
|
|
161
253
|
readonly timestamps: ReadonlyArray<TimestampBytes>;
|
|
162
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Range exchanged during sync: {@link SkipRange}, {@link FingerprintRange}, or
|
|
257
|
+
* {@link TimestampsRange}.
|
|
258
|
+
*
|
|
259
|
+
* @group Ranges
|
|
260
|
+
*/
|
|
163
261
|
export type Range = SkipRange | FingerprintRange | TimestampsRange;
|
|
164
|
-
/**
|
|
262
|
+
/**
|
|
263
|
+
* An encrypted {@link CrdtMessage}.
|
|
264
|
+
*
|
|
265
|
+
* @group Messages
|
|
266
|
+
*/
|
|
165
267
|
export interface EncryptedCrdtMessage {
|
|
166
268
|
readonly timestamp: Timestamp;
|
|
167
269
|
readonly change: EncryptedDbChange;
|
|
168
270
|
}
|
|
169
|
-
/**
|
|
271
|
+
/**
|
|
272
|
+
* Encrypted DbChange
|
|
273
|
+
*
|
|
274
|
+
* @group Messages
|
|
275
|
+
*/
|
|
170
276
|
export type EncryptedDbChange = Uint8Array & Brand<"EncryptedDbChange">;
|
|
171
277
|
/**
|
|
172
278
|
* A CRDT message combining a unique {@link Timestamp} with a {@link DbChange}.
|
|
@@ -174,17 +280,38 @@ export type EncryptedDbChange = Uint8Array & Brand<"EncryptedDbChange">;
|
|
|
174
280
|
* Used in Evolu's sync protocol to replicate data changes across devices. Evolu
|
|
175
281
|
* operates as a durable queue, providing exactly-once delivery guarantees for
|
|
176
282
|
* reliable synchronization across application restarts and network failures.
|
|
283
|
+
*
|
|
284
|
+
* @group Messages
|
|
177
285
|
*/
|
|
178
286
|
export interface CrdtMessage {
|
|
179
287
|
readonly timestamp: Timestamp;
|
|
180
288
|
readonly change: DbChange;
|
|
181
289
|
}
|
|
182
|
-
/**
|
|
290
|
+
/**
|
|
291
|
+
* Test helper for creating a simple {@link CrdtMessage}.
|
|
292
|
+
*
|
|
293
|
+
* @group Testing
|
|
294
|
+
*/
|
|
183
295
|
export declare const testCreateCrdtMessage: (id: Id, millis: number, name: string) => CrdtMessage;
|
|
296
|
+
/**
|
|
297
|
+
* Column values of a {@link DbChange}, keyed by column name.
|
|
298
|
+
*
|
|
299
|
+
* @group Messages
|
|
300
|
+
*/
|
|
184
301
|
export declare const DbChangeValues: import("../Type.ts").RecordType<import("../Type.ts").Type<"String", string, string, import("../Type.ts").TypeOfError<"String">, null, import("../Type.ts").TypeOfError<"String">, never, string, true>, UnionType<readonly [import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Number", number, number, import("../Type.ts").TypeOfError<"Number">, null, import("../Type.ts").TypeOfError<"Number">, never, number, true>, "NonNaN", import("../Type.ts").NonNaNError>, "Finite", import("../Type.ts").FiniteError>, import("../Type.ts").LiteralType<null>, import("../Type.ts").Type<"String", string, string, import("../Type.ts").TypeOfError<"String">, null, import("../Type.ts").TypeOfError<"String">, never, string, true>, import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>]>>;
|
|
185
302
|
export type DbChangeValues = typeof DbChangeValues.Output;
|
|
303
|
+
/**
|
|
304
|
+
* Column values that contain no reserved system columns.
|
|
305
|
+
*
|
|
306
|
+
* @group Messages
|
|
307
|
+
*/
|
|
186
308
|
export declare const ValidDbChangeValues: import("../Type.ts").BrandType<import("../Type.ts").RecordType<import("../Type.ts").Type<"String", string, string, import("../Type.ts").TypeOfError<"String">, null, import("../Type.ts").TypeOfError<"String">, never, string, true>, UnionType<readonly [import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Number", number, number, import("../Type.ts").TypeOfError<"Number">, null, import("../Type.ts").TypeOfError<"Number">, never, number, true>, "NonNaN", import("../Type.ts").NonNaNError>, "Finite", import("../Type.ts").FiniteError>, import("../Type.ts").LiteralType<null>, import("../Type.ts").Type<"String", string, string, import("../Type.ts").TypeOfError<"String">, null, import("../Type.ts").TypeOfError<"String">, never, string, true>, import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>]>>, "ValidDbChangeValues", ValidDbChangeValuesError>;
|
|
187
309
|
export type ValidDbChangeValues = typeof ValidDbChangeValues.Output;
|
|
310
|
+
/**
|
|
311
|
+
* Error produced when {@link DbChangeValues} contain reserved system columns.
|
|
312
|
+
*
|
|
313
|
+
* @group Messages
|
|
314
|
+
*/
|
|
188
315
|
export interface ValidDbChangeValuesError extends TypeError<"ValidDbChangeValues"> {
|
|
189
316
|
readonly value: DbChangeValues;
|
|
190
317
|
readonly invalidColumns: ReadonlyArray<string>;
|
|
@@ -192,6 +319,8 @@ export interface ValidDbChangeValuesError extends TypeError<"ValidDbChangeValues
|
|
|
192
319
|
/**
|
|
193
320
|
* A DbChange is a change to a table row. Together with a unique
|
|
194
321
|
* {@link Timestamp}, it forms a {@link CrdtMessage}.
|
|
322
|
+
*
|
|
323
|
+
* @group Messages
|
|
195
324
|
*/
|
|
196
325
|
export declare const DbChange: ObjectType<{
|
|
197
326
|
readonly table: typeof String;
|
|
@@ -231,19 +360,36 @@ export interface DbChange extends InferType<typeof DbChange> {
|
|
|
231
360
|
* each other, if necessary. One relay should handle hundreds of thousands of
|
|
232
361
|
* users, and when it goes down, nothing happens, because it will be
|
|
233
362
|
* synchronized later.
|
|
363
|
+
*
|
|
364
|
+
* @group SQLite
|
|
234
365
|
*/
|
|
235
366
|
export interface BaseSqliteStorage extends Omit<Storage, "validateWriteKey" | "setWriteKey" | "writeMessages" | "readDbChange"> {
|
|
236
|
-
/**
|
|
237
|
-
|
|
367
|
+
/**
|
|
368
|
+
* Inserts a timestamp for an owner into the skiplist-based storage.
|
|
369
|
+
*
|
|
370
|
+
* Returns whether the timestamp was new. An existing timestamp is left
|
|
371
|
+
* unchanged.
|
|
372
|
+
*/
|
|
373
|
+
readonly insertTimestamp: (ownerId: OwnerIdBytes, timestamp: TimestampBytes, strategy: StorageInsertTimestampStrategy) => boolean;
|
|
238
374
|
/**
|
|
239
375
|
* Efficiently checks which timestamps already exist in the database using a
|
|
240
376
|
* single CTE query instead of N individual queries.
|
|
241
377
|
*/
|
|
242
378
|
readonly getExistingTimestamps: (ownerIdBytes: OwnerIdBytes, timestampsBytes: NonEmptyReadonlyArray<TimestampBytes>) => ReadonlyArray<TimestampBytes>;
|
|
243
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* Dependency wrapper for {@link BaseSqliteStorage}.
|
|
382
|
+
*
|
|
383
|
+
* @group SQLite
|
|
384
|
+
*/
|
|
244
385
|
export interface BaseSqliteStorageDep {
|
|
245
386
|
readonly baseSqliteStorage: BaseSqliteStorage;
|
|
246
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Dependencies required by {@link createBaseSqliteStorage}.
|
|
390
|
+
*
|
|
391
|
+
* @group SQLite
|
|
392
|
+
*/
|
|
247
393
|
export type SqliteStorageDeps = RandomDep & SqliteDep;
|
|
248
394
|
/**
|
|
249
395
|
* Creates a {@link BaseSqliteStorage} implementation.
|
|
@@ -257,25 +403,55 @@ export type SqliteStorageDeps = RandomDep & SqliteDep;
|
|
|
257
403
|
* Cloudflare Workers with Durable Objects, and other platforms where memory
|
|
258
404
|
* doesn't persist between requests. While not extensively tested in all these
|
|
259
405
|
* environments yet, the stateless design should work well across them.
|
|
406
|
+
*
|
|
407
|
+
* @group SQLite
|
|
260
408
|
*/
|
|
261
409
|
export declare const createBaseSqliteStorage: (deps: SqliteStorageDeps) => BaseSqliteStorage;
|
|
410
|
+
/**
|
|
411
|
+
* Creates the SQLite tables used by {@link BaseSqliteStorage}.
|
|
412
|
+
*
|
|
413
|
+
* @group SQLite
|
|
414
|
+
*/
|
|
262
415
|
export declare const createBaseSqliteStorageTables: (deps: SqliteDep) => void;
|
|
416
|
+
/**
|
|
417
|
+
* Position at which a timestamp is inserted relative to existing timestamps.
|
|
418
|
+
*
|
|
419
|
+
* @group SQLite
|
|
420
|
+
*/
|
|
263
421
|
export type StorageInsertTimestampStrategy = "append" | "prepend" | "insert";
|
|
264
422
|
/**
|
|
265
423
|
* Determines the insertion strategy for a timestamp based on its position
|
|
266
424
|
* relative to the current first and last timestamps.
|
|
267
425
|
*
|
|
268
426
|
* Returns a tuple with the strategy and updated timestamp bounds.
|
|
427
|
+
*
|
|
428
|
+
* @group SQLite
|
|
269
429
|
*/
|
|
270
430
|
export declare const getTimestampInsertStrategy: (timestamp: TimestampBytes, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes) => [strategy: StorageInsertTimestampStrategy, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes];
|
|
431
|
+
/**
|
|
432
|
+
* Computes the {@link Fingerprint} of a single timestamp.
|
|
433
|
+
*
|
|
434
|
+
* @group Ranges
|
|
435
|
+
*/
|
|
271
436
|
export declare const timestampBytesToFingerprint: (timestamp: TimestampBytes) => Fingerprint;
|
|
272
437
|
/**
|
|
273
438
|
* Computes a brute-force {@link Fingerprint} from {@link TimestampBytes} values
|
|
274
439
|
* for tests and benchmarks.
|
|
440
|
+
*
|
|
441
|
+
* @group Testing
|
|
275
442
|
*/
|
|
276
443
|
export declare const testFingerprintTimestamps: (timestamps: ReadonlyArray<TimestampBytes>) => Fingerprint;
|
|
444
|
+
/**
|
|
445
|
+
* Reads the timestamp at a position within an owner's ordered timestamps.
|
|
446
|
+
*
|
|
447
|
+
* @group SQLite
|
|
448
|
+
*/
|
|
277
449
|
export declare const getTimestampByIndex: (deps: SqliteDep) => (ownerId: OwnerIdBytes, index: NonNegativeInt) => TimestampBytes;
|
|
278
|
-
/**
|
|
450
|
+
/**
|
|
451
|
+
* Reads owner usage from SQLite and returns default bounds when absent.
|
|
452
|
+
*
|
|
453
|
+
* @group SQLite
|
|
454
|
+
*/
|
|
279
455
|
export declare const readOwnerUsageOrDefault: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, initialTimestamp: TimestampBytes) => {
|
|
280
456
|
readonly storedBytes: NonNegativeInt | null;
|
|
281
457
|
readonly firstTimestamp: TimestampBytes;
|
|
@@ -286,6 +462,8 @@ export declare const readOwnerUsageOrDefault: (deps: SqliteDep) => (ownerIdBytes
|
|
|
286
462
|
*
|
|
287
463
|
* Used by both relay and client to maintain firstTimestamp/lastTimestamp after
|
|
288
464
|
* processing messages.
|
|
465
|
+
*
|
|
466
|
+
* @group SQLite
|
|
289
467
|
*/
|
|
290
468
|
export declare const updateOwnerUsage: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, storedBytes: PositiveInt, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes) => void;
|
|
291
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;
|
|
1
|
+
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/local-first/Storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,16 +15,39 @@ import { Boolean, brand, Id, NonNegativeInt, Null, nullOr, object, PositiveInt,
|
|
|
15
15
|
import { OwnerId, OwnerWriteKey } from "./Owner.js";
|
|
16
16
|
import { systemColumnsWithId } from "./Schema.js";
|
|
17
17
|
import { createTimestamp, orderTimestampBytes, Timestamp, TimestampBytes, } from "./Timestamp.js";
|
|
18
|
+
/**
|
|
19
|
+
* Number of leading SHA-256 bytes that form a {@link Fingerprint}.
|
|
20
|
+
*
|
|
21
|
+
* @group Ranges
|
|
22
|
+
*/
|
|
18
23
|
export const fingerprintSize = /*#__PURE__*/ NonNegativeInt.orThrow(12);
|
|
19
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* A fingerprint of an empty range.
|
|
26
|
+
*
|
|
27
|
+
* @group Ranges
|
|
28
|
+
*/
|
|
20
29
|
export const zeroFingerprint = /*#__PURE__*/ new Uint8Array(fingerprintSize);
|
|
30
|
+
/**
|
|
31
|
+
* Sentinel {@link RangeUpperBound} for a range without an upper limit.
|
|
32
|
+
*
|
|
33
|
+
* @group Ranges
|
|
34
|
+
*/
|
|
21
35
|
export const InfiniteUpperBound = /*#__PURE__*/ Symbol("evolu.local-first.Storage.InfiniteUpperBound");
|
|
36
|
+
/**
|
|
37
|
+
* Numeric tags discriminating {@link Range} variants.
|
|
38
|
+
*
|
|
39
|
+
* @group Ranges
|
|
40
|
+
*/
|
|
22
41
|
export const RangeType = {
|
|
23
42
|
Fingerprint: 1,
|
|
24
43
|
Skip: 0,
|
|
25
44
|
Timestamps: 2,
|
|
26
45
|
};
|
|
27
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Test helper for creating a simple {@link CrdtMessage}.
|
|
48
|
+
*
|
|
49
|
+
* @group Testing
|
|
50
|
+
*/
|
|
28
51
|
export const testCreateCrdtMessage = (id, millis, name) => ({
|
|
29
52
|
timestamp: createTimestamp({
|
|
30
53
|
millis: Millis.orThrow(millis),
|
|
@@ -38,7 +61,17 @@ export const testCreateCrdtMessage = (id, millis, name) => ({
|
|
|
38
61
|
isDelete: false,
|
|
39
62
|
}),
|
|
40
63
|
});
|
|
64
|
+
/**
|
|
65
|
+
* Column values of a {@link DbChange}, keyed by column name.
|
|
66
|
+
*
|
|
67
|
+
* @group Messages
|
|
68
|
+
*/
|
|
41
69
|
export const DbChangeValues = /*#__PURE__*/ record(String, SqliteValue);
|
|
70
|
+
/**
|
|
71
|
+
* Column values that contain no reserved system columns.
|
|
72
|
+
*
|
|
73
|
+
* @group Messages
|
|
74
|
+
*/
|
|
42
75
|
export const ValidDbChangeValues = /*#__PURE__*/ brand("ValidDbChangeValues", DbChangeValues, (value) => {
|
|
43
76
|
const invalidColumns = systemColumnsWithId.filter((key) => key in value);
|
|
44
77
|
if (invalidColumns.length > 0)
|
|
@@ -52,6 +85,8 @@ export const ValidDbChangeValues = /*#__PURE__*/ brand("ValidDbChangeValues", Db
|
|
|
52
85
|
/**
|
|
53
86
|
* A DbChange is a change to a table row. Together with a unique
|
|
54
87
|
* {@link Timestamp}, it forms a {@link CrdtMessage}.
|
|
88
|
+
*
|
|
89
|
+
* @group Messages
|
|
55
90
|
*/
|
|
56
91
|
export const DbChange = /*#__PURE__*/ object({
|
|
57
92
|
table: String,
|
|
@@ -72,11 +107,13 @@ export const DbChange = /*#__PURE__*/ object({
|
|
|
72
107
|
* Cloudflare Workers with Durable Objects, and other platforms where memory
|
|
73
108
|
* doesn't persist between requests. While not extensively tested in all these
|
|
74
109
|
* environments yet, the stateless design should work well across them.
|
|
110
|
+
*
|
|
111
|
+
* @group SQLite
|
|
75
112
|
*/
|
|
76
113
|
export const createBaseSqliteStorage = (deps) => ({
|
|
77
114
|
insertTimestamp: (ownerId, timestamp, strategy) => {
|
|
78
115
|
const level = randomSkiplistLevel(deps);
|
|
79
|
-
insertTimestamp(deps)(ownerId, timestamp, level, strategy);
|
|
116
|
+
return insertTimestamp(deps)(ownerId, timestamp, level, strategy);
|
|
80
117
|
},
|
|
81
118
|
getExistingTimestamps: (ownerIdBytes, timestampsBytes) => {
|
|
82
119
|
const concatenatedTimestamps = concatBytes(...timestampsBytes);
|
|
@@ -150,6 +187,11 @@ export const createBaseSqliteStorage = (deps) => ({
|
|
|
150
187
|
const assertBeginEnd = (begin, end) => {
|
|
151
188
|
assert(begin <= end, "invalid begin or end");
|
|
152
189
|
};
|
|
190
|
+
/**
|
|
191
|
+
* Creates the SQLite tables used by {@link BaseSqliteStorage}.
|
|
192
|
+
*
|
|
193
|
+
* @group SQLite
|
|
194
|
+
*/
|
|
153
195
|
export const createBaseSqliteStorageTables = (deps) => {
|
|
154
196
|
for (const query of [
|
|
155
197
|
/**
|
|
@@ -218,6 +260,8 @@ export const createBaseSqliteStorageTables = (deps) => {
|
|
|
218
260
|
* relative to the current first and last timestamps.
|
|
219
261
|
*
|
|
220
262
|
* Returns a tuple with the strategy and updated timestamp bounds.
|
|
263
|
+
*
|
|
264
|
+
* @group SQLite
|
|
221
265
|
*/
|
|
222
266
|
export const getTimestampInsertStrategy = (timestamp, firstTimestamp, lastTimestamp) => {
|
|
223
267
|
if (orderTimestampBytes(timestamp, lastTimestamp) === 1) {
|
|
@@ -251,13 +295,13 @@ export const getTimestampInsertStrategy = (timestamp, firstTimestamp, lastTimest
|
|
|
251
295
|
* key instead of repeating the same correlated range lookup for every column.
|
|
252
296
|
*
|
|
253
297
|
* Inserts are idempotent to support direct calls and message replay. `on
|
|
254
|
-
* conflict do nothing` makes a duplicate insertion a no-op
|
|
255
|
-
*
|
|
256
|
-
*
|
|
298
|
+
* conflict do nothing` makes a duplicate insertion a no-op that reports the
|
|
299
|
+
* timestamp as not new, so the follow-up statements update metadata only for a
|
|
300
|
+
* new timestamp.
|
|
257
301
|
*/
|
|
258
302
|
const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
259
303
|
const [h1, h2] = fingerprintToSqliteFingerprint(timestampBytesToFingerprint(timestamp));
|
|
260
|
-
let queries
|
|
304
|
+
let queries;
|
|
261
305
|
switch (strategy) {
|
|
262
306
|
case "append":
|
|
263
307
|
queries = [
|
|
@@ -431,10 +475,7 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
431
475
|
h2 = u.h2,
|
|
432
476
|
c = c + 1
|
|
433
477
|
from u
|
|
434
|
-
where
|
|
435
|
-
changes() > 0
|
|
436
|
-
and ownerId = ${ownerId}
|
|
437
|
-
and evolu_timestamp.t = u.t;
|
|
478
|
+
where ownerId = ${ownerId} and evolu_timestamp.t = u.t;
|
|
438
479
|
`,
|
|
439
480
|
];
|
|
440
481
|
break;
|
|
@@ -499,10 +540,7 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
499
540
|
h2 = u.h2,
|
|
500
541
|
c = c + 1
|
|
501
542
|
from u
|
|
502
|
-
where
|
|
503
|
-
changes() > 0
|
|
504
|
-
and ownerId = ${ownerId}
|
|
505
|
-
and evolu_timestamp.t = u.t;
|
|
543
|
+
where ownerId = ${ownerId} and evolu_timestamp.t = u.t;
|
|
506
544
|
`,
|
|
507
545
|
]
|
|
508
546
|
: [
|
|
@@ -684,15 +722,25 @@ const insertTimestamp = (deps) => (ownerId, timestamp, level, strategy) => {
|
|
|
684
722
|
h2 = uh2,
|
|
685
723
|
c = uc
|
|
686
724
|
from u
|
|
687
|
-
where
|
|
725
|
+
where ownerId = ${ownerId} and t = ut;
|
|
688
726
|
`,
|
|
689
727
|
];
|
|
690
728
|
break;
|
|
691
729
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
730
|
+
// The insert uses "on conflict do nothing". An existing timestamp changes
|
|
731
|
+
// nothing, and the metadata updates are skipped.
|
|
732
|
+
const [insert, ...updates] = queries;
|
|
733
|
+
if (deps.sqlite.exec(insert).changes === 0)
|
|
734
|
+
return false;
|
|
735
|
+
for (const update of updates)
|
|
736
|
+
deps.sqlite.exec(update);
|
|
737
|
+
return true;
|
|
695
738
|
};
|
|
739
|
+
/**
|
|
740
|
+
* Computes the {@link Fingerprint} of a single timestamp.
|
|
741
|
+
*
|
|
742
|
+
* @group Ranges
|
|
743
|
+
*/
|
|
696
744
|
export const timestampBytesToFingerprint = (timestamp) => {
|
|
697
745
|
const hash = sha256(timestamp).slice(0, fingerprintSize);
|
|
698
746
|
return hash;
|
|
@@ -700,6 +748,8 @@ export const timestampBytesToFingerprint = (timestamp) => {
|
|
|
700
748
|
/**
|
|
701
749
|
* Computes a brute-force {@link Fingerprint} from {@link TimestampBytes} values
|
|
702
750
|
* for tests and benchmarks.
|
|
751
|
+
*
|
|
752
|
+
* @group Testing
|
|
703
753
|
*/
|
|
704
754
|
export const testFingerprintTimestamps = (timestamps) => timestamps
|
|
705
755
|
.map(timestampBytesToFingerprint)
|
|
@@ -1013,6 +1063,11 @@ const fingerprintRanges = (deps) => (ownerId, buckets, upperBound = InfiniteUppe
|
|
|
1013
1063
|
};
|
|
1014
1064
|
// XOR in SQLite
|
|
1015
1065
|
const x = (a, b) => sql.raw(`(${a} | ${b}) - (${a} & ${b})`);
|
|
1066
|
+
/**
|
|
1067
|
+
* Reads the timestamp at a position within an owner's ordered timestamps.
|
|
1068
|
+
*
|
|
1069
|
+
* @group SQLite
|
|
1070
|
+
*/
|
|
1016
1071
|
export const getTimestampByIndex = (deps) => (ownerId, index) => {
|
|
1017
1072
|
const result = deps.sqlite.exec(sql.prepared `
|
|
1018
1073
|
with
|
|
@@ -1087,7 +1142,11 @@ export const getTimestampByIndex = (deps) => (ownerId, index) => {
|
|
|
1087
1142
|
`);
|
|
1088
1143
|
return result.rows[0].pt;
|
|
1089
1144
|
};
|
|
1090
|
-
/**
|
|
1145
|
+
/**
|
|
1146
|
+
* Reads owner usage from SQLite and returns default bounds when absent.
|
|
1147
|
+
*
|
|
1148
|
+
* @group SQLite
|
|
1149
|
+
*/
|
|
1091
1150
|
export const readOwnerUsageOrDefault = (deps) => (ownerIdBytes, initialTimestamp) => {
|
|
1092
1151
|
const result = deps.sqlite.exec(sql `
|
|
1093
1152
|
select storedBytes, firstTimestamp, lastTimestamp
|
|
@@ -1115,6 +1174,8 @@ export const readOwnerUsageOrDefault = (deps) => (ownerIdBytes, initialTimestamp
|
|
|
1115
1174
|
*
|
|
1116
1175
|
* Used by both relay and client to maintain firstTimestamp/lastTimestamp after
|
|
1117
1176
|
* processing messages.
|
|
1177
|
+
*
|
|
1178
|
+
* @group SQLite
|
|
1118
1179
|
*/
|
|
1119
1180
|
export const updateOwnerUsage = (deps) => (ownerIdBytes, storedBytes, firstTimestamp, lastTimestamp) => {
|
|
1120
1181
|
deps.sqlite.exec(sql `
|