@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 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/drizzle-partial-sync-store.ts"],"names":[],"mappings":"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","file":"chunk-JHZRO76J.js","sourcesContent":["import type {\n\tPartialSyncRowShape,\n\tPartialSyncServerBridgeStore,\n\tRangeCondition,\n\tSyncRange,\n\tSyncRangeSort,\n} from \"@firtoz/collection-sync\";\nimport { compareInterestValues } from \"@firtoz/collection-sync/partial-sync-interest\";\nimport {\n\tdefaultPredicateColumnValue,\n\tmatchesPredicate,\n} from \"@firtoz/collection-sync/partial-sync-predicate-match\";\nimport type { SyncMessage } from \"@firtoz/db-helpers\";\nimport { exhaustiveGuard } from \"@firtoz/maybe-error\";\nimport {\n\tand,\n\tasc,\n\tcount,\n\tdesc,\n\teq,\n\tgt,\n\tlt,\n\tmax,\n\tor,\n\ttype InferSelectModel,\n\ttype SQL,\n} from \"drizzle-orm\";\nimport { getTableColumns } from \"drizzle-orm\";\nimport type { SQLiteTable } from \"drizzle-orm/sqlite-core\";\nimport type {\n\tChangelogOperation,\n\tDrizzleChangelogHelper,\n} from \"./drizzle-partial-sync-changelog\";\nimport type { PartialSyncSqliteDatabase } from \"./partial-sync-sqlite-db\";\nimport {\n\tpredicateWhereFromConditions,\n\tsortColumnFromConfig,\n\ttype PartialSyncTableConfig,\n} from \"./partial-sync-predicate-sql\";\n\nexport type CreateDrizzlePartialSyncStoreOptions<\n\tTSchema extends Record<string, unknown>,\n\tTRow extends PartialSyncRowShape,\n> = {\n\tdb: PartialSyncSqliteDatabase<TSchema>;\n\ttable: SQLiteTable;\n\tcolumnConfig: PartialSyncTableConfig;\n\tchangelogHelper: DrizzleChangelogHelper<TSchema>;\n\tdeserializeJson: (raw: string) => unknown;\n\t/** Column name on `table` used for `changesSince` watermark (e.g. `updatedAt`). */\n\tupdatedAtColumnName: keyof TRow & string;\n};\n\nexport function createDrizzlePartialSyncStore<\n\tTSchema extends Record<string, unknown>,\n\tTRow extends PartialSyncRowShape,\n>(\n\toptions: CreateDrizzlePartialSyncStoreOptions<TSchema, TRow>,\n): PartialSyncServerBridgeStore<TRow> {\n\tconst { db, table, columnConfig, changelogHelper, deserializeJson } = options;\n\tconst tableColumns = getTableColumns(table);\n\tconst idCol = tableColumns.id;\n\tconst updatedAtCol = tableColumns[options.updatedAtColumnName];\n\tif (idCol === undefined) {\n\t\tthrow new Error(\"Partial sync table must have an id column\");\n\t}\n\tif (updatedAtCol === undefined) {\n\t\tthrow new Error(\n\t\t\t`Partial sync table missing updatedAt column: ${String(options.updatedAtColumnName)}`,\n\t\t);\n\t}\n\n\tasync function* queryRange(opts: {\n\t\tsort: SyncRangeSort;\n\t\tlimit: number;\n\t\tafterCursor: unknown | null;\n\t\tchunkSize: number;\n\t}): AsyncIterable<TRow[]> {\n\t\tlet remaining = opts.limit;\n\t\tlet cursor = opts.afterCursor;\n\t\tconst sortColumn = sortColumnFromConfig(\n\t\t\ttable,\n\t\t\topts.sort.column,\n\t\t\tcolumnConfig,\n\t\t);\n\t\twhile (remaining > 0) {\n\t\t\tconst currentLimit = Math.min(opts.chunkSize, remaining);\n\t\t\tconst directionExpr =\n\t\t\t\topts.sort.direction === \"asc\" ? asc(sortColumn) : desc(sortColumn);\n\t\t\tconst whereCursor =\n\t\t\t\tcursor === null\n\t\t\t\t\t? undefined\n\t\t\t\t\t: opts.sort.direction === \"asc\"\n\t\t\t\t\t\t? gt(sortColumn, cursor as never)\n\t\t\t\t\t\t: lt(sortColumn, cursor as never);\n\t\t\tconst rows = await db\n\t\t\t\t.select()\n\t\t\t\t.from(table)\n\t\t\t\t.where(whereCursor ? and(whereCursor) : undefined)\n\t\t\t\t.orderBy(directionExpr, asc(idCol))\n\t\t\t\t.limit(currentLimit);\n\t\t\tif (rows.length === 0) break;\n\t\t\tyield rows as TRow[];\n\t\t\tremaining -= rows.length;\n\t\t\tif (rows.length < currentLimit) break;\n\t\t\tconst last = rows[rows.length - 1] as Record<string, unknown>;\n\t\t\tcursor = last[opts.sort.column];\n\t\t}\n\t}\n\n\tasync function* queryByOffset(opts: {\n\t\tsort: SyncRangeSort;\n\t\tlimit: number;\n\t\toffset: number;\n\t\tchunkSize: number;\n\t}): AsyncIterable<TRow[]> {\n\t\tlet remaining = opts.limit;\n\t\tlet sqlOffset = opts.offset;\n\t\tconst sortColumn = sortColumnFromConfig(\n\t\t\ttable,\n\t\t\topts.sort.column,\n\t\t\tcolumnConfig,\n\t\t);\n\t\twhile (remaining > 0) {\n\t\t\tconst currentLimit = Math.min(opts.chunkSize, remaining);\n\t\t\tconst directionExpr =\n\t\t\t\topts.sort.direction === \"asc\" ? asc(sortColumn) : desc(sortColumn);\n\t\t\tconst rows = await db\n\t\t\t\t.select()\n\t\t\t\t.from(table)\n\t\t\t\t.orderBy(directionExpr, asc(idCol))\n\t\t\t\t.limit(currentLimit)\n\t\t\t\t.offset(sqlOffset);\n\t\t\tif (rows.length === 0) break;\n\t\t\tyield rows as TRow[];\n\t\t\tremaining -= rows.length;\n\t\t\tsqlOffset += rows.length;\n\t\t\tif (rows.length < currentLimit) break;\n\t\t}\n\t}\n\n\tasync function getTotalCount(): Promise<number> {\n\t\tconst rows = await db.select({ c: count() }).from(table);\n\t\treturn rows[0]?.c ?? 0;\n\t}\n\n\tfunction getSortValue(row: TRow, column: string): unknown {\n\t\treturn (row as Record<string, unknown>)[column];\n\t}\n\n\tasync function* queryByPredicate(opts: {\n\t\tconditions: RangeCondition[];\n\t\tsort?: SyncRangeSort;\n\t\tlimit?: number;\n\t\tchunkSize: number;\n\t}): AsyncIterable<TRow[]> {\n\t\tconst limit = opts.limit ?? opts.chunkSize;\n\t\tlet remaining = limit;\n\t\tlet offset = 0;\n\t\tconst where = predicateWhereFromConditions(\n\t\t\ttable,\n\t\t\topts.conditions,\n\t\t\tcolumnConfig,\n\t\t);\n\t\tconst sortColumnName = opts.sort?.column ?? columnConfig.sortableColumns[0];\n\t\tif (sortColumnName === undefined) {\n\t\t\tthrow new Error(\"queryByPredicate requires sort or sortableColumns[0]\");\n\t\t}\n\t\tconst sortColumn = sortColumnFromConfig(\n\t\t\ttable,\n\t\t\tsortColumnName,\n\t\t\tcolumnConfig,\n\t\t);\n\t\tconst directionExpr =\n\t\t\topts.sort?.direction === \"desc\" ? desc(sortColumn) : asc(sortColumn);\n\t\twhile (remaining > 0) {\n\t\t\tconst currentLimit = Math.min(opts.chunkSize, remaining);\n\t\t\tconst rows = await db\n\t\t\t\t.select()\n\t\t\t\t.from(table)\n\t\t\t\t.where(where)\n\t\t\t\t.orderBy(directionExpr, asc(idCol))\n\t\t\t\t.limit(currentLimit)\n\t\t\t\t.offset(offset);\n\t\t\tif (rows.length === 0) break;\n\t\t\tyield rows as TRow[];\n\t\t\tremaining -= rows.length;\n\t\t\toffset += rows.length;\n\t\t\tif (rows.length < currentLimit) break;\n\t\t}\n\t}\n\n\tasync function getPredicateCount(\n\t\tconditions: RangeCondition[],\n\t): Promise<number> {\n\t\tconst where = predicateWhereFromConditions(table, conditions, columnConfig);\n\t\tconst rows = await db.select({ c: count() }).from(table).where(where);\n\t\treturn rows[0]?.c ?? 0;\n\t}\n\n\tfunction strictlyBeforeInSortOrder(\n\t\tsortCol: ReturnType<typeof sortColumnFromConfig>,\n\t\tidColSQLite: typeof idCol,\n\t\trowSort: unknown,\n\t\trowId: unknown,\n\t\tdirection: \"asc\" | \"desc\",\n\t): SQL {\n\t\tif (direction === \"asc\") {\n\t\t\treturn or(\n\t\t\t\tlt(sortCol, rowSort as never),\n\t\t\t\tand(eq(sortCol, rowSort as never), lt(idColSQLite, rowId as never)),\n\t\t\t) as SQL;\n\t\t}\n\t\treturn or(\n\t\t\tgt(sortCol, rowSort as never),\n\t\t\tand(eq(sortCol, rowSort as never), lt(idColSQLite, rowId as never)),\n\t\t) as SQL;\n\t}\n\n\tasync function rowInIndexRange(\n\t\trow: TRow,\n\t\tindexRange: Extract<SyncRange, { kind: \"index\" }>,\n\t): Promise<boolean> {\n\t\tconst sortName = indexRange.sort.column;\n\t\tconst rowRec = row as Record<string, unknown>;\n\t\tconst rowSort = rowRec[sortName];\n\t\tconst rowId = rowRec.id;\n\t\tconst sortCol = sortColumnFromConfig(table, sortName, columnConfig);\n\n\t\tif (indexRange.mode === \"cursor\") {\n\t\t\tconst ac = indexRange.afterCursor;\n\t\t\tif (ac !== null) {\n\t\t\t\tconst cmp = compareInterestValues(rowSort, ac);\n\t\t\t\tif (indexRange.sort.direction === \"asc\" && cmp <= 0) return false;\n\t\t\t\tif (indexRange.sort.direction === \"desc\" && cmp >= 0) return false;\n\t\t\t}\n\t\t}\n\n\t\tconst before = strictlyBeforeInSortOrder(\n\t\t\tsortCol,\n\t\t\tidCol,\n\t\t\trowSort,\n\t\t\trowId,\n\t\t\tindexRange.sort.direction,\n\t\t);\n\t\tlet whereRank: SQL = before;\n\t\tif (indexRange.mode === \"cursor\" && indexRange.afterCursor !== null) {\n\t\t\tconst eligible =\n\t\t\t\tindexRange.sort.direction === \"asc\"\n\t\t\t\t\t? gt(sortCol, indexRange.afterCursor as never)\n\t\t\t\t\t: lt(sortCol, indexRange.afterCursor as never);\n\t\t\twhereRank = and(eligible, before) as SQL;\n\t\t}\n\t\tconst rankRows = await db\n\t\t\t.select({ c: count() })\n\t\t\t.from(table)\n\t\t\t.where(whereRank);\n\t\tconst rank = rankRows[0]?.c ?? 0;\n\n\t\tif (indexRange.mode === \"offset\") {\n\t\t\treturn (\n\t\t\t\trank >= indexRange.offset && rank < indexRange.offset + indexRange.limit\n\t\t\t);\n\t\t}\n\t\treturn rank < indexRange.limit;\n\t}\n\n\tasync function changelogEntryMatchesRange(\n\t\top: ChangelogOperation,\n\t\tpayloadJson: unknown,\n\t\trange: SyncRange,\n\t): Promise<boolean> {\n\t\tif (range.kind === \"predicate\") {\n\t\t\tconst conds = range.conditions;\n\t\t\tswitch (op) {\n\t\t\t\tcase \"delete\": {\n\t\t\t\t\tif (payloadJson === null || typeof payloadJson !== \"string\") {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tconst prev = deserializeJson(payloadJson) as TRow;\n\t\t\t\t\treturn matchesPredicate(prev, conds, defaultPredicateColumnValue);\n\t\t\t\t}\n\t\t\t\tcase \"insert\": {\n\t\t\t\t\tif (payloadJson === null || typeof payloadJson !== \"string\") {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tconst value = deserializeJson(payloadJson) as TRow;\n\t\t\t\t\treturn matchesPredicate(value, conds, defaultPredicateColumnValue);\n\t\t\t\t}\n\t\t\t\tcase \"update\": {\n\t\t\t\t\tif (payloadJson === null || typeof payloadJson !== \"string\") {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tconst parsed = deserializeJson(payloadJson) as {\n\t\t\t\t\t\tvalue: TRow;\n\t\t\t\t\t\tpreviousValue: TRow;\n\t\t\t\t\t};\n\t\t\t\t\treturn (\n\t\t\t\t\t\tmatchesPredicate(\n\t\t\t\t\t\t\tparsed.value,\n\t\t\t\t\t\t\tconds,\n\t\t\t\t\t\t\tdefaultPredicateColumnValue,\n\t\t\t\t\t\t) ||\n\t\t\t\t\t\tmatchesPredicate(\n\t\t\t\t\t\t\tparsed.previousValue,\n\t\t\t\t\t\t\tconds,\n\t\t\t\t\t\t\tdefaultPredicateColumnValue,\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\texhaustiveGuard(op);\n\t\t\t}\n\t\t}\n\t\tif (range.kind === \"index\") {\n\t\t\tswitch (op) {\n\t\t\t\tcase \"delete\": {\n\t\t\t\t\tif (payloadJson === null || typeof payloadJson !== \"string\") {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tconst prev = deserializeJson(payloadJson) as TRow;\n\t\t\t\t\treturn rowInIndexRange(prev, range);\n\t\t\t\t}\n\t\t\t\tcase \"insert\": {\n\t\t\t\t\tif (payloadJson === null || typeof payloadJson !== \"string\") {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tconst value = deserializeJson(payloadJson) as TRow;\n\t\t\t\t\treturn rowInIndexRange(value, range);\n\t\t\t\t}\n\t\t\t\tcase \"update\": {\n\t\t\t\t\tif (payloadJson === null || typeof payloadJson !== \"string\") {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tconst parsed = deserializeJson(payloadJson) as {\n\t\t\t\t\t\tvalue: TRow;\n\t\t\t\t\t\tpreviousValue: TRow;\n\t\t\t\t\t};\n\t\t\t\t\treturn (\n\t\t\t\t\t\t(await rowInIndexRange(parsed.value, range)) ||\n\t\t\t\t\t\t(await rowInIndexRange(parsed.previousValue, range))\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\texhaustiveGuard(op);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tasync function changesSince(opts: {\n\t\trange: SyncRange;\n\t\tsinceVersion: number;\n\t\tchunkSize: number;\n\t}): Promise<{ changes: SyncMessage<TRow>[]; totalCount: number } | null> {\n\t\tconst totalCount = await getTotalCount();\n\t\tconst maxRow = await db.select({ m: max(updatedAtCol) }).from(table);\n\t\tconst m = maxRow[0]?.m;\n\t\tconst maxMs = m instanceof Date ? m.getTime() : Number(m ?? 0);\n\t\tif (opts.sinceVersion >= maxMs) {\n\t\t\treturn { changes: [], totalCount };\n\t\t}\n\t\tconst logRows = await changelogHelper.selectAfterVersion(opts.sinceVersion);\n\t\tif (logRows.length === 0) {\n\t\t\treturn null;\n\t\t}\n\t\tconst changes: SyncMessage<TRow>[] = [];\n\t\tfor (const entry of logRows) {\n\t\t\tconst row = entry as Record<string, unknown>;\n\t\t\tconst op = row.operation;\n\t\t\tconst rowId = row.rowId;\n\t\t\tconst payloadJson = row.payloadJson;\n\t\t\tif (typeof op !== \"string\" || typeof rowId !== \"string\") {\n\t\t\t\tthrow new Error(\"Invalid changelog row shape\");\n\t\t\t}\n\t\t\tif (op !== \"insert\" && op !== \"update\" && op !== \"delete\") {\n\t\t\t\tthrow new Error(`Unknown changelog operation: ${op}`);\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!(await changelogEntryMatchesRange(\n\t\t\t\t\top as ChangelogOperation,\n\t\t\t\t\tpayloadJson,\n\t\t\t\t\topts.range,\n\t\t\t\t))\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tswitch (op) {\n\t\t\t\tcase \"delete\":\n\t\t\t\t\tchanges.push({ type: \"delete\", key: rowId });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"insert\": {\n\t\t\t\t\tif (payloadJson === null || typeof payloadJson !== \"string\") break;\n\t\t\t\t\tconst value = deserializeJson(payloadJson) as TRow;\n\t\t\t\t\tchanges.push({ type: \"insert\", value });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"update\": {\n\t\t\t\t\tif (payloadJson === null || typeof payloadJson !== \"string\") break;\n\t\t\t\t\tconst parsed = deserializeJson(payloadJson) as {\n\t\t\t\t\t\tvalue: TRow;\n\t\t\t\t\t\tpreviousValue: TRow;\n\t\t\t\t\t};\n\t\t\t\t\tchanges.push({\n\t\t\t\t\t\ttype: \"update\",\n\t\t\t\t\t\tvalue: parsed.value,\n\t\t\t\t\t\tpreviousValue: parsed.previousValue,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\texhaustiveGuard(op);\n\t\t\t}\n\t\t}\n\t\treturn { changes, totalCount };\n\t}\n\n\tasync function getRow(key: string | number): Promise<TRow | undefined> {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(table)\n\t\t\t.where(eq(idCol, key as never))\n\t\t\t.limit(1);\n\t\tconst r = rows[0];\n\t\treturn r !== undefined ? (r as TRow) : undefined;\n\t}\n\n\treturn {\n\t\tqueryRange,\n\t\tqueryByOffset,\n\t\tgetTotalCount,\n\t\tgetSortValue,\n\t\tqueryByPredicate,\n\t\tgetPredicateCount,\n\t\tchangesSince,\n\t\tgetRow,\n\t};\n}\n\n/** Infer row type from a Drizzle SQLite table. */\nexport type InferPartialSyncRow<TTable extends SQLiteTable> =\n\tInferSelectModel<TTable>;\n"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { exhaustiveGuard } from '@firtoz/maybe-error';
|
|
2
|
+
|
|
3
|
+
// src/durable-sqlite-sync-server.ts
|
|
4
|
+
async function applyDurableMutationIntents(collection, intents) {
|
|
5
|
+
const changes = [];
|
|
6
|
+
const acceptedMutationIds = [];
|
|
7
|
+
for (const intent of intents) {
|
|
8
|
+
switch (intent.type) {
|
|
9
|
+
case "insert": {
|
|
10
|
+
const tx = collection.insert(intent.value);
|
|
11
|
+
await tx.isPersisted.promise;
|
|
12
|
+
changes.push({ type: "insert", value: intent.value });
|
|
13
|
+
acceptedMutationIds.push(intent.clientMutationId);
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
case "update": {
|
|
17
|
+
const tx = collection.update(intent.key, (draft) => {
|
|
18
|
+
Object.assign(draft, intent.value);
|
|
19
|
+
});
|
|
20
|
+
await tx.isPersisted.promise;
|
|
21
|
+
changes.push({
|
|
22
|
+
type: "update",
|
|
23
|
+
value: intent.value,
|
|
24
|
+
previousValue: intent.previousValue
|
|
25
|
+
});
|
|
26
|
+
acceptedMutationIds.push(intent.clientMutationId);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
case "delete": {
|
|
30
|
+
const tx = collection.delete(intent.key);
|
|
31
|
+
await tx.isPersisted.promise;
|
|
32
|
+
changes.push({ type: "delete", key: intent.key });
|
|
33
|
+
acceptedMutationIds.push(intent.clientMutationId);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
case "truncate":
|
|
37
|
+
await collection.utils.truncate();
|
|
38
|
+
changes.push({ type: "truncate" });
|
|
39
|
+
acceptedMutationIds.push(intent.clientMutationId);
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
exhaustiveGuard(intent);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { changes, acceptedMutationIds };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { applyDurableMutationIntents };
|
|
49
|
+
//# sourceMappingURL=chunk-NJ7RJSGZ.js.map
|
|
50
|
+
//# sourceMappingURL=chunk-NJ7RJSGZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/durable-sqlite-sync-server.ts"],"names":[],"mappings":";;;AAsCA,eAAsB,2BAAA,CAGrB,YACA,OAAA,EAIE;AACF,EAAA,MAAM,UAAgC,EAAC;AACvC,EAAA,MAAM,sBAAgC,EAAC;AAEvC,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC7B,IAAA,QAAQ,OAAO,IAAA;AAAM,MACpB,KAAK,QAAA,EAAU;AACd,QAAA,MAAM,EAAA,GAAK,UAAA,CAAW,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AACzC,QAAA,MAAM,GAAG,WAAA,CAAY,OAAA;AACrB,QAAA,OAAA,CAAQ,KAAK,EAAE,IAAA,EAAM,UAAU,KAAA,EAAO,MAAA,CAAO,OAAO,CAAA;AACpD,QAAA,mBAAA,CAAoB,IAAA,CAAK,OAAO,gBAAgB,CAAA;AAChD,QAAA;AAAA,MACD;AAAA,MACA,KAAK,QAAA,EAAU;AACd,QAAA,MAAM,KAAK,UAAA,CAAW,MAAA,CAAO,MAAA,CAAO,GAAA,EAAK,CAAC,KAAA,KAAU;AACnD,UAAA,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,MAAA,CAAO,KAAK,CAAA;AAAA,QAClC,CAAC,CAAA;AACD,QAAA,MAAM,GAAG,WAAA,CAAY,OAAA;AACrB,QAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,UACZ,IAAA,EAAM,QAAA;AAAA,UACN,OAAO,MAAA,CAAO,KAAA;AAAA,UACd,eAAe,MAAA,CAAO;AAAA,SACtB,CAAA;AACD,QAAA,mBAAA,CAAoB,IAAA,CAAK,OAAO,gBAAgB,CAAA;AAChD,QAAA;AAAA,MACD;AAAA,MACA,KAAK,QAAA,EAAU;AACd,QAAA,MAAM,EAAA,GAAK,UAAA,CAAW,MAAA,CAAO,MAAA,CAAO,GAAG,CAAA;AACvC,QAAA,MAAM,GAAG,WAAA,CAAY,OAAA;AACrB,QAAA,OAAA,CAAQ,KAAK,EAAE,IAAA,EAAM,UAAU,GAAA,EAAK,MAAA,CAAO,KAAK,CAAA;AAChD,QAAA,mBAAA,CAAoB,IAAA,CAAK,OAAO,gBAAgB,CAAA;AAChD,QAAA;AAAA,MACD;AAAA,MACA,KAAK,UAAA;AACJ,QAAA,MAAM,UAAA,CAAW,MAAM,QAAA,EAAS;AAChC,QAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,IAAA,EAAM,UAAA,EAAY,CAAA;AACjC,QAAA,mBAAA,CAAoB,IAAA,CAAK,OAAO,gBAAgB,CAAA;AAChD,QAAA;AAAA,MACD;AACC,QAAA,eAAA,CAAgB,MAAM,CAAA;AAAA;AACxB,EACD;AAEA,EAAA,OAAO,EAAE,SAAS,mBAAA,EAAoB;AACvC","file":"chunk-NJ7RJSGZ.js","sourcesContent":["import type { SyncMessage } from \"@firtoz/db-helpers\";\nimport { exhaustiveGuard } from \"@firtoz/maybe-error\";\n\ntype MutationIntent<TItem> =\n\t| {\n\t\t\tclientMutationId: string;\n\t\t\ttype: \"insert\";\n\t\t\tvalue: TItem;\n\t }\n\t| {\n\t\t\tclientMutationId: string;\n\t\t\ttype: \"update\";\n\t\t\tkey: string | number;\n\t\t\tvalue: TItem;\n\t\t\tpreviousValue: TItem;\n\t }\n\t| {\n\t\t\tclientMutationId: string;\n\t\t\ttype: \"delete\";\n\t\t\tkey: string | number;\n\t }\n\t| {\n\t\t\tclientMutationId: string;\n\t\t\ttype: \"truncate\";\n\t };\n\nexport interface DurableCollectionLike<TItem> {\n\tinsert: (item: Partial<TItem>) => { isPersisted: { promise: Promise<void> } };\n\tupdate: (\n\t\tkey: string | number,\n\t\tupdater: (draft: TItem) => void,\n\t) => { isPersisted: { promise: Promise<void> } };\n\tdelete: (key: string | number) => { isPersisted: { promise: Promise<void> } };\n\tutils: {\n\t\ttruncate: () => Promise<void>;\n\t};\n}\n\nexport async function applyDurableMutationIntents<\n\tTItem extends { id: string | number },\n>(\n\tcollection: DurableCollectionLike<TItem>,\n\tintents: MutationIntent<TItem>[],\n): Promise<{\n\tchanges: SyncMessage<TItem>[];\n\tacceptedMutationIds: string[];\n}> {\n\tconst changes: SyncMessage<TItem>[] = [];\n\tconst acceptedMutationIds: string[] = [];\n\n\tfor (const intent of intents) {\n\t\tswitch (intent.type) {\n\t\t\tcase \"insert\": {\n\t\t\t\tconst tx = collection.insert(intent.value);\n\t\t\t\tawait tx.isPersisted.promise;\n\t\t\t\tchanges.push({ type: \"insert\", value: intent.value });\n\t\t\t\tacceptedMutationIds.push(intent.clientMutationId);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"update\": {\n\t\t\t\tconst tx = collection.update(intent.key, (draft) => {\n\t\t\t\t\tObject.assign(draft, intent.value);\n\t\t\t\t});\n\t\t\t\tawait tx.isPersisted.promise;\n\t\t\t\tchanges.push({\n\t\t\t\t\ttype: \"update\",\n\t\t\t\t\tvalue: intent.value,\n\t\t\t\t\tpreviousValue: intent.previousValue,\n\t\t\t\t});\n\t\t\t\tacceptedMutationIds.push(intent.clientMutationId);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"delete\": {\n\t\t\t\tconst tx = collection.delete(intent.key);\n\t\t\t\tawait tx.isPersisted.promise;\n\t\t\t\tchanges.push({ type: \"delete\", key: intent.key });\n\t\t\t\tacceptedMutationIds.push(intent.clientMutationId);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"truncate\":\n\t\t\t\tawait collection.utils.truncate();\n\t\t\t\tchanges.push({ type: \"truncate\" });\n\t\t\t\tacceptedMutationIds.push(intent.clientMutationId);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\texhaustiveGuard(intent);\n\t\t}\n\t}\n\n\treturn { changes, acceptedMutationIds };\n}\n"]}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { durableSqliteCollectionOptions } from './chunk-YA4MAETI.js';
|
|
2
|
+
import { SyncServerBridge, createClientMessageSchema, createServerMessageSchema } from '@firtoz/collection-sync';
|
|
3
|
+
import { createCollection } from '@tanstack/db';
|
|
4
|
+
import { drizzle } from 'drizzle-orm/durable-sqlite';
|
|
5
|
+
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
|
|
6
|
+
import { StandardSchemaWebSocketDO, StandardSchemaSession } from '@firtoz/websocket-do';
|
|
7
|
+
|
|
8
|
+
function createSessionCodecOptions(enableBufferMessages, serializeJson, deserializeJson) {
|
|
9
|
+
const clientSchema = createClientMessageSchema();
|
|
10
|
+
const serverSchema = createServerMessageSchema();
|
|
11
|
+
if (!enableBufferMessages) {
|
|
12
|
+
return {
|
|
13
|
+
clientSchema,
|
|
14
|
+
serverSchema,
|
|
15
|
+
enableBufferMessages: false,
|
|
16
|
+
...serializeJson && deserializeJson ? { serializeJson, deserializeJson } : {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
clientSchema,
|
|
21
|
+
serverSchema,
|
|
22
|
+
enableBufferMessages: true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
var SyncTableSession = class extends StandardSchemaSession {
|
|
26
|
+
constructor(websocket, sessions, options, bridge) {
|
|
27
|
+
const generatedClientId = crypto.randomUUID();
|
|
28
|
+
super(websocket, sessions, options, {
|
|
29
|
+
createData: () => ({ clientId: generatedClientId }),
|
|
30
|
+
handleValidatedMessage: async (message) => {
|
|
31
|
+
this.clientId = message.clientId;
|
|
32
|
+
await bridge.handleClientMessage(message);
|
|
33
|
+
},
|
|
34
|
+
handleClose: async () => {
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
this.clientId = generatedClientId;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var SyncableDurableObject = class extends StandardSchemaWebSocketDO {
|
|
41
|
+
constructor(ctx, env, config) {
|
|
42
|
+
let bridgeRef;
|
|
43
|
+
super(ctx, env, {
|
|
44
|
+
standardSchemaSessionOptions: (sessionCtx) => {
|
|
45
|
+
const useMsgpack = sessionCtx !== void 0 && new URL(sessionCtx.req.url).searchParams.get("transport") === "msgpack";
|
|
46
|
+
return createSessionCodecOptions(
|
|
47
|
+
useMsgpack,
|
|
48
|
+
config.serializeJson,
|
|
49
|
+
config.deserializeJson
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
createStandardSchemaSession: (_sessionCtx, websocket, options) => {
|
|
53
|
+
return new SyncTableSession(
|
|
54
|
+
websocket,
|
|
55
|
+
this.sessions,
|
|
56
|
+
options,
|
|
57
|
+
bridgeRef
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
this.app = this.getBaseApp().get("/health", (c) => c.text("ok"));
|
|
62
|
+
this.ctx.blockConcurrencyWhile(async () => {
|
|
63
|
+
const db = drizzle(ctx.storage, { schema: config.schema });
|
|
64
|
+
migrate(db, config.migrations);
|
|
65
|
+
const tableName = config.tableName;
|
|
66
|
+
const collection = createCollection(
|
|
67
|
+
durableSqliteCollectionOptions({
|
|
68
|
+
drizzle: db,
|
|
69
|
+
tableName,
|
|
70
|
+
syncMode: config.syncMode ?? "eager"
|
|
71
|
+
// biome-ignore lint/suspicious/noExplicitAny: TanStack collection + Drizzle generic row inference is too heavy for TS here.
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
const col = collection;
|
|
75
|
+
col.preload();
|
|
76
|
+
await new Promise((resolve) => {
|
|
77
|
+
col.onFirstReady(() => resolve());
|
|
78
|
+
});
|
|
79
|
+
bridgeRef = new SyncServerBridge({
|
|
80
|
+
store: {
|
|
81
|
+
applySyncMessages: async (messages) => {
|
|
82
|
+
for (const message of messages) {
|
|
83
|
+
if (message.type === "insert") {
|
|
84
|
+
const tx = col.insert(message.value);
|
|
85
|
+
await tx.isPersisted.promise;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (message.type === "update") {
|
|
89
|
+
const value = message.value;
|
|
90
|
+
const tx = col.update(value.id, (draft) => {
|
|
91
|
+
Object.assign(draft, message.value);
|
|
92
|
+
});
|
|
93
|
+
await tx.isPersisted.promise;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (message.type === "delete") {
|
|
97
|
+
const tx = col.delete(message.key);
|
|
98
|
+
await tx.isPersisted.promise;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
await col.utils.truncate();
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
getSnapshotMessages: async () => {
|
|
105
|
+
return col.toArray.map((row) => ({
|
|
106
|
+
type: "insert",
|
|
107
|
+
value: row
|
|
108
|
+
}));
|
|
109
|
+
},
|
|
110
|
+
getRow: async (key) => {
|
|
111
|
+
return col.state.get(key);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
sendToClient: (clientId, message) => {
|
|
115
|
+
for (const session of this.sessions.values()) {
|
|
116
|
+
const s = session;
|
|
117
|
+
if (s.clientId === clientId) {
|
|
118
|
+
s.send(message);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
broadcastExcept: (excludeClientId, message) => {
|
|
124
|
+
for (const session of this.sessions.values()) {
|
|
125
|
+
const s = session;
|
|
126
|
+
if (s.clientId === excludeClientId) continue;
|
|
127
|
+
s.send(message);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
broadcastAll: (message) => {
|
|
131
|
+
for (const session of this.sessions.values()) {
|
|
132
|
+
session.send(message);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
this.bridge = bridgeRef;
|
|
137
|
+
this.collection = collection;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export { SyncableDurableObject };
|
|
143
|
+
//# sourceMappingURL=chunk-ONDKTPGP.js.map
|
|
144
|
+
//# sourceMappingURL=chunk-ONDKTPGP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/syncable-durable-object.ts"],"names":[],"mappings":"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","file":"chunk-ONDKTPGP.js","sourcesContent":["import {\n\tSyncServerBridge,\n\tcreateClientMessageSchema,\n\tcreateServerMessageSchema,\n\ttype PartialSyncRowShape,\n\ttype SyncClientMessage,\n\ttype SyncServerMessage,\n} from \"@firtoz/collection-sync\";\nimport type { SyncMessage } from \"@firtoz/db-helpers\";\nimport type { InferSchemaOutput, SyncMode } from \"@tanstack/db\";\nimport { createCollection } from \"@tanstack/db\";\nimport { drizzle } from \"drizzle-orm/durable-sqlite\";\nimport { migrate } from \"drizzle-orm/durable-sqlite/migrator\";\nimport type { Context } from \"hono\";\nimport type {\n\tDrizzleSqliteTableCollection,\n\tSelectSchema,\n\tTableWithRequiredFields,\n} from \"@firtoz/drizzle-utils\";\nimport {\n\tStandardSchemaSession,\n\tStandardSchemaWebSocketDO,\n\ttype StandardSchemaSessionOptions,\n} from \"@firtoz/websocket-do\";\nimport {\n\tdurableSqliteCollectionOptions,\n\ttype ValidTableNames,\n} from \"./durable-sqlite-collection\";\n\n/**\n * Drizzle/Valibot `InferSchemaOutput` is not always structurally assignable to\n * {@link PartialSyncRowShape}. Intersecting keeps inferred columns while requiring sync row keys for\n * {@link SyncServerBridge}.\n */\ntype SyncBridgeRowFromTable<TTable extends TableWithRequiredFields> =\n\tInferSchemaOutput<SelectSchema<TTable>> & PartialSyncRowShape;\n\nexport type SyncableDurableObjectSyncRow<\n\tTSchema extends Record<string, unknown>,\n\tTTableName extends ValidTableNames<TSchema>,\n> = SyncBridgeRowFromTable<TSchema[TTableName] & TableWithRequiredFields>;\n\ntype SessionData = { clientId: string };\n\nfunction createSessionCodecOptions<TItem extends PartialSyncRowShape>(\n\tenableBufferMessages: boolean,\n\tserializeJson?: (value: unknown) => string,\n\tdeserializeJson?: (raw: string) => unknown,\n): StandardSchemaSessionOptions<SyncClientMessage, SyncServerMessage<TItem>> {\n\tconst clientSchema = createClientMessageSchema();\n\tconst serverSchema = createServerMessageSchema<TItem>();\n\tif (!enableBufferMessages) {\n\t\treturn {\n\t\t\tclientSchema,\n\t\t\tserverSchema,\n\t\t\tenableBufferMessages: false,\n\t\t\t...(serializeJson && deserializeJson\n\t\t\t\t? { serializeJson, deserializeJson }\n\t\t\t\t: {}),\n\t\t};\n\t}\n\treturn {\n\t\tclientSchema,\n\t\tserverSchema,\n\t\tenableBufferMessages: true,\n\t};\n}\n\nclass SyncTableSession<\n\tTItem extends PartialSyncRowShape,\n\tTEnv extends Cloudflare.Env,\n> extends StandardSchemaSession<\n\tSessionData,\n\tSyncServerMessage<TItem>,\n\tSyncClientMessage,\n\tTEnv\n> {\n\tpublic clientId: string;\n\n\tconstructor(\n\t\twebsocket: WebSocket,\n\t\tsessions: Map<WebSocket, SyncTableSession<TItem, TEnv>>,\n\t\toptions: StandardSchemaSessionOptions<\n\t\t\tSyncClientMessage,\n\t\t\tSyncServerMessage<TItem>\n\t\t>,\n\t\tbridge: SyncServerBridge<TItem>,\n\t) {\n\t\tconst generatedClientId = crypto.randomUUID();\n\t\tsuper(websocket, sessions, options, {\n\t\t\tcreateData: () => ({ clientId: generatedClientId }),\n\t\t\thandleValidatedMessage: async (message: SyncClientMessage) => {\n\t\t\t\tthis.clientId = message.clientId;\n\t\t\t\tawait bridge.handleClientMessage(message);\n\t\t\t},\n\t\t\thandleClose: async () => {},\n\t\t});\n\t\tthis.clientId = generatedClientId;\n\t}\n}\n\nexport type SyncableDurableObjectConfig<\n\tTSchema extends Record<string, unknown>,\n\tTTableName extends ValidTableNames<TSchema>,\n> = {\n\tschema: TSchema;\n\ttableName: TTableName;\n\tmigrations: Parameters<typeof migrate>[1];\n\tsyncMode?: SyncMode;\n\tserializeJson?: (value: unknown) => string;\n\tdeserializeJson?: (raw: string) => unknown;\n};\n\n/**\n * Durable Object base class: Drizzle SQLite + {@link SyncServerBridge} + WebSocket sessions.\n */\nexport abstract class SyncableDurableObject<\n\tTSchema extends Record<string, unknown>,\n\tTTableName extends ValidTableNames<TSchema>,\n\tTEnv extends Cloudflare.Env = Cloudflare.Env,\n> extends StandardSchemaWebSocketDO<\n\t// biome-ignore lint/suspicious/noExplicitAny: StandardSchemaWebSocketDO session generic is internal; row type is fixed in constructor.\n\tany,\n\tSyncClientMessage,\n\tSyncServerMessage<unknown>,\n\tTEnv\n> {\n\tprotected bridge!: SyncServerBridge<\n\t\tSyncableDurableObjectSyncRow<TSchema, TTableName>\n\t>;\n\n\tprotected collection!: DrizzleSqliteTableCollection<\n\t\tTSchema[TTableName] & TableWithRequiredFields\n\t>;\n\n\treadonly app = this.getBaseApp().get(\"/health\", (c: Context) => c.text(\"ok\"));\n\n\tconstructor(\n\t\tctx: DurableObjectState,\n\t\tenv: TEnv,\n\t\tconfig: SyncableDurableObjectConfig<TSchema, TTableName>,\n\t) {\n\t\ttype TTable = TSchema[TTableName] & TableWithRequiredFields;\n\t\ttype TRow = SyncBridgeRowFromTable<TTable>;\n\n\t\tlet bridgeRef!: SyncServerBridge<TRow>;\n\n\t\tsuper(ctx, env, {\n\t\t\tstandardSchemaSessionOptions: (\n\t\t\t\tsessionCtx: Context<{ Bindings: TEnv }> | undefined,\n\t\t\t) => {\n\t\t\t\tconst useMsgpack =\n\t\t\t\t\tsessionCtx !== undefined &&\n\t\t\t\t\tnew URL(sessionCtx.req.url).searchParams.get(\"transport\") ===\n\t\t\t\t\t\t\"msgpack\";\n\t\t\t\treturn createSessionCodecOptions<TRow>(\n\t\t\t\t\tuseMsgpack,\n\t\t\t\t\tconfig.serializeJson,\n\t\t\t\t\tconfig.deserializeJson,\n\t\t\t\t);\n\t\t\t},\n\t\t\tcreateStandardSchemaSession: (\n\t\t\t\t_sessionCtx: Context<{ Bindings: TEnv }> | undefined,\n\t\t\t\twebsocket: WebSocket,\n\t\t\t\toptions: StandardSchemaSessionOptions<\n\t\t\t\t\tSyncClientMessage,\n\t\t\t\t\tSyncServerMessage<unknown>\n\t\t\t\t>,\n\t\t\t) => {\n\t\t\t\treturn new SyncTableSession<TRow, TEnv>(\n\t\t\t\t\twebsocket,\n\t\t\t\t\tthis.sessions,\n\t\t\t\t\toptions as StandardSchemaSessionOptions<\n\t\t\t\t\t\tSyncClientMessage,\n\t\t\t\t\t\tSyncServerMessage<TRow>\n\t\t\t\t\t>,\n\t\t\t\t\tbridgeRef,\n\t\t\t\t);\n\t\t\t},\n\t\t});\n\n\t\tthis.ctx.blockConcurrencyWhile(async () => {\n\t\t\tconst db = drizzle(ctx.storage, { schema: config.schema });\n\t\t\tmigrate(db, config.migrations);\n\n\t\t\tconst tableName = config.tableName as never;\n\n\t\t\tconst collection = createCollection(\n\t\t\t\tdurableSqliteCollectionOptions({\n\t\t\t\t\tdrizzle: db,\n\t\t\t\t\ttableName,\n\t\t\t\t\tsyncMode: config.syncMode ?? \"eager\",\n\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: TanStack collection + Drizzle generic row inference is too heavy for TS here.\n\t\t\t\t}) as any,\n\t\t\t) as DrizzleSqliteTableCollection<TTable>;\n\n\t\t\tconst col = collection as unknown as {\n\t\t\t\tinsert: (v: unknown) => { isPersisted: { promise: Promise<void> } };\n\t\t\t\tupdate: (\n\t\t\t\t\tkey: string | number,\n\t\t\t\t\tfn: (draft: unknown) => void,\n\t\t\t\t) => { isPersisted: { promise: Promise<void> } };\n\t\t\t\tdelete: (key: string | number) => {\n\t\t\t\t\tisPersisted: { promise: Promise<void> };\n\t\t\t\t};\n\t\t\t\tutils: { truncate: () => Promise<void> };\n\t\t\t\ttoArray: unknown[];\n\t\t\t\tstate: { get: (key: string | number) => unknown | undefined };\n\t\t\t\tpreload: () => void;\n\t\t\t\tonFirstReady: (cb: () => void) => void;\n\t\t\t};\n\n\t\t\tcol.preload();\n\t\t\tawait new Promise<void>((resolve) => {\n\t\t\t\tcol.onFirstReady(() => resolve());\n\t\t\t});\n\n\t\t\tbridgeRef = new SyncServerBridge<TRow>({\n\t\t\t\tstore: {\n\t\t\t\t\tapplySyncMessages: async (messages: SyncMessage<TRow>[]) => {\n\t\t\t\t\t\tfor (const message of messages) {\n\t\t\t\t\t\t\tif (message.type === \"insert\") {\n\t\t\t\t\t\t\t\tconst tx = col.insert(message.value);\n\t\t\t\t\t\t\t\tawait tx.isPersisted.promise;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (message.type === \"update\") {\n\t\t\t\t\t\t\t\tconst value = message.value as { id: string | number };\n\t\t\t\t\t\t\t\tconst tx = col.update(value.id, (draft) => {\n\t\t\t\t\t\t\t\t\tObject.assign(draft as object, message.value);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tawait tx.isPersisted.promise;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (message.type === \"delete\") {\n\t\t\t\t\t\t\t\tconst tx = col.delete(message.key);\n\t\t\t\t\t\t\t\tawait tx.isPersisted.promise;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tawait col.utils.truncate();\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tgetSnapshotMessages: async () => {\n\t\t\t\t\t\treturn (col.toArray as TRow[]).map((row) => ({\n\t\t\t\t\t\t\ttype: \"insert\" as const,\n\t\t\t\t\t\t\tvalue: row,\n\t\t\t\t\t\t}));\n\t\t\t\t\t},\n\t\t\t\t\tgetRow: async (key: string | number) => {\n\t\t\t\t\t\treturn col.state.get(key) as TRow | undefined;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tsendToClient: (clientId: string, message: SyncServerMessage<TRow>) => {\n\t\t\t\t\tfor (const session of this.sessions.values()) {\n\t\t\t\t\t\tconst s = session as SyncTableSession<TRow, TEnv>;\n\t\t\t\t\t\tif (s.clientId === clientId) {\n\t\t\t\t\t\t\ts.send(message);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tbroadcastExcept: (\n\t\t\t\t\texcludeClientId: string,\n\t\t\t\t\tmessage: SyncServerMessage<TRow>,\n\t\t\t\t) => {\n\t\t\t\t\tfor (const session of this.sessions.values()) {\n\t\t\t\t\t\tconst s = session as SyncTableSession<TRow, TEnv>;\n\t\t\t\t\t\tif (s.clientId === excludeClientId) continue;\n\t\t\t\t\t\ts.send(message);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tbroadcastAll: (message: SyncServerMessage<TRow>) => {\n\t\t\t\t\tfor (const session of this.sessions.values()) {\n\t\t\t\t\t\t(session as SyncTableSession<TRow, TEnv>).send(message);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.bridge = bridgeRef as SyncableDurableObject<\n\t\t\t\tTSchema,\n\t\t\t\tTTableName,\n\t\t\t\tTEnv\n\t\t\t>[\"bridge\"];\n\t\t\tthis.collection = collection;\n\t\t});\n\t}\n}\n"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { exhaustiveGuard } from '@firtoz/maybe-error';
|
|
2
|
+
import { getTableColumns, eq } from 'drizzle-orm';
|
|
3
|
+
|
|
4
|
+
// src/drizzle-mutation-store.ts
|
|
5
|
+
function createDrizzleMutationStore(options) {
|
|
6
|
+
const { db, table, changelogHelper, updateColumns } = options;
|
|
7
|
+
const tableColumns = getTableColumns(table);
|
|
8
|
+
const idCol = tableColumns.id;
|
|
9
|
+
if (idCol === void 0) {
|
|
10
|
+
throw new Error("Mutation table must have an id column");
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
applySyncMessages: async (messages) => {
|
|
14
|
+
for (const message of messages) {
|
|
15
|
+
switch (message.type) {
|
|
16
|
+
case "insert":
|
|
17
|
+
await db.insert(table).values(message.value);
|
|
18
|
+
await changelogHelper.append(
|
|
19
|
+
"insert",
|
|
20
|
+
String(message.value.id),
|
|
21
|
+
message.value
|
|
22
|
+
);
|
|
23
|
+
break;
|
|
24
|
+
case "update": {
|
|
25
|
+
const setPayload = {};
|
|
26
|
+
const v = message.value;
|
|
27
|
+
for (const col of updateColumns) {
|
|
28
|
+
setPayload[col] = v[col];
|
|
29
|
+
}
|
|
30
|
+
await db.update(table).set(setPayload).where(eq(idCol, message.value.id));
|
|
31
|
+
await changelogHelper.append("update", String(message.value.id), {
|
|
32
|
+
value: message.value,
|
|
33
|
+
previousValue: message.previousValue
|
|
34
|
+
});
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case "delete": {
|
|
38
|
+
const existing = await db.select().from(table).where(eq(idCol, message.key)).limit(1);
|
|
39
|
+
const prev = existing[0];
|
|
40
|
+
await db.delete(table).where(eq(idCol, message.key));
|
|
41
|
+
await changelogHelper.append(
|
|
42
|
+
"delete",
|
|
43
|
+
String(message.key),
|
|
44
|
+
prev ?? null
|
|
45
|
+
);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
case "truncate":
|
|
49
|
+
await changelogHelper.deleteAll();
|
|
50
|
+
await db.delete(table);
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
exhaustiveGuard(message);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
getSnapshotMessages: async () => {
|
|
58
|
+
const rows = await db.select().from(table);
|
|
59
|
+
return rows.map((row) => ({
|
|
60
|
+
type: "insert",
|
|
61
|
+
value: row
|
|
62
|
+
}));
|
|
63
|
+
},
|
|
64
|
+
getRow: async (key) => {
|
|
65
|
+
const rows = await db.select().from(table).where(eq(idCol, key)).limit(1);
|
|
66
|
+
return rows[0];
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { createDrizzleMutationStore };
|
|
72
|
+
//# sourceMappingURL=chunk-PA5TYHIW.js.map
|
|
73
|
+
//# sourceMappingURL=chunk-PA5TYHIW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/drizzle-mutation-store.ts"],"names":[],"mappings":";;;;AAmBO,SAAS,2BAIf,OAAA,EAC8B;AAC9B,EAAA,MAAM,EAAE,EAAA,EAAI,KAAA,EAAO,eAAA,EAAiB,eAAc,GAAI,OAAA;AACtD,EAAA,MAAM,YAAA,GAAe,gBAAgB,KAAK,CAAA;AAC1C,EAAA,MAAM,QAAQ,YAAA,CAAa,EAAA;AAC3B,EAAA,IAAI,UAAU,MAAA,EAAW;AACxB,IAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,EACxD;AAEA,EAAA,OAAO;AAAA,IACN,iBAAA,EAAmB,OAAO,QAAA,KAAkC;AAC3D,MAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC/B,QAAA,QAAQ,QAAQ,IAAA;AAAM,UACrB,KAAK,QAAA;AACJ,YAAA,MAAM,GACJ,MAAA,CAAO,KAAK,CAAA,CACZ,MAAA,CAAO,QAAQ,KAAgC,CAAA;AACjD,YAAA,MAAM,eAAA,CAAgB,MAAA;AAAA,cACrB,QAAA;AAAA,cACA,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,EAAE,CAAA;AAAA,cACvB,OAAA,CAAQ;AAAA,aACT;AACA,YAAA;AAAA,UACD,KAAK,QAAA,EAAU;AACd,YAAA,MAAM,aAAsC,EAAC;AAC7C,YAAA,MAAM,IAAI,OAAA,CAAQ,KAAA;AAClB,YAAA,KAAA,MAAW,OAAO,aAAA,EAAe;AAChC,cAAA,UAAA,CAAW,GAAG,CAAA,GAAI,CAAA,CAAE,GAAG,CAAA;AAAA,YACxB;AACA,YAAA,MAAM,EAAA,CACJ,MAAA,CAAO,KAAK,CAAA,CACZ,GAAA,CAAI,UAAmB,CAAA,CACvB,KAAA,CAAM,EAAA,CAAG,KAAA,EAAO,OAAA,CAAQ,KAAA,CAAM,EAAW,CAAC,CAAA;AAC5C,YAAA,MAAM,gBAAgB,MAAA,CAAO,QAAA,EAAU,OAAO,OAAA,CAAQ,KAAA,CAAM,EAAE,CAAA,EAAG;AAAA,cAChE,OAAO,OAAA,CAAQ,KAAA;AAAA,cACf,eAAe,OAAA,CAAQ;AAAA,aACvB,CAAA;AACD,YAAA;AAAA,UACD;AAAA,UACA,KAAK,QAAA,EAAU;AACd,YAAA,MAAM,WAAW,MAAM,EAAA,CACrB,MAAA,EAAO,CACP,KAAK,KAAK,CAAA,CACV,KAAA,CAAM,EAAA,CAAG,OAAO,OAAA,CAAQ,GAAY,CAAC,CAAA,CACrC,MAAM,CAAC,CAAA;AACT,YAAA,MAAM,IAAA,GAAO,SAAS,CAAC,CAAA;AACvB,YAAA,MAAM,EAAA,CAAG,OAAO,KAAK,CAAA,CAAE,MAAM,EAAA,CAAG,KAAA,EAAO,OAAA,CAAQ,GAAY,CAAC,CAAA;AAC5D,YAAA,MAAM,eAAA,CAAgB,MAAA;AAAA,cACrB,QAAA;AAAA,cACA,MAAA,CAAO,QAAQ,GAAG,CAAA;AAAA,cAClB,IAAA,IAAQ;AAAA,aACT;AACA,YAAA;AAAA,UACD;AAAA,UACA,KAAK,UAAA;AACJ,YAAA,MAAM,gBAAgB,SAAA,EAAU;AAChC,YAAA,MAAM,EAAA,CAAG,OAAO,KAAK,CAAA;AACrB,YAAA;AAAA,UACD;AACC,YAAA,eAAA,CAAgB,OAAO,CAAA;AAAA;AACzB,MACD;AAAA,IACD,CAAA;AAAA,IAEA,qBAAqB,YAAY;AAChC,MAAA,MAAM,OAAO,MAAM,EAAA,CAAG,MAAA,EAAO,CAAE,KAAK,KAAK,CAAA;AACzC,MAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,QACzB,IAAA,EAAM,QAAA;AAAA,QACN,KAAA,EAAO;AAAA,OACR,CAAE,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,MAAA,EAAQ,OAAO,GAAA,KAAyB;AACvC,MAAA,MAAM,IAAA,GAAO,MAAM,EAAA,CACjB,MAAA,GACA,IAAA,CAAK,KAAK,CAAA,CACV,KAAA,CAAM,GAAG,KAAA,EAAO,GAAY,CAAC,CAAA,CAC7B,MAAM,CAAC,CAAA;AACT,MAAA,OAAO,KAAK,CAAC,CAAA;AAAA,IACd;AAAA,GACD;AACD","file":"chunk-PA5TYHIW.js","sourcesContent":["import type { SyncServerBridgeStore } from \"@firtoz/collection-sync\";\nimport type { SyncMessage } from \"@firtoz/db-helpers\";\nimport { exhaustiveGuard } from \"@firtoz/maybe-error\";\nimport { eq, getTableColumns } from \"drizzle-orm\";\nimport type { SQLiteTable } from \"drizzle-orm/sqlite-core\";\nimport type { DrizzleChangelogHelper } from \"./drizzle-partial-sync-changelog\";\nimport type { PartialSyncSqliteDatabase } from \"./partial-sync-sqlite-db\";\n\nexport type CreateDrizzleMutationStoreOptions<\n\tTSchema extends Record<string, unknown>,\n\tTRow extends { id: string | number },\n> = {\n\tdb: PartialSyncSqliteDatabase<TSchema>;\n\ttable: SQLiteTable;\n\tchangelogHelper: DrizzleChangelogHelper<TSchema>;\n\t/** Columns to copy from `update` message.value into SET (excluding id). */\n\tupdateColumns: readonly (keyof TRow & string)[];\n};\n\nexport function createDrizzleMutationStore<\n\tTSchema extends Record<string, unknown>,\n\tTRow extends { id: string | number },\n>(\n\toptions: CreateDrizzleMutationStoreOptions<TSchema, TRow>,\n): SyncServerBridgeStore<TRow> {\n\tconst { db, table, changelogHelper, updateColumns } = options;\n\tconst tableColumns = getTableColumns(table);\n\tconst idCol = tableColumns.id;\n\tif (idCol === undefined) {\n\t\tthrow new Error(\"Mutation table must have an id column\");\n\t}\n\n\treturn {\n\t\tapplySyncMessages: async (messages: SyncMessage<TRow>[]) => {\n\t\t\tfor (const message of messages) {\n\t\t\t\tswitch (message.type) {\n\t\t\t\t\tcase \"insert\":\n\t\t\t\t\t\tawait db\n\t\t\t\t\t\t\t.insert(table)\n\t\t\t\t\t\t\t.values(message.value as Record<string, unknown>);\n\t\t\t\t\t\tawait changelogHelper.append(\n\t\t\t\t\t\t\t\"insert\",\n\t\t\t\t\t\t\tString(message.value.id),\n\t\t\t\t\t\t\tmessage.value,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"update\": {\n\t\t\t\t\t\tconst setPayload: Record<string, unknown> = {};\n\t\t\t\t\t\tconst v = message.value as Record<string, unknown>;\n\t\t\t\t\t\tfor (const col of updateColumns) {\n\t\t\t\t\t\t\tsetPayload[col] = v[col];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait db\n\t\t\t\t\t\t\t.update(table)\n\t\t\t\t\t\t\t.set(setPayload as never)\n\t\t\t\t\t\t\t.where(eq(idCol, message.value.id as never));\n\t\t\t\t\t\tawait changelogHelper.append(\"update\", String(message.value.id), {\n\t\t\t\t\t\t\tvalue: message.value,\n\t\t\t\t\t\t\tpreviousValue: message.previousValue,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"delete\": {\n\t\t\t\t\t\tconst existing = await db\n\t\t\t\t\t\t\t.select()\n\t\t\t\t\t\t\t.from(table)\n\t\t\t\t\t\t\t.where(eq(idCol, message.key as never))\n\t\t\t\t\t\t\t.limit(1);\n\t\t\t\t\t\tconst prev = existing[0];\n\t\t\t\t\t\tawait db.delete(table).where(eq(idCol, message.key as never));\n\t\t\t\t\t\tawait changelogHelper.append(\n\t\t\t\t\t\t\t\"delete\",\n\t\t\t\t\t\t\tString(message.key),\n\t\t\t\t\t\t\tprev ?? null,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"truncate\":\n\t\t\t\t\t\tawait changelogHelper.deleteAll();\n\t\t\t\t\t\tawait db.delete(table);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\texhaustiveGuard(message);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tgetSnapshotMessages: async () => {\n\t\t\tconst rows = await db.select().from(table);\n\t\t\treturn rows.map((row) => ({\n\t\t\t\ttype: \"insert\" as const,\n\t\t\t\tvalue: row as TRow,\n\t\t\t}));\n\t\t},\n\n\t\tgetRow: async (key: string | number) => {\n\t\t\tconst rows = await db\n\t\t\t\t.select()\n\t\t\t\t.from(table)\n\t\t\t\t.where(eq(idCol, key as never))\n\t\t\t\t.limit(1);\n\t\t\treturn rows[0] as TRow | undefined;\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { exhaustiveGuard } from '@firtoz/maybe-error';
|
|
2
|
+
import { getTableColumns, and, gte, lte, lt, gt, ne, eq } from 'drizzle-orm';
|
|
3
|
+
|
|
4
|
+
// src/partial-sync-predicate-sql.ts
|
|
5
|
+
function columnRefForPredicate(table, columnName, columnConfig) {
|
|
6
|
+
const meta = columnConfig.columns[columnName];
|
|
7
|
+
if (meta === void 0) {
|
|
8
|
+
throw new Error(`Unsupported predicate column: ${columnName}`);
|
|
9
|
+
}
|
|
10
|
+
const cols = getTableColumns(table);
|
|
11
|
+
const col = cols[columnName];
|
|
12
|
+
if (col === void 0) {
|
|
13
|
+
throw new Error(`Table has no column: ${columnName}`);
|
|
14
|
+
}
|
|
15
|
+
return col;
|
|
16
|
+
}
|
|
17
|
+
function coercePredicateScalar(column, value, columnConfig) {
|
|
18
|
+
const meta = columnConfig.columns[column];
|
|
19
|
+
if (meta === void 0) {
|
|
20
|
+
throw new Error(`Unsupported predicate column: ${column}`);
|
|
21
|
+
}
|
|
22
|
+
if (meta.kind === "integer") {
|
|
23
|
+
const n = Number(value);
|
|
24
|
+
if (!Number.isFinite(n)) {
|
|
25
|
+
throw new Error(`Predicate ${column} value must be a finite number`);
|
|
26
|
+
}
|
|
27
|
+
return meta.truncateInteger === true ? Math.trunc(n) : n;
|
|
28
|
+
}
|
|
29
|
+
return String(value);
|
|
30
|
+
}
|
|
31
|
+
function rangeConditionToSQL(table, condition, columnConfig) {
|
|
32
|
+
const col = columnRefForPredicate(table, condition.column, columnConfig);
|
|
33
|
+
switch (condition.op) {
|
|
34
|
+
case "eq":
|
|
35
|
+
return eq(
|
|
36
|
+
col,
|
|
37
|
+
coercePredicateScalar(condition.column, condition.value, columnConfig)
|
|
38
|
+
);
|
|
39
|
+
case "neq":
|
|
40
|
+
return ne(
|
|
41
|
+
col,
|
|
42
|
+
coercePredicateScalar(condition.column, condition.value, columnConfig)
|
|
43
|
+
);
|
|
44
|
+
case "gt":
|
|
45
|
+
return gt(
|
|
46
|
+
col,
|
|
47
|
+
coercePredicateScalar(condition.column, condition.value, columnConfig)
|
|
48
|
+
);
|
|
49
|
+
case "gte":
|
|
50
|
+
return gte(
|
|
51
|
+
col,
|
|
52
|
+
coercePredicateScalar(condition.column, condition.value, columnConfig)
|
|
53
|
+
);
|
|
54
|
+
case "lt":
|
|
55
|
+
return lt(
|
|
56
|
+
col,
|
|
57
|
+
coercePredicateScalar(condition.column, condition.value, columnConfig)
|
|
58
|
+
);
|
|
59
|
+
case "lte":
|
|
60
|
+
return lte(
|
|
61
|
+
col,
|
|
62
|
+
coercePredicateScalar(condition.column, condition.value, columnConfig)
|
|
63
|
+
);
|
|
64
|
+
case "between": {
|
|
65
|
+
const from = coercePredicateScalar(
|
|
66
|
+
condition.column,
|
|
67
|
+
condition.value,
|
|
68
|
+
columnConfig
|
|
69
|
+
);
|
|
70
|
+
const to = coercePredicateScalar(
|
|
71
|
+
condition.column,
|
|
72
|
+
condition.valueTo,
|
|
73
|
+
columnConfig
|
|
74
|
+
);
|
|
75
|
+
return and(gte(col, from), lte(col, to));
|
|
76
|
+
}
|
|
77
|
+
default:
|
|
78
|
+
exhaustiveGuard(condition.op);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function predicateWhereFromConditions(table, conditions, columnConfig) {
|
|
82
|
+
if (conditions.length === 0) return void 0;
|
|
83
|
+
const parts = conditions.map(
|
|
84
|
+
(c) => rangeConditionToSQL(table, c, columnConfig)
|
|
85
|
+
);
|
|
86
|
+
return parts.length === 1 ? parts[0] : and(...parts);
|
|
87
|
+
}
|
|
88
|
+
function sortColumnFromConfig(table, columnName, columnConfig) {
|
|
89
|
+
if (!columnConfig.sortableColumns.includes(columnName)) {
|
|
90
|
+
throw new Error(`Unsupported sort column: ${columnName}`);
|
|
91
|
+
}
|
|
92
|
+
if (columnConfig.columns[columnName] === void 0) {
|
|
93
|
+
throw new Error(`Unknown column in sort: ${columnName}`);
|
|
94
|
+
}
|
|
95
|
+
const cols = getTableColumns(table);
|
|
96
|
+
const col = cols[columnName];
|
|
97
|
+
if (col === void 0) {
|
|
98
|
+
throw new Error(`Table has no column: ${columnName}`);
|
|
99
|
+
}
|
|
100
|
+
return col;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { coercePredicateScalar, columnRefForPredicate, predicateWhereFromConditions, rangeConditionToSQL, sortColumnFromConfig };
|
|
104
|
+
//# sourceMappingURL=chunk-QHM6T5OI.js.map
|
|
105
|
+
//# sourceMappingURL=chunk-QHM6T5OI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/partial-sync-predicate-sql.ts"],"names":[],"mappings":"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","file":"chunk-QHM6T5OI.js","sourcesContent":["import type { RangeCondition } from \"@firtoz/collection-sync\";\nimport { exhaustiveGuard } from \"@firtoz/maybe-error\";\nimport {\n\tand,\n\teq,\n\tgetTableColumns,\n\tgt,\n\tgte,\n\tlt,\n\tlte,\n\tne,\n\ttype SQL,\n} from \"drizzle-orm\";\nimport type { SQLiteColumn, SQLiteTable } from \"drizzle-orm/sqlite-core\";\n\n/** Column kind for predicate coercion and sort. */\nexport type PartialSyncColumnKind = \"text\" | \"integer\";\n\nexport type PartialSyncTableColumnConfig = {\n\tkind: PartialSyncColumnKind;\n\t/**\n\t * When kind is `integer`, use `Math.trunc` after `Number()` (grid coordinates).\n\t * Default false: finite number only.\n\t */\n\ttruncateInteger?: boolean;\n};\n\n/**\n * Declares which table columns exist for predicates/sorts and how to coerce literals.\n * `sortableColumns` must be a subset of keys in `columns`.\n */\nexport type PartialSyncTableConfig<TSortable extends string = string> = {\n\tcolumns: Record<string, PartialSyncTableColumnConfig>;\n\tsortableColumns: readonly TSortable[];\n};\n\nexport function columnRefForPredicate(\n\ttable: SQLiteTable,\n\tcolumnName: string,\n\tcolumnConfig: PartialSyncTableConfig,\n): SQLiteColumn {\n\tconst meta = columnConfig.columns[columnName];\n\tif (meta === undefined) {\n\t\tthrow new Error(`Unsupported predicate column: ${columnName}`);\n\t}\n\tconst cols = getTableColumns(table);\n\tconst col = cols[columnName];\n\tif (col === undefined) {\n\t\tthrow new Error(`Table has no column: ${columnName}`);\n\t}\n\treturn col as SQLiteColumn;\n}\n\nexport function coercePredicateScalar(\n\tcolumn: string,\n\tvalue: unknown,\n\tcolumnConfig: PartialSyncTableConfig,\n): string | number {\n\tconst meta = columnConfig.columns[column];\n\tif (meta === undefined) {\n\t\tthrow new Error(`Unsupported predicate column: ${column}`);\n\t}\n\tif (meta.kind === \"integer\") {\n\t\tconst n = Number(value);\n\t\tif (!Number.isFinite(n)) {\n\t\t\tthrow new Error(`Predicate ${column} value must be a finite number`);\n\t\t}\n\t\treturn meta.truncateInteger === true ? Math.trunc(n) : n;\n\t}\n\treturn String(value);\n}\n\nexport function rangeConditionToSQL(\n\ttable: SQLiteTable,\n\tcondition: RangeCondition,\n\tcolumnConfig: PartialSyncTableConfig,\n): SQL {\n\tconst col = columnRefForPredicate(table, condition.column, columnConfig);\n\tswitch (condition.op) {\n\t\tcase \"eq\":\n\t\t\treturn eq(\n\t\t\t\tcol,\n\t\t\t\tcoercePredicateScalar(condition.column, condition.value, columnConfig),\n\t\t\t);\n\t\tcase \"neq\":\n\t\t\treturn ne(\n\t\t\t\tcol,\n\t\t\t\tcoercePredicateScalar(condition.column, condition.value, columnConfig),\n\t\t\t);\n\t\tcase \"gt\":\n\t\t\treturn gt(\n\t\t\t\tcol,\n\t\t\t\tcoercePredicateScalar(condition.column, condition.value, columnConfig),\n\t\t\t);\n\t\tcase \"gte\":\n\t\t\treturn gte(\n\t\t\t\tcol,\n\t\t\t\tcoercePredicateScalar(condition.column, condition.value, columnConfig),\n\t\t\t);\n\t\tcase \"lt\":\n\t\t\treturn lt(\n\t\t\t\tcol,\n\t\t\t\tcoercePredicateScalar(condition.column, condition.value, columnConfig),\n\t\t\t);\n\t\tcase \"lte\":\n\t\t\treturn lte(\n\t\t\t\tcol,\n\t\t\t\tcoercePredicateScalar(condition.column, condition.value, columnConfig),\n\t\t\t);\n\t\tcase \"between\": {\n\t\t\tconst from = coercePredicateScalar(\n\t\t\t\tcondition.column,\n\t\t\t\tcondition.value,\n\t\t\t\tcolumnConfig,\n\t\t\t);\n\t\t\tconst to = coercePredicateScalar(\n\t\t\t\tcondition.column,\n\t\t\t\tcondition.valueTo,\n\t\t\t\tcolumnConfig,\n\t\t\t);\n\t\t\treturn and(gte(col, from), lte(col, to)) as SQL;\n\t\t}\n\t\tdefault:\n\t\t\texhaustiveGuard(condition.op);\n\t}\n}\n\nexport function predicateWhereFromConditions(\n\ttable: SQLiteTable,\n\tconditions: RangeCondition[],\n\tcolumnConfig: PartialSyncTableConfig,\n): SQL | undefined {\n\tif (conditions.length === 0) return undefined;\n\tconst parts = conditions.map((c) =>\n\t\trangeConditionToSQL(table, c, columnConfig),\n\t);\n\treturn parts.length === 1 ? parts[0] : (and(...parts) as SQL);\n}\n\nexport function sortColumnFromConfig(\n\ttable: SQLiteTable,\n\tcolumnName: string,\n\tcolumnConfig: PartialSyncTableConfig,\n): SQLiteColumn {\n\tif (!columnConfig.sortableColumns.includes(columnName as never)) {\n\t\tthrow new Error(`Unsupported sort column: ${columnName}`);\n\t}\n\tif (columnConfig.columns[columnName] === undefined) {\n\t\tthrow new Error(`Unknown column in sort: ${columnName}`);\n\t}\n\tconst cols = getTableColumns(table);\n\tconst col = cols[columnName];\n\tif (col === undefined) {\n\t\tthrow new Error(`Table has no column: ${columnName}`);\n\t}\n\treturn col as SQLiteColumn;\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createSqliteTableSyncBackend, createSyncFunction, createInsertSchemaWithIdDefault, createCollectionConfig } from '@firtoz/drizzle-utils';
|
|
2
|
+
|
|
3
|
+
// src/durable-sqlite-collection.ts
|
|
4
|
+
function durableSqliteCollectionOptions(config) {
|
|
5
|
+
const tableName = config.tableName;
|
|
6
|
+
const table = config.drizzle._.fullSchema[tableName];
|
|
7
|
+
const getKey = (item) => item.id;
|
|
8
|
+
const backend = createSqliteTableSyncBackend({
|
|
9
|
+
drizzle: config.drizzle,
|
|
10
|
+
table,
|
|
11
|
+
tableName: config.tableName,
|
|
12
|
+
debug: config.debug,
|
|
13
|
+
interceptor: config.interceptor,
|
|
14
|
+
driverMode: "sync"
|
|
15
|
+
});
|
|
16
|
+
const baseSyncConfig = {
|
|
17
|
+
table,
|
|
18
|
+
readyPromise: config.readyPromise ?? Promise.resolve(),
|
|
19
|
+
syncMode: config.syncMode,
|
|
20
|
+
debug: config.debug,
|
|
21
|
+
getSyncPersistKey: (item) => String(getKey(item))
|
|
22
|
+
};
|
|
23
|
+
const syncResult = createSyncFunction(baseSyncConfig, backend);
|
|
24
|
+
const schema = createInsertSchemaWithIdDefault(table);
|
|
25
|
+
return createCollectionConfig({
|
|
26
|
+
schema,
|
|
27
|
+
getKey,
|
|
28
|
+
syncResult,
|
|
29
|
+
onInsert: config.debug ? async (params) => {
|
|
30
|
+
console.log("onInsert", params);
|
|
31
|
+
await syncResult.onInsert(params);
|
|
32
|
+
} : void 0,
|
|
33
|
+
onUpdate: config.debug ? async (params) => {
|
|
34
|
+
console.log("onUpdate", params);
|
|
35
|
+
await syncResult.onUpdate(params);
|
|
36
|
+
} : void 0,
|
|
37
|
+
onDelete: config.debug ? async (params) => {
|
|
38
|
+
console.log("onDelete", params);
|
|
39
|
+
await syncResult.onDelete(params);
|
|
40
|
+
} : void 0,
|
|
41
|
+
syncMode: config.syncMode
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { durableSqliteCollectionOptions };
|
|
46
|
+
//# sourceMappingURL=chunk-YA4MAETI.js.map
|
|
47
|
+
//# sourceMappingURL=chunk-YA4MAETI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/durable-sqlite-collection.ts"],"names":[],"mappings":";;;AA6EO,SAAS,+BAOf,MAAA,EAC8C;AAC9C,EAAA,MAAM,YAAY,MAAA,CAAO,SAAA;AAGzB,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,CAAA,CAAE,WAAW,SAAS,CAAA;AAEnD,EAAA,MAAM,MAAA,GAAS,CACd,IAAA,KACmB,IAAA,CAA8B,EAAA;AAElD,EAAA,MAAM,UAAU,4BAAA,CAA6B;AAAA,IAC5C,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,KAAA;AAAA,IACA,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,OAAO,MAAA,CAAO,KAAA;AAAA,IACd,aAAa,MAAA,CAAO,WAAA;AAAA,IACpB,UAAA,EAAY;AAAA,GACZ,CAAA;AAED,EAAA,MAAM,cAAA,GAAyC;AAAA,IAC9C,KAAA;AAAA,IACA,YAAA,EAAc,MAAA,CAAO,YAAA,IAAgB,OAAA,CAAQ,OAAA,EAAQ;AAAA,IACrD,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,OAAO,MAAA,CAAO,KAAA;AAAA,IACd,mBAAmB,CAAC,IAAA,KAAS,MAAA,CAAO,MAAA,CAAO,IAAI,CAAC;AAAA,GACjD;AAEA,EAAA,MAAM,UAAA,GAAa,kBAAA,CAAmB,cAAA,EAAgB,OAAO,CAAA;AAE7D,EAAA,MAAM,MAAA,GAAS,gCAAgC,KAAK,CAAA;AAEpD,EAAA,OAAO,sBAAA,CAAuB;AAAA,IAC7B,MAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA,EAAU,MAAA,CAAO,KAAA,GACd,OAAO,MAAA,KAAW;AAClB,MAAA,OAAA,CAAQ,GAAA,CAAI,YAAY,MAAM,CAAA;AAE9B,MAAA,MAAM,UAAA,CAAW,SAAU,MAAM,CAAA;AAAA,IAClC,CAAA,GACC,MAAA;AAAA,IACH,QAAA,EAAU,MAAA,CAAO,KAAA,GACd,OAAO,MAAA,KAAW;AAClB,MAAA,OAAA,CAAQ,GAAA,CAAI,YAAY,MAAM,CAAA;AAE9B,MAAA,MAAM,UAAA,CAAW,SAAU,MAAM,CAAA;AAAA,IAClC,CAAA,GACC,MAAA;AAAA,IACH,QAAA,EAAU,MAAA,CAAO,KAAA,GACd,OAAO,MAAA,KAAW;AAClB,MAAA,OAAA,CAAQ,GAAA,CAAI,YAAY,MAAM,CAAA;AAE9B,MAAA,MAAM,UAAA,CAAW,SAAU,MAAM,CAAA;AAAA,IAClC,CAAA,GACC,MAAA;AAAA,IACH,UAAU,MAAA,CAAO;AAAA,GACjB,CAAA;AACF","file":"chunk-YA4MAETI.js","sourcesContent":["import type {\n\tInferSchemaOutput,\n\tSyncMode,\n\tCollectionConfig,\n} from \"@tanstack/db\";\nimport type { Table } from \"drizzle-orm\";\nimport type { DrizzleSqliteDODatabase } from \"drizzle-orm/durable-sqlite\";\nimport type { CollectionUtils } from \"@firtoz/db-helpers\";\nimport type {\n\tSelectSchema,\n\tInsertToSelectSchema,\n\tTableWithRequiredFields,\n\tBaseSyncConfig,\n\tIdOf,\n} from \"@firtoz/drizzle-utils\";\nimport {\n\tcreateSyncFunction,\n\tcreateInsertSchemaWithIdDefault,\n\tcreateCollectionConfig,\n\tcreateSqliteTableSyncBackend,\n\ttype SQLOperation,\n\ttype SQLInterceptor,\n} from \"@firtoz/drizzle-utils\";\nexport type { SQLOperation, SQLInterceptor };\n\n/**\n * Drizzle database type for `drizzle-orm/durable-sqlite` (Cloudflare DO SQLite).\n */\nexport type AnyDurableSqliteDatabase = DrizzleSqliteDODatabase<\n\tRecord<string, unknown>\n>;\n\nexport type DurableDrizzleSchema<TDrizzle extends AnyDurableSqliteDatabase> =\n\tTDrizzle[\"_\"][\"fullSchema\"];\n\nexport interface DurableSqliteCollectionConfig<\n\tTDrizzle extends AnyDurableSqliteDatabase,\n\tTTableName extends ValidTableNames<DurableDrizzleSchema<TDrizzle>>,\n> {\n\tdrizzle: TDrizzle;\n\ttableName: ValidTableNames<DurableDrizzleSchema<TDrizzle>> extends never\n\t\t? {\n\t\t\t\t$error: \"The schema needs to include at least one table that uses the syncableTable function.\";\n\t\t\t}\n\t\t: TTableName;\n\t/**\n\t * Await before running sync queries (e.g. migrations finishing). Omit or leave undefined to use an already-resolved promise (no extra wait).\n\t */\n\treadyPromise?: Promise<void>;\n\tsyncMode?: SyncMode;\n\tdebug?: boolean;\n\tinterceptor?: SQLInterceptor;\n}\n\nexport type ValidTableNames<TSchema extends Record<string, unknown>> = {\n\t[K in keyof TSchema]: TSchema[K] extends TableWithRequiredFields ? K : never;\n}[keyof TSchema];\n\nexport type DurableSqliteCollectionConfigResult<TTable extends Table> = Omit<\n\tCollectionConfig<\n\t\tInferSchemaOutput<SelectSchema<TTable>>,\n\t\tIdOf<TTable>,\n\t\tInsertToSelectSchema<TTable>,\n\t\tCollectionUtils<InferSchemaOutput<SelectSchema<TTable>>>\n\t>,\n\t\"utils\"\n> & {\n\tschema: InsertToSelectSchema<TTable>;\n\tutils: CollectionUtils<InferSchemaOutput<SelectSchema<TTable>>>;\n};\n\n/**\n * TanStack DB collection configuration for a table stored in Durable Object SQLite via Drizzle.\n *\n * Uses `driverMode: \"sync\"` internally: DO SQLite runs `transactionSync`, so mutations use\n * `.all()` / `.run()` inside a synchronous transaction callback (see `createSqliteTableSyncBackend` in `@firtoz/drizzle-utils`).\n */\nexport function durableSqliteCollectionOptions<\n\tconst TDrizzle extends AnyDurableSqliteDatabase,\n\tconst TTableName extends string &\n\t\tValidTableNames<DurableDrizzleSchema<TDrizzle>>,\n\tTTable extends DurableDrizzleSchema<TDrizzle>[TTableName] &\n\t\tTableWithRequiredFields,\n>(\n\tconfig: DurableSqliteCollectionConfig<TDrizzle, TTableName>,\n): DurableSqliteCollectionConfigResult<TTable> {\n\tconst tableName = config.tableName as string &\n\t\tValidTableNames<DurableDrizzleSchema<TDrizzle>>;\n\n\tconst table = config.drizzle._.fullSchema[tableName] as TTable;\n\n\tconst getKey = (\n\t\titem: InferSchemaOutput<SelectSchema<TTable>>,\n\t): IdOf<TTable> => (item as { id: IdOf<TTable> }).id;\n\n\tconst backend = createSqliteTableSyncBackend({\n\t\tdrizzle: config.drizzle,\n\t\ttable,\n\t\ttableName: config.tableName as string,\n\t\tdebug: config.debug,\n\t\tinterceptor: config.interceptor,\n\t\tdriverMode: \"sync\",\n\t});\n\n\tconst baseSyncConfig: BaseSyncConfig<TTable> = {\n\t\ttable,\n\t\treadyPromise: config.readyPromise ?? Promise.resolve(),\n\t\tsyncMode: config.syncMode,\n\t\tdebug: config.debug,\n\t\tgetSyncPersistKey: (item) => String(getKey(item)),\n\t};\n\n\tconst syncResult = createSyncFunction(baseSyncConfig, backend);\n\n\tconst schema = createInsertSchemaWithIdDefault(table);\n\n\treturn createCollectionConfig({\n\t\tschema,\n\t\tgetKey,\n\t\tsyncResult,\n\t\tonInsert: config.debug\n\t\t\t? async (params) => {\n\t\t\t\t\tconsole.log(\"onInsert\", params);\n\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: defined when sync runs\n\t\t\t\t\tawait syncResult.onInsert!(params);\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tonUpdate: config.debug\n\t\t\t? async (params) => {\n\t\t\t\t\tconsole.log(\"onUpdate\", params);\n\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: defined when sync runs\n\t\t\t\t\tawait syncResult.onUpdate!(params);\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tonDelete: config.debug\n\t\t\t? async (params) => {\n\t\t\t\t\tconsole.log(\"onDelete\", params);\n\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: defined when sync runs\n\t\t\t\t\tawait syncResult.onDelete!(params);\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tsyncMode: config.syncMode,\n\t});\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SyncServerBridgeStore } from '@firtoz/collection-sync';
|
|
2
|
+
import { SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
3
|
+
import { DrizzleChangelogHelper } from './drizzle-partial-sync-changelog.js';
|
|
4
|
+
import { PartialSyncSqliteDatabase } from './partial-sync-sqlite-db.js';
|
|
5
|
+
import 'drizzle-orm/durable-sqlite';
|
|
6
|
+
|
|
7
|
+
type CreateDrizzleMutationStoreOptions<TSchema extends Record<string, unknown>, TRow extends {
|
|
8
|
+
id: string | number;
|
|
9
|
+
}> = {
|
|
10
|
+
db: PartialSyncSqliteDatabase<TSchema>;
|
|
11
|
+
table: SQLiteTable;
|
|
12
|
+
changelogHelper: DrizzleChangelogHelper<TSchema>;
|
|
13
|
+
/** Columns to copy from `update` message.value into SET (excluding id). */
|
|
14
|
+
updateColumns: readonly (keyof TRow & string)[];
|
|
15
|
+
};
|
|
16
|
+
declare function createDrizzleMutationStore<TSchema extends Record<string, unknown>, TRow extends {
|
|
17
|
+
id: string | number;
|
|
18
|
+
}>(options: CreateDrizzleMutationStoreOptions<TSchema, TRow>): SyncServerBridgeStore<TRow>;
|
|
19
|
+
|
|
20
|
+
export { type CreateDrizzleMutationStoreOptions, createDrizzleMutationStore };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"drizzle-mutation-store.js"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SQLiteTable } from 'drizzle-orm/sqlite-core';
|
|
2
|
+
import { PartialSyncSqliteDatabase } from './partial-sync-sqlite-db.js';
|
|
3
|
+
import 'drizzle-orm/durable-sqlite';
|
|
4
|
+
|
|
5
|
+
type ChangelogOperation = "insert" | "update" | "delete";
|
|
6
|
+
type DrizzleChangelogHelperOptions<TSchema extends Record<string, unknown>> = {
|
|
7
|
+
db: PartialSyncSqliteDatabase<TSchema>;
|
|
8
|
+
changelogTable: SQLiteTable;
|
|
9
|
+
serializeJson: (value: unknown) => string;
|
|
10
|
+
};
|
|
11
|
+
declare function createDrizzleChangelogHelper<TSchema extends Record<string, unknown>>(options: DrizzleChangelogHelperOptions<TSchema>): {
|
|
12
|
+
append: (operation: ChangelogOperation, rowId: string, payload: unknown) => Promise<void>;
|
|
13
|
+
selectAfterVersion: (sinceVersionMs: number) => Promise<{
|
|
14
|
+
[x: string]: any;
|
|
15
|
+
}[]>;
|
|
16
|
+
deleteAll: () => Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
type DrizzleChangelogHelper<TSchema extends Record<string, unknown>> = ReturnType<typeof createDrizzleChangelogHelper<TSchema>>;
|
|
19
|
+
|
|
20
|
+
export { type ChangelogOperation, type DrizzleChangelogHelper, type DrizzleChangelogHelperOptions, createDrizzleChangelogHelper };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"drizzle-partial-sync-changelog.js"}
|