@firtoz/drizzle-durable-sqlite 2.0.0 → 2.1.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/CHANGELOG.md +17 -0
- package/README.md +9 -1
- package/dist/chunk-3XAF3IZW.js +206 -0
- package/dist/chunk-3XAF3IZW.js.map +1 -0
- package/dist/chunk-DTRYQ3SF.js +36 -0
- package/dist/chunk-DTRYQ3SF.js.map +1 -0
- package/dist/chunk-JHZRO76J.js +288 -0
- package/dist/chunk-JHZRO76J.js.map +1 -0
- package/dist/chunk-NJ7RJSGZ.js +50 -0
- package/dist/chunk-NJ7RJSGZ.js.map +1 -0
- package/dist/chunk-ONDKTPGP.js +144 -0
- package/dist/chunk-ONDKTPGP.js.map +1 -0
- package/dist/chunk-PA5TYHIW.js +73 -0
- package/dist/chunk-PA5TYHIW.js.map +1 -0
- package/dist/chunk-QHM6T5OI.js +105 -0
- package/dist/chunk-QHM6T5OI.js.map +1 -0
- package/dist/chunk-YA4MAETI.js +47 -0
- package/dist/chunk-YA4MAETI.js.map +1 -0
- package/dist/drizzle-mutation-store.d.ts +20 -0
- package/dist/drizzle-mutation-store.js +3 -0
- package/dist/drizzle-mutation-store.js.map +1 -0
- package/dist/drizzle-partial-sync-changelog.d.ts +20 -0
- package/dist/drizzle-partial-sync-changelog.js +3 -0
- package/dist/drizzle-partial-sync-changelog.js.map +1 -0
- package/dist/drizzle-partial-sync-store.d.ts +22 -0
- package/dist/drizzle-partial-sync-store.js +4 -0
- package/dist/drizzle-partial-sync-store.js.map +1 -0
- package/dist/durable-sqlite-collection.d.ts +41 -0
- package/dist/durable-sqlite-collection.js +3 -0
- package/dist/durable-sqlite-collection.js.map +1 -0
- package/dist/durable-sqlite-sync-server.d.ts +48 -0
- package/dist/durable-sqlite-sync-server.js +3 -0
- package/dist/durable-sqlite-sync-server.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/partial-sync-predicate-sql.d.ts +29 -0
- package/dist/partial-sync-predicate-sql.js +3 -0
- package/dist/partial-sync-predicate-sql.js.map +1 -0
- package/dist/partial-sync-sqlite-db.d.ts +9 -0
- package/dist/partial-sync-sqlite-db.js +3 -0
- package/dist/partial-sync-sqlite-db.js.map +1 -0
- package/dist/queryable-durable-object.d.ts +128 -0
- package/dist/queryable-durable-object.js +3 -0
- package/dist/queryable-durable-object.js.map +1 -0
- package/dist/syncable-durable-object.d.ts +58 -0
- package/dist/syncable-durable-object.js +4 -0
- package/dist/syncable-durable-object.js.map +1 -0
- package/package.json +20 -18
- package/src/durable-sqlite-collection.ts +4 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PartialSyncRowShape, PartialSyncServerBridgeStore } from '@firtoz/collection-sync';
|
|
2
|
+
import { InferSelectModel } from 'drizzle-orm';
|
|
3
|
+
import { SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
4
|
+
import { DrizzleChangelogHelper } from './drizzle-partial-sync-changelog.js';
|
|
5
|
+
import { PartialSyncSqliteDatabase } from './partial-sync-sqlite-db.js';
|
|
6
|
+
import { PartialSyncTableConfig } from './partial-sync-predicate-sql.js';
|
|
7
|
+
import 'drizzle-orm/durable-sqlite';
|
|
8
|
+
|
|
9
|
+
type CreateDrizzlePartialSyncStoreOptions<TSchema extends Record<string, unknown>, TRow extends PartialSyncRowShape> = {
|
|
10
|
+
db: PartialSyncSqliteDatabase<TSchema>;
|
|
11
|
+
table: SQLiteTable;
|
|
12
|
+
columnConfig: PartialSyncTableConfig;
|
|
13
|
+
changelogHelper: DrizzleChangelogHelper<TSchema>;
|
|
14
|
+
deserializeJson: (raw: string) => unknown;
|
|
15
|
+
/** Column name on `table` used for `changesSince` watermark (e.g. `updatedAt`). */
|
|
16
|
+
updatedAtColumnName: keyof TRow & string;
|
|
17
|
+
};
|
|
18
|
+
declare function createDrizzlePartialSyncStore<TSchema extends Record<string, unknown>, TRow extends PartialSyncRowShape>(options: CreateDrizzlePartialSyncStoreOptions<TSchema, TRow>): PartialSyncServerBridgeStore<TRow>;
|
|
19
|
+
/** Infer row type from a Drizzle SQLite table. */
|
|
20
|
+
type InferPartialSyncRow<TTable extends SQLiteTable> = InferSelectModel<TTable>;
|
|
21
|
+
|
|
22
|
+
export { type CreateDrizzlePartialSyncStoreOptions, type InferPartialSyncRow, createDrizzlePartialSyncStore };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"drizzle-partial-sync-store.js"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SyncMode, CollectionConfig, InferSchemaOutput } from '@tanstack/db';
|
|
2
|
+
import { Table } from 'drizzle-orm';
|
|
3
|
+
import { DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
|
|
4
|
+
import { CollectionUtils } from '@firtoz/db-helpers';
|
|
5
|
+
import { TableWithRequiredFields, SQLInterceptor, SelectSchema, IdOf, InsertToSelectSchema } from '@firtoz/drizzle-utils';
|
|
6
|
+
export { SQLInterceptor, SQLOperation } from '@firtoz/drizzle-utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Drizzle database type for `drizzle-orm/durable-sqlite` (Cloudflare DO SQLite).
|
|
10
|
+
*/
|
|
11
|
+
type AnyDurableSqliteDatabase = DrizzleSqliteDODatabase<Record<string, unknown>>;
|
|
12
|
+
type DurableDrizzleSchema<TDrizzle extends AnyDurableSqliteDatabase> = TDrizzle["_"]["fullSchema"];
|
|
13
|
+
interface DurableSqliteCollectionConfig<TDrizzle extends AnyDurableSqliteDatabase, TTableName extends ValidTableNames<DurableDrizzleSchema<TDrizzle>>> {
|
|
14
|
+
drizzle: TDrizzle;
|
|
15
|
+
tableName: ValidTableNames<DurableDrizzleSchema<TDrizzle>> extends never ? {
|
|
16
|
+
$error: "The schema needs to include at least one table that uses the syncableTable function.";
|
|
17
|
+
} : TTableName;
|
|
18
|
+
/**
|
|
19
|
+
* Await before running sync queries (e.g. migrations finishing). Omit or leave undefined to use an already-resolved promise (no extra wait).
|
|
20
|
+
*/
|
|
21
|
+
readyPromise?: Promise<void>;
|
|
22
|
+
syncMode?: SyncMode;
|
|
23
|
+
debug?: boolean;
|
|
24
|
+
interceptor?: SQLInterceptor;
|
|
25
|
+
}
|
|
26
|
+
type ValidTableNames<TSchema extends Record<string, unknown>> = {
|
|
27
|
+
[K in keyof TSchema]: TSchema[K] extends TableWithRequiredFields ? K : never;
|
|
28
|
+
}[keyof TSchema];
|
|
29
|
+
type DurableSqliteCollectionConfigResult<TTable extends Table> = Omit<CollectionConfig<InferSchemaOutput<SelectSchema<TTable>>, IdOf<TTable>, InsertToSelectSchema<TTable>, CollectionUtils<InferSchemaOutput<SelectSchema<TTable>>>>, "utils"> & {
|
|
30
|
+
schema: InsertToSelectSchema<TTable>;
|
|
31
|
+
utils: CollectionUtils<InferSchemaOutput<SelectSchema<TTable>>>;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* TanStack DB collection configuration for a table stored in Durable Object SQLite via Drizzle.
|
|
35
|
+
*
|
|
36
|
+
* Uses `driverMode: "sync"` internally: DO SQLite runs `transactionSync`, so mutations use
|
|
37
|
+
* `.all()` / `.run()` inside a synchronous transaction callback (see `createSqliteTableSyncBackend` in `@firtoz/drizzle-utils`).
|
|
38
|
+
*/
|
|
39
|
+
declare function durableSqliteCollectionOptions<const TDrizzle extends AnyDurableSqliteDatabase, const TTableName extends string & ValidTableNames<DurableDrizzleSchema<TDrizzle>>, TTable extends DurableDrizzleSchema<TDrizzle>[TTableName] & TableWithRequiredFields>(config: DurableSqliteCollectionConfig<TDrizzle, TTableName>): DurableSqliteCollectionConfigResult<TTable>;
|
|
40
|
+
|
|
41
|
+
export { type AnyDurableSqliteDatabase, type DurableDrizzleSchema, type DurableSqliteCollectionConfig, type DurableSqliteCollectionConfigResult, type ValidTableNames, durableSqliteCollectionOptions };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"durable-sqlite-collection.js"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SyncMessage } from '@firtoz/db-helpers';
|
|
2
|
+
|
|
3
|
+
type MutationIntent<TItem> = {
|
|
4
|
+
clientMutationId: string;
|
|
5
|
+
type: "insert";
|
|
6
|
+
value: TItem;
|
|
7
|
+
} | {
|
|
8
|
+
clientMutationId: string;
|
|
9
|
+
type: "update";
|
|
10
|
+
key: string | number;
|
|
11
|
+
value: TItem;
|
|
12
|
+
previousValue: TItem;
|
|
13
|
+
} | {
|
|
14
|
+
clientMutationId: string;
|
|
15
|
+
type: "delete";
|
|
16
|
+
key: string | number;
|
|
17
|
+
} | {
|
|
18
|
+
clientMutationId: string;
|
|
19
|
+
type: "truncate";
|
|
20
|
+
};
|
|
21
|
+
interface DurableCollectionLike<TItem> {
|
|
22
|
+
insert: (item: Partial<TItem>) => {
|
|
23
|
+
isPersisted: {
|
|
24
|
+
promise: Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
update: (key: string | number, updater: (draft: TItem) => void) => {
|
|
28
|
+
isPersisted: {
|
|
29
|
+
promise: Promise<void>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
delete: (key: string | number) => {
|
|
33
|
+
isPersisted: {
|
|
34
|
+
promise: Promise<void>;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
utils: {
|
|
38
|
+
truncate: () => Promise<void>;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
declare function applyDurableMutationIntents<TItem extends {
|
|
42
|
+
id: string | number;
|
|
43
|
+
}>(collection: DurableCollectionLike<TItem>, intents: MutationIntent<TItem>[]): Promise<{
|
|
44
|
+
changes: SyncMessage<TItem>[];
|
|
45
|
+
acceptedMutationIds: string[];
|
|
46
|
+
}>;
|
|
47
|
+
|
|
48
|
+
export { type DurableCollectionLike, applyDurableMutationIntents };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"durable-sqlite-sync-server.js"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { AnyDurableSqliteDatabase, DurableDrizzleSchema, DurableSqliteCollectionConfig, DurableSqliteCollectionConfigResult, ValidTableNames, durableSqliteCollectionOptions } from './durable-sqlite-collection.js';
|
|
2
|
+
export { DurableCollectionLike, applyDurableMutationIntents } from './durable-sqlite-sync-server.js';
|
|
3
|
+
export { SyncableDurableObject, SyncableDurableObjectConfig, SyncableDurableObjectSyncRow } from './syncable-durable-object.js';
|
|
4
|
+
export { QueryableDurableObject, QueryableDurableObjectConfig } from './queryable-durable-object.js';
|
|
5
|
+
export { ChangelogOperation, DrizzleChangelogHelper, DrizzleChangelogHelperOptions, createDrizzleChangelogHelper } from './drizzle-partial-sync-changelog.js';
|
|
6
|
+
export { PartialSyncSqliteDatabase } from './partial-sync-sqlite-db.js';
|
|
7
|
+
export { CreateDrizzleMutationStoreOptions, createDrizzleMutationStore } from './drizzle-mutation-store.js';
|
|
8
|
+
export { CreateDrizzlePartialSyncStoreOptions, InferPartialSyncRow, createDrizzlePartialSyncStore } from './drizzle-partial-sync-store.js';
|
|
9
|
+
export { PartialSyncColumnKind, PartialSyncTableColumnConfig, PartialSyncTableConfig, coercePredicateScalar, columnRefForPredicate, predicateWhereFromConditions, rangeConditionToSQL, sortColumnFromConfig } from './partial-sync-predicate-sql.js';
|
|
10
|
+
export { SQLInterceptor, SQLOperation } from '@firtoz/drizzle-utils';
|
|
11
|
+
import '@tanstack/db';
|
|
12
|
+
import 'drizzle-orm';
|
|
13
|
+
import 'drizzle-orm/durable-sqlite';
|
|
14
|
+
import '@firtoz/db-helpers';
|
|
15
|
+
import 'hono/hono-base';
|
|
16
|
+
import 'hono/utils/http-status';
|
|
17
|
+
import '@firtoz/collection-sync';
|
|
18
|
+
import 'drizzle-orm/durable-sqlite/migrator';
|
|
19
|
+
import '@firtoz/websocket-do';
|
|
20
|
+
import 'drizzle-orm/sqlite-core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { QueryableDurableObject } from './chunk-3XAF3IZW.js';
|
|
2
|
+
export { SyncableDurableObject } from './chunk-ONDKTPGP.js';
|
|
3
|
+
export { createDrizzleMutationStore } from './chunk-PA5TYHIW.js';
|
|
4
|
+
export { createDrizzleChangelogHelper } from './chunk-DTRYQ3SF.js';
|
|
5
|
+
export { createDrizzlePartialSyncStore } from './chunk-JHZRO76J.js';
|
|
6
|
+
export { durableSqliteCollectionOptions } from './chunk-YA4MAETI.js';
|
|
7
|
+
export { applyDurableMutationIntents } from './chunk-NJ7RJSGZ.js';
|
|
8
|
+
export { coercePredicateScalar, columnRefForPredicate, predicateWhereFromConditions, rangeConditionToSQL, sortColumnFromConfig } from './chunk-QHM6T5OI.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { RangeCondition } from '@firtoz/collection-sync';
|
|
2
|
+
import { SQL } from 'drizzle-orm';
|
|
3
|
+
import { SQLiteTable, SQLiteColumn } from 'drizzle-orm/sqlite-core';
|
|
4
|
+
|
|
5
|
+
/** Column kind for predicate coercion and sort. */
|
|
6
|
+
type PartialSyncColumnKind = "text" | "integer";
|
|
7
|
+
type PartialSyncTableColumnConfig = {
|
|
8
|
+
kind: PartialSyncColumnKind;
|
|
9
|
+
/**
|
|
10
|
+
* When kind is `integer`, use `Math.trunc` after `Number()` (grid coordinates).
|
|
11
|
+
* Default false: finite number only.
|
|
12
|
+
*/
|
|
13
|
+
truncateInteger?: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Declares which table columns exist for predicates/sorts and how to coerce literals.
|
|
17
|
+
* `sortableColumns` must be a subset of keys in `columns`.
|
|
18
|
+
*/
|
|
19
|
+
type PartialSyncTableConfig<TSortable extends string = string> = {
|
|
20
|
+
columns: Record<string, PartialSyncTableColumnConfig>;
|
|
21
|
+
sortableColumns: readonly TSortable[];
|
|
22
|
+
};
|
|
23
|
+
declare function columnRefForPredicate(table: SQLiteTable, columnName: string, columnConfig: PartialSyncTableConfig): SQLiteColumn;
|
|
24
|
+
declare function coercePredicateScalar(column: string, value: unknown, columnConfig: PartialSyncTableConfig): string | number;
|
|
25
|
+
declare function rangeConditionToSQL(table: SQLiteTable, condition: RangeCondition, columnConfig: PartialSyncTableConfig): SQL;
|
|
26
|
+
declare function predicateWhereFromConditions(table: SQLiteTable, conditions: RangeCondition[], columnConfig: PartialSyncTableConfig): SQL | undefined;
|
|
27
|
+
declare function sortColumnFromConfig(table: SQLiteTable, columnName: string, columnConfig: PartialSyncTableConfig): SQLiteColumn;
|
|
28
|
+
|
|
29
|
+
export { type PartialSyncColumnKind, type PartialSyncTableColumnConfig, type PartialSyncTableConfig, coercePredicateScalar, columnRefForPredicate, predicateWhereFromConditions, rangeConditionToSQL, sortColumnFromConfig };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"partial-sync-predicate-sql.js"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Drizzle Durable Object SQLite database used by partial-sync helpers.
|
|
5
|
+
* (Bun/libsql drivers differ in `select` overloads; use DO SQLite in Workers.)
|
|
6
|
+
*/
|
|
7
|
+
type PartialSyncSqliteDatabase<TSchema extends Record<string, unknown>> = DrizzleSqliteDODatabase<TSchema>;
|
|
8
|
+
|
|
9
|
+
export type { PartialSyncSqliteDatabase };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"partial-sync-sqlite-db.js"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as hono_hono_base from 'hono/hono-base';
|
|
2
|
+
import * as hono_utils_http_status from 'hono/utils/http-status';
|
|
3
|
+
import { PartialSyncRowShape, SyncServerMessage, SyncClientMessage, PartialSyncServerBridge, PartialSyncMutationHandler, PartialSyncServerBridgeStore, RangeCondition, SyncRange, SyncRangeSort, SyncServerBridgeStore } from '@firtoz/collection-sync';
|
|
4
|
+
import { SyncMessage } from '@firtoz/db-helpers';
|
|
5
|
+
import { StandardSchemaWebSocketDO, StandardSchemaSession, StandardSchemaSessionOptions } from '@firtoz/websocket-do';
|
|
6
|
+
import { drizzle, DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
|
|
7
|
+
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
|
|
8
|
+
|
|
9
|
+
type SessionData = {
|
|
10
|
+
clientId: string;
|
|
11
|
+
};
|
|
12
|
+
type MutationSyncRow = PartialSyncRowShape;
|
|
13
|
+
type SessionDispatch<TRow extends MutationSyncRow> = {
|
|
14
|
+
partialBridge: PartialSyncServerBridge<TRow>;
|
|
15
|
+
partialMutationHandler?: PartialSyncMutationHandler<TRow>;
|
|
16
|
+
};
|
|
17
|
+
type SessionSlot<TRow extends MutationSyncRow> = {
|
|
18
|
+
dispatch?: SessionDispatch<TRow>;
|
|
19
|
+
pending: SyncClientMessage[];
|
|
20
|
+
};
|
|
21
|
+
declare class QueryableSession<TItem extends PartialSyncRowShape, TEnv extends Cloudflare.Env> extends StandardSchemaSession<SessionData, SyncServerMessage<TItem>, SyncClientMessage, TEnv> {
|
|
22
|
+
private readonly sessionSlot;
|
|
23
|
+
clientId: string;
|
|
24
|
+
constructor(websocket: WebSocket, sessions: Map<WebSocket, QueryableSession<TItem, TEnv>>, options: StandardSchemaSessionOptions<SyncClientMessage, SyncServerMessage<TItem>>, sessionSlot: SessionSlot<TItem>);
|
|
25
|
+
}
|
|
26
|
+
type QueryableDurableObjectConfig<TSchema extends Record<string, unknown>, TRow extends PartialSyncRowShape = PartialSyncRowShape> = {
|
|
27
|
+
schema: TSchema;
|
|
28
|
+
migrations: Parameters<typeof migrate>[1];
|
|
29
|
+
queryChunkSize?: number;
|
|
30
|
+
seedInBackground?: boolean;
|
|
31
|
+
serializeJson?: (value: unknown) => string;
|
|
32
|
+
deserializeJson?: (raw: string) => unknown;
|
|
33
|
+
/**
|
|
34
|
+
* When set, used as the partial-sync store instead of overriding
|
|
35
|
+
* {@link QueryableDurableObject.queryRange}, {@link QueryableDurableObject.queryByOffset},
|
|
36
|
+
* and {@link QueryableDurableObject.getTotalCount}.
|
|
37
|
+
*/
|
|
38
|
+
createPartialSyncStore?: (db: DrizzleSqliteDODatabase<TSchema>) => PartialSyncServerBridgeStore<TRow>;
|
|
39
|
+
/**
|
|
40
|
+
* Multiplex key for partial-sync WebSocket messages.
|
|
41
|
+
* When using {@link PartialSyncMutationHandler} on the same socket, use the same id unless you multiplex multiple collections.
|
|
42
|
+
*/
|
|
43
|
+
collectionId?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Server-side narrowing of client predicate viewports (e.g. fog of war). Passed to
|
|
46
|
+
* {@link PartialSyncServerBridge}.
|
|
47
|
+
*/
|
|
48
|
+
resolveClientVisibility?: (clientId: string, requestedConditions: RangeCondition[]) => RangeCondition[] | Promise<RangeCondition[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Optional hints for rows that left the client's range during `rangeReconcile`. Return `null` for
|
|
51
|
+
* fog of war (default when omitted).
|
|
52
|
+
*/
|
|
53
|
+
resolveMovedHint?: (row: TRow, range: SyncRange) => Record<string, unknown> | null | Promise<Record<string, unknown> | null>;
|
|
54
|
+
};
|
|
55
|
+
declare abstract class QueryableDurableObject<TRow extends PartialSyncRowShape, TSchema extends Record<string, unknown>, TEnv extends Cloudflare.Env = Cloudflare.Env> extends StandardSchemaWebSocketDO<QueryableSession<TRow, TEnv>, SyncClientMessage, SyncServerMessage<TRow>, TEnv> {
|
|
56
|
+
protected db: ReturnType<typeof drizzle>;
|
|
57
|
+
protected bridge: PartialSyncServerBridge<TRow>;
|
|
58
|
+
protected partialMutationHandler?: PartialSyncMutationHandler<TRow>;
|
|
59
|
+
readonly app: hono_hono_base.HonoBase<{
|
|
60
|
+
Bindings: TEnv;
|
|
61
|
+
}, {
|
|
62
|
+
"/websocket": {
|
|
63
|
+
$get: {
|
|
64
|
+
input: {};
|
|
65
|
+
output: {};
|
|
66
|
+
outputFormat: string;
|
|
67
|
+
status: hono_utils_http_status.StatusCode;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
} & {
|
|
71
|
+
"/health": {
|
|
72
|
+
$get: {
|
|
73
|
+
input: {};
|
|
74
|
+
output: "ok";
|
|
75
|
+
outputFormat: "text";
|
|
76
|
+
status: hono_utils_http_status.ContentfulStatusCode;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
}, "/", "/health">;
|
|
80
|
+
constructor(ctx: DurableObjectState, env: TEnv, config: QueryableDurableObjectConfig<TSchema, TRow>);
|
|
81
|
+
protected queryRange(_options: {
|
|
82
|
+
sort: {
|
|
83
|
+
column: string;
|
|
84
|
+
direction: "asc" | "desc";
|
|
85
|
+
};
|
|
86
|
+
limit: number;
|
|
87
|
+
afterCursor: unknown | null;
|
|
88
|
+
chunkSize: number;
|
|
89
|
+
}): AsyncIterable<TRow[]>;
|
|
90
|
+
protected queryByOffset(_options: {
|
|
91
|
+
sort: {
|
|
92
|
+
column: string;
|
|
93
|
+
direction: "asc" | "desc";
|
|
94
|
+
};
|
|
95
|
+
limit: number;
|
|
96
|
+
offset: number;
|
|
97
|
+
chunkSize: number;
|
|
98
|
+
}): AsyncIterable<TRow[]>;
|
|
99
|
+
protected getTotalCount(): Promise<number>;
|
|
100
|
+
protected queryByPredicate?(_options: {
|
|
101
|
+
conditions: RangeCondition[];
|
|
102
|
+
sort?: SyncRangeSort;
|
|
103
|
+
limit?: number;
|
|
104
|
+
chunkSize: number;
|
|
105
|
+
}): AsyncIterable<TRow[]>;
|
|
106
|
+
protected getPredicateCount?(_conditions: RangeCondition[]): Promise<number>;
|
|
107
|
+
protected changesSince?(_options: {
|
|
108
|
+
range: SyncRange;
|
|
109
|
+
sinceVersion: number;
|
|
110
|
+
chunkSize: number;
|
|
111
|
+
}): Promise<{
|
|
112
|
+
changes: SyncMessage<TRow>[];
|
|
113
|
+
totalCount: number;
|
|
114
|
+
} | null>;
|
|
115
|
+
protected getSortValue(row: TRow, column: string): unknown;
|
|
116
|
+
/**
|
|
117
|
+
* When overridden to return a store, `mutateBatch` is handled by {@link PartialSyncMutationHandler}
|
|
118
|
+
* (interest-scoped `rangePatch` + `ack` with `serverVersion: 0`). `syncHello` is not handled on this path.
|
|
119
|
+
* Range traffic stays on {@link PartialSyncServerBridge}.
|
|
120
|
+
*/
|
|
121
|
+
protected createClientMutationSyncStore(): SyncServerBridgeStore<TRow> | undefined;
|
|
122
|
+
protected seedData(): Promise<void>;
|
|
123
|
+
pushServerChanges(changes: SyncMessage<TRow>[]): Promise<void>;
|
|
124
|
+
protected sendToClient(clientId: string, message: SyncServerMessage<TRow>): void;
|
|
125
|
+
protected broadcastExcept(excludeClientId: string, message: SyncServerMessage<TRow>): void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { QueryableDurableObject, type QueryableDurableObjectConfig };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"queryable-durable-object.js"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as hono_hono_base from 'hono/hono-base';
|
|
2
|
+
import * as hono_utils_http_status from 'hono/utils/http-status';
|
|
3
|
+
import { SyncClientMessage, SyncServerMessage, SyncServerBridge, PartialSyncRowShape } from '@firtoz/collection-sync';
|
|
4
|
+
import { InferSchemaOutput, SyncMode } from '@tanstack/db';
|
|
5
|
+
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
|
|
6
|
+
import { TableWithRequiredFields, SelectSchema, DrizzleSqliteTableCollection } from '@firtoz/drizzle-utils';
|
|
7
|
+
import { StandardSchemaWebSocketDO } from '@firtoz/websocket-do';
|
|
8
|
+
import { ValidTableNames } from './durable-sqlite-collection.js';
|
|
9
|
+
import 'drizzle-orm';
|
|
10
|
+
import 'drizzle-orm/durable-sqlite';
|
|
11
|
+
import '@firtoz/db-helpers';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Drizzle/Valibot `InferSchemaOutput` is not always structurally assignable to
|
|
15
|
+
* {@link PartialSyncRowShape}. Intersecting keeps inferred columns while requiring sync row keys for
|
|
16
|
+
* {@link SyncServerBridge}.
|
|
17
|
+
*/
|
|
18
|
+
type SyncBridgeRowFromTable<TTable extends TableWithRequiredFields> = InferSchemaOutput<SelectSchema<TTable>> & PartialSyncRowShape;
|
|
19
|
+
type SyncableDurableObjectSyncRow<TSchema extends Record<string, unknown>, TTableName extends ValidTableNames<TSchema>> = SyncBridgeRowFromTable<TSchema[TTableName] & TableWithRequiredFields>;
|
|
20
|
+
type SyncableDurableObjectConfig<TSchema extends Record<string, unknown>, TTableName extends ValidTableNames<TSchema>> = {
|
|
21
|
+
schema: TSchema;
|
|
22
|
+
tableName: TTableName;
|
|
23
|
+
migrations: Parameters<typeof migrate>[1];
|
|
24
|
+
syncMode?: SyncMode;
|
|
25
|
+
serializeJson?: (value: unknown) => string;
|
|
26
|
+
deserializeJson?: (raw: string) => unknown;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Durable Object base class: Drizzle SQLite + {@link SyncServerBridge} + WebSocket sessions.
|
|
30
|
+
*/
|
|
31
|
+
declare abstract class SyncableDurableObject<TSchema extends Record<string, unknown>, TTableName extends ValidTableNames<TSchema>, TEnv extends Cloudflare.Env = Cloudflare.Env> extends StandardSchemaWebSocketDO<any, SyncClientMessage, SyncServerMessage<unknown>, TEnv> {
|
|
32
|
+
protected bridge: SyncServerBridge<SyncableDurableObjectSyncRow<TSchema, TTableName>>;
|
|
33
|
+
protected collection: DrizzleSqliteTableCollection<TSchema[TTableName] & TableWithRequiredFields>;
|
|
34
|
+
readonly app: hono_hono_base.HonoBase<{
|
|
35
|
+
Bindings: TEnv;
|
|
36
|
+
}, {
|
|
37
|
+
"/websocket": {
|
|
38
|
+
$get: {
|
|
39
|
+
input: {};
|
|
40
|
+
output: {};
|
|
41
|
+
outputFormat: string;
|
|
42
|
+
status: hono_utils_http_status.StatusCode;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
} & {
|
|
46
|
+
"/health": {
|
|
47
|
+
$get: {
|
|
48
|
+
input: {};
|
|
49
|
+
output: "ok";
|
|
50
|
+
outputFormat: "text";
|
|
51
|
+
status: hono_utils_http_status.ContentfulStatusCode;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
}, "/", "/health">;
|
|
55
|
+
constructor(ctx: DurableObjectState, env: TEnv, config: SyncableDurableObjectConfig<TSchema, TTableName>);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { SyncableDurableObject, type SyncableDurableObjectConfig, type SyncableDurableObjectSyncRow };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"syncable-durable-object.js"}
|
package/package.json
CHANGED
|
@@ -1,35 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firtoz/drizzle-durable-sqlite",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "TanStack DB collections backed by Drizzle on Cloudflare Durable Object SQLite",
|
|
5
|
-
"main": "./
|
|
6
|
-
"module": "./
|
|
7
|
-
"types": "./
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"types": "./
|
|
12
|
-
"import": "./
|
|
13
|
-
"require": "./src/index.ts"
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
14
13
|
},
|
|
15
14
|
"./durableSqliteCollectionOptions": {
|
|
16
|
-
"types": "./
|
|
17
|
-
"import": "./
|
|
18
|
-
"require": "./src/durable-sqlite-collection.ts"
|
|
15
|
+
"types": "./dist/durable-sqlite-collection.d.ts",
|
|
16
|
+
"import": "./dist/durable-sqlite-collection.js"
|
|
19
17
|
},
|
|
20
18
|
"./*": {
|
|
21
|
-
"types": "./
|
|
22
|
-
"import": "./
|
|
23
|
-
"require": "./src/*.ts"
|
|
19
|
+
"types": "./dist/*.d.ts",
|
|
20
|
+
"import": "./dist/*.js"
|
|
24
21
|
}
|
|
25
22
|
},
|
|
26
23
|
"files": [
|
|
24
|
+
"dist/**/*.js",
|
|
25
|
+
"dist/**/*.js.map",
|
|
26
|
+
"dist/**/*.d.ts",
|
|
27
27
|
"src/**/*.ts",
|
|
28
28
|
"!src/**/*.test.ts",
|
|
29
29
|
"README.md",
|
|
30
30
|
"CHANGELOG.md"
|
|
31
31
|
],
|
|
32
32
|
"scripts": {
|
|
33
|
+
"build": "tsup",
|
|
34
|
+
"prepack": "bun run build",
|
|
33
35
|
"typecheck": "tsgo --noEmit -p ./tsconfig.json",
|
|
34
36
|
"lint": "biome check --write src",
|
|
35
37
|
"lint:ci": "biome ci src",
|
|
@@ -68,11 +70,11 @@
|
|
|
68
70
|
"valibot": ">=1.3.1"
|
|
69
71
|
},
|
|
70
72
|
"dependencies": {
|
|
71
|
-
"@firtoz/collection-sync": "^
|
|
72
|
-
"@firtoz/db-helpers": "^2.
|
|
73
|
-
"@firtoz/drizzle-utils": "^1.
|
|
74
|
-
"@firtoz/maybe-error": "^1.
|
|
75
|
-
"@firtoz/websocket-do": "^
|
|
73
|
+
"@firtoz/collection-sync": "^6.0.0",
|
|
74
|
+
"@firtoz/db-helpers": "^2.2.0",
|
|
75
|
+
"@firtoz/drizzle-utils": "^1.3.0",
|
|
76
|
+
"@firtoz/maybe-error": "^1.6.0",
|
|
77
|
+
"@firtoz/websocket-do": "^13.0.0"
|
|
76
78
|
},
|
|
77
79
|
"devDependencies": {
|
|
78
80
|
"@cloudflare/workers-types": "^4.20260329.1",
|
|
@@ -16,7 +16,6 @@ import type {
|
|
|
16
16
|
import {
|
|
17
17
|
createSyncFunction,
|
|
18
18
|
createInsertSchemaWithIdDefault,
|
|
19
|
-
createGetKeyFunction,
|
|
20
19
|
createCollectionConfig,
|
|
21
20
|
createSqliteTableSyncBackend,
|
|
22
21
|
type SQLOperation,
|
|
@@ -90,8 +89,9 @@ export function durableSqliteCollectionOptions<
|
|
|
90
89
|
|
|
91
90
|
const table = config.drizzle._.fullSchema[tableName] as TTable;
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
const getKey = (
|
|
93
|
+
item: InferSchemaOutput<SelectSchema<TTable>>,
|
|
94
|
+
): IdOf<TTable> => (item as { id: IdOf<TTable> }).id;
|
|
95
95
|
|
|
96
96
|
const backend = createSqliteTableSyncBackend({
|
|
97
97
|
drizzle: config.drizzle,
|
|
@@ -107,7 +107,7 @@ export function durableSqliteCollectionOptions<
|
|
|
107
107
|
readyPromise: config.readyPromise ?? Promise.resolve(),
|
|
108
108
|
syncMode: config.syncMode,
|
|
109
109
|
debug: config.debug,
|
|
110
|
-
getSyncPersistKey: (item
|
|
110
|
+
getSyncPersistKey: (item) => String(getKey(item)),
|
|
111
111
|
};
|
|
112
112
|
|
|
113
113
|
const syncResult = createSyncFunction(baseSyncConfig, backend);
|