@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
|
@@ -23,13 +23,17 @@ 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,
|
|
33
37
|
Null,
|
|
34
38
|
nullOr,
|
|
35
39
|
object,
|
|
@@ -38,8 +42,13 @@ import {
|
|
|
38
42
|
type withDefault,
|
|
39
43
|
} from "../Type.ts";
|
|
40
44
|
import type { CompileTimeError, Simplify } from "../Types.ts";
|
|
41
|
-
import type { AppOwner } from "./Owner.ts";
|
|
42
|
-
import {
|
|
45
|
+
import type { AppOwner, OwnerIdBytes } from "./Owner.ts";
|
|
46
|
+
import {
|
|
47
|
+
OwnerEncryptionKey,
|
|
48
|
+
OwnerId,
|
|
49
|
+
OwnerSecret,
|
|
50
|
+
OwnerWriteKey,
|
|
51
|
+
} from "./Owner.ts";
|
|
43
52
|
import type {
|
|
44
53
|
evoluJsonArrayFrom,
|
|
45
54
|
evoluJsonObjectFrom,
|
|
@@ -49,7 +58,11 @@ import type {
|
|
|
49
58
|
import type { CrdtMessage, DbChange } from "./Storage.ts";
|
|
50
59
|
import { TimestampBytes } from "./Timestamp.ts";
|
|
51
60
|
|
|
52
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* Any Standard Schema V1 declaration.
|
|
63
|
+
*
|
|
64
|
+
* @group Core
|
|
65
|
+
*/
|
|
53
66
|
export type AnyStandardSchemaV1 = StandardSchemaV1<any, any>;
|
|
54
67
|
|
|
55
68
|
/**
|
|
@@ -121,6 +134,8 @@ export type AnyStandardSchemaV1 = StandardSchemaV1<any, any>;
|
|
|
121
134
|
* };
|
|
122
135
|
* assertTrue(ZodSchema.todo.title.safeParse("Write docs").success);
|
|
123
136
|
* ```
|
|
137
|
+
*
|
|
138
|
+
* @group Core
|
|
124
139
|
*/
|
|
125
140
|
export type EvoluSchema = ReadonlyRecord<
|
|
126
141
|
string,
|
|
@@ -128,9 +143,127 @@ export type EvoluSchema = ReadonlyRecord<
|
|
|
128
143
|
TableSchema
|
|
129
144
|
>;
|
|
130
145
|
|
|
131
|
-
/**
|
|
146
|
+
/**
|
|
147
|
+
* A table schema: column names mapped to Standard Schema validators.
|
|
148
|
+
*
|
|
149
|
+
* @group Core
|
|
150
|
+
*/
|
|
132
151
|
export type TableSchema = ReadonlyRecord<string, AnyStandardSchemaV1>;
|
|
133
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Whether a table is local-only: its name starts with an underscore, so its
|
|
155
|
+
* changes are stored without synchronization.
|
|
156
|
+
*
|
|
157
|
+
* @group Core
|
|
158
|
+
*/
|
|
159
|
+
export const isLocalOnlyTable = (table: string): boolean =>
|
|
160
|
+
table.startsWith("_");
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Todo ID Type for {@link testEvoluSchema}.
|
|
164
|
+
*
|
|
165
|
+
* @group Testing
|
|
166
|
+
*/
|
|
167
|
+
export const TestTodoId = /*#__PURE__*/ id("Todo");
|
|
168
|
+
export type TestTodoId = typeof TestTodoId.Output;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Deterministic {@link TestTodoId} for tests and examples.
|
|
172
|
+
*
|
|
173
|
+
* @group Testing
|
|
174
|
+
*/
|
|
175
|
+
export const testTodoId = /*#__PURE__*/ TestTodoId.orThrow(
|
|
176
|
+
/*#__PURE__*/ createIdFromString("testTodo"),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Project ID Type for {@link testEvoluSchema}.
|
|
181
|
+
*
|
|
182
|
+
* @group Testing
|
|
183
|
+
*/
|
|
184
|
+
export const TestProjectId = /*#__PURE__*/ id("Project");
|
|
185
|
+
export type TestProjectId = typeof TestProjectId.Output;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Deterministic {@link TestProjectId} for tests and examples.
|
|
189
|
+
*
|
|
190
|
+
* @group Testing
|
|
191
|
+
*/
|
|
192
|
+
export const testProjectId = /*#__PURE__*/ TestProjectId.orThrow(
|
|
193
|
+
/*#__PURE__*/ createIdFromString("testProject"),
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Todo and project schema for tests and examples. A todo can belong to a
|
|
198
|
+
* project or have no project. Use an explicit schema when teaching schema
|
|
199
|
+
* definition.
|
|
200
|
+
*
|
|
201
|
+
* ### Example
|
|
202
|
+
*
|
|
203
|
+
* ```ts
|
|
204
|
+
* import {
|
|
205
|
+
* assertOk,
|
|
206
|
+
* testEvoluSchema,
|
|
207
|
+
* testProjectId,
|
|
208
|
+
* testTodoId,
|
|
209
|
+
* } from "@evolu/common";
|
|
210
|
+
*
|
|
211
|
+
* assertOk(testEvoluSchema.todo.id.from(testTodoId), testTodoId);
|
|
212
|
+
* assertOk(
|
|
213
|
+
* testEvoluSchema.todo.projectId.from(testProjectId),
|
|
214
|
+
* testProjectId,
|
|
215
|
+
* );
|
|
216
|
+
* ```
|
|
217
|
+
*
|
|
218
|
+
* @group Testing
|
|
219
|
+
*/
|
|
220
|
+
export const testEvoluSchema = {
|
|
221
|
+
todo: {
|
|
222
|
+
id: TestTodoId,
|
|
223
|
+
title: NonEmptyTrimmedString100,
|
|
224
|
+
isCompleted: /*#__PURE__*/ nullOr(SqliteBoolean),
|
|
225
|
+
projectId: /*#__PURE__*/ nullOr(TestProjectId),
|
|
226
|
+
},
|
|
227
|
+
project: {
|
|
228
|
+
id: TestProjectId,
|
|
229
|
+
name: NonEmptyTrimmedString100,
|
|
230
|
+
},
|
|
231
|
+
} as const satisfies EvoluSchema;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Schema type of {@link testEvoluSchema}.
|
|
235
|
+
*
|
|
236
|
+
* @group Testing
|
|
237
|
+
*/
|
|
238
|
+
export type TestEvoluSchema = typeof testEvoluSchema;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* App-owner registry schema with local tables for tests and examples.
|
|
242
|
+
*
|
|
243
|
+
* Stores operational keys separately from optional recovery material. A null
|
|
244
|
+
* secret represents an owner whose recovery material is managed elsewhere or
|
|
245
|
+
* unavailable. Names are optional device-local labels; identicons can be
|
|
246
|
+
* derived from the owner identity without an additional column.
|
|
247
|
+
*
|
|
248
|
+
* This fixture does not define the production registry's persistence contract.
|
|
249
|
+
*
|
|
250
|
+
* @group Testing
|
|
251
|
+
*/
|
|
252
|
+
export const testLocalOnlyEvoluSchema = {
|
|
253
|
+
_appOwner: {
|
|
254
|
+
id: OwnerId,
|
|
255
|
+
encryptionKey: OwnerEncryptionKey,
|
|
256
|
+
writeKey: OwnerWriteKey,
|
|
257
|
+
secret: /*#__PURE__*/ nullOr(OwnerSecret),
|
|
258
|
+
name: /*#__PURE__*/ nullOr(NonEmptyTrimmedString100),
|
|
259
|
+
},
|
|
260
|
+
} as const satisfies EvoluSchema;
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Dependency wrapper for {@link SqliteSchema}.
|
|
264
|
+
*
|
|
265
|
+
* @group SQLite
|
|
266
|
+
*/
|
|
134
267
|
export interface SqliteSchemaDep {
|
|
135
268
|
readonly sqliteSchema: SqliteSchema;
|
|
136
269
|
}
|
|
@@ -147,6 +280,8 @@ export interface SqliteSchemaDep {
|
|
|
147
280
|
* 2. The 'id' column output type must extend {@link Id}
|
|
148
281
|
* 3. Tables cannot use system column names (createdAt, updatedAt, isDeleted)
|
|
149
282
|
* 4. All column output types must be compatible with SQLite (extend SqliteValue)
|
|
283
|
+
*
|
|
284
|
+
* @group Validation
|
|
150
285
|
*/
|
|
151
286
|
export type ValidateSchema<S extends EvoluSchema> =
|
|
152
287
|
ValidateSchemaHasId<S> extends never
|
|
@@ -159,10 +294,106 @@ export type ValidateSchema<S extends EvoluSchema> =
|
|
|
159
294
|
: ValidateIdColumnType<S>
|
|
160
295
|
: ValidateSchemaHasId<S>;
|
|
161
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Defines SQLite indexes with Kysely's index builder.
|
|
299
|
+
*
|
|
300
|
+
* @group SQLite
|
|
301
|
+
*/
|
|
162
302
|
export type IndexesConfig = (
|
|
163
303
|
create: (indexName: string) => Kysely.CreateIndexBuilder,
|
|
164
304
|
) => ReadonlyArray<Kysely.CreateIndexBuilder<any>>;
|
|
165
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Why a message is stored in `evolu_message_quarantine` instead of being
|
|
308
|
+
* applied to its table. Persisted codes: names may change, but numbers must not
|
|
309
|
+
* be reassigned.
|
|
310
|
+
*
|
|
311
|
+
* Quarantine is queryable state, not an error. An application subscribes to a
|
|
312
|
+
* query over `evolu_message_quarantine` to tell the user what is waiting. Each
|
|
313
|
+
* row is one column of a message, so distinct `ownerId` and `timestamp` pairs
|
|
314
|
+
* count messages. `origin` records whether this database stamped the message
|
|
315
|
+
* for a local mutation or received it from sync, see {@link QuarantineOrigin}.
|
|
316
|
+
* `quarantinedAt` is the system time captured for the request that quarantined
|
|
317
|
+
* the row, in milliseconds; it is null for rows written before Evolu recorded
|
|
318
|
+
* it. The clock-drift rules are described in the Timestamp module.
|
|
319
|
+
*
|
|
320
|
+
* ### Example
|
|
321
|
+
*
|
|
322
|
+
* ```ts
|
|
323
|
+
* import {
|
|
324
|
+
* assertType,
|
|
325
|
+
* createQueryBuilder,
|
|
326
|
+
* type Millis,
|
|
327
|
+
* type OwnerIdBytes,
|
|
328
|
+
* type QuarantineOrigin,
|
|
329
|
+
* QuarantineReason,
|
|
330
|
+
* testEvoluSchema,
|
|
331
|
+
* type TimestampBytes,
|
|
332
|
+
* } from "@evolu/common";
|
|
333
|
+
*
|
|
334
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
335
|
+
*
|
|
336
|
+
* // Messages waiting in drift quarantine, one row per message.
|
|
337
|
+
* const driftQuarantineQuery = createQuery((db) =>
|
|
338
|
+
* db
|
|
339
|
+
* .selectFrom("evolu_message_quarantine")
|
|
340
|
+
* .select(["ownerId", "timestamp", "origin", "quarantinedAt"])
|
|
341
|
+
* .where("reason", "=", QuarantineReason.TimestampDrift)
|
|
342
|
+
* .distinct(),
|
|
343
|
+
* );
|
|
344
|
+
*
|
|
345
|
+
* assertType<
|
|
346
|
+
* typeof driftQuarantineQuery.Row,
|
|
347
|
+
* {
|
|
348
|
+
* ownerId: OwnerIdBytes;
|
|
349
|
+
* timestamp: TimestampBytes;
|
|
350
|
+
* origin: QuarantineOrigin;
|
|
351
|
+
* quarantinedAt: Millis | null;
|
|
352
|
+
* }
|
|
353
|
+
* >();
|
|
354
|
+
* ```
|
|
355
|
+
*
|
|
356
|
+
* @group Queries
|
|
357
|
+
*/
|
|
358
|
+
export const QuarantineReason = {
|
|
359
|
+
/**
|
|
360
|
+
* The message has a table or column the current schema does not define. It is
|
|
361
|
+
* applied automatically once a schema update defines them. A received change
|
|
362
|
+
* to a {@link isLocalOnlyTable | local-only} table is also stored here and is
|
|
363
|
+
* never applied.
|
|
364
|
+
*/
|
|
365
|
+
Schema: 0,
|
|
366
|
+
/**
|
|
367
|
+
* The message's timestamp exceeded the drift limit when it was stored. It is
|
|
368
|
+
* applied when the database worker starts, once system time comes within the
|
|
369
|
+
* limit of the timestamp.
|
|
370
|
+
*/
|
|
371
|
+
TimestampDrift: 1,
|
|
372
|
+
} as const;
|
|
373
|
+
|
|
374
|
+
export type QuarantineReason =
|
|
375
|
+
(typeof QuarantineReason)[keyof typeof QuarantineReason];
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Whether a quarantined message was stamped by this database for a local
|
|
379
|
+
* mutation or received from sync. Persisted codes: names may change, but
|
|
380
|
+
* numbers must not be reassigned.
|
|
381
|
+
*
|
|
382
|
+
* @group Queries
|
|
383
|
+
*/
|
|
384
|
+
export const QuarantineOrigin = {
|
|
385
|
+
LocalMutation: 0,
|
|
386
|
+
ReceivedMessage: 1,
|
|
387
|
+
} as const;
|
|
388
|
+
|
|
389
|
+
export type QuarantineOrigin =
|
|
390
|
+
(typeof QuarantineOrigin)[keyof typeof QuarantineOrigin];
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Typed query factory returned by {@link createQueryBuilder}.
|
|
394
|
+
*
|
|
395
|
+
* @group Queries
|
|
396
|
+
*/
|
|
166
397
|
export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
|
|
167
398
|
queryCallback: (
|
|
168
399
|
db: Pick<
|
|
@@ -175,6 +406,7 @@ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
|
|
|
175
406
|
} & SystemColumns;
|
|
176
407
|
} & {
|
|
177
408
|
readonly evolu_history: {
|
|
409
|
+
readonly ownerId: OwnerIdBytes;
|
|
178
410
|
readonly timestamp: TimestampBytes;
|
|
179
411
|
readonly table: keyof S;
|
|
180
412
|
readonly id: IdBytes;
|
|
@@ -182,11 +414,15 @@ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
|
|
|
182
414
|
readonly value: SqliteValue;
|
|
183
415
|
};
|
|
184
416
|
readonly evolu_message_quarantine: {
|
|
417
|
+
readonly ownerId: OwnerIdBytes;
|
|
185
418
|
readonly timestamp: TimestampBytes;
|
|
186
419
|
readonly table: string;
|
|
187
420
|
readonly id: IdBytes;
|
|
188
421
|
readonly column: string;
|
|
189
422
|
readonly value: SqliteValue;
|
|
423
|
+
readonly reason: QuarantineReason;
|
|
424
|
+
readonly origin: QuarantineOrigin;
|
|
425
|
+
readonly quarantinedAt: Millis | null;
|
|
190
426
|
};
|
|
191
427
|
}
|
|
192
428
|
>,
|
|
@@ -204,6 +440,8 @@ export type CreateQuery<S extends EvoluSchema> = <R extends Row>(
|
|
|
204
440
|
* - `isDeleted`: Soft delete flag created by Evolu and used by the developer to
|
|
205
441
|
* mark rows as deleted.
|
|
206
442
|
* - `ownerId`: Represents ownership and logically partitions the database.
|
|
443
|
+
*
|
|
444
|
+
* @group Core
|
|
207
445
|
*/
|
|
208
446
|
export const SystemColumns: ObjectType<{
|
|
209
447
|
readonly createdAt: typeof DateIso;
|
|
@@ -218,6 +456,11 @@ export const SystemColumns: ObjectType<{
|
|
|
218
456
|
});
|
|
219
457
|
export interface SystemColumns extends InferType<typeof SystemColumns> {}
|
|
220
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Kind of a {@link Mutation}: insert, update, or upsert.
|
|
461
|
+
*
|
|
462
|
+
* @group Mutations
|
|
463
|
+
*/
|
|
221
464
|
export type MutationKind = "insert" | "update" | "upsert";
|
|
222
465
|
|
|
223
466
|
/**
|
|
@@ -239,6 +482,8 @@ export type MutationKind = "insert" | "update" | "upsert";
|
|
|
239
482
|
* `id` omitted (auto-generated)
|
|
240
483
|
* - **update**: only `id` required, everything else optional
|
|
241
484
|
* - **upsert**: like insert but `id` required too
|
|
485
|
+
*
|
|
486
|
+
* @group Mutations
|
|
242
487
|
*/
|
|
243
488
|
export type Mutation<S extends EvoluSchema, Kind extends MutationKind> = <
|
|
244
489
|
TableName extends keyof S,
|
|
@@ -248,11 +493,26 @@ export type Mutation<S extends EvoluSchema, Kind extends MutationKind> = <
|
|
|
248
493
|
options?: MutationOptions,
|
|
249
494
|
) => { readonly id: StandardSchemaV1.InferOutput<S[TableName]["id"]> };
|
|
250
495
|
|
|
496
|
+
/**
|
|
497
|
+
* Options accepted by every {@link Mutation}.
|
|
498
|
+
*
|
|
499
|
+
* @group Mutations
|
|
500
|
+
*/
|
|
251
501
|
export interface MutationOptions {
|
|
252
502
|
/**
|
|
253
|
-
* Called after the mutation
|
|
254
|
-
* Useful for
|
|
255
|
-
* insert, update, or upsert.
|
|
503
|
+
* Called after the mutation's changes are stored and subscribed queries
|
|
504
|
+
* reflect them. Useful for follow-up work (e.g., notifications, navigation)
|
|
505
|
+
* after insert, update, or upsert. It never runs when the database is
|
|
506
|
+
* unavailable.
|
|
507
|
+
*
|
|
508
|
+
* Stored does not always mean visible. A change quarantined for
|
|
509
|
+
* {@link QuarantineReason.TimestampDrift} is stored in
|
|
510
|
+
* `evolu_message_quarantine` without changing application rows. `onComplete`
|
|
511
|
+
* still fires after storage commits; no drift error is reported. Applications
|
|
512
|
+
* can subscribe to the quarantine table; those query results reflect the
|
|
513
|
+
* change before `onComplete` runs. See the
|
|
514
|
+
* {@link @evolu/common!"local-first/Timestamp" | Timestamp module} for drift
|
|
515
|
+
* and release behavior.
|
|
256
516
|
*/
|
|
257
517
|
readonly onComplete?: () => void;
|
|
258
518
|
|
|
@@ -317,6 +577,12 @@ export interface MutationOptions {
|
|
|
317
577
|
readonly ownerId?: OwnerId;
|
|
318
578
|
}
|
|
319
579
|
|
|
580
|
+
/**
|
|
581
|
+
* Database change produced by a {@link Mutation}, attributed to the
|
|
582
|
+
* {@link OwnerId} that owns the row.
|
|
583
|
+
*
|
|
584
|
+
* @group Mutations
|
|
585
|
+
*/
|
|
320
586
|
export interface MutationChange extends DbChange {
|
|
321
587
|
readonly ownerId: OwnerId;
|
|
322
588
|
}
|
|
@@ -324,6 +590,8 @@ export interface MutationChange extends DbChange {
|
|
|
324
590
|
/**
|
|
325
591
|
* Derives the expected values type for a mutation from a table's column schemas
|
|
326
592
|
* and a {@link MutationKind}.
|
|
593
|
+
*
|
|
594
|
+
* @group Mutations
|
|
327
595
|
*/
|
|
328
596
|
export type MutationValues<
|
|
329
597
|
T extends TableSchema,
|
|
@@ -339,6 +607,8 @@ export type MutationValues<
|
|
|
339
607
|
/**
|
|
340
608
|
* Insert values: `id` omitted (auto-generated), nullable columns optional,
|
|
341
609
|
* non-nullable columns required.
|
|
610
|
+
*
|
|
611
|
+
* @group Mutations
|
|
342
612
|
*/
|
|
343
613
|
export type InsertValues<T extends TableSchema> = Omit<
|
|
344
614
|
NullableColumnsToOptional<T>,
|
|
@@ -348,6 +618,8 @@ export type InsertValues<T extends TableSchema> = Omit<
|
|
|
348
618
|
/**
|
|
349
619
|
* Update values: `id` required, all other columns optional. Includes
|
|
350
620
|
* `isDeleted` for soft deletes.
|
|
621
|
+
*
|
|
622
|
+
* @group Mutations
|
|
351
623
|
*/
|
|
352
624
|
export type UpdateValues<T extends TableSchema> = {
|
|
353
625
|
readonly id: StandardSchemaV1.InferOutput<T["id"]>;
|
|
@@ -360,12 +632,19 @@ export type UpdateValues<T extends TableSchema> = {
|
|
|
360
632
|
/**
|
|
361
633
|
* Upsert values: `id` required, nullable columns optional, non-nullable columns
|
|
362
634
|
* required. Includes `isDeleted` for soft deletes.
|
|
635
|
+
*
|
|
636
|
+
* @group Mutations
|
|
363
637
|
*/
|
|
364
638
|
export type UpsertValues<T extends TableSchema> =
|
|
365
639
|
NullableColumnsToOptional<T> & {
|
|
366
640
|
readonly isDeleted?: SqliteBoolean;
|
|
367
641
|
};
|
|
368
642
|
|
|
643
|
+
/**
|
|
644
|
+
* Requires an `id` column in every table.
|
|
645
|
+
*
|
|
646
|
+
* @group Validation
|
|
647
|
+
*/
|
|
369
648
|
export type ValidateSchemaHasId<S extends EvoluSchema> =
|
|
370
649
|
keyof S extends infer TableName
|
|
371
650
|
? TableName extends keyof S
|
|
@@ -375,6 +654,11 @@ export type ValidateSchemaHasId<S extends EvoluSchema> =
|
|
|
375
654
|
: never
|
|
376
655
|
: never;
|
|
377
656
|
|
|
657
|
+
/**
|
|
658
|
+
* Requires every `id` column output type to extend {@link Id}.
|
|
659
|
+
*
|
|
660
|
+
* @group Validation
|
|
661
|
+
*/
|
|
378
662
|
export type ValidateIdColumnType<S extends EvoluSchema> =
|
|
379
663
|
keyof S extends infer TableName
|
|
380
664
|
? TableName extends keyof S
|
|
@@ -386,6 +670,11 @@ export type ValidateIdColumnType<S extends EvoluSchema> =
|
|
|
386
670
|
: never
|
|
387
671
|
: never;
|
|
388
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Rejects tables that define system column names.
|
|
675
|
+
*
|
|
676
|
+
* @group Validation
|
|
677
|
+
*/
|
|
389
678
|
export type ValidateNoSystemColumns<S extends EvoluSchema> =
|
|
390
679
|
keyof S extends infer TableName
|
|
391
680
|
? TableName extends keyof S
|
|
@@ -400,6 +689,11 @@ export type ValidateNoSystemColumns<S extends EvoluSchema> =
|
|
|
400
689
|
: never
|
|
401
690
|
: never;
|
|
402
691
|
|
|
692
|
+
/**
|
|
693
|
+
* Requires every column output type to be compatible with SQLite.
|
|
694
|
+
*
|
|
695
|
+
* @group Validation
|
|
696
|
+
*/
|
|
403
697
|
export type ValidateColumnTypes<S extends EvoluSchema> =
|
|
404
698
|
keyof S extends infer TableName
|
|
405
699
|
? TableName extends keyof S
|
|
@@ -414,36 +708,69 @@ export type ValidateColumnTypes<S extends EvoluSchema> =
|
|
|
414
708
|
: never
|
|
415
709
|
: never;
|
|
416
710
|
|
|
417
|
-
/**
|
|
711
|
+
/**
|
|
712
|
+
* Schema validation error that shows clear, readable messages
|
|
713
|
+
*
|
|
714
|
+
* @group Validation
|
|
715
|
+
*/
|
|
418
716
|
export type SchemaValidationError<Message extends string> = CompileTimeError<
|
|
419
717
|
"Schema",
|
|
420
718
|
Message
|
|
421
719
|
>;
|
|
422
720
|
|
|
423
|
-
/**
|
|
721
|
+
/**
|
|
722
|
+
* Makes columns whose output type includes `null` optional.
|
|
723
|
+
*
|
|
724
|
+
* @group Mutations
|
|
725
|
+
*/
|
|
424
726
|
export type NullableColumnsToOptional<T extends TableSchema> = {
|
|
425
727
|
readonly [K in RequiredColumnKeys<T>]: StandardSchemaV1.InferOutput<T[K]>;
|
|
426
728
|
} & {
|
|
427
729
|
readonly [K in OptionalColumnKeys<T>]?: StandardSchemaV1.InferOutput<T[K]>;
|
|
428
730
|
};
|
|
429
731
|
|
|
732
|
+
/**
|
|
733
|
+
* Column names whose output type excludes `null`.
|
|
734
|
+
*
|
|
735
|
+
* @group Mutations
|
|
736
|
+
*/
|
|
430
737
|
export type RequiredColumnKeys<T extends TableSchema> = {
|
|
431
738
|
[K in keyof T]: null extends StandardSchemaV1.InferOutput<T[K]> ? never : K;
|
|
432
739
|
}[keyof T];
|
|
433
740
|
|
|
741
|
+
/**
|
|
742
|
+
* Column names whose output type includes `null`.
|
|
743
|
+
*
|
|
744
|
+
* @group Mutations
|
|
745
|
+
*/
|
|
434
746
|
export type OptionalColumnKeys<T extends TableSchema> = {
|
|
435
747
|
[K in keyof T]: null extends StandardSchemaV1.InferOutput<T[K]> ? K : never;
|
|
436
748
|
}[keyof T];
|
|
437
749
|
|
|
750
|
+
/**
|
|
751
|
+
* Names of {@link SystemColumns}.
|
|
752
|
+
*
|
|
753
|
+
* @group Core
|
|
754
|
+
*/
|
|
438
755
|
export const systemColumns: ReadonlySet<string> = /*#__PURE__*/ new Set(
|
|
439
756
|
/*#__PURE__*/ Object.keys(SystemColumns.props),
|
|
440
757
|
);
|
|
441
758
|
|
|
759
|
+
/**
|
|
760
|
+
* Names of {@link SystemColumns} together with `id`.
|
|
761
|
+
*
|
|
762
|
+
* @group Core
|
|
763
|
+
*/
|
|
442
764
|
export const systemColumnsWithId: ReadonlyArray<string> = [
|
|
443
765
|
...systemColumns,
|
|
444
766
|
"id",
|
|
445
767
|
];
|
|
446
768
|
|
|
769
|
+
/**
|
|
770
|
+
* Derives {@link SqliteSchema} tables and indexes from an {@link EvoluSchema}.
|
|
771
|
+
*
|
|
772
|
+
* @group SQLite
|
|
773
|
+
*/
|
|
447
774
|
export const evoluSchemaToSqliteSchema = <S extends EvoluSchema>(
|
|
448
775
|
schema: ValidateSchema<S> extends never ? S : ValidateSchema<S>,
|
|
449
776
|
indexesConfig?: IndexesConfig,
|
|
@@ -480,24 +807,14 @@ export const evoluSchemaToSqliteSchema = <S extends EvoluSchema>(
|
|
|
480
807
|
* import {
|
|
481
808
|
* assertType,
|
|
482
809
|
* createQueryBuilder,
|
|
483
|
-
*
|
|
810
|
+
* testEvoluSchema,
|
|
811
|
+
* type TestTodoId,
|
|
484
812
|
* NonEmptyTrimmedString100,
|
|
485
|
-
* nullOr,
|
|
486
813
|
* SqliteBoolean,
|
|
487
814
|
* } from "@evolu/common";
|
|
488
815
|
*
|
|
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
816
|
* // Create one typed builder per schema and reuse it for every query.
|
|
500
|
-
* const createQuery = createQueryBuilder(
|
|
817
|
+
* const createQuery = createQueryBuilder(testEvoluSchema);
|
|
501
818
|
* const todosQuery = createQuery((db) =>
|
|
502
819
|
* db.selectFrom("todo").select(["id", "title", "isCompleted"]),
|
|
503
820
|
* );
|
|
@@ -505,12 +822,14 @@ export const evoluSchemaToSqliteSchema = <S extends EvoluSchema>(
|
|
|
505
822
|
* assertType<
|
|
506
823
|
* typeof todosQuery.Row,
|
|
507
824
|
* {
|
|
508
|
-
* id:
|
|
825
|
+
* id: TestTodoId;
|
|
509
826
|
* title: NonEmptyTrimmedString100 | null;
|
|
510
827
|
* isCompleted: SqliteBoolean | null;
|
|
511
828
|
* }
|
|
512
829
|
* >();
|
|
513
830
|
* ```
|
|
831
|
+
*
|
|
832
|
+
* @group Queries
|
|
514
833
|
*/
|
|
515
834
|
export const createQueryBuilder = <S extends EvoluSchema>(
|
|
516
835
|
_schema: S,
|
|
@@ -530,6 +849,12 @@ export const createQueryBuilder = <S extends EvoluSchema>(
|
|
|
530
849
|
return createQuery;
|
|
531
850
|
};
|
|
532
851
|
|
|
852
|
+
/**
|
|
853
|
+
* Creates missing tables, columns, and indexes, and drops indexes that the new
|
|
854
|
+
* schema no longer defines.
|
|
855
|
+
*
|
|
856
|
+
* @group SQLite
|
|
857
|
+
*/
|
|
533
858
|
export const ensureSqliteSchema =
|
|
534
859
|
(deps: SqliteDep) =>
|
|
535
860
|
(newSchema: SqliteSchema, currentSchema?: SqliteSchema): void => {
|
|
@@ -581,10 +906,24 @@ export const ensureSqliteSchema =
|
|
|
581
906
|
}
|
|
582
907
|
};
|
|
583
908
|
|
|
909
|
+
/**
|
|
910
|
+
* Reads the current application {@link SqliteSchema}, excluding Evolu's internal
|
|
911
|
+
* indexes.
|
|
912
|
+
*
|
|
913
|
+
* @group SQLite
|
|
914
|
+
*/
|
|
584
915
|
export const getEvoluSqliteSchema = (deps: SqliteDep) => (): SqliteSchema =>
|
|
585
916
|
getSqliteSchema(deps)({ excludeIndexNamePrefix: "evolu_" });
|
|
586
917
|
|
|
587
|
-
|
|
918
|
+
/**
|
|
919
|
+
* Kysely instance that only compiles queries to SQL. It never executes them;
|
|
920
|
+
* Evolu runs the compiled SQL itself.
|
|
921
|
+
*
|
|
922
|
+
* See [Splitting query building and
|
|
923
|
+
* execution](https://kysely.dev/docs/recipes/splitting-query-building-and-execution).
|
|
924
|
+
*
|
|
925
|
+
* @group Queries
|
|
926
|
+
*/
|
|
588
927
|
export const kysely = /*#__PURE__*/ new Kysely.Kysely({
|
|
589
928
|
dialect: {
|
|
590
929
|
createAdapter: () => new Kysely.SqliteAdapter(),
|