@evolu/common 8.10.0 → 8.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/Bytes.d.ts +39 -2
- package/dist/src/Bytes.d.ts.map +1 -1
- package/dist/src/Bytes.js +50 -2
- package/dist/src/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 +412 -213
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +181 -18
- 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 +106 -19
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +162 -60
- 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/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +4 -2
- package/dist/src/local-first/Schema.d.ts +346 -23
- 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 +195 -17
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +85 -22
- 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/Bytes.test.ts +27 -0
- package/src/Bytes.ts +58 -2
- 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 +20 -6
- package/src/local-first/Db.ts +644 -339
- package/src/local-first/Evolu.test.ts +994 -22
- package/src/local-first/Evolu.ts +625 -232
- package/src/local-first/Owner.ts +13 -30
- package/src/local-first/Protocol.test.ts +634 -10
- package/src/local-first/Protocol.ts +255 -109
- package/src/local-first/Query.ts +8 -15
- package/src/local-first/Relay.ts +4 -2
- package/src/local-first/Schema.test.ts +143 -0
- package/src/local-first/Schema.ts +376 -26
- package/src/local-first/Shared.test.ts +7731 -559
- package/src/local-first/Shared.ts +2036 -267
- package/src/local-first/Storage.ts +224 -36
- 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
|
@@ -23,13 +23,18 @@ import {
|
|
|
23
23
|
type SqliteSchema,
|
|
24
24
|
SqliteValue,
|
|
25
25
|
} from "../Sqlite.ts";
|
|
26
|
+
import type { Millis } from "../Time.ts";
|
|
26
27
|
import {
|
|
27
28
|
assertType,
|
|
29
|
+
createIdFromString,
|
|
28
30
|
DateIso,
|
|
29
31
|
type FiniteNumber,
|
|
30
32
|
type Id,
|
|
33
|
+
id,
|
|
31
34
|
IdBytes,
|
|
32
35
|
type InferType,
|
|
36
|
+
NonEmptyTrimmedString100,
|
|
37
|
+
type NonEmptyTrimmedString1000,
|
|
33
38
|
Null,
|
|
34
39
|
nullOr,
|
|
35
40
|
object,
|
|
@@ -38,8 +43,14 @@ import {
|
|
|
38
43
|
type withDefault,
|
|
39
44
|
} from "../Type.ts";
|
|
40
45
|
import type { CompileTimeError, Simplify } from "../Types.ts";
|
|
41
|
-
import type {
|
|
42
|
-
import {
|
|
46
|
+
import type { Evolu, maxMutationSize } from "./Evolu.ts";
|
|
47
|
+
import type { AppOwner, OwnerIdBytes } from "./Owner.ts";
|
|
48
|
+
import {
|
|
49
|
+
OwnerEncryptionKey,
|
|
50
|
+
OwnerId,
|
|
51
|
+
OwnerSecret,
|
|
52
|
+
OwnerWriteKey,
|
|
53
|
+
} from "./Owner.ts";
|
|
43
54
|
import type {
|
|
44
55
|
evoluJsonArrayFrom,
|
|
45
56
|
evoluJsonObjectFrom,
|
|
@@ -49,7 +60,11 @@ import type {
|
|
|
49
60
|
import type { CrdtMessage, DbChange } from "./Storage.ts";
|
|
50
61
|
import { TimestampBytes } from "./Timestamp.ts";
|
|
51
62
|
|
|
52
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Any Standard Schema V1 declaration.
|
|
65
|
+
*
|
|
66
|
+
* @group Core
|
|
67
|
+
*/
|
|
53
68
|
export type AnyStandardSchemaV1 = StandardSchemaV1<any, any>;
|
|
54
69
|
|
|
55
70
|
/**
|
|
@@ -121,6 +136,8 @@ export type AnyStandardSchemaV1 = StandardSchemaV1<any, any>;
|
|
|
121
136
|
* };
|
|
122
137
|
* assertTrue(ZodSchema.todo.title.safeParse("Write docs").success);
|
|
123
138
|
* ```
|
|
139
|
+
*
|
|
140
|
+
* @group Core
|
|
124
141
|
*/
|
|
125
142
|
export type EvoluSchema = ReadonlyRecord<
|
|
126
143
|
string,
|
|
@@ -128,9 +145,127 @@ export type EvoluSchema = ReadonlyRecord<
|
|
|
128
145
|
TableSchema
|
|
129
146
|
>;
|
|
130
147
|
|
|
131
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* A table schema: column names mapped to Standard Schema validators.
|
|
150
|
+
*
|
|
151
|
+
* @group Core
|
|
152
|
+
*/
|
|
132
153
|
export type TableSchema = ReadonlyRecord<string, AnyStandardSchemaV1>;
|
|
133
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Whether a table is local-only: its name starts with an underscore, so its
|
|
157
|
+
* changes are stored without synchronization.
|
|
158
|
+
*
|
|
159
|
+
* @group Core
|
|
160
|
+
*/
|
|
161
|
+
export const isLocalOnlyTable = (table: string): boolean =>
|
|
162
|
+
table.startsWith("_");
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Todo ID Type for {@link testEvoluSchema}.
|
|
166
|
+
*
|
|
167
|
+
* @group Testing
|
|
168
|
+
*/
|
|
169
|
+
export const TestTodoId = /*#__PURE__*/ id("Todo");
|
|
170
|
+
export type TestTodoId = typeof TestTodoId.Output;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Deterministic {@link TestTodoId} for tests and examples.
|
|
174
|
+
*
|
|
175
|
+
* @group Testing
|
|
176
|
+
*/
|
|
177
|
+
export const testTodoId = /*#__PURE__*/ TestTodoId.orThrow(
|
|
178
|
+
/*#__PURE__*/ createIdFromString("testTodo"),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Project ID Type for {@link testEvoluSchema}.
|
|
183
|
+
*
|
|
184
|
+
* @group Testing
|
|
185
|
+
*/
|
|
186
|
+
export const TestProjectId = /*#__PURE__*/ id("Project");
|
|
187
|
+
export type TestProjectId = typeof TestProjectId.Output;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Deterministic {@link TestProjectId} for tests and examples.
|
|
191
|
+
*
|
|
192
|
+
* @group Testing
|
|
193
|
+
*/
|
|
194
|
+
export const testProjectId = /*#__PURE__*/ TestProjectId.orThrow(
|
|
195
|
+
/*#__PURE__*/ createIdFromString("testProject"),
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Todo and project schema for tests and examples. A todo can belong to a
|
|
200
|
+
* project or have no project. Use an explicit schema when teaching schema
|
|
201
|
+
* definition.
|
|
202
|
+
*
|
|
203
|
+
* ### Example
|
|
204
|
+
*
|
|
205
|
+
* ```ts
|
|
206
|
+
* import {
|
|
207
|
+
* assertOk,
|
|
208
|
+
* testEvoluSchema,
|
|
209
|
+
* testProjectId,
|
|
210
|
+
* testTodoId,
|
|
211
|
+
* } from "@evolu/common";
|
|
212
|
+
*
|
|
213
|
+
* assertOk(testEvoluSchema.todo.id.from(testTodoId), testTodoId);
|
|
214
|
+
* assertOk(
|
|
215
|
+
* testEvoluSchema.todo.projectId.from(testProjectId),
|
|
216
|
+
* testProjectId,
|
|
217
|
+
* );
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* @group Testing
|
|
221
|
+
*/
|
|
222
|
+
export const testEvoluSchema = {
|
|
223
|
+
todo: {
|
|
224
|
+
id: TestTodoId,
|
|
225
|
+
title: NonEmptyTrimmedString100,
|
|
226
|
+
isCompleted: /*#__PURE__*/ nullOr(SqliteBoolean),
|
|
227
|
+
projectId: /*#__PURE__*/ nullOr(TestProjectId),
|
|
228
|
+
},
|
|
229
|
+
project: {
|
|
230
|
+
id: TestProjectId,
|
|
231
|
+
name: NonEmptyTrimmedString100,
|
|
232
|
+
},
|
|
233
|
+
} as const satisfies EvoluSchema;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Schema type of {@link testEvoluSchema}.
|
|
237
|
+
*
|
|
238
|
+
* @group Testing
|
|
239
|
+
*/
|
|
240
|
+
export type TestEvoluSchema = typeof testEvoluSchema;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* App-owner registry schema with local tables for tests and examples.
|
|
244
|
+
*
|
|
245
|
+
* Stores operational keys separately from optional recovery material. A null
|
|
246
|
+
* secret represents an owner whose recovery material is managed elsewhere or
|
|
247
|
+
* unavailable. Names are optional device-local labels; identicons can be
|
|
248
|
+
* derived from the owner identity without an additional column.
|
|
249
|
+
*
|
|
250
|
+
* This fixture does not define the production registry's persistence contract.
|
|
251
|
+
*
|
|
252
|
+
* @group Testing
|
|
253
|
+
*/
|
|
254
|
+
export const testLocalOnlyEvoluSchema = {
|
|
255
|
+
_appOwner: {
|
|
256
|
+
id: OwnerId,
|
|
257
|
+
encryptionKey: OwnerEncryptionKey,
|
|
258
|
+
writeKey: OwnerWriteKey,
|
|
259
|
+
secret: /*#__PURE__*/ nullOr(OwnerSecret),
|
|
260
|
+
name: /*#__PURE__*/ nullOr(NonEmptyTrimmedString100),
|
|
261
|
+
},
|
|
262
|
+
} as const satisfies EvoluSchema;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Dependency wrapper for {@link SqliteSchema}.
|
|
266
|
+
*
|
|
267
|
+
* @group SQLite
|
|
268
|
+
*/
|
|
134
269
|
export interface SqliteSchemaDep {
|
|
135
270
|
readonly sqliteSchema: SqliteSchema;
|
|
136
271
|
}
|
|
@@ -147,6 +282,8 @@ export interface SqliteSchemaDep {
|
|
|
147
282
|
* 2. The 'id' column output type must extend {@link Id}
|
|
148
283
|
* 3. Tables cannot use system column names (createdAt, updatedAt, isDeleted)
|
|
149
284
|
* 4. All column output types must be compatible with SQLite (extend SqliteValue)
|
|
285
|
+
*
|
|
286
|
+
* @group Validation
|
|
150
287
|
*/
|
|
151
288
|
export type ValidateSchema<S extends EvoluSchema> =
|
|
152
289
|
ValidateSchemaHasId<S> extends never
|
|
@@ -159,10 +296,106 @@ export type ValidateSchema<S extends EvoluSchema> =
|
|
|
159
296
|
: ValidateIdColumnType<S>
|
|
160
297
|
: ValidateSchemaHasId<S>;
|
|
161
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Defines SQLite indexes with Kysely's index builder.
|
|
301
|
+
*
|
|
302
|
+
* @group SQLite
|
|
303
|
+
*/
|
|
162
304
|
export type IndexesConfig = (
|
|
163
305
|
create: (indexName: string) => Kysely.CreateIndexBuilder,
|
|
164
306
|
) => ReadonlyArray<Kysely.CreateIndexBuilder<any>>;
|
|
165
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Why a message is stored in `evolu_message_quarantine` instead of being
|
|
310
|
+
* applied to its table. Persisted codes: names may change, but numbers must not
|
|
311
|
+
* be reassigned.
|
|
312
|
+
*
|
|
313
|
+
* Quarantine is queryable state, not an error. An application subscribes to a
|
|
314
|
+
* query over `evolu_message_quarantine` to tell the user what is waiting. Each
|
|
315
|
+
* row is one column of a message, so distinct `ownerId` and `timestamp` pairs
|
|
316
|
+
* count messages. `origin` records whether this database stamped the message
|
|
317
|
+
* for a local mutation or received it from sync, see {@link QuarantineOrigin}.
|
|
318
|
+
* `quarantinedAt` is the system time captured for the request that quarantined
|
|
319
|
+
* the row, in milliseconds; it is null for rows written before Evolu recorded
|
|
320
|
+
* it. The clock-drift rules are described in the Timestamp module.
|
|
321
|
+
*
|
|
322
|
+
* ### Example
|
|
323
|
+
*
|
|
324
|
+
* ```ts
|
|
325
|
+
* import {
|
|
326
|
+
* assertType,
|
|
327
|
+
* createQueryBuilder,
|
|
328
|
+
* type Millis,
|
|
329
|
+
* type OwnerIdBytes,
|
|
330
|
+
* type QuarantineOrigin,
|
|
331
|
+
* QuarantineReason,
|
|
332
|
+
* testEvoluSchema,
|
|
333
|
+
* type TimestampBytes,
|
|
334
|
+
* } from "@evolu/common";
|
|
335
|
+
*
|
|
336
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
337
|
+
*
|
|
338
|
+
* // Messages waiting in drift quarantine, one row per message.
|
|
339
|
+
* const driftQuarantineQuery = createQuery((db) =>
|
|
340
|
+
* db
|
|
341
|
+
* .selectFrom("evolu_message_quarantine")
|
|
342
|
+
* .select(["ownerId", "timestamp", "origin", "quarantinedAt"])
|
|
343
|
+
* .where("reason", "=", QuarantineReason.TimestampDrift)
|
|
344
|
+
* .distinct(),
|
|
345
|
+
* );
|
|
346
|
+
*
|
|
347
|
+
* assertType<
|
|
348
|
+
* typeof driftQuarantineQuery.Row,
|
|
349
|
+
* {
|
|
350
|
+
* ownerId: OwnerIdBytes;
|
|
351
|
+
* timestamp: TimestampBytes;
|
|
352
|
+
* origin: QuarantineOrigin;
|
|
353
|
+
* quarantinedAt: Millis | null;
|
|
354
|
+
* }
|
|
355
|
+
* >();
|
|
356
|
+
* ```
|
|
357
|
+
*
|
|
358
|
+
* @group Queries
|
|
359
|
+
*/
|
|
360
|
+
export const QuarantineReason = {
|
|
361
|
+
/**
|
|
362
|
+
* The message has a table or column the current schema does not define. It is
|
|
363
|
+
* applied automatically once a schema update defines them. A received change
|
|
364
|
+
* to a {@link isLocalOnlyTable | local-only} table is also stored here and is
|
|
365
|
+
* never applied.
|
|
366
|
+
*/
|
|
367
|
+
Schema: 0,
|
|
368
|
+
/**
|
|
369
|
+
* The message's timestamp exceeded the drift limit when it was stored. It is
|
|
370
|
+
* applied when the database worker starts, once system time comes within the
|
|
371
|
+
* limit of the timestamp.
|
|
372
|
+
*/
|
|
373
|
+
TimestampDrift: 1,
|
|
374
|
+
} as const;
|
|
375
|
+
|
|
376
|
+
export type QuarantineReason =
|
|
377
|
+
(typeof QuarantineReason)[keyof typeof QuarantineReason];
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Whether a quarantined message was stamped by this database for a local
|
|
381
|
+
* mutation or received from sync. Persisted codes: names may change, but
|
|
382
|
+
* numbers must not be reassigned.
|
|
383
|
+
*
|
|
384
|
+
* @group Queries
|
|
385
|
+
*/
|
|
386
|
+
export const QuarantineOrigin = {
|
|
387
|
+
LocalMutation: 0,
|
|
388
|
+
ReceivedMessage: 1,
|
|
389
|
+
} as const;
|
|
390
|
+
|
|
391
|
+
export type QuarantineOrigin =
|
|
392
|
+
(typeof QuarantineOrigin)[keyof typeof QuarantineOrigin];
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Typed query factory returned by {@link createQueryBuilder}.
|
|
396
|
+
*
|
|
397
|
+
* @group Queries
|
|
398
|
+
*/
|
|
166
399
|
export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
|
|
167
400
|
queryCallback: (
|
|
168
401
|
db: Pick<
|
|
@@ -175,6 +408,7 @@ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
|
|
|
175
408
|
} & SystemColumns;
|
|
176
409
|
} & {
|
|
177
410
|
readonly evolu_history: {
|
|
411
|
+
readonly ownerId: OwnerIdBytes;
|
|
178
412
|
readonly timestamp: TimestampBytes;
|
|
179
413
|
readonly table: keyof S;
|
|
180
414
|
readonly id: IdBytes;
|
|
@@ -182,11 +416,15 @@ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
|
|
|
182
416
|
readonly value: SqliteValue;
|
|
183
417
|
};
|
|
184
418
|
readonly evolu_message_quarantine: {
|
|
419
|
+
readonly ownerId: OwnerIdBytes;
|
|
185
420
|
readonly timestamp: TimestampBytes;
|
|
186
421
|
readonly table: string;
|
|
187
422
|
readonly id: IdBytes;
|
|
188
423
|
readonly column: string;
|
|
189
424
|
readonly value: SqliteValue;
|
|
425
|
+
readonly reason: QuarantineReason;
|
|
426
|
+
readonly origin: QuarantineOrigin;
|
|
427
|
+
readonly quarantinedAt: Millis | null;
|
|
190
428
|
};
|
|
191
429
|
}
|
|
192
430
|
>,
|
|
@@ -204,6 +442,8 @@ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
|
|
|
204
442
|
* - `isDeleted`: Soft delete flag created by Evolu and used by the developer to
|
|
205
443
|
* mark rows as deleted.
|
|
206
444
|
* - `ownerId`: Represents ownership and logically partitions the database.
|
|
445
|
+
*
|
|
446
|
+
* @group Core
|
|
207
447
|
*/
|
|
208
448
|
export const SystemColumns: ObjectType<{
|
|
209
449
|
readonly createdAt: typeof DateIso;
|
|
@@ -218,6 +458,11 @@ export const SystemColumns: ObjectType<{
|
|
|
218
458
|
});
|
|
219
459
|
export interface SystemColumns extends InferType<typeof SystemColumns> {}
|
|
220
460
|
|
|
461
|
+
/**
|
|
462
|
+
* Kind of a {@link Mutation}: insert, update, or upsert.
|
|
463
|
+
*
|
|
464
|
+
* @group Mutations
|
|
465
|
+
*/
|
|
221
466
|
export type MutationKind = "insert" | "update" | "upsert";
|
|
222
467
|
|
|
223
468
|
/**
|
|
@@ -232,13 +477,24 @@ export type MutationKind = "insert" | "update" | "upsert";
|
|
|
232
477
|
* inadvertently generate a large volume of CRDT messages. Each mutation
|
|
233
478
|
* produces exactly one {@link CrdtMessage} containing all provided columns.
|
|
234
479
|
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
480
|
+
* Each mutation must fit within {@link maxMutationSize}. Give every column a
|
|
481
|
+
* Type with a maximum length, such as {@link NonEmptyTrimmedString1000} or
|
|
482
|
+
* `maxLength(100_000)(Uint8Array)`, so that a table's largest values add up to
|
|
483
|
+
* less than the limit and input that is too large is rejected where it enters
|
|
484
|
+
* the app. A larger mutation throws before anything is saved, so the code after
|
|
485
|
+
* it does not run. Check unbounded input with {@link Evolu.getMutationSize}.
|
|
486
|
+
* Large binary data, such as images or videos, does not belong in a single
|
|
487
|
+
* mutation; a chunked API for it is planned.
|
|
488
|
+
*
|
|
489
|
+
* Binary values are copied when the mutation is made, so later changes to a
|
|
490
|
+
* `Uint8Array` do not change what is saved.
|
|
237
491
|
*
|
|
238
492
|
* - **insert**: all non-nullable columns required, nullable columns optional,
|
|
239
493
|
* `id` omitted (auto-generated)
|
|
240
494
|
* - **update**: only `id` required, everything else optional
|
|
241
495
|
* - **upsert**: like insert but `id` required too
|
|
496
|
+
*
|
|
497
|
+
* @group Mutations
|
|
242
498
|
*/
|
|
243
499
|
export type Mutation<S extends EvoluSchema, Kind extends MutationKind> = <
|
|
244
500
|
TableName extends keyof S,
|
|
@@ -248,11 +504,26 @@ export type Mutation<S extends EvoluSchema, Kind extends MutationKind> = <
|
|
|
248
504
|
options?: MutationOptions,
|
|
249
505
|
) => { readonly id: StandardSchemaV1.InferOutput<S[TableName]["id"]> };
|
|
250
506
|
|
|
507
|
+
/**
|
|
508
|
+
* Options accepted by every {@link Mutation}.
|
|
509
|
+
*
|
|
510
|
+
* @group Mutations
|
|
511
|
+
*/
|
|
251
512
|
export interface MutationOptions {
|
|
252
513
|
/**
|
|
253
|
-
* Called after the mutation
|
|
254
|
-
* Useful for
|
|
255
|
-
* insert, update, or upsert.
|
|
514
|
+
* Called after the mutation's changes are stored and subscribed queries
|
|
515
|
+
* reflect them. Useful for follow-up work (e.g., notifications, navigation)
|
|
516
|
+
* after insert, update, or upsert. It never runs when the database is
|
|
517
|
+
* unavailable.
|
|
518
|
+
*
|
|
519
|
+
* Stored does not always mean visible. A change quarantined for
|
|
520
|
+
* {@link QuarantineReason.TimestampDrift} is stored in
|
|
521
|
+
* `evolu_message_quarantine` without changing application rows. `onComplete`
|
|
522
|
+
* still fires after storage commits; no drift error is reported. Applications
|
|
523
|
+
* can subscribe to the quarantine table; those query results reflect the
|
|
524
|
+
* change before `onComplete` runs. See the
|
|
525
|
+
* {@link @evolu/common!"local-first/Timestamp" | Timestamp module} for drift
|
|
526
|
+
* and release behavior.
|
|
256
527
|
*/
|
|
257
528
|
readonly onComplete?: () => void;
|
|
258
529
|
|
|
@@ -317,6 +588,12 @@ export interface MutationOptions {
|
|
|
317
588
|
readonly ownerId?: OwnerId;
|
|
318
589
|
}
|
|
319
590
|
|
|
591
|
+
/**
|
|
592
|
+
* Database change produced by a {@link Mutation}, attributed to the
|
|
593
|
+
* {@link OwnerId} that owns the row.
|
|
594
|
+
*
|
|
595
|
+
* @group Mutations
|
|
596
|
+
*/
|
|
320
597
|
export interface MutationChange extends DbChange {
|
|
321
598
|
readonly ownerId: OwnerId;
|
|
322
599
|
}
|
|
@@ -324,6 +601,8 @@ export interface MutationChange extends DbChange {
|
|
|
324
601
|
/**
|
|
325
602
|
* Derives the expected values type for a mutation from a table's column schemas
|
|
326
603
|
* and a {@link MutationKind}.
|
|
604
|
+
*
|
|
605
|
+
* @group Mutations
|
|
327
606
|
*/
|
|
328
607
|
export type MutationValues<
|
|
329
608
|
T extends TableSchema,
|
|
@@ -339,6 +618,8 @@ export type MutationValues<
|
|
|
339
618
|
/**
|
|
340
619
|
* Insert values: `id` omitted (auto-generated), nullable columns optional,
|
|
341
620
|
* non-nullable columns required.
|
|
621
|
+
*
|
|
622
|
+
* @group Mutations
|
|
342
623
|
*/
|
|
343
624
|
export type InsertValues<T extends TableSchema> = Omit<
|
|
344
625
|
NullableColumnsToOptional<T>,
|
|
@@ -348,6 +629,8 @@ export type InsertValues<T extends TableSchema> = Omit<
|
|
|
348
629
|
/**
|
|
349
630
|
* Update values: `id` required, all other columns optional. Includes
|
|
350
631
|
* `isDeleted` for soft deletes.
|
|
632
|
+
*
|
|
633
|
+
* @group Mutations
|
|
351
634
|
*/
|
|
352
635
|
export type UpdateValues<T extends TableSchema> = {
|
|
353
636
|
readonly id: StandardSchemaV1.InferOutput<T["id"]>;
|
|
@@ -360,12 +643,19 @@ export type UpdateValues<T extends TableSchema> = {
|
|
|
360
643
|
/**
|
|
361
644
|
* Upsert values: `id` required, nullable columns optional, non-nullable columns
|
|
362
645
|
* required. Includes `isDeleted` for soft deletes.
|
|
646
|
+
*
|
|
647
|
+
* @group Mutations
|
|
363
648
|
*/
|
|
364
649
|
export type UpsertValues<T extends TableSchema> =
|
|
365
650
|
NullableColumnsToOptional<T> & {
|
|
366
651
|
readonly isDeleted?: SqliteBoolean;
|
|
367
652
|
};
|
|
368
653
|
|
|
654
|
+
/**
|
|
655
|
+
* Requires an `id` column in every table.
|
|
656
|
+
*
|
|
657
|
+
* @group Validation
|
|
658
|
+
*/
|
|
369
659
|
export type ValidateSchemaHasId<S extends EvoluSchema> =
|
|
370
660
|
keyof S extends infer TableName
|
|
371
661
|
? TableName extends keyof S
|
|
@@ -375,6 +665,11 @@ export type ValidateSchemaHasId<S extends EvoluSchema> =
|
|
|
375
665
|
: never
|
|
376
666
|
: never;
|
|
377
667
|
|
|
668
|
+
/**
|
|
669
|
+
* Requires every `id` column output type to extend {@link Id}.
|
|
670
|
+
*
|
|
671
|
+
* @group Validation
|
|
672
|
+
*/
|
|
378
673
|
export type ValidateIdColumnType<S extends EvoluSchema> =
|
|
379
674
|
keyof S extends infer TableName
|
|
380
675
|
? TableName extends keyof S
|
|
@@ -386,6 +681,11 @@ export type ValidateIdColumnType<S extends EvoluSchema> =
|
|
|
386
681
|
: never
|
|
387
682
|
: never;
|
|
388
683
|
|
|
684
|
+
/**
|
|
685
|
+
* Rejects tables that define system column names.
|
|
686
|
+
*
|
|
687
|
+
* @group Validation
|
|
688
|
+
*/
|
|
389
689
|
export type ValidateNoSystemColumns<S extends EvoluSchema> =
|
|
390
690
|
keyof S extends infer TableName
|
|
391
691
|
? TableName extends keyof S
|
|
@@ -400,6 +700,11 @@ export type ValidateNoSystemColumns<S extends EvoluSchema> =
|
|
|
400
700
|
: never
|
|
401
701
|
: never;
|
|
402
702
|
|
|
703
|
+
/**
|
|
704
|
+
* Requires every column output type to be compatible with SQLite.
|
|
705
|
+
*
|
|
706
|
+
* @group Validation
|
|
707
|
+
*/
|
|
403
708
|
export type ValidateColumnTypes<S extends EvoluSchema> =
|
|
404
709
|
keyof S extends infer TableName
|
|
405
710
|
? TableName extends keyof S
|
|
@@ -414,36 +719,69 @@ export type ValidateColumnTypes<S extends EvoluSchema> =
|
|
|
414
719
|
: never
|
|
415
720
|
: never;
|
|
416
721
|
|
|
417
|
-
/**
|
|
722
|
+
/**
|
|
723
|
+
* Schema validation error that shows clear, readable messages
|
|
724
|
+
*
|
|
725
|
+
* @group Validation
|
|
726
|
+
*/
|
|
418
727
|
export type SchemaValidationError<Message extends string> = CompileTimeError<
|
|
419
728
|
"Schema",
|
|
420
729
|
Message
|
|
421
730
|
>;
|
|
422
731
|
|
|
423
|
-
/**
|
|
732
|
+
/**
|
|
733
|
+
* Makes columns whose output type includes `null` optional.
|
|
734
|
+
*
|
|
735
|
+
* @group Mutations
|
|
736
|
+
*/
|
|
424
737
|
export type NullableColumnsToOptional<T extends TableSchema> = {
|
|
425
738
|
readonly [K in RequiredColumnKeys<T>]: StandardSchemaV1.InferOutput<T[K]>;
|
|
426
739
|
} & {
|
|
427
740
|
readonly [K in OptionalColumnKeys<T>]?: StandardSchemaV1.InferOutput<T[K]>;
|
|
428
741
|
};
|
|
429
742
|
|
|
743
|
+
/**
|
|
744
|
+
* Column names whose output type excludes `null`.
|
|
745
|
+
*
|
|
746
|
+
* @group Mutations
|
|
747
|
+
*/
|
|
430
748
|
export type RequiredColumnKeys<T extends TableSchema> = {
|
|
431
749
|
[K in keyof T]: null extends StandardSchemaV1.InferOutput<T[K]> ? never : K;
|
|
432
750
|
}[keyof T];
|
|
433
751
|
|
|
752
|
+
/**
|
|
753
|
+
* Column names whose output type includes `null`.
|
|
754
|
+
*
|
|
755
|
+
* @group Mutations
|
|
756
|
+
*/
|
|
434
757
|
export type OptionalColumnKeys<T extends TableSchema> = {
|
|
435
758
|
[K in keyof T]: null extends StandardSchemaV1.InferOutput<T[K]> ? K : never;
|
|
436
759
|
}[keyof T];
|
|
437
760
|
|
|
761
|
+
/**
|
|
762
|
+
* Names of {@link SystemColumns}.
|
|
763
|
+
*
|
|
764
|
+
* @group Core
|
|
765
|
+
*/
|
|
438
766
|
export const systemColumns: ReadonlySet<string> = /*#__PURE__*/ new Set(
|
|
439
767
|
/*#__PURE__*/ Object.keys(SystemColumns.props),
|
|
440
768
|
);
|
|
441
769
|
|
|
770
|
+
/**
|
|
771
|
+
* Names of {@link SystemColumns} together with `id`.
|
|
772
|
+
*
|
|
773
|
+
* @group Core
|
|
774
|
+
*/
|
|
442
775
|
export const systemColumnsWithId: ReadonlyArray<string> = [
|
|
443
776
|
...systemColumns,
|
|
444
777
|
"id",
|
|
445
778
|
];
|
|
446
779
|
|
|
780
|
+
/**
|
|
781
|
+
* Derives {@link SqliteSchema} tables and indexes from an {@link EvoluSchema}.
|
|
782
|
+
*
|
|
783
|
+
* @group SQLite
|
|
784
|
+
*/
|
|
447
785
|
export const evoluSchemaToSqliteSchema = <S extends EvoluSchema>(
|
|
448
786
|
schema: ValidateSchema<S> extends never ? S : ValidateSchema<S>,
|
|
449
787
|
indexesConfig?: IndexesConfig,
|
|
@@ -480,24 +818,14 @@ export const evoluSchemaToSqliteSchema = <S extends EvoluSchema>(
|
|
|
480
818
|
* import {
|
|
481
819
|
* assertType,
|
|
482
820
|
* createQueryBuilder,
|
|
483
|
-
*
|
|
821
|
+
* testEvoluSchema,
|
|
822
|
+
* type TestTodoId,
|
|
484
823
|
* NonEmptyTrimmedString100,
|
|
485
|
-
* nullOr,
|
|
486
824
|
* SqliteBoolean,
|
|
487
825
|
* } from "@evolu/common";
|
|
488
826
|
*
|
|
489
|
-
* const TodoId = id("Todo");
|
|
490
|
-
* type TodoId = typeof TodoId.Output;
|
|
491
|
-
* const Schema = {
|
|
492
|
-
* todo: {
|
|
493
|
-
* id: TodoId,
|
|
494
|
-
* title: NonEmptyTrimmedString100,
|
|
495
|
-
* isCompleted: nullOr(SqliteBoolean),
|
|
496
|
-
* },
|
|
497
|
-
* };
|
|
498
|
-
*
|
|
499
827
|
* // Create one typed builder per schema and reuse it for every query.
|
|
500
|
-
* const createQuery = createQueryBuilder(
|
|
828
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
501
829
|
* const todosQuery = createQuery((db) =>
|
|
502
830
|
* db.selectFrom("todo").select(["id", "title", "isCompleted"]),
|
|
503
831
|
* );
|
|
@@ -505,12 +833,14 @@ export const evoluSchemaToSqliteSchema = <S extends EvoluSchema>(
|
|
|
505
833
|
* assertType<
|
|
506
834
|
* typeof todosQuery.Row,
|
|
507
835
|
* {
|
|
508
|
-
* id:
|
|
836
|
+
* id: TestTodoId;
|
|
509
837
|
* title: NonEmptyTrimmedString100 | null;
|
|
510
838
|
* isCompleted: SqliteBoolean | null;
|
|
511
839
|
* }
|
|
512
840
|
* >();
|
|
513
841
|
* ```
|
|
842
|
+
*
|
|
843
|
+
* @group Queries
|
|
514
844
|
*/
|
|
515
845
|
export const createQueryBuilder = <S extends EvoluSchema>(
|
|
516
846
|
_schema: S,
|
|
@@ -530,6 +860,12 @@ export const createQueryBuilder = <S extends EvoluSchema>(
|
|
|
530
860
|
return createQuery;
|
|
531
861
|
};
|
|
532
862
|
|
|
863
|
+
/**
|
|
864
|
+
* Creates missing tables, columns, and indexes, and drops indexes that the new
|
|
865
|
+
* schema no longer defines.
|
|
866
|
+
*
|
|
867
|
+
* @group SQLite
|
|
868
|
+
*/
|
|
533
869
|
export const ensureSqliteSchema =
|
|
534
870
|
(deps: SqliteDep) =>
|
|
535
871
|
(newSchema: SqliteSchema, currentSchema?: SqliteSchema): void => {
|
|
@@ -581,10 +917,24 @@ export const ensureSqliteSchema =
|
|
|
581
917
|
}
|
|
582
918
|
};
|
|
583
919
|
|
|
920
|
+
/**
|
|
921
|
+
* Reads the current application {@link SqliteSchema}, excluding Evolu's internal
|
|
922
|
+
* indexes.
|
|
923
|
+
*
|
|
924
|
+
* @group SQLite
|
|
925
|
+
*/
|
|
584
926
|
export const getEvoluSqliteSchema = (deps: SqliteDep) => (): SqliteSchema =>
|
|
585
927
|
getSqliteSchema(deps)({ excludeIndexNamePrefix: "evolu_" });
|
|
586
928
|
|
|
587
|
-
|
|
929
|
+
/**
|
|
930
|
+
* Kysely instance that only compiles queries to SQL. It never executes them;
|
|
931
|
+
* Evolu runs the compiled SQL itself.
|
|
932
|
+
*
|
|
933
|
+
* See [Splitting query building and
|
|
934
|
+
* execution](https://kysely.dev/docs/recipes/splitting-query-building-and-execution).
|
|
935
|
+
*
|
|
936
|
+
* @group Queries
|
|
937
|
+
*/
|
|
588
938
|
export const kysely = /*#__PURE__*/ new Kysely.Kysely({
|
|
589
939
|
dialect: {
|
|
590
940
|
createAdapter: () => new Kysely.SqliteAdapter(),
|