@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.
Files changed (152) hide show
  1. package/dist/src/Bytes.d.ts +39 -2
  2. package/dist/src/Bytes.d.ts.map +1 -1
  3. package/dist/src/Bytes.js +50 -2
  4. package/dist/src/Config.d.ts +22 -22
  5. package/dist/src/Config.d.ts.map +1 -1
  6. package/dist/src/Console.d.ts +62 -7
  7. package/dist/src/Console.d.ts.map +1 -1
  8. package/dist/src/Console.js +20 -4
  9. package/dist/src/Crypto.d.ts +76 -4
  10. package/dist/src/Crypto.d.ts.map +1 -1
  11. package/dist/src/Crypto.js +55 -4
  12. package/dist/src/Error.d.ts +45 -0
  13. package/dist/src/Error.d.ts.map +1 -1
  14. package/dist/src/Error.js +69 -0
  15. package/dist/src/Fs.d.ts +92 -18
  16. package/dist/src/Fs.d.ts.map +1 -1
  17. package/dist/src/Fs.js +2 -0
  18. package/dist/src/Identicon.d.ts +2 -2
  19. package/dist/src/Identicon.js +2 -2
  20. package/dist/src/LeakDetector.d.ts +22 -3
  21. package/dist/src/LeakDetector.d.ts.map +1 -1
  22. package/dist/src/LeakDetector.js +12 -2
  23. package/dist/src/LockManager.d.ts +8 -0
  24. package/dist/src/LockManager.d.ts.map +1 -1
  25. package/dist/src/LockManager.js +6 -0
  26. package/dist/src/Object.d.ts.map +1 -1
  27. package/dist/src/Object.js +5 -0
  28. package/dist/src/Platform.d.ts +47 -7
  29. package/dist/src/Platform.d.ts.map +1 -1
  30. package/dist/src/Platform.js +24 -5
  31. package/dist/src/Random.d.ts +25 -2
  32. package/dist/src/Random.d.ts.map +1 -1
  33. package/dist/src/Random.js +14 -2
  34. package/dist/src/Resource.d.ts +156 -1
  35. package/dist/src/Resource.d.ts.map +1 -1
  36. package/dist/src/Resource.js +201 -72
  37. package/dist/src/Schedule.d.ts +11 -10
  38. package/dist/src/Schedule.d.ts.map +1 -1
  39. package/dist/src/Schedule.js +1 -1
  40. package/dist/src/Sqlite.d.ts +132 -16
  41. package/dist/src/Sqlite.d.ts.map +1 -1
  42. package/dist/src/Sqlite.js +63 -9
  43. package/dist/src/Task.d.ts +15 -4
  44. package/dist/src/Task.d.ts.map +1 -1
  45. package/dist/src/Task.js +41 -15
  46. package/dist/src/Test.d.ts +9 -0
  47. package/dist/src/Test.d.ts.map +1 -1
  48. package/dist/src/Test.js +4 -0
  49. package/dist/src/Time.d.ts +106 -9
  50. package/dist/src/Time.d.ts.map +1 -1
  51. package/dist/src/Time.js +55 -4
  52. package/dist/src/Type.d.ts +1455 -1310
  53. package/dist/src/Type.d.ts.map +1 -1
  54. package/dist/src/Type.js +1274 -517
  55. package/dist/src/WebSocket.d.ts +164 -13
  56. package/dist/src/WebSocket.d.ts.map +1 -1
  57. package/dist/src/WebSocket.js +133 -24
  58. package/dist/src/Worker.d.ts +90 -8
  59. package/dist/src/Worker.d.ts.map +1 -1
  60. package/dist/src/Worker.js +28 -2
  61. package/dist/src/index.d.ts +6 -7
  62. package/dist/src/index.d.ts.map +1 -1
  63. package/dist/src/index.js +2 -3
  64. package/dist/src/local-first/Db.d.ts +52 -3
  65. package/dist/src/local-first/Db.d.ts.map +1 -1
  66. package/dist/src/local-first/Db.js +412 -137
  67. package/dist/src/local-first/Evolu.d.ts +412 -213
  68. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  69. package/dist/src/local-first/Evolu.js +181 -18
  70. package/dist/src/local-first/Owner.d.ts +13 -30
  71. package/dist/src/local-first/Owner.d.ts.map +1 -1
  72. package/dist/src/local-first/Owner.js +13 -30
  73. package/dist/src/local-first/Protocol.d.ts +106 -19
  74. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  75. package/dist/src/local-first/Protocol.js +162 -60
  76. package/dist/src/local-first/Query.d.ts +8 -15
  77. package/dist/src/local-first/Query.d.ts.map +1 -1
  78. package/dist/src/local-first/Relay.d.ts.map +1 -1
  79. package/dist/src/local-first/Relay.js +4 -2
  80. package/dist/src/local-first/Schema.d.ts +346 -23
  81. package/dist/src/local-first/Schema.d.ts.map +1 -1
  82. package/dist/src/local-first/Schema.js +214 -17
  83. package/dist/src/local-first/Shared.d.ts +537 -22
  84. package/dist/src/local-first/Shared.d.ts.map +1 -1
  85. package/dist/src/local-first/Shared.js +1437 -234
  86. package/dist/src/local-first/Storage.d.ts +195 -17
  87. package/dist/src/local-first/Storage.d.ts.map +1 -1
  88. package/dist/src/local-first/Storage.js +85 -22
  89. package/dist/src/local-first/Timestamp.d.ts +392 -41
  90. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  91. package/dist/src/local-first/Timestamp.js +403 -81
  92. package/dist/src/local-first/index.d.ts +0 -1
  93. package/dist/src/local-first/index.d.ts.map +1 -1
  94. package/dist/src/local-first/index.js +0 -1
  95. package/package.json +1 -1
  96. package/src/Assert.test.ts +2 -5
  97. package/src/Bytes.test.ts +27 -0
  98. package/src/Bytes.ts +58 -2
  99. package/src/Config.test.ts +2 -6
  100. package/src/Config.ts +133 -133
  101. package/src/Console.ts +62 -7
  102. package/src/Crypto.ts +76 -4
  103. package/src/Eq.test.ts +2 -3
  104. package/src/Error.test.ts +76 -3
  105. package/src/Error.ts +71 -0
  106. package/src/Fs.ts +92 -18
  107. package/src/Identicon.ts +2 -2
  108. package/src/LeakDetector.ts +22 -3
  109. package/src/LockManager.ts +8 -0
  110. package/src/Object.test.ts +27 -12
  111. package/src/Object.ts +5 -0
  112. package/src/Platform.ts +50 -8
  113. package/src/Random.ts +25 -2
  114. package/src/Resource.test.ts +837 -0
  115. package/src/Resource.ts +235 -15
  116. package/src/Schedule.test.ts +50 -12
  117. package/src/Schedule.ts +24 -14
  118. package/src/Sqlite.ts +137 -17
  119. package/src/Task.test.ts +189 -8
  120. package/src/Task.ts +56 -17
  121. package/src/Test.ts +9 -0
  122. package/src/Time.ts +106 -9
  123. package/src/Type.test.ts +946 -1028
  124. package/src/Type.ts +4195 -3136
  125. package/src/Types.test.ts +4 -14
  126. package/src/WebSocket.ts +313 -40
  127. package/src/Worker.ts +90 -8
  128. package/src/index.ts +20 -6
  129. package/src/local-first/Db.ts +644 -339
  130. package/src/local-first/Evolu.test.ts +994 -22
  131. package/src/local-first/Evolu.ts +625 -232
  132. package/src/local-first/Owner.ts +13 -30
  133. package/src/local-first/Protocol.test.ts +634 -10
  134. package/src/local-first/Protocol.ts +255 -109
  135. package/src/local-first/Query.ts +8 -15
  136. package/src/local-first/Relay.ts +4 -2
  137. package/src/local-first/Schema.test.ts +143 -0
  138. package/src/local-first/Schema.ts +376 -26
  139. package/src/local-first/Shared.test.ts +7731 -559
  140. package/src/local-first/Shared.ts +2036 -267
  141. package/src/local-first/Storage.ts +224 -36
  142. package/src/local-first/Timestamp.test.ts +344 -70
  143. package/src/local-first/Timestamp.ts +434 -118
  144. package/src/local-first/index.ts +0 -1
  145. package/dist/src/local-first/Error.d.ts +0 -12
  146. package/dist/src/local-first/Error.d.ts.map +0 -1
  147. package/dist/src/local-first/Error.js +0 -6
  148. package/dist/src/local-first/LocalAuth.d.ts +0 -150
  149. package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
  150. package/dist/src/local-first/LocalAuth.js +0 -179
  151. package/src/local-first/Error.ts +0 -17
  152. 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 { AppOwner } from "./Owner.ts";
42
- import { OwnerId } from "./Owner.ts";
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
- /** Any Standard Schema V1 declaration. */
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
- /** A table schema: column names mapped to Standard Schema validators. */
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
- * Mutations never fail — values are already validated by the caller, and
236
- * changes are stored locally in SQLite.
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 is completed and the local state is updated.
254
- * Useful for triggering side effects (e.g., notifications, UI updates) after
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
- /** Schema validation error that shows clear, readable messages */
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
- /** Makes columns whose output type includes `null` optional. */
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
- * id,
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(Schema);
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: TodoId;
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
- // https://kysely.dev/docs/recipes/splitting-query-building-and-execution
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(),