@evolu/common 6.0.1-preview.8 → 7.0.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/README.md +2 -2
- package/dist/src/Array.d.ts +256 -12
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +215 -9
- package/dist/src/Assert.d.ts +0 -13
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +0 -15
- 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 +8 -7
- package/dist/src/Cache.d.ts +44 -0
- package/dist/src/Cache.d.ts.map +1 -0
- package/dist/src/Cache.js +52 -0
- package/dist/src/Callbacks.d.ts +45 -12
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +14 -7
- 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 +56 -42
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +40 -53
- package/dist/src/Evolu/Db.d.ts +162 -74
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +284 -702
- 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 +214 -134
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +189 -180
- 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 +150 -0
- package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
- package/dist/src/Evolu/LocalAuth.js +174 -0
- package/dist/src/Evolu/Owner.d.ts +273 -120
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +130 -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 +268 -240
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +600 -454
- 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.d.ts.map +1 -1
- package/dist/src/Evolu/PublicKysely.js +3 -4
- package/dist/src/Evolu/Query.d.ts +2 -1
- package/dist/src/Evolu/Query.d.ts.map +1 -1
- package/dist/src/Evolu/Query.js +1 -1
- package/dist/src/Evolu/Relay.d.ts +91 -8
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +214 -88
- package/dist/src/Evolu/Schema.d.ts +125 -47
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.js +175 -31
- package/dist/src/Evolu/Storage.d.ts +249 -27
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +198 -92
- 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 +469 -20
- package/dist/src/Evolu/Timestamp.d.ts +83 -30
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +79 -34
- package/dist/src/Identicon.d.ts +35 -0
- package/dist/src/Identicon.d.ts.map +1 -0
- package/dist/src/Identicon.js +143 -0
- package/dist/src/Instances.d.ts +34 -0
- package/dist/src/Instances.d.ts.map +1 -0
- package/dist/src/Instances.js +44 -0
- package/dist/src/Number.d.ts +4 -3
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +5 -4
- package/dist/src/Object.d.ts +10 -4
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +9 -3
- package/dist/src/Platform.d.ts +20 -0
- package/dist/src/Platform.d.ts.map +1 -0
- package/dist/src/Platform.js +22 -0
- package/dist/src/Random.d.ts +3 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Relation.d.ts +79 -0
- package/dist/src/Relation.d.ts.map +1 -0
- package/dist/src/Relation.js +127 -0
- package/dist/src/Resources.d.ts +118 -0
- package/dist/src/Resources.d.ts.map +1 -0
- package/dist/src/Resources.js +197 -0
- package/dist/src/Result.d.ts +184 -52
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +30 -241
- package/dist/src/Skiplist.js +2 -1
- package/dist/src/Sqlite.d.ts +89 -5
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +136 -9
- package/dist/src/Task.d.ts +586 -0
- package/dist/src/Task.d.ts.map +1 -0
- package/dist/src/Task.js +469 -0
- package/dist/src/Time.d.ts +66 -1
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +99 -5
- package/dist/src/Type.d.ts +676 -343
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +718 -467
- package/dist/src/Types.d.ts +1 -75
- 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 -18
- 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 +8 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -3
- package/package.json +15 -14
- package/src/Array.ts +301 -19
- package/src/Assert.ts +0 -21
- package/src/Brand.ts +75 -0
- package/src/Buffer.ts +7 -7
- package/src/Cache.ts +85 -0
- package/src/Callbacks.ts +62 -22
- package/src/Console.ts +91 -11
- package/src/Crypto.ts +94 -90
- package/src/Evolu/Db.ts +519 -1026
- package/src/Evolu/Diff.ts +7 -5
- package/src/Evolu/Evolu.ts +477 -361
- package/src/Evolu/Internal.ts +0 -2
- package/src/Evolu/LocalAuth.ts +463 -0
- package/src/Evolu/Owner.ts +355 -228
- package/src/Evolu/Platform.ts +9 -9
- package/src/Evolu/Protocol.ts +842 -742
- package/src/Evolu/Public.ts +7 -14
- package/src/Evolu/PublicKysely.ts +4 -5
- package/src/Evolu/Query.ts +3 -2
- package/src/Evolu/Relay.ts +406 -103
- package/src/Evolu/Schema.ts +323 -91
- package/src/Evolu/Storage.ts +559 -137
- package/src/Evolu/Sync.ts +819 -36
- package/src/Evolu/Timestamp.ts +90 -58
- package/src/Identicon.ts +197 -0
- package/src/Instances.ts +90 -0
- package/src/Number.ts +6 -10
- package/src/Object.ts +13 -5
- package/src/Platform.ts +26 -0
- package/src/Random.ts +3 -2
- package/src/Relation.ts +234 -0
- package/src/Resources.ts +367 -0
- package/src/Result.ts +191 -54
- package/src/Skiplist.ts +1 -1
- package/src/Sqlite.ts +152 -17
- package/src/Task.ts +901 -0
- package/src/Time.ts +180 -5
- package/src/Type.ts +1135 -730
- package/src/Types.ts +1 -77
- package/src/WebSocket.ts +27 -25
- package/src/Worker.ts +72 -23
- package/src/index.ts +8 -3
- 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/ManyToManyMap.d.ts +0 -26
- package/dist/src/ManyToManyMap.d.ts.map +0 -1
- package/dist/src/ManyToManyMap.js +0 -92
- 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/Evolu/Config.ts +0 -83
- package/src/Evolu/Kysely.ts +0 -38
- package/src/ManyToManyMap.ts +0 -140
- package/src/NanoId.ts +0 -39
- package/src/Promise.ts +0 -295
|
@@ -1,10 +1,205 @@
|
|
|
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 { MaybeAsync } from "../Task.js";
|
|
7
|
+
import { NonNegativeInt, PositiveInt, TypeError } from "../Type.js";
|
|
8
|
+
import { BaseOwnerError, OwnerId, OwnerIdBytes, OwnerWriteKey } from "./Owner.js";
|
|
9
|
+
import { Timestamp, TimestampBytes } from "./Timestamp.js";
|
|
10
|
+
export interface StorageConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Callback called before an attempt to write, to check if an {@link OwnerId}
|
|
13
|
+
* has sufficient quota for the write.
|
|
14
|
+
*
|
|
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.
|
|
19
|
+
*
|
|
20
|
+
* The callback can be synchronous (for SQLite or in-memory checks) or
|
|
21
|
+
* asynchronous (for calling remote APIs).
|
|
22
|
+
*
|
|
23
|
+
* The callback returns a boolean rather than an error because error handling
|
|
24
|
+
* and logging are the responsibility of the callback implementation.
|
|
25
|
+
*
|
|
26
|
+
* ### Example
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* // Client
|
|
30
|
+
* // evolu.subscribeError
|
|
31
|
+
*
|
|
32
|
+
* // Relay
|
|
33
|
+
* isOwnerWithinQuota: (ownerId, requiredBytes) => {
|
|
34
|
+
* console.log(ownerId, requiredBytes);
|
|
35
|
+
* // Check error via evolu.subscribeError
|
|
36
|
+
* return true;
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
readonly isOwnerWithinQuota: (ownerId: OwnerId, requiredBytes: PositiveInt) => MaybeAsync<boolean>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Evolu Storage
|
|
44
|
+
*
|
|
45
|
+
* Evolu protocol using Storage is agnostic to storage implementation
|
|
46
|
+
* details—any storage can be plugged in, as long as it implements this
|
|
47
|
+
* interface. Implementations must handle their own errors; return values only
|
|
48
|
+
* indicate overall success or failure.
|
|
49
|
+
*
|
|
50
|
+
* The Storage API is synchronous because SQLite's synchronous API is the
|
|
51
|
+
* fastest way to use SQLite. Synchronous bindings (like better-sqlite3) call
|
|
52
|
+
* SQLite's C API directly with no context switching between the event loop and
|
|
53
|
+
* native code, and no promise microtasks or await overhead.
|
|
54
|
+
*
|
|
55
|
+
* The only exception is {@link Storage#writeMessages}, which is async to allow
|
|
56
|
+
* for async validation logic before writing to storage. The write operation
|
|
57
|
+
* itself remains synchronous.
|
|
58
|
+
*/
|
|
59
|
+
export interface Storage {
|
|
60
|
+
readonly getSize: (ownerId: OwnerIdBytes) => NonNegativeInt | null;
|
|
61
|
+
readonly fingerprint: (ownerId: OwnerIdBytes, begin: NonNegativeInt, end: NonNegativeInt) => Fingerprint | null;
|
|
62
|
+
/**
|
|
63
|
+
* Computes fingerprints with their upper bounds in one call.
|
|
64
|
+
*
|
|
65
|
+
* This function can be replaced with many fingerprint/findLowerBound calls,
|
|
66
|
+
* but implementations can leverage it for batching and more efficient
|
|
67
|
+
* fingerprint computation.
|
|
68
|
+
*/
|
|
69
|
+
readonly fingerprintRanges: (ownerId: OwnerIdBytes, buckets: ReadonlyArray<NonNegativeInt>, upperBound?: RangeUpperBound) => ReadonlyArray<FingerprintRange> | null;
|
|
70
|
+
readonly findLowerBound: (ownerId: OwnerIdBytes, begin: NonNegativeInt, end: NonNegativeInt, upperBound: RangeUpperBound) => NonNegativeInt | null;
|
|
71
|
+
readonly iterate: (ownerId: OwnerIdBytes, begin: NonNegativeInt, end: NonNegativeInt, callback: (timestamp: TimestampBytes, index: NonNegativeInt) => boolean) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Validates the {@link OwnerWriteKey} for the given {@link Owner}.
|
|
74
|
+
*
|
|
75
|
+
* Returns `true` if the write key is valid, `false` otherwise.
|
|
76
|
+
*/
|
|
77
|
+
readonly validateWriteKey: (ownerId: OwnerIdBytes, writeKey: OwnerWriteKey) => boolean;
|
|
78
|
+
/** Sets the {@link OwnerWriteKey} for the given {@link Owner}. */
|
|
79
|
+
readonly setWriteKey: (ownerId: OwnerIdBytes, writeKey: OwnerWriteKey) => boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Write encrypted {@link CrdtMessage}s to storage.
|
|
82
|
+
*
|
|
83
|
+
* Must use a mutex per ownerId to ensure sequential processing and proper
|
|
84
|
+
* protocol logic handling during sync operations.
|
|
85
|
+
*
|
|
86
|
+
* TODO: Use MaybeAsync
|
|
87
|
+
*/
|
|
88
|
+
readonly writeMessages: (ownerIdBytes: OwnerIdBytes, messages: NonEmptyReadonlyArray<EncryptedCrdtMessage>) => MaybeAsync<Result<void, StorageWriteError | StorageQuotaError>>;
|
|
89
|
+
/** Read encrypted {@link DbChange}s from storage. */
|
|
90
|
+
readonly readDbChange: (ownerId: OwnerIdBytes, timestamp: TimestampBytes) => EncryptedDbChange | null;
|
|
91
|
+
/**
|
|
92
|
+
* Delete all data for the given {@link Owner}.
|
|
93
|
+
*
|
|
94
|
+
* Returns `true` on success, `false` on failure.
|
|
95
|
+
*/
|
|
96
|
+
readonly deleteOwner: (ownerId: OwnerIdBytes) => boolean;
|
|
97
|
+
}
|
|
98
|
+
export interface StorageDep {
|
|
99
|
+
readonly storage: Storage;
|
|
100
|
+
}
|
|
101
|
+
/** Error indicating a serious write failure. */
|
|
102
|
+
export interface StorageWriteError extends BaseOwnerError {
|
|
103
|
+
readonly type: "StorageWriteError";
|
|
104
|
+
}
|
|
105
|
+
/** Error when storage or billing quota is exceeded. */
|
|
106
|
+
export interface StorageQuotaError extends BaseOwnerError {
|
|
107
|
+
readonly type: "StorageQuotaError";
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A cryptographic hash used for efficiently comparing collections of
|
|
111
|
+
* {@link TimestampBytes}s.
|
|
112
|
+
*
|
|
113
|
+
* It consists of the first {@link fingerprintSize} bytes of the SHA-256 hash of
|
|
114
|
+
* one or more timestamps.
|
|
115
|
+
*/
|
|
116
|
+
export type Fingerprint = Uint8Array & Brand<"Fingerprint">;
|
|
117
|
+
export declare const fingerprintSize: number & Brand<"Int"> & Brand<"NonNegative">;
|
|
118
|
+
/** A fingerprint of an empty range. */
|
|
119
|
+
export declare const zeroFingerprint: Fingerprint;
|
|
120
|
+
export interface BaseRange {
|
|
121
|
+
readonly upperBound: RangeUpperBound;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Union type for Range's upperBound: either a {@link TimestampBytes} or
|
|
125
|
+
* {@link InfiniteUpperBound}.
|
|
126
|
+
*/
|
|
127
|
+
export type RangeUpperBound = TimestampBytes | InfiniteUpperBound;
|
|
128
|
+
export declare const InfiniteUpperBound: unique symbol;
|
|
129
|
+
export type InfiniteUpperBound = typeof InfiniteUpperBound;
|
|
130
|
+
export declare const RangeType: {
|
|
131
|
+
readonly Fingerprint: 1;
|
|
132
|
+
readonly Skip: 0;
|
|
133
|
+
readonly Timestamps: 2;
|
|
134
|
+
};
|
|
135
|
+
export type RangeType = (typeof RangeType)[keyof typeof RangeType];
|
|
136
|
+
export interface SkipRange extends BaseRange {
|
|
137
|
+
readonly type: typeof RangeType.Skip;
|
|
138
|
+
}
|
|
139
|
+
export interface FingerprintRange extends BaseRange {
|
|
140
|
+
readonly type: typeof RangeType.Fingerprint;
|
|
141
|
+
readonly fingerprint: Fingerprint;
|
|
142
|
+
}
|
|
143
|
+
export interface TimestampsRange extends BaseRange {
|
|
144
|
+
readonly type: typeof RangeType.Timestamps;
|
|
145
|
+
readonly timestamps: ReadonlyArray<TimestampBytes>;
|
|
146
|
+
}
|
|
147
|
+
export type Range = SkipRange | FingerprintRange | TimestampsRange;
|
|
148
|
+
/** An encrypted {@link CrdtMessage}. */
|
|
149
|
+
export interface EncryptedCrdtMessage {
|
|
150
|
+
readonly timestamp: Timestamp;
|
|
151
|
+
readonly change: EncryptedDbChange;
|
|
152
|
+
}
|
|
153
|
+
/** Encrypted DbChange */
|
|
154
|
+
export type EncryptedDbChange = Uint8Array & Brand<"EncryptedDbChange">;
|
|
155
|
+
/**
|
|
156
|
+
* A CRDT message combining a unique {@link Timestamp} with a {@link DbChange}.
|
|
157
|
+
*
|
|
158
|
+
* Used in Evolu's sync protocol to replicate data changes across devices. Evolu
|
|
159
|
+
* operates as a durable queue, providing exactly-once delivery guarantees for
|
|
160
|
+
* reliable synchronization across application restarts and network failures.
|
|
161
|
+
*/
|
|
162
|
+
export interface CrdtMessage {
|
|
163
|
+
readonly timestamp: Timestamp;
|
|
164
|
+
readonly change: DbChange;
|
|
165
|
+
}
|
|
166
|
+
export declare const DbChangeValues: 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>]>>;
|
|
167
|
+
export type DbChangeValues = typeof DbChangeValues.Type;
|
|
168
|
+
export declare const ValidDbChangeValues: import("../Type.js").BrandType<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>]>>, "ValidDbChangeValues", ValidDbChangeValuesError, import("../Type.js").RecordError<import("../Type.js").StringError, never> | import("../Type.js").RecordError<import("../Type.js").StringError, import("../Type.js").UnionError<import("../Type.js").Uint8ArrayError | import("../Type.js").NumberError | import("../Type.js").StringError | import("../Type.js").NullError>>>;
|
|
169
|
+
export type ValidDbChangeValues = typeof ValidDbChangeValues.Type;
|
|
170
|
+
export interface ValidDbChangeValuesError extends TypeError<"ValidDbChangeValues"> {
|
|
171
|
+
readonly invalidColumns: ReadonlyArray<string>;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* A DbChange is a change to a table row. Together with a unique
|
|
175
|
+
* {@link Timestamp}, it forms a {@link CrdtMessage}.
|
|
176
|
+
*/
|
|
177
|
+
export declare const DbChange: import("../Type.js").ObjectType<{
|
|
178
|
+
table: import("../Type.js").Type<"String", string, string, import("../Type.js").StringError, string, import("../Type.js").StringError>;
|
|
179
|
+
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>;
|
|
180
|
+
values: import("../Type.js").BrandType<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>]>>, "ValidDbChangeValues", ValidDbChangeValuesError, import("../Type.js").RecordError<import("../Type.js").StringError, never> | import("../Type.js").RecordError<import("../Type.js").StringError, import("../Type.js").UnionError<import("../Type.js").Uint8ArrayError | import("../Type.js").NumberError | import("../Type.js").StringError | import("../Type.js").NullError>>>;
|
|
181
|
+
isInsert: import("../Type.js").Type<"Boolean", boolean, boolean, import("../Type.js").BooleanError, boolean, import("../Type.js").BooleanError>;
|
|
182
|
+
isDelete: import("../Type.js").UnionType<[import("../Type.js").Type<"Null", null, null, import("../Type.js").NullError, null, import("../Type.js").NullError>, import("../Type.js").Type<"Boolean", boolean, boolean, import("../Type.js").BooleanError, boolean, import("../Type.js").BooleanError>]>;
|
|
183
|
+
}>;
|
|
184
|
+
export type DbChange = typeof DbChange.Type;
|
|
1
185
|
/**
|
|
2
|
-
*
|
|
186
|
+
* Common interface for both client and relay SQLite storages.
|
|
3
187
|
*
|
|
4
188
|
* Evolu uses a Skiplist, which leverages SQLite indexes. The core logic is
|
|
5
189
|
* implemented in SQL, so it doesn't have to make roundtrips to the DB.
|
|
6
190
|
*
|
|
7
|
-
*
|
|
191
|
+
* While the SQL implementation may look sophisticated, it's conceptually simple
|
|
192
|
+
* and LLMs can explain how it works. The Skiplist data structure is well
|
|
193
|
+
* explained in [this Stack Overflow
|
|
194
|
+
* answer](https://stackoverflow.com/questions/61944198/what-is-a-zip-tree-and-how-does-it-work).
|
|
195
|
+
* The logic resembles [Negentropy's C++
|
|
196
|
+
* storage](https://github.com/hoytech/negentropy), except we use a Skiplist to
|
|
197
|
+
* leverage SQLite indexes, which makes the code simpler.
|
|
198
|
+
*
|
|
199
|
+
* Note: A paid review by the SQLite team is planned, as they use the same
|
|
200
|
+
* algorithm for their rsync tool.
|
|
201
|
+
*
|
|
202
|
+
* The ideal storage for a Relay should use an architecture like
|
|
8
203
|
* [strfry](https://github.com/hoytech/strfry) (a KV storage), but with Skiplist
|
|
9
204
|
* to ensure that insertion order doesn't matter (local-first apps can often
|
|
10
205
|
* write in the past.)
|
|
@@ -19,32 +214,59 @@
|
|
|
19
214
|
* each other, if necessary. One relay should handle hundreds of thousands of
|
|
20
215
|
* users, and when it goes down, nothing happens, because it will be
|
|
21
216
|
* synchronized later.
|
|
22
|
-
*
|
|
23
|
-
* @module
|
|
24
217
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
readonly
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
readonly deleteOwner: Storage["deleteOwner"];
|
|
40
|
-
}
|
|
41
|
-
export interface SqliteStorageBaseDep {
|
|
42
|
-
readonly storage: SqliteStorageBase;
|
|
43
|
-
}
|
|
44
|
-
export type SqliteStorageDeps = SqliteDep & RandomDep;
|
|
45
|
-
export interface CreateSqliteStorageBaseOptions {
|
|
218
|
+
export interface BaseSqliteStorage extends Pick<Storage, "getSize" | "fingerprint" | "fingerprintRanges" | "findLowerBound" | "iterate" | "deleteOwner"> {
|
|
219
|
+
/** Inserts a timestamp for an owner into the skiplist-based storage. */
|
|
220
|
+
readonly insertTimestamp: (ownerId: OwnerIdBytes, timestamp: TimestampBytes, strategy: StorageInsertTimestampStrategy) => Result<void, SqliteError>;
|
|
221
|
+
/**
|
|
222
|
+
* Efficiently checks which timestamps already exist in the database using a
|
|
223
|
+
* single CTE query instead of N individual queries.
|
|
224
|
+
*/
|
|
225
|
+
readonly getExistingTimestamps: (ownerIdBytes: OwnerIdBytes, timestampsBytes: NonEmptyReadonlyArray<TimestampBytes>) => Result<ReadonlyArray<TimestampBytes>, SqliteError>;
|
|
226
|
+
}
|
|
227
|
+
export interface BaseSqliteStorageDep {
|
|
228
|
+
readonly storage: BaseSqliteStorage;
|
|
229
|
+
}
|
|
230
|
+
export type SqliteStorageDeps = RandomDep & SqliteDep;
|
|
231
|
+
export interface CreateBaseSqliteStorageConfig extends StorageConfig {
|
|
46
232
|
onStorageError: (error: SqliteError) => void;
|
|
47
233
|
}
|
|
48
|
-
|
|
49
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Creates a {@link BaseSqliteStorage} implementation.
|
|
236
|
+
*
|
|
237
|
+
* # Stateless Design
|
|
238
|
+
*
|
|
239
|
+
* This implementation is fully stateless - it requires no in-memory state
|
|
240
|
+
* between invocations. All necessary metadata (timestamp bounds for insertion
|
|
241
|
+
* strategy optimization) is persisted in the evolu_usage table. This makes
|
|
242
|
+
* Evolu Relay suitable for stateless serverless environments like AWS Lambda,
|
|
243
|
+
* Cloudflare Workers with Durable Objects, and other platforms where memory
|
|
244
|
+
* doesn't persist between requests. While not extensively tested in all these
|
|
245
|
+
* environments yet, the stateless design should work well across them.
|
|
246
|
+
*/
|
|
247
|
+
export declare const createBaseSqliteStorage: (deps: SqliteStorageDeps) => (config: CreateBaseSqliteStorageConfig) => BaseSqliteStorage;
|
|
248
|
+
export declare const createBaseSqliteStorageTables: (deps: SqliteDep) => Result<void, SqliteError>;
|
|
249
|
+
export type StorageInsertTimestampStrategy = "append" | "prepend" | "insert";
|
|
250
|
+
/**
|
|
251
|
+
* Determines the insertion strategy for a timestamp based on its position
|
|
252
|
+
* relative to the current first and last timestamps.
|
|
253
|
+
*
|
|
254
|
+
* Returns a tuple with the strategy and updated timestamp bounds.
|
|
255
|
+
*/
|
|
256
|
+
export declare const getTimestampInsertStrategy: (timestamp: TimestampBytes, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes) => [strategy: StorageInsertTimestampStrategy, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes];
|
|
257
|
+
export declare const timestampBytesToFingerprint: (timestamp: TimestampBytes) => Fingerprint;
|
|
258
|
+
export declare const getTimestampByIndex: (deps: SqliteDep) => (ownerId: OwnerIdBytes, index: NonNegativeInt) => Result<TimestampBytes, SqliteError>;
|
|
259
|
+
/** Retrieves usage information for an owner from the evolu_usage table. */
|
|
260
|
+
export declare const getOwnerUsage: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, initialTimestamp: TimestampBytes) => Result<{
|
|
261
|
+
storedBytes: NonNegativeInt | null;
|
|
262
|
+
firstTimestamp: TimestampBytes;
|
|
263
|
+
lastTimestamp: TimestampBytes;
|
|
264
|
+
}, SqliteError>;
|
|
265
|
+
/**
|
|
266
|
+
* Updates timestamp bounds in evolu_usage table.
|
|
267
|
+
*
|
|
268
|
+
* Used by both relay and client to maintain firstTimestamp/lastTimestamp after
|
|
269
|
+
* processing messages.
|
|
270
|
+
*/
|
|
271
|
+
export declare const updateOwnerUsage: (deps: SqliteDep) => (ownerIdBytes: OwnerIdBytes, storedBytes: PositiveInt, firstTimestamp: TimestampBytes, lastTimestamp: TimestampBytes) => Result<void, SqliteError>;
|
|
50
272
|
//# 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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"}
|