@sqlite-sync/core 0.6.0 → 0.7.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/{chunk-3L4QG3BZ.js → chunk-LBYL5L4O.js} +371 -34
- package/dist/chunk-LBYL5L4O.js.map +1 -0
- package/dist/{crdt-sync-remote-source-CJrVzW-B.d.ts → crdt-sync-remote-source-BFl7v5_I.d.ts} +135 -16
- package/dist/index.d.ts +70 -5
- package/dist/index.js +230 -74
- package/dist/index.js.map +1 -1
- package/dist/reset-state-D6UslXaj.d.ts +19 -0
- package/dist/server.d.ts +2 -2
- package/dist/worker.d.ts +10 -3
- package/dist/worker.js +12 -2
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3L4QG3BZ.js.map +0 -1
- package/dist/reset-state-Comk7IC0.d.ts +0 -81
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as kysely from 'kysely';
|
|
2
2
|
import { Kysely, SchemaModule } from 'kysely';
|
|
3
|
-
import { S as SQLiteDbWrapper, L as Logger, T as TypedEvent, I as InternalSQLiteWrapper, a as StoredValue, C as CrdtStorage, E as ExecuteParams,
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
export { a as CreateCrdtSchemaOptions, R as RESET_REQUEST_TTL_MS, b as ResetRequest, d as ResetStore, c as createSyncDbSchema } from './reset-state-Comk7IC0.js';
|
|
3
|
+
import { S as SQLiteDbWrapper, L as Logger, T as TypedEvent, I as InternalSQLiteWrapper, a as StoredValue, b as SyncSchemaTables, c as SyncDbSchema, A as AnyTableBuilder, d as TableBuilder, e as TableColumns, f as InferRow, R as ReadonlyTable, M as Migrations, N as NewCrdtEvent, C as CrdtStorage, E as ExecuteParams, g as ExecuteResult, Q as QueryBuilderOutput, K as KyselyQueryFactory, h as SQLiteTransactionWrapper, W as WorkerState, D as DeSyncDetectedReason, i as CrdtTableConfig } from './crdt-sync-remote-source-BFl7v5_I.js';
|
|
4
|
+
export { U as AnyColumnBuilder, ab as CRDT_EVENT_NO_OP_PAYLOAD, V as ColumnBuilder, X as ColumnKind, Y as ColumnMeta, m as ColumnMetadata, ac as CrdtEventOrigin, ad as CrdtEventStatus, ae as CrdtEventType, a6 as CrdtSyncRemoteSource, af as CrdtUpdateLogItem, ag as CrdtUpdateLogPayload, n as DatabaseIntrospection, ao as DeferredPromise, ap as DistributiveOmit, a8 as EventsPullRequest, a9 as EventsPushRequest, aa as EventsPushResponse, H as HLC, l as HLCCounter, ak as KyselyStatementFactory, q as LogLevel, v as MigratableEvent, w as MigrationsDb, a5 as OwnCrdtEvent, Z as PayloadValidationResult, a3 as PendingCrdtEvent, ai as PersistedCrdtEvent, al as PreparedStatement, _ as SqliteStorageType, x as SyncDbMigrator, J as SystemDbConfig, O as SystemMigration, P as SystemMigrationContext, p as TableMetadata, $ as TableOptions, at as TypedBroadcastChannel, au as TypedEventTarget, ax as WorkerConfig, ay as WorkerNotificationMessage, y as applyMemoryDbSchema, z as applyWorkerDbSchema, B as baseSystemMigrations, j as compareHLC, a1 as createCrdtApplyFunction, a4 as createCrdtStorage, a7 as createCrdtSyncRemoteSource, am as createDeferredPromise, t as createMigrations, u as createMigrator, a2 as createSQLiteCrdtApplyFunction, aj as createStoredValue, F as createSystemDbConfig, an as createTypedEventTarget, k as deserializeHLC, aq as generateId, o as introspectDb, ah as isNoOpCrdtEventPayload, ar as jsonSafeParse, as as quoteId, G as runSystemMigrations, s as serializeHLC, r as startPerformanceLogger, a0 as t, av as tryCatch, aw as tryCatchAsync } from './crdt-sync-remote-source-BFl7v5_I.js';
|
|
5
|
+
export { R as RESET_REQUEST_TTL_MS, a as ResetRequest, b as ResetStore } from './reset-state-D6UslXaj.js';
|
|
7
6
|
import '@sqlite.org/sqlite-wasm';
|
|
8
7
|
|
|
9
8
|
declare const dummyKysely: Kysely<any>;
|
|
@@ -106,6 +105,72 @@ declare function createSQLiteKvStore({ db, metaTableName }: {
|
|
|
106
105
|
createNumberStoredValue: (key: string, defaultValue: number) => StoredValue<number>;
|
|
107
106
|
};
|
|
108
107
|
|
|
108
|
+
type Simplify<T> = {
|
|
109
|
+
[K in keyof T]: T[K];
|
|
110
|
+
} & {};
|
|
111
|
+
type RowOf<Table extends AnyTableBuilder> = Table extends TableBuilder<infer Cols extends TableColumns, any> ? InferRow<Cols> : never;
|
|
112
|
+
type BaseNameOf<CrdtName, Table extends AnyTableBuilder> = string extends Table["~baseName"] ? `_${CrdtName & string}` : [Table["~baseName"]] extends [string] ? Table["~baseName"] : `_${CrdtName & string}`;
|
|
113
|
+
type ClientSchemaOf<Tables extends SyncSchemaTables> = Simplify<{
|
|
114
|
+
[K in keyof Tables]: RowOf<Tables[K]>;
|
|
115
|
+
} & {
|
|
116
|
+
[K in keyof Tables as BaseNameOf<K, Tables[K]>]: ReadonlyTable<RowOf<Tables[K]>>;
|
|
117
|
+
}>;
|
|
118
|
+
type ServerSchemaOf<Tables extends SyncSchemaTables> = Simplify<{
|
|
119
|
+
[K in keyof Tables]: ReadonlyTable<RowOf<Tables[K]>>;
|
|
120
|
+
} & {
|
|
121
|
+
[K in keyof Tables as BaseNameOf<K, Tables[K]>]: ReadonlyTable<RowOf<Tables[K]>>;
|
|
122
|
+
}>;
|
|
123
|
+
type MutationsSchemaOf<Tables extends SyncSchemaTables> = Simplify<{
|
|
124
|
+
[K in keyof Tables as BaseNameOf<K, Tables[K]>]: RowOf<Tables[K]>;
|
|
125
|
+
}>;
|
|
126
|
+
interface DefinedSyncSchema<Tables extends SyncSchemaTables> extends SyncDbSchema<ClientSchemaOf<Tables>, ServerSchemaOf<Tables>, MutationsSchemaOf<Tables>> {
|
|
127
|
+
/** The table builders, for type extraction (`typeof schema.tables.item.$row`) and runtime metadata. */
|
|
128
|
+
tables: Tables;
|
|
129
|
+
}
|
|
130
|
+
type DefineSyncSchemaConfig<Tables extends SyncSchemaTables> = {
|
|
131
|
+
tables: Tables;
|
|
132
|
+
/** Full migration history (including the initial createTable version), built with `createMigrations`. */
|
|
133
|
+
migrations: Migrations;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Define a sync database schema from `t.table()` builders.
|
|
137
|
+
*
|
|
138
|
+
* Record keys are the crdt (view) table names; base table names default to the key
|
|
139
|
+
* prefixed with "_", overridable per table via `t.table(cols, { baseName })`.
|
|
140
|
+
*/
|
|
141
|
+
declare function defineSyncSchema<Tables extends SyncSchemaTables>({ tables, migrations, }: DefineSyncSchemaConfig<Tables>): DefinedSyncSchema<Tables>;
|
|
142
|
+
|
|
143
|
+
type NewCrdtEventValidationResult = {
|
|
144
|
+
success: true;
|
|
145
|
+
event: NewCrdtEvent;
|
|
146
|
+
} | {
|
|
147
|
+
success: false;
|
|
148
|
+
errors: string[];
|
|
149
|
+
};
|
|
150
|
+
declare class CrdtEventValidationError extends Error {
|
|
151
|
+
readonly errors: string[];
|
|
152
|
+
constructor(errors: string[]);
|
|
153
|
+
}
|
|
154
|
+
declare function validateNewCrdtEvent(schema: Pick<SyncDbSchema, "tables" | "tablesConfig">, input: unknown): NewCrdtEventValidationResult;
|
|
155
|
+
|
|
156
|
+
type SchemaVerificationIssue = {
|
|
157
|
+
table: string;
|
|
158
|
+
column?: string;
|
|
159
|
+
message: string;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Verify that replaying the full migration history on a fresh database produces
|
|
163
|
+
* exactly the declared table schema. Creates a throwaway in-memory SQLite db,
|
|
164
|
+
* applies all migrations, introspects the result, and diffs it against the
|
|
165
|
+
* `t.table()` declarations.
|
|
166
|
+
*
|
|
167
|
+
* Returns an empty array when migrations and schema agree. Intended for dev-time
|
|
168
|
+
* checks (`startDbWorker({ verifySchema: true })`) and tests:
|
|
169
|
+
* `expect(await verifySyncSchema(syncDbSchema)).toEqual([])`.
|
|
170
|
+
*/
|
|
171
|
+
declare function verifySyncSchema(schema: SyncDbSchema): Promise<SchemaVerificationIssue[]>;
|
|
172
|
+
declare function formatSchemaVerificationIssues(issues: SchemaVerificationIssue[]): string;
|
|
173
|
+
|
|
109
174
|
type CrdtStorageMutator<Database> = ReturnType<typeof createCrdtStorageMutator<Database>>;
|
|
110
175
|
type CommitEventOptions<Database, Table extends keyof Database & string> = {
|
|
111
176
|
type: "item-created";
|
|
@@ -238,4 +303,4 @@ declare function createSyncedDb<Database, Props = undefined>(options: SyncedDbOp
|
|
|
238
303
|
}>;
|
|
239
304
|
type SyncedDb<Database> = Awaited<ReturnType<typeof createSyncedDb<Database>>>;
|
|
240
305
|
|
|
241
|
-
export { CrdtStorage, type CrdtStorageMutator, CrdtTableConfig, ExecuteParams, ExecuteResult, InternalSQLiteWrapper, type KvStoreItem, KyselyQueryFactory, Logger, QueryBuilderOutput, SQLiteDbWrapper, SQLiteReactiveDb, SQLiteTransactionWrapper, type SharedLiveQuery, type SharedLiveQuerySnapshot, StoredValue, SyncDbSchema, type SyncedDb, TypedEvent, WorkerState, createCrdtStorageMutator, createCrdtSyncProducer, createKvStoreTableQuery, createSQLiteKvStore, createSyncedDb, dummyKysely, makeCrdtTable, parametersAreEqual, xxhash };
|
|
306
|
+
export { AnyTableBuilder, CrdtEventValidationError, CrdtStorage, type CrdtStorageMutator, CrdtTableConfig, type DefineSyncSchemaConfig, type DefinedSyncSchema, ExecuteParams, ExecuteResult, InferRow, InternalSQLiteWrapper, type KvStoreItem, KyselyQueryFactory, Logger, Migrations, type NewCrdtEventValidationResult, QueryBuilderOutput, ReadonlyTable, SQLiteDbWrapper, SQLiteReactiveDb, SQLiteTransactionWrapper, type SchemaVerificationIssue, type SharedLiveQuery, type SharedLiveQuerySnapshot, StoredValue, SyncDbSchema, SyncSchemaTables, type SyncedDb, TableBuilder, TableColumns, TypedEvent, WorkerState, createCrdtStorageMutator, createCrdtSyncProducer, createKvStoreTableQuery, createSQLiteKvStore, createSyncedDb, defineSyncSchema, dummyKysely, formatSchemaVerificationIssues, makeCrdtTable, parametersAreEqual, validateNewCrdtEvent, verifySyncSchema, xxhash };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CRDT_EVENT_NO_OP_PAYLOAD,
|
|
3
|
+
CrdtEventValidationError,
|
|
3
4
|
HLCCounter,
|
|
4
5
|
RESET_REQUEST_TTL_MS,
|
|
5
6
|
SQLiteDbWrapper,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
createSystemDbConfig,
|
|
22
23
|
deserializeHLC,
|
|
23
24
|
dummyKysely,
|
|
25
|
+
formatSchemaVerificationIssues,
|
|
24
26
|
introspectDb,
|
|
25
27
|
isNoOpCrdtEventPayload,
|
|
26
28
|
isWorkerErrorResponseMessage,
|
|
@@ -31,8 +33,10 @@ import {
|
|
|
31
33
|
serializeHLC,
|
|
32
34
|
startPerformanceLogger,
|
|
33
35
|
syncDbClientLockName,
|
|
36
|
+
validateNewCrdtEvent,
|
|
37
|
+
verifySyncSchema,
|
|
34
38
|
xxhash
|
|
35
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-LBYL5L4O.js";
|
|
36
40
|
import {
|
|
37
41
|
TypedBroadcastChannel,
|
|
38
42
|
TypedEvent,
|
|
@@ -458,45 +462,202 @@ function parametersAreEqual(a, b) {
|
|
|
458
462
|
return true;
|
|
459
463
|
}
|
|
460
464
|
|
|
461
|
-
// src/
|
|
462
|
-
function
|
|
463
|
-
|
|
465
|
+
// src/schema/define-sync-schema.ts
|
|
466
|
+
function defineSyncSchema({
|
|
467
|
+
tables,
|
|
468
|
+
migrations
|
|
469
|
+
}) {
|
|
470
|
+
const tablesConfig = Object.entries(tables).map(([crdtTableName, table]) => ({
|
|
471
|
+
crdtTableName,
|
|
472
|
+
baseTableName: table.baseName ?? `_${crdtTableName}`
|
|
473
|
+
}));
|
|
474
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
475
|
+
for (const { crdtTableName, baseTableName } of tablesConfig) {
|
|
476
|
+
for (const name of [crdtTableName, baseTableName]) {
|
|
477
|
+
if (seenNames.has(name)) {
|
|
478
|
+
throw new Error(`Duplicate table name "${name}" in sync schema`);
|
|
479
|
+
}
|
|
480
|
+
seenNames.add(name);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
tables,
|
|
485
|
+
get tablesConfig() {
|
|
486
|
+
return tablesConfig;
|
|
487
|
+
},
|
|
488
|
+
get migrations() {
|
|
489
|
+
return migrations;
|
|
490
|
+
},
|
|
491
|
+
get "~clientSchema"() {
|
|
492
|
+
throw new Error("~clientSchema is type-only and cannot be accessed at runtime");
|
|
493
|
+
},
|
|
494
|
+
get "~serverSchema"() {
|
|
495
|
+
throw new Error("~serverSchema is type-only and cannot be accessed at runtime");
|
|
496
|
+
},
|
|
497
|
+
get "~mutationsSchema"() {
|
|
498
|
+
throw new Error("~mutationsSchema is type-only and cannot be accessed at runtime");
|
|
499
|
+
}
|
|
500
|
+
};
|
|
464
501
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
502
|
+
|
|
503
|
+
// src/schema/table-builder.ts
|
|
504
|
+
var ColumnBuilder = class _ColumnBuilder {
|
|
505
|
+
constructor(meta) {
|
|
506
|
+
this.meta = meta;
|
|
468
507
|
}
|
|
469
|
-
|
|
470
|
-
|
|
508
|
+
/** Allow SQL NULL; widens the value type with `null`. */
|
|
509
|
+
nullable() {
|
|
510
|
+
return new _ColumnBuilder({ ...this.meta, nullable: true });
|
|
471
511
|
}
|
|
472
|
-
|
|
473
|
-
|
|
512
|
+
/** Declare a column default. The value is used both in DDL and when migrating older events. */
|
|
513
|
+
default(value) {
|
|
514
|
+
return new _ColumnBuilder({ ...this.meta, hasDefault: true, defaultValue: value });
|
|
474
515
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
return
|
|
516
|
+
/** Narrow the TypeScript value type without changing storage, e.g. `t.text().$type<"movie" | "tv">()`. */
|
|
517
|
+
$type() {
|
|
518
|
+
return this;
|
|
478
519
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
return
|
|
520
|
+
/** Human/AI-readable description, surfaced in generated schema docs. */
|
|
521
|
+
describe(description) {
|
|
522
|
+
return new _ColumnBuilder({ ...this.meta, description });
|
|
482
523
|
}
|
|
483
|
-
|
|
484
|
-
|
|
524
|
+
};
|
|
525
|
+
var RESERVED_COLUMNS = ["id", "tombstone"];
|
|
526
|
+
var idColumnMeta = {
|
|
527
|
+
kind: "text",
|
|
528
|
+
sqlType: "text",
|
|
529
|
+
nullable: false,
|
|
530
|
+
hasDefault: false,
|
|
531
|
+
description: "Unique immutable item id"
|
|
532
|
+
};
|
|
533
|
+
var tombstoneColumnMeta = {
|
|
534
|
+
kind: "boolean",
|
|
535
|
+
sqlType: "integer",
|
|
536
|
+
nullable: false,
|
|
537
|
+
hasDefault: true,
|
|
538
|
+
defaultValue: false,
|
|
539
|
+
description: "Soft-delete marker"
|
|
540
|
+
};
|
|
541
|
+
var TableBuilder = class _TableBuilder {
|
|
542
|
+
constructor(userColumns, options) {
|
|
543
|
+
this.userColumns = userColumns;
|
|
544
|
+
for (const reserved of RESERVED_COLUMNS) {
|
|
545
|
+
if (reserved in userColumns) {
|
|
546
|
+
throw new Error(`Column "${reserved}" is added automatically to every synced table and cannot be declared`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
this.columns = {
|
|
550
|
+
id: idColumnMeta,
|
|
551
|
+
...Object.fromEntries(Object.entries(userColumns).map(([name, column2]) => [name, column2.meta])),
|
|
552
|
+
tombstone: tombstoneColumnMeta
|
|
553
|
+
};
|
|
554
|
+
this.baseName = options?.baseName;
|
|
555
|
+
this.description = options?.description;
|
|
556
|
+
}
|
|
557
|
+
/** All columns in DDL order, including the auto-injected `id` and `tombstone`. */
|
|
558
|
+
columns;
|
|
559
|
+
baseName;
|
|
560
|
+
description;
|
|
561
|
+
describe(description) {
|
|
562
|
+
return new _TableBuilder(this.userColumns, { baseName: this.baseName, description });
|
|
563
|
+
}
|
|
564
|
+
/** Type-only: the row shape returned by queries. Do not access at runtime. */
|
|
565
|
+
get $row() {
|
|
485
566
|
return null;
|
|
486
567
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
568
|
+
/**
|
|
569
|
+
* Validate a CRDT event payload against the declared columns.
|
|
570
|
+
* `item-created` payloads must contain every column without a default (nullable columns may be omitted);
|
|
571
|
+
* `item-updated` payloads are partial and must not touch `id` or set `tombstone` to a deleted value.
|
|
572
|
+
*/
|
|
573
|
+
validatePayload(payload, options) {
|
|
574
|
+
const event = options?.event ?? "item-created";
|
|
575
|
+
const errors = [];
|
|
576
|
+
for (const key of Object.keys(payload)) {
|
|
577
|
+
if (!(key in this.columns)) {
|
|
578
|
+
errors.push(`Unknown column "${key}"`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (event === "item-updated") {
|
|
582
|
+
if ("id" in payload) {
|
|
583
|
+
errors.push(`Column "id" is immutable and cannot appear in an update payload`);
|
|
584
|
+
}
|
|
585
|
+
if (payload.tombstone === true || payload.tombstone === 1) {
|
|
586
|
+
errors.push(`Column "tombstone" cannot be set to true/1 in an update payload; use an item-deleted event`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (event === "item-created") {
|
|
590
|
+
for (const [name, meta] of Object.entries(this.columns)) {
|
|
591
|
+
if (name in payload || name === "tombstone" || meta.nullable || meta.hasDefault) {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
errors.push(`Missing required column "${name}"`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
for (const [name, value] of Object.entries(payload)) {
|
|
598
|
+
const meta = this.columns[name];
|
|
599
|
+
if (!meta || event === "item-updated" && name === "id") {
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
const error = validateValue(name, value, meta);
|
|
603
|
+
if (error) {
|
|
604
|
+
errors.push(error);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return errors.length === 0 ? { success: true } : { success: false, errors };
|
|
496
608
|
}
|
|
497
|
-
|
|
498
|
-
|
|
609
|
+
};
|
|
610
|
+
function describeValue(value) {
|
|
611
|
+
if (value === null) return "null";
|
|
612
|
+
if (Array.isArray(value)) return "array";
|
|
613
|
+
return typeof value;
|
|
614
|
+
}
|
|
615
|
+
function validateValue(name, value, meta) {
|
|
616
|
+
if (value === null) {
|
|
617
|
+
return meta.nullable ? null : `Column "${name}" is not nullable, got null`;
|
|
499
618
|
}
|
|
619
|
+
switch (meta.kind) {
|
|
620
|
+
case "text":
|
|
621
|
+
return typeof value === "string" ? null : `Column "${name}" expects text, got ${describeValue(value)}`;
|
|
622
|
+
case "integer":
|
|
623
|
+
return typeof value === "number" && Number.isInteger(value) ? null : `Column "${name}" expects an integer, got ${describeValue(value)}`;
|
|
624
|
+
case "real":
|
|
625
|
+
return typeof value === "number" && Number.isFinite(value) ? null : `Column "${name}" expects a number, got ${describeValue(value)}`;
|
|
626
|
+
case "boolean":
|
|
627
|
+
return typeof value === "boolean" || value === 0 || value === 1 ? null : `Column "${name}" expects a boolean (true/false or 0/1), got ${describeValue(value)}`;
|
|
628
|
+
case "enum": {
|
|
629
|
+
if (typeof value !== "string") {
|
|
630
|
+
return `Column "${name}" expects one of ${formatEnumValues(meta)}, got ${describeValue(value)}`;
|
|
631
|
+
}
|
|
632
|
+
return meta.enumValues?.includes(value) ? null : `Column "${name}" expects one of ${formatEnumValues(meta)}, got "${value}"`;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function formatEnumValues(meta) {
|
|
637
|
+
return (meta.enumValues ?? []).map((value) => `"${value}"`).join(" | ");
|
|
638
|
+
}
|
|
639
|
+
function column(kind, sqlType) {
|
|
640
|
+
return new ColumnBuilder({ kind, sqlType, nullable: false, hasDefault: false });
|
|
641
|
+
}
|
|
642
|
+
var t = {
|
|
643
|
+
text: () => column("text", "text"),
|
|
644
|
+
integer: () => column("integer", "integer"),
|
|
645
|
+
real: () => column("real", "real"),
|
|
646
|
+
/** Stored as INTEGER 0/1; always bind booleans as 0/1 when writing raw SQL. */
|
|
647
|
+
boolean: () => column("boolean", "integer"),
|
|
648
|
+
/**
|
|
649
|
+
* Union of string literals stored as TEXT, e.g. `t.enum(["movie", "tv"])`. Unlike `$type`, the
|
|
650
|
+
* allowed values are kept as runtime metadata, so payloads are validated against them.
|
|
651
|
+
* For an open-ended set, use `t.text().$type<"idle" | (string & {})>()` instead.
|
|
652
|
+
*/
|
|
653
|
+
enum: (values) => new ColumnBuilder({
|
|
654
|
+
kind: "enum",
|
|
655
|
+
sqlType: "text",
|
|
656
|
+
nullable: false,
|
|
657
|
+
hasDefault: false,
|
|
658
|
+
enumValues: values
|
|
659
|
+
}),
|
|
660
|
+
table: (columns, options) => new TableBuilder(columns, options)
|
|
500
661
|
};
|
|
501
662
|
|
|
502
663
|
// src/sqlite-crdt/crdt-storage-mutator.ts
|
|
@@ -583,7 +744,7 @@ select * from ${quoteId(baseTableName)}
|
|
|
583
744
|
where tombstone = 0;`,
|
|
584
745
|
{ loggerLevel: "system" }
|
|
585
746
|
);
|
|
586
|
-
const allColumnNames = tableSchema.columns.map((
|
|
747
|
+
const allColumnNames = tableSchema.columns.map((column2) => column2.name);
|
|
587
748
|
const jsonPayload = (from) => `'{'||${allColumnNames.map((col) => `'"${col}":'||json_quote(${from}.${quoteId(col)})`).join("||','||")}||'}'`;
|
|
588
749
|
db.execute(
|
|
589
750
|
`
|
|
@@ -636,17 +797,12 @@ function registerCrdtFunctions({
|
|
|
636
797
|
innocuous: false,
|
|
637
798
|
callback: (dataset, payloadRaw) => {
|
|
638
799
|
const payload = JSON.parse(payloadRaw);
|
|
639
|
-
storage.
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
},
|
|
646
|
-
{
|
|
647
|
-
wrapInTransaction: false
|
|
648
|
-
}
|
|
649
|
-
);
|
|
800
|
+
storage.internal.applyOwnEventFromTransaction(reactiveDb.db, {
|
|
801
|
+
type: "item-created",
|
|
802
|
+
dataset,
|
|
803
|
+
item_id: payload.id,
|
|
804
|
+
payload: payloadRaw
|
|
805
|
+
});
|
|
650
806
|
eventApplied = true;
|
|
651
807
|
return void 0;
|
|
652
808
|
}
|
|
@@ -668,34 +824,29 @@ function registerCrdtFunctions({
|
|
|
668
824
|
const newPayload = JSON.parse(newPayloadRaw);
|
|
669
825
|
let hasDiff = false;
|
|
670
826
|
const updatePayload = {};
|
|
671
|
-
for (const
|
|
672
|
-
const oldValue = oldPayload[
|
|
673
|
-
const newValue = newPayload[
|
|
827
|
+
for (const column2 of tableSchema.columns) {
|
|
828
|
+
const oldValue = oldPayload[column2.name];
|
|
829
|
+
const newValue = newPayload[column2.name];
|
|
674
830
|
if (oldValue === newValue) {
|
|
675
831
|
continue;
|
|
676
832
|
}
|
|
677
|
-
if (
|
|
833
|
+
if (column2.name === "id") {
|
|
678
834
|
throw new Error(
|
|
679
835
|
`Cannot update the "id" column of an item. It is used to identify the item and must be immutable.`
|
|
680
836
|
);
|
|
681
837
|
}
|
|
682
838
|
hasDiff = true;
|
|
683
|
-
updatePayload[
|
|
839
|
+
updatePayload[column2.name] = newValue;
|
|
684
840
|
}
|
|
685
841
|
if (!hasDiff) {
|
|
686
842
|
return;
|
|
687
843
|
}
|
|
688
|
-
storage.
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
},
|
|
695
|
-
{
|
|
696
|
-
wrapInTransaction: false
|
|
697
|
-
}
|
|
698
|
-
);
|
|
844
|
+
storage.internal.applyOwnEventFromTransaction(reactiveDb.db, {
|
|
845
|
+
type: "item-updated",
|
|
846
|
+
dataset,
|
|
847
|
+
item_id: oldPayload.id,
|
|
848
|
+
payload: JSON.stringify(updatePayload)
|
|
849
|
+
});
|
|
699
850
|
eventApplied = true;
|
|
700
851
|
return void 0;
|
|
701
852
|
}
|
|
@@ -706,17 +857,12 @@ function registerCrdtFunctions({
|
|
|
706
857
|
directOnly: false,
|
|
707
858
|
innocuous: false,
|
|
708
859
|
callback: (dataset, itemId) => {
|
|
709
|
-
storage.
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
},
|
|
716
|
-
{
|
|
717
|
-
wrapInTransaction: false
|
|
718
|
-
}
|
|
719
|
-
);
|
|
860
|
+
storage.internal.applyOwnEventFromTransaction(reactiveDb.db, {
|
|
861
|
+
type: "item-deleted",
|
|
862
|
+
dataset,
|
|
863
|
+
item_id: itemId,
|
|
864
|
+
payload: "{}"
|
|
865
|
+
});
|
|
720
866
|
eventApplied = true;
|
|
721
867
|
return void 0;
|
|
722
868
|
}
|
|
@@ -724,7 +870,7 @@ function registerCrdtFunctions({
|
|
|
724
870
|
reactiveDb.addEventListener("transaction-committed", () => {
|
|
725
871
|
if (eventApplied) {
|
|
726
872
|
eventApplied = false;
|
|
727
|
-
storage.
|
|
873
|
+
void storage.internal.processEnqueuedEvents();
|
|
728
874
|
}
|
|
729
875
|
});
|
|
730
876
|
reactiveDb.addEventListener("transaction-rolled-back", () => {
|
|
@@ -799,7 +945,8 @@ async function createMemoryDb({
|
|
|
799
945
|
crdtTables,
|
|
800
946
|
initializeSchema = true,
|
|
801
947
|
initialSyncId,
|
|
802
|
-
eventHlcAccumulator
|
|
948
|
+
eventHlcAccumulator,
|
|
949
|
+
syncDbSchema
|
|
803
950
|
}) {
|
|
804
951
|
await xxhash.ensureLoaded();
|
|
805
952
|
const reactiveDb = _reactiveDb;
|
|
@@ -821,7 +968,8 @@ async function createMemoryDb({
|
|
|
821
968
|
migrator,
|
|
822
969
|
db,
|
|
823
970
|
dbConfig: memoryDbConfig,
|
|
824
|
-
eventHlcAccumulator
|
|
971
|
+
eventHlcAccumulator,
|
|
972
|
+
schema: syncDbSchema
|
|
825
973
|
});
|
|
826
974
|
registerCrdtFunctions({
|
|
827
975
|
reactiveDb,
|
|
@@ -1004,7 +1152,8 @@ async function createSyncedDb(options) {
|
|
|
1004
1152
|
migrator: memoryDbMigrator,
|
|
1005
1153
|
reactiveDb,
|
|
1006
1154
|
hlcCounter,
|
|
1007
|
-
crdtTables: options.syncDbSchema.tablesConfig
|
|
1155
|
+
crdtTables: options.syncDbSchema.tablesConfig,
|
|
1156
|
+
syncDbSchema: options.syncDbSchema
|
|
1008
1157
|
});
|
|
1009
1158
|
const pullSyncId = createStoredValue({
|
|
1010
1159
|
initialValue: workerClientSnapshot.syncId
|
|
@@ -1105,10 +1254,13 @@ async function createSyncedDb(options) {
|
|
|
1105
1254
|
}
|
|
1106
1255
|
export {
|
|
1107
1256
|
CRDT_EVENT_NO_OP_PAYLOAD,
|
|
1257
|
+
ColumnBuilder,
|
|
1258
|
+
CrdtEventValidationError,
|
|
1108
1259
|
HLCCounter,
|
|
1109
1260
|
RESET_REQUEST_TTL_MS,
|
|
1110
1261
|
SQLiteDbWrapper,
|
|
1111
1262
|
SQLiteReactiveDb,
|
|
1263
|
+
TableBuilder,
|
|
1112
1264
|
TypedBroadcastChannel,
|
|
1113
1265
|
TypedEvent,
|
|
1114
1266
|
applyMemoryDbSchema,
|
|
@@ -1127,12 +1279,13 @@ export {
|
|
|
1127
1279
|
createSQLiteCrdtApplyFunction,
|
|
1128
1280
|
createSQLiteKvStore,
|
|
1129
1281
|
createStoredValue,
|
|
1130
|
-
createSyncDbSchema,
|
|
1131
1282
|
createSyncedDb,
|
|
1132
1283
|
createSystemDbConfig,
|
|
1133
1284
|
createTypedEventTarget,
|
|
1285
|
+
defineSyncSchema,
|
|
1134
1286
|
deserializeHLC,
|
|
1135
1287
|
dummyKysely,
|
|
1288
|
+
formatSchemaVerificationIssues,
|
|
1136
1289
|
generateId,
|
|
1137
1290
|
introspectDb,
|
|
1138
1291
|
isNoOpCrdtEventPayload,
|
|
@@ -1143,8 +1296,11 @@ export {
|
|
|
1143
1296
|
runSystemMigrations,
|
|
1144
1297
|
serializeHLC,
|
|
1145
1298
|
startPerformanceLogger,
|
|
1299
|
+
t,
|
|
1146
1300
|
tryCatch,
|
|
1147
1301
|
tryCatchAsync,
|
|
1302
|
+
validateNewCrdtEvent,
|
|
1303
|
+
verifySyncSchema,
|
|
1148
1304
|
xxhash
|
|
1149
1305
|
};
|
|
1150
1306
|
//# sourceMappingURL=index.js.map
|