@tanstack/powersync-db-collection 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/powersync.cjs +17 -7
- package/dist/cjs/powersync.cjs.map +1 -1
- package/dist/esm/powersync.js +18 -8
- package/dist/esm/powersync.js.map +1 -1
- package/package.json +2 -2
- package/src/powersync.ts +23 -11
package/dist/cjs/powersync.cjs
CHANGED
|
@@ -119,7 +119,7 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
async function createDiffTrigger(options, appliedReceipts) {
|
|
122
|
-
const { setupContext,
|
|
122
|
+
const { setupContext, when, writeType, batchQuery } = options;
|
|
123
123
|
return await database.triggers.createDiffTrigger({
|
|
124
124
|
source: viewName,
|
|
125
125
|
destination: trackedTableName,
|
|
@@ -131,7 +131,7 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
131
131
|
let currentBatchCount = syncBatchSize;
|
|
132
132
|
let cursor = 0;
|
|
133
133
|
while (currentBatchCount == syncBatchSize) {
|
|
134
|
-
begin(
|
|
134
|
+
begin();
|
|
135
135
|
const batchItems = await batchQuery(
|
|
136
136
|
context,
|
|
137
137
|
syncBatchSize,
|
|
@@ -260,9 +260,6 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
260
260
|
const appliedReceipts = [];
|
|
261
261
|
await establishTracking(
|
|
262
262
|
{
|
|
263
|
-
// Initial eager hydration must make the source usable before
|
|
264
|
-
// PowerSync can persist a mutation queued during startup.
|
|
265
|
-
immediate: true,
|
|
266
263
|
when: {
|
|
267
264
|
[common.DiffTriggerOperation.INSERT]: `TRUE`,
|
|
268
265
|
[common.DiffTriggerOperation.UPDATE]: `TRUE`,
|
|
@@ -276,7 +273,7 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
276
273
|
},
|
|
277
274
|
appliedReceipts
|
|
278
275
|
);
|
|
279
|
-
await Promise.all(appliedReceipts);
|
|
276
|
+
await Promise.all(appliedReceipts.map(db.whenSyncAccepted));
|
|
280
277
|
markReady();
|
|
281
278
|
}).catch((error) => {
|
|
282
279
|
database.logger.log({
|
|
@@ -680,8 +677,21 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
680
677
|
getSyncMetadata: void 0
|
|
681
678
|
};
|
|
682
679
|
const getKey = (record) => helpers.asPowerSyncRecord(record).id;
|
|
680
|
+
const {
|
|
681
|
+
onLoad: _onLoad,
|
|
682
|
+
onLoadSubset: _onLoadSubset,
|
|
683
|
+
deserializationSchema: _deserializationSchema,
|
|
684
|
+
serializer: _serializer,
|
|
685
|
+
onDeserializationError: _onDeserializationError,
|
|
686
|
+
...collectionConfig
|
|
687
|
+
} = {
|
|
688
|
+
deserializationSchema,
|
|
689
|
+
serializer,
|
|
690
|
+
onDeserializationError,
|
|
691
|
+
...restConfig
|
|
692
|
+
};
|
|
683
693
|
const outputConfig = {
|
|
684
|
-
...
|
|
694
|
+
...collectionConfig,
|
|
685
695
|
schema: schema$1,
|
|
686
696
|
getKey,
|
|
687
697
|
// Syncing should start immediately since we need to monitor the changes for mutations
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"powersync.cjs","sources":["../../src/powersync.ts"],"sourcesContent":["import { DiffTriggerOperation, LogLevels, sanitizeSQL } from '@powersync/common'\nimport { or, withCollectionConfigFactory } from '@tanstack/db'\nimport { compileSQLite } from './sqlite-compiler'\nimport { PendingOperationStore } from './PendingOperationStore'\nimport { PowerSyncTransactor } from './PowerSyncTransactor'\nimport { DEFAULT_BATCH_SIZE } from './definitions'\nimport { asPowerSyncRecord, mapOperation } from './helpers'\nimport { convertTableToSchema } from './schema'\nimport { serializeForSQLite } from './serialization'\nimport type {\n CleanupFn,\n LoadSubsetOptions,\n OperationType,\n SyncAppliedReceipt,\n SyncConfig,\n} from '@tanstack/db'\nimport type {\n AnyTableColumnType,\n ExtractedTable,\n OptionalExtractedTable,\n} from './helpers'\nimport type {\n BasePowerSyncCollectionConfig,\n ConfigWithArbitraryCollectionTypes,\n ConfigWithSQLiteInputType,\n ConfigWithSQLiteTypes,\n EnhancedPowerSyncCollectionConfig,\n InferPowerSyncOutputType,\n PowerSyncCollectionConfig,\n PowerSyncCollectionUtils,\n} from './definitions'\nimport type { PendingOperation } from './PendingOperationStore'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { LockContext, Table, TriggerDiffRecord } from '@powersync/common'\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n !!value &&\n (typeof value === `object` || typeof value === `function`) &&\n typeof (value as { then?: unknown }).then === `function`\n )\n}\n\n/**\n * Creates PowerSync collection options for use with a standard Collection.\n *\n * @template TTable - The SQLite-based typing\n * @template TSchema - The validation schema type (optionally supports a custom input type)\n * @param config - Configuration options for the PowerSync collection\n * @returns Collection options with utilities\n */\n\n// Overload 1: No schema is provided\n\n/**\n * Creates a PowerSync collection configuration with basic default validation.\n * Input and Output types are the SQLite column types.\n *\n * @example\n * ```typescript\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * }),\n * })\n *\n * type Document = (typeof APP_SCHEMA)[\"types\"][\"documents\"]\n *\n * const db = new PowerSyncDatabase({\n * database: {\n * dbFilename: \"test.sqlite\",\n * },\n * schema: APP_SCHEMA,\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<TTable extends Table = Table>(\n config: BasePowerSyncCollectionConfig<TTable, never> & ConfigWithSQLiteTypes,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n OptionalExtractedTable<TTable>,\n never\n>\n\n// Overload 2: Schema is provided and the TInput matches SQLite types.\n\n/**\n * Creates a PowerSync collection configuration with schema validation.\n *\n * The input types satisfy the SQLite column types.\n *\n * The output types are defined by the provided schema. This schema can enforce additional\n * validation or type transforms.\n * Arbitrary output typed mutations are encoded to SQLite for persistence. We provide a basic standard\n * serialization implementation to serialize column values. Custom or advanced types require providing additional\n * serializer specifications. Partial column overrides can be supplied to `serializer`.\n *\n * @example\n * ```typescript\n * import { z } from \"zod\"\n *\n * // The PowerSync SQLite schema\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * // Dates are stored as ISO date strings in SQLite\n * created_at: column.text\n * }),\n * })\n *\n * // Advanced Zod validations. The output type of this schema\n * // is constrained to the SQLite schema of APP_SCHEMA\n * const schema = z.object({\n * id: z.string(),\n * // Notice that `name` is not nullable (is required) here and it has additional validation\n * name: z.string().min(3, { message: \"Should be at least 3 characters\" }).nullable(),\n * // The input type is still the SQLite string type. While collections will output smart Date instances.\n * created_at: z.string().transform(val => new Date(val))\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents,\n * schema,\n * serializer: {\n * // The default is toISOString, this is just to demonstrate custom overrides\n * created_at: (outputValue) => outputValue.toISOString(),\n * },\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<\n // TInput is the SQLite types. We can use the supplied schema to validate sync input\n OptionalExtractedTable<TTable>,\n AnyTableColumnType<TTable>\n >,\n>(\n config: BasePowerSyncCollectionConfig<TTable, TSchema> &\n ConfigWithSQLiteInputType<TTable, TSchema>,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n InferPowerSyncOutputType<TTable, TSchema>,\n TSchema\n> & {\n schema: TSchema\n}\n\n// Overload 3: Schema is provided with arbitrary TInput and TOutput\n/**\n * Creates a PowerSync collection configuration with schema validation.\n *\n * The input types are not linked to the internal SQLite table types. This can\n * give greater flexibility, e.g. by accepting rich types as input for `insert` or `update` operations.\n * An additional `deserializationSchema` is required in order to process incoming SQLite updates to the output type.\n *\n * The output types are defined by the provided schema. This schema can enforce additional\n * validation or type transforms.\n * Arbitrary output typed mutations are encoded to SQLite for persistence. We provide a basic standard\n * serialization implementation to serialize column values. Custom or advanced types require providing additional\n * serializer specifications. Partial column overrides can be supplied to `serializer`.\n *\n * @example\n * ```typescript\n * import { z } from \"zod\"\n *\n * // The PowerSync SQLite schema\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * // Booleans are represented as integers in SQLite\n * is_active: column.integer\n * }),\n * })\n *\n * // Advanced Zod validations.\n * // We accept boolean values as input for operations and expose Booleans in query results\n * const schema = z.object({\n * id: z.string(),\n * isActive: z.boolean(), // TInput and TOutput are boolean\n * })\n *\n * // The deserializationSchema converts the SQLite synced INTEGER (0/1) values to booleans.\n * const deserializationSchema = z.object({\n * id: z.string(),\n * isActive: z.number().nullable().transform((val) => val == null ? true : val > 0),\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents,\n * schema,\n * deserializationSchema,\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<\n // The input and output must have the same keys, the value types can be arbitrary\n AnyTableColumnType<TTable>,\n AnyTableColumnType<TTable>\n >,\n>(\n config: BasePowerSyncCollectionConfig<TTable, TSchema> &\n ConfigWithArbitraryCollectionTypes<TTable, TSchema>,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n InferPowerSyncOutputType<TTable, TSchema>,\n TSchema\n> & {\n utils: PowerSyncCollectionUtils<TTable>\n schema: TSchema\n}\n\n/**\n * Implementation of powerSyncCollectionOptions that handles both schema and non-schema configurations.\n */\n\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<any> = never,\n>(\n config: PowerSyncCollectionConfig<TTable, TSchema>,\n): ReturnType<typeof createPowerSyncCollectionConfig<TTable, TSchema>> {\n const outputConfig = createPowerSyncCollectionConfig(config)\n return withCollectionConfigFactory(outputConfig, () =>\n createPowerSyncCollectionConfig(config),\n )\n}\n\nfunction createPowerSyncCollectionConfig<\n TTable extends Table,\n TSchema extends StandardSchemaV1<any> = never,\n>(config: PowerSyncCollectionConfig<TTable, TSchema>) {\n const {\n database,\n table,\n schema: inputSchema,\n syncBatchSize = DEFAULT_BATCH_SIZE,\n syncMode = 'eager',\n ...restConfig\n } = config\n\n const deserializationSchema =\n `deserializationSchema` in config ? config.deserializationSchema : null\n const serializer = `serializer` in config ? config.serializer : undefined\n const onDeserializationError =\n `onDeserializationError` in config\n ? config.onDeserializationError\n : undefined\n\n // The SQLite table type\n type TableType = ExtractedTable<TTable>\n\n // The collection output type\n type OutputType = InferPowerSyncOutputType<TTable, TSchema>\n\n const { viewName, trackMetadata: metadataIsTracked } = table\n\n /**\n * Deserializes data from the incoming sync stream\n */\n const deserializeSyncRow = (value: TableType): OutputType => {\n const validationSchema = deserializationSchema || schema\n const validation = validationSchema[`~standard`].validate(value)\n if (`value` in validation) {\n return validation.value\n } else if (`issues` in validation) {\n const issueMessage = `Failed to validate incoming data for ${viewName}. Issues: ${validation.issues.map((issue) => `${issue.path} - ${issue.message}`)}`\n database.logger.log({ level: LogLevels.error, message: issueMessage })\n onDeserializationError!(validation)\n throw new Error(issueMessage)\n } else {\n const unknownErrorMessage = `Unknown deserialization error for ${viewName}`\n database.logger.log({\n level: LogLevels.error,\n message: unknownErrorMessage,\n })\n onDeserializationError!({ issues: [{ message: unknownErrorMessage }] })\n throw new Error(unknownErrorMessage)\n }\n }\n\n // We can do basic runtime validations for columns if not explicit schema has been provided\n const schema = inputSchema ?? (convertTableToSchema(table) as TSchema)\n /**\n * The onInsert, onUpdate, and onDelete handlers should only return\n * after we have written the changes to TanStack DB.\n * We currently only write to TanStack DB from a diff trigger.\n * We wait for the diff trigger to observe the change,\n * and only then return from the on[X] handlers.\n * This ensures that when the transaction is reported as\n * complete to the caller, the in-memory state is already\n * consistent with the database.\n */\n const pendingOperationStore = PendingOperationStore.GLOBAL\n // Keep the tracked table unique in case of multiple tabs.\n const trackedTableName = `__${viewName}_tracking_${Math.floor(\n Math.random() * 0xffffffff,\n )\n .toString(16)\n .padStart(8, `0`)}`\n\n const transactor = new PowerSyncTransactor({\n database,\n })\n\n /**\n * \"sync\"\n * Notice that this describes the Sync between the local SQLite table\n * and the in-memory tanstack-db collection.\n */\n const sync: SyncConfig<OutputType, string> = {\n sync: (params) => {\n const { begin, write, collection, commit, markReady, markError } = params\n const abortController = new AbortController()\n\n let disposeTracking:\n | ((options?: { context?: LockContext }) => Promise<void>)\n | null = null\n let trackingSetup: Promise<void> | null = null\n let activeTrackingDisposal: Promise<void> | null = null\n let abortTrackingDisposal: Promise<void> | null = null\n\n if (syncMode === `eager`) {\n return runEagerSync()\n } else {\n return runOnDemandSync()\n }\n\n /**\n * Disposes the current diff trigger, if one is active, and clears the\n * tracking state.\n */\n async function safelyDisposeTracking(\n context?: LockContext,\n ): Promise<void> {\n // Cleanup can race trigger creation. Wait until the disposer has been\n // published so an abort cannot strand a freshly-created trigger.\n const setup = trackingSetup\n if (setup) {\n await setup.catch(() => undefined)\n }\n\n const dispose = disposeTracking\n if (!dispose) {\n // Reconciliation may have claimed the disposer after this caller\n // observed setup. Join that invocation instead of settling early.\n await activeTrackingDisposal\n return\n }\n\n disposeTracking = null\n let disposal: Promise<void>\n try {\n disposal = Promise.resolve(dispose(context ? { context } : undefined))\n } catch (error) {\n disposal = Promise.reject(error)\n }\n activeTrackingDisposal = disposal\n try {\n await disposal\n } finally {\n if (activeTrackingDisposal === disposal) {\n activeTrackingDisposal = null\n }\n }\n }\n\n function disposeTrackingAfterAbort(): Promise<void> {\n const disposal = (abortTrackingDisposal ??= safelyDisposeTracking())\n // Abort event dispatch does not observe a listener's returned promise.\n // The eager cleanup callback awaits this same promise below.\n void disposal.catch(() => undefined)\n return disposal\n }\n\n async function establishTracking(\n options: Parameters<typeof createDiffTrigger>[0],\n appliedReceipts: Array<SyncAppliedReceipt>,\n ): Promise<void> {\n const setup = (async () => {\n const dispose = await createDiffTrigger(options, appliedReceipts)\n disposeTracking = dispose\n })()\n trackingSetup = setup\n\n try {\n await setup\n } finally {\n if (trackingSetup === setup) {\n trackingSetup = null\n }\n }\n }\n\n async function createDiffTrigger(\n options: {\n setupContext?: LockContext\n immediate?: boolean\n when: Record<DiffTriggerOperation, string>\n writeType: (rowId: string) => OperationType\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) => Promise<Array<TableType>>\n },\n appliedReceipts: Array<SyncAppliedReceipt>,\n ) {\n const { setupContext, immediate, when, writeType, batchQuery } = options\n\n return await database.triggers.createDiffTrigger({\n source: viewName,\n destination: trackedTableName,\n columns: table.columns.map((column) => column.name),\n setupContext,\n when,\n hooks: {\n beforeCreate: async (context) => {\n let currentBatchCount = syncBatchSize\n let cursor = 0\n while (currentBatchCount == syncBatchSize) {\n begin(immediate ? { immediate: true } : undefined)\n\n const batchItems = await batchQuery(\n context,\n syncBatchSize,\n cursor,\n )\n currentBatchCount = batchItems.length\n cursor += currentBatchCount\n for (const row of batchItems) {\n write({\n type: writeType(row.id),\n value: deserializeSyncRow(row),\n })\n }\n appliedReceipts.push(commit())\n }\n database.logger.log({\n level: LogLevels.info,\n message: `Sync is ready for ${viewName} into ${trackedTableName}`,\n })\n },\n },\n })\n }\n\n async function flushDiffRecords(): Promise<void> {\n // PowerSync can notify after creating the tracking table but before its\n // create call returns. Preserve that notification until the disposer,\n // which proves the trigger is usable, has been published.\n const setup = trackingSetup\n if (setup) {\n await setup.catch(() => undefined)\n }\n if (!disposeTracking) {\n return\n }\n\n const ignoredReceipts: Array<SyncAppliedReceipt> = []\n await database\n .writeTransaction(async (context) => {\n await flushDiffRecordsWithContext(context, ignoredReceipts)\n })\n .catch((error) => {\n database.logger.log({\n level: LogLevels.error,\n message: `An error has been detected in the sync handler`,\n error,\n })\n })\n }\n\n // We can use this directly if we want to pair a flush with dispose+recreate diff trigger.\n async function flushDiffRecordsWithContext(\n context: LockContext,\n appliedReceipts: Array<SyncAppliedReceipt>,\n ): Promise<void> {\n // There is nothing to flush if no tracking table is currently active.\n if (!disposeTracking) {\n return\n }\n\n try {\n begin()\n const operations = await context.getAll<TriggerDiffRecord>(\n `SELECT * FROM ${trackedTableName} ORDER BY operation_id ASC`,\n )\n const pendingOperations: Array<PendingOperation> = []\n\n for (const op of operations) {\n const { id, operation, timestamp, value } = op\n const parsedValue = deserializeSyncRow({\n id,\n ...JSON.parse(value),\n })\n const parsedPreviousValue =\n op.operation == DiffTriggerOperation.UPDATE\n ? deserializeSyncRow({\n id,\n ...JSON.parse(op.previous_value),\n })\n : undefined\n write({\n type: mapOperation(operation),\n value: parsedValue,\n previousValue: parsedPreviousValue,\n })\n pendingOperations.push({\n id,\n operation,\n timestamp,\n tableName: viewName,\n })\n }\n\n // clear the current operations\n await context.execute(`DELETE FROM ${trackedTableName}`)\n\n const applied = commit()\n appliedReceipts.push(applied)\n // Mutation persistence is what releases the Collection's FIFO gate.\n // Confirm these local operations after the sync transaction is\n // staged; waiting for its applied receipt would deadlock the user\n // transaction that currently parks it.\n pendingOperationStore.resolvePendingFor(pendingOperations)\n } catch (error) {\n database.logger.log({\n level: LogLevels.error,\n message: `An error has been detected in the sync handler`,\n error,\n })\n }\n }\n\n // The sync function needs to be synchronous.\n async function start(afterOnChangeRegistered?: () => Promise<void>) {\n database.logger.log({\n level: LogLevels.info,\n message: `Sync is starting for ${viewName} into ${trackedTableName}`,\n })\n database.onChangeWithCallback(\n {\n onChange: async () => {\n await flushDiffRecords()\n },\n },\n {\n signal: abortController.signal,\n triggerImmediate: false,\n tables: [trackedTableName],\n },\n )\n\n await afterOnChangeRegistered?.()\n\n // If the abort controller was aborted while processing the request above\n if (abortController.signal.aborted) {\n await disposeTrackingAfterAbort()\n } else {\n abortController.signal.addEventListener(\n `abort`,\n () => {\n void disposeTrackingAfterAbort()\n },\n { once: true },\n )\n }\n }\n\n // Eager mode.\n // Registers a diff trigger for the entire table.\n function runEagerSync() {\n let onUnload: CleanupFn | void | null = null\n let onUnloadStarted = false\n\n const startup = start(async () => {\n const cleanup = await restConfig.onLoad?.()\n onUnload = cleanup\n if (abortController.signal.aborted) {\n return\n }\n\n const appliedReceipts: Array<SyncAppliedReceipt> = []\n await establishTracking(\n {\n // Initial eager hydration must make the source usable before\n // PowerSync can persist a mutation queued during startup.\n immediate: true,\n when: {\n [DiffTriggerOperation.INSERT]: `TRUE`,\n [DiffTriggerOperation.UPDATE]: `TRUE`,\n [DiffTriggerOperation.DELETE]: `TRUE`,\n },\n writeType: (_rowId: string) => `insert`,\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) =>\n lockContext.getAll<TableType>(\n sanitizeSQL`SELECT * FROM ${viewName} LIMIT ? OFFSET ?`,\n [batchSize, cursor],\n ),\n },\n appliedReceipts,\n )\n await Promise.all(appliedReceipts)\n markReady()\n }).catch((error) => {\n database.logger.log({\n level: LogLevels.error,\n message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,\n error,\n })\n if (collection.status === `loading`) {\n markError(error)\n }\n })\n\n const invokeOnUnload = (): void | Promise<void> => {\n if (onUnloadStarted || !onUnload) return\n onUnloadStarted = true\n const cleanup = onUnload\n onUnload = null\n return cleanup()\n }\n\n return async () => {\n database.logger.log({\n level: LogLevels.info,\n message: `Sync has been stopped for ${viewName} into ${trackedTableName}`,\n })\n abortController.abort()\n const trackingDisposal = disposeTrackingAfterAbort()\n\n let onUnloadFailure: { error: unknown } | undefined\n const settleOnUnload = async () => {\n try {\n await invokeOnUnload()\n } catch (error) {\n onUnloadFailure ??= { error }\n }\n }\n\n // Invoke an already-installed callback in this stack, but also wait\n // for startup to publish a callback acquired concurrently with abort.\n const settlements = await Promise.allSettled([\n startup,\n trackingDisposal,\n settleOnUnload(),\n ])\n const disposalSettlement = settlements[1]\n await settleOnUnload()\n if (disposalSettlement.status === `rejected` && onUnloadFailure) {\n throw new AggregateError(\n [disposalSettlement.reason, onUnloadFailure.error],\n `PowerSync tracking disposal and load-hook cleanup both failed`,\n { cause: disposalSettlement.reason },\n )\n }\n if (disposalSettlement.status === `rejected`) {\n throw disposalSettlement.reason\n }\n if (onUnloadFailure) throw onUnloadFailure.error\n }\n }\n\n // On-demand mode.\n // Registers a diff trigger for the active WHERE expressions.\n function runOnDemandSync() {\n type DemandRecord = {\n options: LoadSubsetOptions\n active: boolean\n cleanup?: CleanupFn\n }\n type PendingRelease = {\n options: LoadSubsetOptions\n failures: number\n }\n type DemandCleanupTask = {\n order: number\n promise: Promise<unknown>\n }\n type DemandCleanupFailure = {\n order: number\n error: unknown\n }\n type DemandCleanupCollector = {\n tasks: Array<DemandCleanupTask>\n ownedTasks: Set<Promise<unknown>>\n failures: Array<DemandCleanupFailure>\n nextOrder: number\n }\n\n const demands = new Map<LoadSubsetOptions, DemandRecord>()\n const releasedSubsets = new WeakSet<LoadSubsetOptions>()\n const pendingReleases: Array<PendingRelease> = []\n const pendingDemandLoads = new Set<Promise<void>>()\n const pendingDemandCleanups = new Set<Promise<unknown>>()\n let stopped = false\n let trackingRevision = 0\n let reconciledTrackingRevision = 0\n let rebuildPromise: Promise<void> | null = null\n let drainingReleases = false\n let releaseRetryTimer: ReturnType<typeof setTimeout> | undefined\n let demandCleanupCollector: DemandCleanupCollector | null = null\n const startup = start()\n void startup.catch((error) =>\n database.logger.log({\n level: LogLevels.error,\n message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,\n error,\n }),\n )\n\n const activeWhereExpressions = () =>\n Array.from(demands.values())\n .filter((demand) => demand.active)\n .map((demand) => demand.options.where)\n\n // One reconciliation owns every queued revision so callers cannot\n // settle against a stale trigger configuration.\n const reconcileTracking = async (): Promise<void> => {\n while (!stopped && reconciledTrackingRevision !== trackingRevision) {\n const revision = trackingRevision\n const isCurrent = () => !stopped && trackingRevision === revision\n const appliedReceipts: Array<SyncAppliedReceipt> = []\n\n await database.writeLock(async (ctx) => {\n if (!isCurrent()) return\n await flushDiffRecordsWithContext(ctx, appliedReceipts)\n if (!isCurrent()) return\n await safelyDisposeTracking(ctx)\n if (!isCurrent()) return\n\n const active = activeWhereExpressions()\n // Tracking was absent during an error. Positive baseline rows\n // alone cannot reveal deletes or predicate exits from that gap.\n const missing =\n collection.status === `error`\n ? new Set(collection.keys())\n : undefined\n if (active.length > 0) {\n const combinedWhere =\n active.length === 1\n ? active[0]\n : or(active[0], active[1], ...active.slice(2))\n const compiledNewData = compileSQLite(\n { where: combinedWhere },\n { jsonColumn: 'NEW.data' },\n )\n const compiledOldData = compileSQLite(\n { where: combinedWhere },\n { jsonColumn: 'OLD.data' },\n )\n const compiledView = compileSQLite({ where: combinedWhere })\n const newDataWhenClause = toInlinedWhereClause(compiledNewData)\n const oldDataWhenClause = toInlinedWhereClause(compiledOldData)\n const viewWhereClause = toInlinedWhereClause(compiledView)\n await establishTracking(\n {\n setupContext: ctx,\n when: {\n [DiffTriggerOperation.INSERT]: newDataWhenClause,\n [DiffTriggerOperation.UPDATE]: `(${newDataWhenClause}) OR (${oldDataWhenClause})`,\n [DiffTriggerOperation.DELETE]: oldDataWhenClause,\n },\n writeType: (rowId: string) =>\n collection.has(rowId) ? `update` : `insert`,\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) =>\n lockContext\n .getAll<TableType>(\n `SELECT * FROM ${viewName} WHERE ${viewWhereClause} LIMIT ? OFFSET ?`,\n [batchSize, cursor],\n )\n .then((rows) => {\n for (const row of rows) missing?.delete(row.id)\n return rows\n }),\n },\n appliedReceipts,\n )\n }\n if (!isCurrent()) await safelyDisposeTracking(ctx)\n else if (missing?.size) {\n begin()\n for (const key of missing) write({ type: `delete`, key })\n appliedReceipts.push(commit())\n }\n })\n await Promise.all(appliedReceipts)\n if (isCurrent()) {\n reconciledTrackingRevision = revision\n // Replacing the trigger alone is not recovery: its baseline\n // writes must also be applied before the source is ready again.\n if (collection.status === `error`) markReady()\n }\n }\n }\n\n const rebuildTracking = async (): Promise<void> => {\n // New demand can join after reconciliation exits but before its\n // shared promise clears. Each waiter must check its revision again.\n while (!stopped && reconciledTrackingRevision !== trackingRevision) {\n await (rebuildPromise ??= reconcileTracking()\n .catch((error) => {\n // A rebuild may already have removed every active diff trigger.\n // Do not leave healthy consumers ready against a stale source.\n if (!stopped) markError(error)\n throw error\n })\n .finally(() => {\n rebuildPromise = null\n }))\n }\n }\n\n const loadSubset = async (\n options: LoadSubsetOptions,\n ): Promise<void> => {\n if (stopped) return\n // Never create a trigger that has no observer to drain its diff table.\n await startup\n if (\n // Cleanup can run while startup is pending.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n stopped ||\n releasedSubsets.has(options) ||\n options.signal?.aborted\n ) {\n return\n }\n\n const demand: DemandRecord = { options, active: false }\n demands.set(options, demand)\n try {\n const cleanup = await restConfig.onLoadSubset?.(options)\n if (cleanup) demand.cleanup = cleanup\n } catch (error) {\n demands.delete(options)\n const collector = demandCleanupCollector\n if (collector) {\n collector.failures.push({\n order: collector.nextOrder++,\n error,\n })\n }\n throw error\n }\n\n if (\n // The user hook can reenter cleanup.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n stopped ||\n releasedSubsets.has(options) ||\n options.signal?.aborted ||\n demands.get(options) !== demand\n ) {\n cleanupDemand(demand)\n return\n }\n\n demand.active = true\n trackingRevision++\n await rebuildTracking()\n }\n\n const trackLoadSubset = (options: LoadSubsetOptions): Promise<void> => {\n const task = loadSubset(options)\n pendingDemandLoads.add(task)\n const finish = () => pendingDemandLoads.delete(task)\n void task.then(finish, finish)\n return task\n }\n\n const toInlinedWhereClause = (compiled: {\n where?: string\n params: Array<unknown>\n }): string => {\n if (!compiled.where) return 'TRUE'\n const sqlParts = compiled.where.split('?')\n return sanitizeSQL(\n sqlParts as unknown as TemplateStringsArray,\n ...compiled.params,\n )\n }\n\n const reportDemandCleanupFailure = (error: unknown): void => {\n database.logger.log({\n level: LogLevels.error,\n message: `Could not clean up subset hook for ${viewName}`,\n error,\n })\n }\n\n const cleanupDemand = (demand: DemandRecord): void => {\n demands.delete(demand.options)\n const collector = demandCleanupCollector\n const order = collector ? collector.nextOrder++ : undefined\n try {\n const cleanup = demand.cleanup\n demand.cleanup = undefined\n const result: unknown = cleanup?.()\n if (!isPromiseLike(result)) return\n\n const task = Promise.resolve(result)\n pendingDemandCleanups.add(task)\n const finish = () => pendingDemandCleanups.delete(task)\n void task.then(finish, finish)\n if (collector) {\n collector.tasks.push({ order: order!, promise: task })\n collector.ownedTasks.add(task)\n void task.catch(() => undefined)\n } else {\n void task.catch((error) => {\n if (!demandCleanupCollector?.ownedTasks.has(task)) {\n reportDemandCleanupFailure(error)\n }\n })\n }\n } catch (error) {\n if (collector) {\n collector.failures.push({ order: order!, error })\n } else {\n reportDemandCleanupFailure(error)\n }\n }\n }\n\n const throwDemandCleanupFailures = (\n collector: DemandCleanupCollector,\n primaryFailures: ReadonlyArray<unknown> = [],\n ): void => {\n const errors = [\n ...primaryFailures,\n ...collector.failures\n .slice()\n .sort((left, right) => left.order - right.order)\n .map(({ error }) => error),\n ]\n if (errors.length === 1) throw errors[0]\n if (errors.length > 1) {\n throw new AggregateError(errors, `PowerSync cleanup failed`, {\n cause: errors[0],\n })\n }\n }\n\n const performPhysicalRelease = async (\n options: LoadSubsetOptions,\n ): Promise<void> => {\n const compiledDeparting = compileSQLite({ where: options.where })\n const departingWhereSQL = toInlinedWhereClause(compiledDeparting)\n let rowsToEvict: Array<{ id: string }>\n for (;;) {\n if (stopped) return\n const revision = trackingRevision\n const active = activeWhereExpressions()\n let evictionSQL: string\n if (active.length === 0) {\n evictionSQL = `SELECT id FROM ${viewName} WHERE ${departingWhereSQL}`\n } else {\n const combinedRemaining =\n active.length === 1\n ? active[0]!\n : or(active[0], active[1], ...active.slice(2))\n const compiledRemaining = compileSQLite({\n where: combinedRemaining,\n })\n const remainingWhereSQL = toInlinedWhereClause(compiledRemaining)\n evictionSQL = `SELECT id FROM ${viewName} WHERE (${departingWhereSQL}) AND NOT (${remainingWhereSQL})`\n }\n\n rowsToEvict = await database.getAll<{ id: string }>(evictionSQL)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- cleanup can run during the query\n if (stopped) return\n if (trackingRevision === revision) break\n }\n if (rowsToEvict.length > 0) {\n begin()\n for (const { id } of rowsToEvict) {\n write({ type: `delete`, key: id })\n }\n void commit()\n }\n await rebuildTracking()\n }\n\n function scheduleReleaseDrain(delay = 0): void {\n if (stopped || drainingReleases || releaseRetryTimer) return\n if (delay > 0) {\n releaseRetryTimer = setTimeout(() => {\n releaseRetryTimer = undefined\n void drainReleases()\n }, delay)\n return\n }\n void drainReleases()\n }\n\n async function drainReleases(): Promise<void> {\n if (stopped || drainingReleases) return\n drainingReleases = true\n let retryDelay = 0\n try {\n const attempts = pendingReleases.length\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- each release can reenter cleanup\n for (let index = 0; !stopped && index < attempts; index++) {\n const pending = pendingReleases.shift()!\n try {\n await performPhysicalRelease(pending.options)\n } catch (error) {\n pending.failures++\n pendingReleases.push(pending)\n const delay = Math.min(\n 1000 * 2 ** (pending.failures - 1),\n 30000,\n )\n retryDelay =\n retryDelay === 0 ? delay : Math.min(retryDelay, delay)\n database.logger.log({\n level: LogLevels.error,\n message: `Could not release subset tracking for ${viewName}; retrying`,\n error,\n })\n }\n }\n } finally {\n drainingReleases = false\n }\n if (pendingReleases.length > 0) scheduleReleaseDrain(retryDelay)\n }\n\n const unloadSubset = (options: LoadSubsetOptions): void => {\n releasedSubsets.add(options)\n const demand = demands.get(options)\n if (!demand) return\n\n const wasActive = demand.active\n if (wasActive) trackingRevision++\n cleanupDemand(demand)\n\n if (wasActive) {\n pendingReleases.push({ options, failures: 0 })\n // New work must not wait for another release's backoff.\n clearTimeout(releaseRetryTimer)\n releaseRetryTimer = undefined\n scheduleReleaseDrain()\n }\n }\n\n markReady()\n\n return {\n cleanup: () => {\n stopped = true\n clearTimeout(releaseRetryTimer)\n releaseRetryTimer = undefined\n database.logger.log({\n level: LogLevels.info,\n message: `Sync has been stopped for ${viewName} into ${trackedTableName}`,\n })\n abortController.abort()\n const trackingDisposal = disposeTrackingAfterAbort()\n const pendingLoads = [...pendingDemandLoads]\n const pendingTasks = [...pendingDemandCleanups]\n const collector: DemandCleanupCollector = {\n tasks: pendingTasks.map((promise, order) => ({ order, promise })),\n ownedTasks: new Set(pendingTasks),\n failures: [],\n nextOrder: pendingTasks.length,\n }\n demandCleanupCollector = collector\n for (const demand of demands.values()) {\n cleanupDemand(demand)\n }\n pendingReleases.length = 0\n\n return (async () => {\n const ownershipSettlements = await Promise.allSettled([\n trackingDisposal,\n ...pendingLoads,\n ])\n const disposalSettlement = ownershipSettlements[0]\n\n let settledTasks = 0\n while (settledTasks < collector.tasks.length) {\n const tasks = collector.tasks.slice(settledTasks)\n settledTasks = collector.tasks.length\n const outcomes = await Promise.allSettled(\n tasks.map(({ promise }) => promise),\n )\n for (const [index, outcome] of outcomes.entries()) {\n if (outcome.status === `rejected`) {\n collector.failures.push({\n order: tasks[index]!.order,\n error: outcome.reason,\n })\n }\n }\n }\n throwDemandCleanupFailures(\n collector,\n disposalSettlement.status === `rejected`\n ? [disposalSettlement.reason]\n : [],\n )\n })().finally(() => {\n if (demandCleanupCollector === collector) {\n demandCleanupCollector = null\n }\n })\n },\n loadSubset: trackLoadSubset,\n unloadSubset,\n }\n }\n },\n // Expose the getSyncMetadata function\n getSyncMetadata: undefined,\n }\n\n const getKey = (record: OutputType) => asPowerSyncRecord(record).id\n\n const outputConfig: EnhancedPowerSyncCollectionConfig<\n TTable,\n OutputType,\n TSchema\n > = {\n ...restConfig,\n schema,\n getKey,\n // Syncing should start immediately since we need to monitor the changes for mutations\n startSync: true,\n syncMode,\n sync,\n onInsert: async (params) => {\n // The transaction here should only ever contain a single insert mutation\n return await transactor.applyTransaction(params.transaction)\n },\n onUpdate: async (params) => {\n // The transaction here should only ever contain a single update mutation\n return await transactor.applyTransaction(params.transaction)\n },\n onDelete: async (params) => {\n // The transaction here should only ever contain a single delete mutation\n return await transactor.applyTransaction(params.transaction)\n },\n utils: {\n getMeta: () => ({\n tableName: viewName,\n trackedTableName,\n metadataIsTracked,\n serializeValue: (value) =>\n serializeForSQLite<TTable>(value, table, serializer),\n }),\n },\n }\n return outputConfig\n}\n"],"names":["withCollectionConfigFactory","DEFAULT_BATCH_SIZE","schema","LogLevels","convertTableToSchema","PendingOperationStore","PowerSyncTransactor","DiffTriggerOperation","mapOperation","sanitizeSQL","or","compileSQLite","asPowerSyncRecord","serializeForSQLite"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"powersync.cjs","sources":["../../src/powersync.ts"],"sourcesContent":["import { DiffTriggerOperation, LogLevels, sanitizeSQL } from '@powersync/common'\nimport { or, whenSyncAccepted, withCollectionConfigFactory } from '@tanstack/db'\nimport { compileSQLite } from './sqlite-compiler'\nimport { PendingOperationStore } from './PendingOperationStore'\nimport { PowerSyncTransactor } from './PowerSyncTransactor'\nimport { DEFAULT_BATCH_SIZE } from './definitions'\nimport { asPowerSyncRecord, mapOperation } from './helpers'\nimport { convertTableToSchema } from './schema'\nimport { serializeForSQLite } from './serialization'\nimport type {\n CleanupFn,\n LoadSubsetOptions,\n OperationType,\n SyncAppliedReceipt,\n SyncConfig,\n} from '@tanstack/db'\nimport type {\n AnyTableColumnType,\n ExtractedTable,\n OptionalExtractedTable,\n} from './helpers'\nimport type {\n BasePowerSyncCollectionConfig,\n ConfigWithArbitraryCollectionTypes,\n ConfigWithSQLiteInputType,\n ConfigWithSQLiteTypes,\n EnhancedPowerSyncCollectionConfig,\n InferPowerSyncOutputType,\n PowerSyncCollectionConfig,\n PowerSyncCollectionUtils,\n} from './definitions'\nimport type { PendingOperation } from './PendingOperationStore'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { LockContext, Table, TriggerDiffRecord } from '@powersync/common'\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n !!value &&\n (typeof value === `object` || typeof value === `function`) &&\n typeof (value as { then?: unknown }).then === `function`\n )\n}\n\n/**\n * Creates PowerSync collection options for use with a standard Collection.\n *\n * @template TTable - The SQLite-based typing\n * @template TSchema - The validation schema type (optionally supports a custom input type)\n * @param config - Configuration options for the PowerSync collection\n * @returns Collection options with utilities\n */\n\n// Overload 1: No schema is provided\n\n/**\n * Creates a PowerSync collection configuration with basic default validation.\n * Input and Output types are the SQLite column types.\n *\n * @example\n * ```typescript\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * }),\n * })\n *\n * type Document = (typeof APP_SCHEMA)[\"types\"][\"documents\"]\n *\n * const db = new PowerSyncDatabase({\n * database: {\n * dbFilename: \"test.sqlite\",\n * },\n * schema: APP_SCHEMA,\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<TTable extends Table = Table>(\n config: BasePowerSyncCollectionConfig<TTable, never> & ConfigWithSQLiteTypes,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n OptionalExtractedTable<TTable>,\n never\n>\n\n// Overload 2: Schema is provided and the TInput matches SQLite types.\n\n/**\n * Creates a PowerSync collection configuration with schema validation.\n *\n * The input types satisfy the SQLite column types.\n *\n * The output types are defined by the provided schema. This schema can enforce additional\n * validation or type transforms.\n * Arbitrary output typed mutations are encoded to SQLite for persistence. We provide a basic standard\n * serialization implementation to serialize column values. Custom or advanced types require providing additional\n * serializer specifications. Partial column overrides can be supplied to `serializer`.\n *\n * @example\n * ```typescript\n * import { z } from \"zod\"\n *\n * // The PowerSync SQLite schema\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * // Dates are stored as ISO date strings in SQLite\n * created_at: column.text\n * }),\n * })\n *\n * // Advanced Zod validations. The output type of this schema\n * // is constrained to the SQLite schema of APP_SCHEMA\n * const schema = z.object({\n * id: z.string(),\n * // Notice that `name` is not nullable (is required) here and it has additional validation\n * name: z.string().min(3, { message: \"Should be at least 3 characters\" }).nullable(),\n * // The input type is still the SQLite string type. While collections will output smart Date instances.\n * created_at: z.string().transform(val => new Date(val))\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents,\n * schema,\n * serializer: {\n * // The default is toISOString, this is just to demonstrate custom overrides\n * created_at: (outputValue) => outputValue.toISOString(),\n * },\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<\n // TInput is the SQLite types. We can use the supplied schema to validate sync input\n OptionalExtractedTable<TTable>,\n AnyTableColumnType<TTable>\n >,\n>(\n config: BasePowerSyncCollectionConfig<TTable, TSchema> &\n ConfigWithSQLiteInputType<TTable, TSchema>,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n InferPowerSyncOutputType<TTable, TSchema>,\n TSchema\n> & {\n schema: TSchema\n}\n\n// Overload 3: Schema is provided with arbitrary TInput and TOutput\n/**\n * Creates a PowerSync collection configuration with schema validation.\n *\n * The input types are not linked to the internal SQLite table types. This can\n * give greater flexibility, e.g. by accepting rich types as input for `insert` or `update` operations.\n * An additional `deserializationSchema` is required in order to process incoming SQLite updates to the output type.\n *\n * The output types are defined by the provided schema. This schema can enforce additional\n * validation or type transforms.\n * Arbitrary output typed mutations are encoded to SQLite for persistence. We provide a basic standard\n * serialization implementation to serialize column values. Custom or advanced types require providing additional\n * serializer specifications. Partial column overrides can be supplied to `serializer`.\n *\n * @example\n * ```typescript\n * import { z } from \"zod\"\n *\n * // The PowerSync SQLite schema\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * // Booleans are represented as integers in SQLite\n * is_active: column.integer\n * }),\n * })\n *\n * // Advanced Zod validations.\n * // We accept boolean values as input for operations and expose Booleans in query results\n * const schema = z.object({\n * id: z.string(),\n * isActive: z.boolean(), // TInput and TOutput are boolean\n * })\n *\n * // The deserializationSchema converts the SQLite synced INTEGER (0/1) values to booleans.\n * const deserializationSchema = z.object({\n * id: z.string(),\n * isActive: z.number().nullable().transform((val) => val == null ? true : val > 0),\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents,\n * schema,\n * deserializationSchema,\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<\n // The input and output must have the same keys, the value types can be arbitrary\n AnyTableColumnType<TTable>,\n AnyTableColumnType<TTable>\n >,\n>(\n config: BasePowerSyncCollectionConfig<TTable, TSchema> &\n ConfigWithArbitraryCollectionTypes<TTable, TSchema>,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n InferPowerSyncOutputType<TTable, TSchema>,\n TSchema\n> & {\n utils: PowerSyncCollectionUtils<TTable>\n schema: TSchema\n}\n\n/**\n * Implementation of powerSyncCollectionOptions that handles both schema and non-schema configurations.\n */\n\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<any> = never,\n>(\n config: PowerSyncCollectionConfig<TTable, TSchema>,\n): ReturnType<typeof createPowerSyncCollectionConfig<TTable, TSchema>> {\n const outputConfig = createPowerSyncCollectionConfig(config)\n return withCollectionConfigFactory(outputConfig, () =>\n createPowerSyncCollectionConfig(config),\n )\n}\n\nfunction createPowerSyncCollectionConfig<\n TTable extends Table,\n TSchema extends StandardSchemaV1<any> = never,\n>(config: PowerSyncCollectionConfig<TTable, TSchema>) {\n const {\n database,\n table,\n schema: inputSchema,\n syncBatchSize = DEFAULT_BATCH_SIZE,\n syncMode = 'eager',\n ...restConfig\n } = config\n\n const deserializationSchema =\n `deserializationSchema` in config ? config.deserializationSchema : null\n const serializer = `serializer` in config ? config.serializer : undefined\n const onDeserializationError =\n `onDeserializationError` in config\n ? config.onDeserializationError\n : undefined\n\n // The SQLite table type\n type TableType = ExtractedTable<TTable>\n\n // The collection output type\n type OutputType = InferPowerSyncOutputType<TTable, TSchema>\n\n const { viewName, trackMetadata: metadataIsTracked } = table\n\n /**\n * Deserializes data from the incoming sync stream\n */\n const deserializeSyncRow = (value: TableType): OutputType => {\n const validationSchema = deserializationSchema || schema\n const validation = validationSchema[`~standard`].validate(value)\n if (`value` in validation) {\n return validation.value\n } else if (`issues` in validation) {\n const issueMessage = `Failed to validate incoming data for ${viewName}. Issues: ${validation.issues.map((issue) => `${issue.path} - ${issue.message}`)}`\n database.logger.log({ level: LogLevels.error, message: issueMessage })\n onDeserializationError!(validation)\n throw new Error(issueMessage)\n } else {\n const unknownErrorMessage = `Unknown deserialization error for ${viewName}`\n database.logger.log({\n level: LogLevels.error,\n message: unknownErrorMessage,\n })\n onDeserializationError!({ issues: [{ message: unknownErrorMessage }] })\n throw new Error(unknownErrorMessage)\n }\n }\n\n // We can do basic runtime validations for columns if not explicit schema has been provided\n const schema = inputSchema ?? (convertTableToSchema(table) as TSchema)\n /**\n * The onInsert, onUpdate, and onDelete handlers should only return\n * after we have written the changes to TanStack DB.\n * We currently only write to TanStack DB from a diff trigger.\n * We wait for the diff trigger to observe the change,\n * and only then return from the on[X] handlers.\n * This ensures that when the transaction is reported as\n * complete to the caller, the in-memory state is already\n * consistent with the database.\n */\n const pendingOperationStore = PendingOperationStore.GLOBAL\n // Keep the tracked table unique in case of multiple tabs.\n const trackedTableName = `__${viewName}_tracking_${Math.floor(\n Math.random() * 0xffffffff,\n )\n .toString(16)\n .padStart(8, `0`)}`\n\n const transactor = new PowerSyncTransactor({\n database,\n })\n\n /**\n * \"sync\"\n * Notice that this describes the Sync between the local SQLite table\n * and the in-memory tanstack-db collection.\n */\n const sync: SyncConfig<OutputType, string> = {\n sync: (params) => {\n const { begin, write, collection, commit, markReady, markError } = params\n const abortController = new AbortController()\n\n let disposeTracking:\n ((options?: { context?: LockContext }) => Promise<void>) | null = null\n let trackingSetup: Promise<void> | null = null\n let activeTrackingDisposal: Promise<void> | null = null\n let abortTrackingDisposal: Promise<void> | null = null\n\n if (syncMode === `eager`) {\n return runEagerSync()\n } else {\n return runOnDemandSync()\n }\n\n /**\n * Disposes the current diff trigger, if one is active, and clears the\n * tracking state.\n */\n async function safelyDisposeTracking(\n context?: LockContext,\n ): Promise<void> {\n // Cleanup can race trigger creation. Wait until the disposer has been\n // published so an abort cannot strand a freshly-created trigger.\n const setup = trackingSetup\n if (setup) {\n await setup.catch(() => undefined)\n }\n\n const dispose = disposeTracking\n if (!dispose) {\n // Reconciliation may have claimed the disposer after this caller\n // observed setup. Join that invocation instead of settling early.\n await activeTrackingDisposal\n return\n }\n\n disposeTracking = null\n let disposal: Promise<void>\n try {\n disposal = Promise.resolve(dispose(context ? { context } : undefined))\n } catch (error) {\n disposal = Promise.reject(error)\n }\n activeTrackingDisposal = disposal\n try {\n await disposal\n } finally {\n if (activeTrackingDisposal === disposal) {\n activeTrackingDisposal = null\n }\n }\n }\n\n function disposeTrackingAfterAbort(): Promise<void> {\n const disposal = (abortTrackingDisposal ??= safelyDisposeTracking())\n // Abort event dispatch does not observe a listener's returned promise.\n // The eager cleanup callback awaits this same promise below.\n void disposal.catch(() => undefined)\n return disposal\n }\n\n async function establishTracking(\n options: Parameters<typeof createDiffTrigger>[0],\n appliedReceipts: Array<SyncAppliedReceipt>,\n ): Promise<void> {\n const setup = (async () => {\n const dispose = await createDiffTrigger(options, appliedReceipts)\n disposeTracking = dispose\n })()\n trackingSetup = setup\n\n try {\n await setup\n } finally {\n if (trackingSetup === setup) {\n trackingSetup = null\n }\n }\n }\n\n async function createDiffTrigger(\n options: {\n setupContext?: LockContext\n when: Record<DiffTriggerOperation, string>\n writeType: (rowId: string) => OperationType\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) => Promise<Array<TableType>>\n },\n appliedReceipts: Array<SyncAppliedReceipt>,\n ) {\n const { setupContext, when, writeType, batchQuery } = options\n\n return await database.triggers.createDiffTrigger({\n source: viewName,\n destination: trackedTableName,\n columns: table.columns.map((column) => column.name),\n setupContext,\n when,\n hooks: {\n beforeCreate: async (context) => {\n let currentBatchCount = syncBatchSize\n let cursor = 0\n while (currentBatchCount == syncBatchSize) {\n begin()\n\n const batchItems = await batchQuery(\n context,\n syncBatchSize,\n cursor,\n )\n currentBatchCount = batchItems.length\n cursor += currentBatchCount\n for (const row of batchItems) {\n write({\n type: writeType(row.id),\n value: deserializeSyncRow(row),\n })\n }\n appliedReceipts.push(commit())\n }\n database.logger.log({\n level: LogLevels.info,\n message: `Sync is ready for ${viewName} into ${trackedTableName}`,\n })\n },\n },\n })\n }\n\n async function flushDiffRecords(): Promise<void> {\n // PowerSync can notify after creating the tracking table but before its\n // create call returns. Preserve that notification until the disposer,\n // which proves the trigger is usable, has been published.\n const setup = trackingSetup\n if (setup) {\n await setup.catch(() => undefined)\n }\n if (!disposeTracking) {\n return\n }\n\n const ignoredReceipts: Array<SyncAppliedReceipt> = []\n await database\n .writeTransaction(async (context) => {\n await flushDiffRecordsWithContext(context, ignoredReceipts)\n })\n .catch((error) => {\n database.logger.log({\n level: LogLevels.error,\n message: `An error has been detected in the sync handler`,\n error,\n })\n })\n }\n\n // We can use this directly if we want to pair a flush with dispose+recreate diff trigger.\n async function flushDiffRecordsWithContext(\n context: LockContext,\n appliedReceipts: Array<SyncAppliedReceipt>,\n ): Promise<void> {\n // There is nothing to flush if no tracking table is currently active.\n if (!disposeTracking) {\n return\n }\n\n try {\n begin()\n const operations = await context.getAll<TriggerDiffRecord>(\n `SELECT * FROM ${trackedTableName} ORDER BY operation_id ASC`,\n )\n const pendingOperations: Array<PendingOperation> = []\n\n for (const op of operations) {\n const { id, operation, timestamp, value } = op\n const parsedValue = deserializeSyncRow({\n id,\n ...JSON.parse(value),\n })\n const parsedPreviousValue =\n op.operation == DiffTriggerOperation.UPDATE\n ? deserializeSyncRow({\n id,\n ...JSON.parse(op.previous_value),\n })\n : undefined\n write({\n type: mapOperation(operation),\n value: parsedValue,\n previousValue: parsedPreviousValue,\n })\n pendingOperations.push({\n id,\n operation,\n timestamp,\n tableName: viewName,\n })\n }\n\n // clear the current operations\n await context.execute(`DELETE FROM ${trackedTableName}`)\n\n const applied = commit()\n appliedReceipts.push(applied)\n // Mutation persistence is what releases the Collection's FIFO gate.\n // Confirm these local operations after the sync transaction is\n // staged; waiting for its applied receipt would deadlock the user\n // transaction that currently parks it.\n pendingOperationStore.resolvePendingFor(pendingOperations)\n } catch (error) {\n database.logger.log({\n level: LogLevels.error,\n message: `An error has been detected in the sync handler`,\n error,\n })\n }\n }\n\n // The sync function needs to be synchronous.\n async function start(afterOnChangeRegistered?: () => Promise<void>) {\n database.logger.log({\n level: LogLevels.info,\n message: `Sync is starting for ${viewName} into ${trackedTableName}`,\n })\n database.onChangeWithCallback(\n {\n onChange: async () => {\n await flushDiffRecords()\n },\n },\n {\n signal: abortController.signal,\n triggerImmediate: false,\n tables: [trackedTableName],\n },\n )\n\n await afterOnChangeRegistered?.()\n\n // If the abort controller was aborted while processing the request above\n if (abortController.signal.aborted) {\n await disposeTrackingAfterAbort()\n } else {\n abortController.signal.addEventListener(\n `abort`,\n () => {\n void disposeTrackingAfterAbort()\n },\n { once: true },\n )\n }\n }\n\n // Eager mode.\n // Registers a diff trigger for the entire table.\n function runEagerSync() {\n let onUnload: CleanupFn | void | null = null\n let onUnloadStarted = false\n\n const startup = start(async () => {\n const cleanup = await restConfig.onLoad?.()\n onUnload = cleanup\n if (abortController.signal.aborted) {\n return\n }\n\n const appliedReceipts: Array<SyncAppliedReceipt> = []\n await establishTracking(\n {\n when: {\n [DiffTriggerOperation.INSERT]: `TRUE`,\n [DiffTriggerOperation.UPDATE]: `TRUE`,\n [DiffTriggerOperation.DELETE]: `TRUE`,\n },\n writeType: (_rowId: string) => `insert`,\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) =>\n lockContext.getAll<TableType>(\n sanitizeSQL`SELECT * FROM ${viewName} LIMIT ? OFFSET ?`,\n [batchSize, cursor],\n ),\n },\n appliedReceipts,\n )\n // Readiness counts accepted rows. A mutation handler waits for it,\n // and its own persisting transaction holds the startup rows.\n await Promise.all(appliedReceipts.map(whenSyncAccepted))\n markReady()\n }).catch((error) => {\n database.logger.log({\n level: LogLevels.error,\n message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,\n error,\n })\n if (collection.status === `loading`) {\n markError(error)\n }\n })\n\n const invokeOnUnload = (): void | Promise<void> => {\n if (onUnloadStarted || !onUnload) return\n onUnloadStarted = true\n const cleanup = onUnload\n onUnload = null\n return cleanup()\n }\n\n return async () => {\n database.logger.log({\n level: LogLevels.info,\n message: `Sync has been stopped for ${viewName} into ${trackedTableName}`,\n })\n abortController.abort()\n const trackingDisposal = disposeTrackingAfterAbort()\n\n let onUnloadFailure: { error: unknown } | undefined\n const settleOnUnload = async () => {\n try {\n await invokeOnUnload()\n } catch (error) {\n onUnloadFailure ??= { error }\n }\n }\n\n // Invoke an already-installed callback in this stack, but also wait\n // for startup to publish a callback acquired concurrently with abort.\n const settlements = await Promise.allSettled([\n startup,\n trackingDisposal,\n settleOnUnload(),\n ])\n const disposalSettlement = settlements[1]\n await settleOnUnload()\n if (disposalSettlement.status === `rejected` && onUnloadFailure) {\n throw new AggregateError(\n [disposalSettlement.reason, onUnloadFailure.error],\n `PowerSync tracking disposal and load-hook cleanup both failed`,\n { cause: disposalSettlement.reason },\n )\n }\n if (disposalSettlement.status === `rejected`) {\n throw disposalSettlement.reason\n }\n if (onUnloadFailure) throw onUnloadFailure.error\n }\n }\n\n // On-demand mode.\n // Registers a diff trigger for the active WHERE expressions.\n function runOnDemandSync() {\n type DemandRecord = {\n options: LoadSubsetOptions\n active: boolean\n cleanup?: CleanupFn\n }\n type PendingRelease = {\n options: LoadSubsetOptions\n failures: number\n }\n type DemandCleanupTask = {\n order: number\n promise: Promise<unknown>\n }\n type DemandCleanupFailure = {\n order: number\n error: unknown\n }\n type DemandCleanupCollector = {\n tasks: Array<DemandCleanupTask>\n ownedTasks: Set<Promise<unknown>>\n failures: Array<DemandCleanupFailure>\n nextOrder: number\n }\n\n const demands = new Map<LoadSubsetOptions, DemandRecord>()\n const releasedSubsets = new WeakSet<LoadSubsetOptions>()\n const pendingReleases: Array<PendingRelease> = []\n const pendingDemandLoads = new Set<Promise<void>>()\n const pendingDemandCleanups = new Set<Promise<unknown>>()\n let stopped = false\n let trackingRevision = 0\n let reconciledTrackingRevision = 0\n let rebuildPromise: Promise<void> | null = null\n let drainingReleases = false\n let releaseRetryTimer: ReturnType<typeof setTimeout> | undefined\n let demandCleanupCollector: DemandCleanupCollector | null = null\n const startup = start()\n void startup.catch((error) =>\n database.logger.log({\n level: LogLevels.error,\n message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,\n error,\n }),\n )\n\n const activeWhereExpressions = () =>\n Array.from(demands.values())\n .filter((demand) => demand.active)\n .map((demand) => demand.options.where)\n\n // One reconciliation owns every queued revision so callers cannot\n // settle against a stale trigger configuration.\n const reconcileTracking = async (): Promise<void> => {\n while (!stopped && reconciledTrackingRevision !== trackingRevision) {\n const revision = trackingRevision\n const isCurrent = () => !stopped && trackingRevision === revision\n const appliedReceipts: Array<SyncAppliedReceipt> = []\n\n await database.writeLock(async (ctx) => {\n if (!isCurrent()) return\n await flushDiffRecordsWithContext(ctx, appliedReceipts)\n if (!isCurrent()) return\n await safelyDisposeTracking(ctx)\n if (!isCurrent()) return\n\n const active = activeWhereExpressions()\n // Tracking was absent during an error. Positive baseline rows\n // alone cannot reveal deletes or predicate exits from that gap.\n const missing =\n collection.status === `error`\n ? new Set(collection.keys())\n : undefined\n if (active.length > 0) {\n const combinedWhere =\n active.length === 1\n ? active[0]\n : or(active[0], active[1], ...active.slice(2))\n const compiledNewData = compileSQLite(\n { where: combinedWhere },\n { jsonColumn: 'NEW.data' },\n )\n const compiledOldData = compileSQLite(\n { where: combinedWhere },\n { jsonColumn: 'OLD.data' },\n )\n const compiledView = compileSQLite({ where: combinedWhere })\n const newDataWhenClause = toInlinedWhereClause(compiledNewData)\n const oldDataWhenClause = toInlinedWhereClause(compiledOldData)\n const viewWhereClause = toInlinedWhereClause(compiledView)\n await establishTracking(\n {\n setupContext: ctx,\n when: {\n [DiffTriggerOperation.INSERT]: newDataWhenClause,\n [DiffTriggerOperation.UPDATE]: `(${newDataWhenClause}) OR (${oldDataWhenClause})`,\n [DiffTriggerOperation.DELETE]: oldDataWhenClause,\n },\n writeType: (rowId: string) =>\n collection.has(rowId) ? `update` : `insert`,\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) =>\n lockContext\n .getAll<TableType>(\n `SELECT * FROM ${viewName} WHERE ${viewWhereClause} LIMIT ? OFFSET ?`,\n [batchSize, cursor],\n )\n .then((rows) => {\n for (const row of rows) missing?.delete(row.id)\n return rows\n }),\n },\n appliedReceipts,\n )\n }\n if (!isCurrent()) await safelyDisposeTracking(ctx)\n else if (missing?.size) {\n begin()\n for (const key of missing) write({ type: `delete`, key })\n appliedReceipts.push(commit())\n }\n })\n await Promise.all(appliedReceipts)\n if (isCurrent()) {\n reconciledTrackingRevision = revision\n // Replacing the trigger alone is not recovery: its baseline\n // writes must also be applied before the source is ready again.\n if (collection.status === `error`) markReady()\n }\n }\n }\n\n const rebuildTracking = async (): Promise<void> => {\n // New demand can join after reconciliation exits but before its\n // shared promise clears. Each waiter must check its revision again.\n while (!stopped && reconciledTrackingRevision !== trackingRevision) {\n await (rebuildPromise ??= reconcileTracking()\n .catch((error) => {\n // A rebuild may already have removed every active diff trigger.\n // Do not leave healthy consumers ready against a stale source.\n if (!stopped) markError(error)\n throw error\n })\n .finally(() => {\n rebuildPromise = null\n }))\n }\n }\n\n const loadSubset = async (\n options: LoadSubsetOptions,\n ): Promise<void> => {\n if (stopped) return\n // Never create a trigger that has no observer to drain its diff table.\n await startup\n if (\n // Cleanup can run while startup is pending.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n stopped ||\n releasedSubsets.has(options) ||\n options.signal?.aborted\n ) {\n return\n }\n\n const demand: DemandRecord = { options, active: false }\n demands.set(options, demand)\n try {\n const cleanup = await restConfig.onLoadSubset?.(options)\n if (cleanup) demand.cleanup = cleanup\n } catch (error) {\n demands.delete(options)\n const collector = demandCleanupCollector\n if (collector) {\n collector.failures.push({\n order: collector.nextOrder++,\n error,\n })\n }\n throw error\n }\n\n if (\n // The user hook can reenter cleanup.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n stopped ||\n releasedSubsets.has(options) ||\n options.signal?.aborted ||\n demands.get(options) !== demand\n ) {\n cleanupDemand(demand)\n return\n }\n\n demand.active = true\n trackingRevision++\n await rebuildTracking()\n }\n\n const trackLoadSubset = (options: LoadSubsetOptions): Promise<void> => {\n const task = loadSubset(options)\n pendingDemandLoads.add(task)\n const finish = () => pendingDemandLoads.delete(task)\n void task.then(finish, finish)\n return task\n }\n\n const toInlinedWhereClause = (compiled: {\n where?: string\n params: Array<unknown>\n }): string => {\n if (!compiled.where) return 'TRUE'\n const sqlParts = compiled.where.split('?')\n return sanitizeSQL(\n sqlParts as unknown as TemplateStringsArray,\n ...compiled.params,\n )\n }\n\n const reportDemandCleanupFailure = (error: unknown): void => {\n database.logger.log({\n level: LogLevels.error,\n message: `Could not clean up subset hook for ${viewName}`,\n error,\n })\n }\n\n const cleanupDemand = (demand: DemandRecord): void => {\n demands.delete(demand.options)\n const collector = demandCleanupCollector\n const order = collector ? collector.nextOrder++ : undefined\n try {\n const cleanup = demand.cleanup\n demand.cleanup = undefined\n const result: unknown = cleanup?.()\n if (!isPromiseLike(result)) return\n\n const task = Promise.resolve(result)\n pendingDemandCleanups.add(task)\n const finish = () => pendingDemandCleanups.delete(task)\n void task.then(finish, finish)\n if (collector) {\n collector.tasks.push({ order: order!, promise: task })\n collector.ownedTasks.add(task)\n void task.catch(() => undefined)\n } else {\n void task.catch((error) => {\n if (!demandCleanupCollector?.ownedTasks.has(task)) {\n reportDemandCleanupFailure(error)\n }\n })\n }\n } catch (error) {\n if (collector) {\n collector.failures.push({ order: order!, error })\n } else {\n reportDemandCleanupFailure(error)\n }\n }\n }\n\n const throwDemandCleanupFailures = (\n collector: DemandCleanupCollector,\n primaryFailures: ReadonlyArray<unknown> = [],\n ): void => {\n const errors = [\n ...primaryFailures,\n ...collector.failures\n .slice()\n .sort((left, right) => left.order - right.order)\n .map(({ error }) => error),\n ]\n if (errors.length === 1) throw errors[0]\n if (errors.length > 1) {\n throw new AggregateError(errors, `PowerSync cleanup failed`, {\n cause: errors[0],\n })\n }\n }\n\n const performPhysicalRelease = async (\n options: LoadSubsetOptions,\n ): Promise<void> => {\n const compiledDeparting = compileSQLite({ where: options.where })\n const departingWhereSQL = toInlinedWhereClause(compiledDeparting)\n let rowsToEvict: Array<{ id: string }>\n for (;;) {\n if (stopped) return\n const revision = trackingRevision\n const active = activeWhereExpressions()\n let evictionSQL: string\n if (active.length === 0) {\n evictionSQL = `SELECT id FROM ${viewName} WHERE ${departingWhereSQL}`\n } else {\n const combinedRemaining =\n active.length === 1\n ? active[0]!\n : or(active[0], active[1], ...active.slice(2))\n const compiledRemaining = compileSQLite({\n where: combinedRemaining,\n })\n const remainingWhereSQL = toInlinedWhereClause(compiledRemaining)\n evictionSQL = `SELECT id FROM ${viewName} WHERE (${departingWhereSQL}) AND NOT (${remainingWhereSQL})`\n }\n\n rowsToEvict = await database.getAll<{ id: string }>(evictionSQL)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- cleanup can run during the query\n if (stopped) return\n if (trackingRevision === revision) break\n }\n if (rowsToEvict.length > 0) {\n begin()\n for (const { id } of rowsToEvict) {\n write({ type: `delete`, key: id })\n }\n void commit()\n }\n await rebuildTracking()\n }\n\n function scheduleReleaseDrain(delay = 0): void {\n if (stopped || drainingReleases || releaseRetryTimer) return\n if (delay > 0) {\n releaseRetryTimer = setTimeout(() => {\n releaseRetryTimer = undefined\n void drainReleases()\n }, delay)\n return\n }\n void drainReleases()\n }\n\n async function drainReleases(): Promise<void> {\n if (stopped || drainingReleases) return\n drainingReleases = true\n let retryDelay = 0\n try {\n const attempts = pendingReleases.length\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- each release can reenter cleanup\n for (let index = 0; !stopped && index < attempts; index++) {\n const pending = pendingReleases.shift()!\n try {\n await performPhysicalRelease(pending.options)\n } catch (error) {\n pending.failures++\n pendingReleases.push(pending)\n const delay = Math.min(\n 1000 * 2 ** (pending.failures - 1),\n 30000,\n )\n retryDelay =\n retryDelay === 0 ? delay : Math.min(retryDelay, delay)\n database.logger.log({\n level: LogLevels.error,\n message: `Could not release subset tracking for ${viewName}; retrying`,\n error,\n })\n }\n }\n } finally {\n drainingReleases = false\n }\n if (pendingReleases.length > 0) scheduleReleaseDrain(retryDelay)\n }\n\n const unloadSubset = (options: LoadSubsetOptions): void => {\n releasedSubsets.add(options)\n const demand = demands.get(options)\n if (!demand) return\n\n const wasActive = demand.active\n if (wasActive) trackingRevision++\n cleanupDemand(demand)\n\n if (wasActive) {\n pendingReleases.push({ options, failures: 0 })\n // New work must not wait for another release's backoff.\n clearTimeout(releaseRetryTimer)\n releaseRetryTimer = undefined\n scheduleReleaseDrain()\n }\n }\n\n markReady()\n\n return {\n cleanup: () => {\n stopped = true\n clearTimeout(releaseRetryTimer)\n releaseRetryTimer = undefined\n database.logger.log({\n level: LogLevels.info,\n message: `Sync has been stopped for ${viewName} into ${trackedTableName}`,\n })\n abortController.abort()\n const trackingDisposal = disposeTrackingAfterAbort()\n const pendingLoads = [...pendingDemandLoads]\n const pendingTasks = [...pendingDemandCleanups]\n const collector: DemandCleanupCollector = {\n tasks: pendingTasks.map((promise, order) => ({ order, promise })),\n ownedTasks: new Set(pendingTasks),\n failures: [],\n nextOrder: pendingTasks.length,\n }\n demandCleanupCollector = collector\n for (const demand of demands.values()) {\n cleanupDemand(demand)\n }\n pendingReleases.length = 0\n\n return (async () => {\n const ownershipSettlements = await Promise.allSettled([\n trackingDisposal,\n ...pendingLoads,\n ])\n const disposalSettlement = ownershipSettlements[0]\n\n let settledTasks = 0\n while (settledTasks < collector.tasks.length) {\n const tasks = collector.tasks.slice(settledTasks)\n settledTasks = collector.tasks.length\n const outcomes = await Promise.allSettled(\n tasks.map(({ promise }) => promise),\n )\n for (const [index, outcome] of outcomes.entries()) {\n if (outcome.status === `rejected`) {\n collector.failures.push({\n order: tasks[index]!.order,\n error: outcome.reason,\n })\n }\n }\n }\n throwDemandCleanupFailures(\n collector,\n disposalSettlement.status === `rejected`\n ? [disposalSettlement.reason]\n : [],\n )\n })().finally(() => {\n if (demandCleanupCollector === collector) {\n demandCleanupCollector = null\n }\n })\n },\n loadSubset: trackLoadSubset,\n unloadSubset,\n }\n }\n },\n // Expose the getSyncMetadata function\n getSyncMetadata: undefined,\n }\n\n const getKey = (record: OutputType) => asPowerSyncRecord(record).id\n\n // Keep adapter hooks and serialization options out of the core config.\n const {\n onLoad: _onLoad,\n onLoadSubset: _onLoadSubset,\n deserializationSchema: _deserializationSchema,\n serializer: _serializer,\n onDeserializationError: _onDeserializationError,\n ...collectionConfig\n } = {\n deserializationSchema,\n serializer,\n onDeserializationError,\n ...restConfig,\n }\n\n const outputConfig: EnhancedPowerSyncCollectionConfig<\n TTable,\n OutputType,\n TSchema\n > = {\n ...collectionConfig,\n schema,\n getKey,\n // Syncing should start immediately since we need to monitor the changes for mutations\n startSync: true,\n syncMode,\n sync,\n onInsert: async (params) => {\n // The transaction here should only ever contain a single insert mutation\n return await transactor.applyTransaction(params.transaction)\n },\n onUpdate: async (params) => {\n // The transaction here should only ever contain a single update mutation\n return await transactor.applyTransaction(params.transaction)\n },\n onDelete: async (params) => {\n // The transaction here should only ever contain a single delete mutation\n return await transactor.applyTransaction(params.transaction)\n },\n utils: {\n getMeta: () => ({\n tableName: viewName,\n trackedTableName,\n metadataIsTracked,\n serializeValue: (value) =>\n serializeForSQLite<TTable>(value, table, serializer),\n }),\n },\n }\n return outputConfig\n}\n"],"names":["withCollectionConfigFactory","DEFAULT_BATCH_SIZE","schema","LogLevels","convertTableToSchema","PendingOperationStore","PowerSyncTransactor","DiffTriggerOperation","mapOperation","sanitizeSQL","whenSyncAccepted","or","compileSQLite","asPowerSyncRecord","serializeForSQLite"],"mappings":"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"}
|
package/dist/esm/powersync.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LogLevels, DiffTriggerOperation, sanitizeSQL } from "@powersync/common";
|
|
2
|
-
import { withCollectionConfigFactory, or } from "@tanstack/db";
|
|
2
|
+
import { withCollectionConfigFactory, whenSyncAccepted, or } from "@tanstack/db";
|
|
3
3
|
import { compileSQLite } from "./sqlite-compiler.js";
|
|
4
4
|
import { PendingOperationStore } from "./PendingOperationStore.js";
|
|
5
5
|
import { PowerSyncTransactor } from "./PowerSyncTransactor.js";
|
|
@@ -117,7 +117,7 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
async function createDiffTrigger(options, appliedReceipts) {
|
|
120
|
-
const { setupContext,
|
|
120
|
+
const { setupContext, when, writeType, batchQuery } = options;
|
|
121
121
|
return await database.triggers.createDiffTrigger({
|
|
122
122
|
source: viewName,
|
|
123
123
|
destination: trackedTableName,
|
|
@@ -129,7 +129,7 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
129
129
|
let currentBatchCount = syncBatchSize;
|
|
130
130
|
let cursor = 0;
|
|
131
131
|
while (currentBatchCount == syncBatchSize) {
|
|
132
|
-
begin(
|
|
132
|
+
begin();
|
|
133
133
|
const batchItems = await batchQuery(
|
|
134
134
|
context,
|
|
135
135
|
syncBatchSize,
|
|
@@ -258,9 +258,6 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
258
258
|
const appliedReceipts = [];
|
|
259
259
|
await establishTracking(
|
|
260
260
|
{
|
|
261
|
-
// Initial eager hydration must make the source usable before
|
|
262
|
-
// PowerSync can persist a mutation queued during startup.
|
|
263
|
-
immediate: true,
|
|
264
261
|
when: {
|
|
265
262
|
[DiffTriggerOperation.INSERT]: `TRUE`,
|
|
266
263
|
[DiffTriggerOperation.UPDATE]: `TRUE`,
|
|
@@ -274,7 +271,7 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
274
271
|
},
|
|
275
272
|
appliedReceipts
|
|
276
273
|
);
|
|
277
|
-
await Promise.all(appliedReceipts);
|
|
274
|
+
await Promise.all(appliedReceipts.map(whenSyncAccepted));
|
|
278
275
|
markReady();
|
|
279
276
|
}).catch((error) => {
|
|
280
277
|
database.logger.log({
|
|
@@ -678,8 +675,21 @@ function createPowerSyncCollectionConfig(config) {
|
|
|
678
675
|
getSyncMetadata: void 0
|
|
679
676
|
};
|
|
680
677
|
const getKey = (record) => asPowerSyncRecord(record).id;
|
|
678
|
+
const {
|
|
679
|
+
onLoad: _onLoad,
|
|
680
|
+
onLoadSubset: _onLoadSubset,
|
|
681
|
+
deserializationSchema: _deserializationSchema,
|
|
682
|
+
serializer: _serializer,
|
|
683
|
+
onDeserializationError: _onDeserializationError,
|
|
684
|
+
...collectionConfig
|
|
685
|
+
} = {
|
|
686
|
+
deserializationSchema,
|
|
687
|
+
serializer,
|
|
688
|
+
onDeserializationError,
|
|
689
|
+
...restConfig
|
|
690
|
+
};
|
|
681
691
|
const outputConfig = {
|
|
682
|
-
...
|
|
692
|
+
...collectionConfig,
|
|
683
693
|
schema,
|
|
684
694
|
getKey,
|
|
685
695
|
// Syncing should start immediately since we need to monitor the changes for mutations
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"powersync.js","sources":["../../src/powersync.ts"],"sourcesContent":["import { DiffTriggerOperation, LogLevels, sanitizeSQL } from '@powersync/common'\nimport { or, withCollectionConfigFactory } from '@tanstack/db'\nimport { compileSQLite } from './sqlite-compiler'\nimport { PendingOperationStore } from './PendingOperationStore'\nimport { PowerSyncTransactor } from './PowerSyncTransactor'\nimport { DEFAULT_BATCH_SIZE } from './definitions'\nimport { asPowerSyncRecord, mapOperation } from './helpers'\nimport { convertTableToSchema } from './schema'\nimport { serializeForSQLite } from './serialization'\nimport type {\n CleanupFn,\n LoadSubsetOptions,\n OperationType,\n SyncAppliedReceipt,\n SyncConfig,\n} from '@tanstack/db'\nimport type {\n AnyTableColumnType,\n ExtractedTable,\n OptionalExtractedTable,\n} from './helpers'\nimport type {\n BasePowerSyncCollectionConfig,\n ConfigWithArbitraryCollectionTypes,\n ConfigWithSQLiteInputType,\n ConfigWithSQLiteTypes,\n EnhancedPowerSyncCollectionConfig,\n InferPowerSyncOutputType,\n PowerSyncCollectionConfig,\n PowerSyncCollectionUtils,\n} from './definitions'\nimport type { PendingOperation } from './PendingOperationStore'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { LockContext, Table, TriggerDiffRecord } from '@powersync/common'\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n !!value &&\n (typeof value === `object` || typeof value === `function`) &&\n typeof (value as { then?: unknown }).then === `function`\n )\n}\n\n/**\n * Creates PowerSync collection options for use with a standard Collection.\n *\n * @template TTable - The SQLite-based typing\n * @template TSchema - The validation schema type (optionally supports a custom input type)\n * @param config - Configuration options for the PowerSync collection\n * @returns Collection options with utilities\n */\n\n// Overload 1: No schema is provided\n\n/**\n * Creates a PowerSync collection configuration with basic default validation.\n * Input and Output types are the SQLite column types.\n *\n * @example\n * ```typescript\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * }),\n * })\n *\n * type Document = (typeof APP_SCHEMA)[\"types\"][\"documents\"]\n *\n * const db = new PowerSyncDatabase({\n * database: {\n * dbFilename: \"test.sqlite\",\n * },\n * schema: APP_SCHEMA,\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<TTable extends Table = Table>(\n config: BasePowerSyncCollectionConfig<TTable, never> & ConfigWithSQLiteTypes,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n OptionalExtractedTable<TTable>,\n never\n>\n\n// Overload 2: Schema is provided and the TInput matches SQLite types.\n\n/**\n * Creates a PowerSync collection configuration with schema validation.\n *\n * The input types satisfy the SQLite column types.\n *\n * The output types are defined by the provided schema. This schema can enforce additional\n * validation or type transforms.\n * Arbitrary output typed mutations are encoded to SQLite for persistence. We provide a basic standard\n * serialization implementation to serialize column values. Custom or advanced types require providing additional\n * serializer specifications. Partial column overrides can be supplied to `serializer`.\n *\n * @example\n * ```typescript\n * import { z } from \"zod\"\n *\n * // The PowerSync SQLite schema\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * // Dates are stored as ISO date strings in SQLite\n * created_at: column.text\n * }),\n * })\n *\n * // Advanced Zod validations. The output type of this schema\n * // is constrained to the SQLite schema of APP_SCHEMA\n * const schema = z.object({\n * id: z.string(),\n * // Notice that `name` is not nullable (is required) here and it has additional validation\n * name: z.string().min(3, { message: \"Should be at least 3 characters\" }).nullable(),\n * // The input type is still the SQLite string type. While collections will output smart Date instances.\n * created_at: z.string().transform(val => new Date(val))\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents,\n * schema,\n * serializer: {\n * // The default is toISOString, this is just to demonstrate custom overrides\n * created_at: (outputValue) => outputValue.toISOString(),\n * },\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<\n // TInput is the SQLite types. We can use the supplied schema to validate sync input\n OptionalExtractedTable<TTable>,\n AnyTableColumnType<TTable>\n >,\n>(\n config: BasePowerSyncCollectionConfig<TTable, TSchema> &\n ConfigWithSQLiteInputType<TTable, TSchema>,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n InferPowerSyncOutputType<TTable, TSchema>,\n TSchema\n> & {\n schema: TSchema\n}\n\n// Overload 3: Schema is provided with arbitrary TInput and TOutput\n/**\n * Creates a PowerSync collection configuration with schema validation.\n *\n * The input types are not linked to the internal SQLite table types. This can\n * give greater flexibility, e.g. by accepting rich types as input for `insert` or `update` operations.\n * An additional `deserializationSchema` is required in order to process incoming SQLite updates to the output type.\n *\n * The output types are defined by the provided schema. This schema can enforce additional\n * validation or type transforms.\n * Arbitrary output typed mutations are encoded to SQLite for persistence. We provide a basic standard\n * serialization implementation to serialize column values. Custom or advanced types require providing additional\n * serializer specifications. Partial column overrides can be supplied to `serializer`.\n *\n * @example\n * ```typescript\n * import { z } from \"zod\"\n *\n * // The PowerSync SQLite schema\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * // Booleans are represented as integers in SQLite\n * is_active: column.integer\n * }),\n * })\n *\n * // Advanced Zod validations.\n * // We accept boolean values as input for operations and expose Booleans in query results\n * const schema = z.object({\n * id: z.string(),\n * isActive: z.boolean(), // TInput and TOutput are boolean\n * })\n *\n * // The deserializationSchema converts the SQLite synced INTEGER (0/1) values to booleans.\n * const deserializationSchema = z.object({\n * id: z.string(),\n * isActive: z.number().nullable().transform((val) => val == null ? true : val > 0),\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents,\n * schema,\n * deserializationSchema,\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<\n // The input and output must have the same keys, the value types can be arbitrary\n AnyTableColumnType<TTable>,\n AnyTableColumnType<TTable>\n >,\n>(\n config: BasePowerSyncCollectionConfig<TTable, TSchema> &\n ConfigWithArbitraryCollectionTypes<TTable, TSchema>,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n InferPowerSyncOutputType<TTable, TSchema>,\n TSchema\n> & {\n utils: PowerSyncCollectionUtils<TTable>\n schema: TSchema\n}\n\n/**\n * Implementation of powerSyncCollectionOptions that handles both schema and non-schema configurations.\n */\n\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<any> = never,\n>(\n config: PowerSyncCollectionConfig<TTable, TSchema>,\n): ReturnType<typeof createPowerSyncCollectionConfig<TTable, TSchema>> {\n const outputConfig = createPowerSyncCollectionConfig(config)\n return withCollectionConfigFactory(outputConfig, () =>\n createPowerSyncCollectionConfig(config),\n )\n}\n\nfunction createPowerSyncCollectionConfig<\n TTable extends Table,\n TSchema extends StandardSchemaV1<any> = never,\n>(config: PowerSyncCollectionConfig<TTable, TSchema>) {\n const {\n database,\n table,\n schema: inputSchema,\n syncBatchSize = DEFAULT_BATCH_SIZE,\n syncMode = 'eager',\n ...restConfig\n } = config\n\n const deserializationSchema =\n `deserializationSchema` in config ? config.deserializationSchema : null\n const serializer = `serializer` in config ? config.serializer : undefined\n const onDeserializationError =\n `onDeserializationError` in config\n ? config.onDeserializationError\n : undefined\n\n // The SQLite table type\n type TableType = ExtractedTable<TTable>\n\n // The collection output type\n type OutputType = InferPowerSyncOutputType<TTable, TSchema>\n\n const { viewName, trackMetadata: metadataIsTracked } = table\n\n /**\n * Deserializes data from the incoming sync stream\n */\n const deserializeSyncRow = (value: TableType): OutputType => {\n const validationSchema = deserializationSchema || schema\n const validation = validationSchema[`~standard`].validate(value)\n if (`value` in validation) {\n return validation.value\n } else if (`issues` in validation) {\n const issueMessage = `Failed to validate incoming data for ${viewName}. Issues: ${validation.issues.map((issue) => `${issue.path} - ${issue.message}`)}`\n database.logger.log({ level: LogLevels.error, message: issueMessage })\n onDeserializationError!(validation)\n throw new Error(issueMessage)\n } else {\n const unknownErrorMessage = `Unknown deserialization error for ${viewName}`\n database.logger.log({\n level: LogLevels.error,\n message: unknownErrorMessage,\n })\n onDeserializationError!({ issues: [{ message: unknownErrorMessage }] })\n throw new Error(unknownErrorMessage)\n }\n }\n\n // We can do basic runtime validations for columns if not explicit schema has been provided\n const schema = inputSchema ?? (convertTableToSchema(table) as TSchema)\n /**\n * The onInsert, onUpdate, and onDelete handlers should only return\n * after we have written the changes to TanStack DB.\n * We currently only write to TanStack DB from a diff trigger.\n * We wait for the diff trigger to observe the change,\n * and only then return from the on[X] handlers.\n * This ensures that when the transaction is reported as\n * complete to the caller, the in-memory state is already\n * consistent with the database.\n */\n const pendingOperationStore = PendingOperationStore.GLOBAL\n // Keep the tracked table unique in case of multiple tabs.\n const trackedTableName = `__${viewName}_tracking_${Math.floor(\n Math.random() * 0xffffffff,\n )\n .toString(16)\n .padStart(8, `0`)}`\n\n const transactor = new PowerSyncTransactor({\n database,\n })\n\n /**\n * \"sync\"\n * Notice that this describes the Sync between the local SQLite table\n * and the in-memory tanstack-db collection.\n */\n const sync: SyncConfig<OutputType, string> = {\n sync: (params) => {\n const { begin, write, collection, commit, markReady, markError } = params\n const abortController = new AbortController()\n\n let disposeTracking:\n | ((options?: { context?: LockContext }) => Promise<void>)\n | null = null\n let trackingSetup: Promise<void> | null = null\n let activeTrackingDisposal: Promise<void> | null = null\n let abortTrackingDisposal: Promise<void> | null = null\n\n if (syncMode === `eager`) {\n return runEagerSync()\n } else {\n return runOnDemandSync()\n }\n\n /**\n * Disposes the current diff trigger, if one is active, and clears the\n * tracking state.\n */\n async function safelyDisposeTracking(\n context?: LockContext,\n ): Promise<void> {\n // Cleanup can race trigger creation. Wait until the disposer has been\n // published so an abort cannot strand a freshly-created trigger.\n const setup = trackingSetup\n if (setup) {\n await setup.catch(() => undefined)\n }\n\n const dispose = disposeTracking\n if (!dispose) {\n // Reconciliation may have claimed the disposer after this caller\n // observed setup. Join that invocation instead of settling early.\n await activeTrackingDisposal\n return\n }\n\n disposeTracking = null\n let disposal: Promise<void>\n try {\n disposal = Promise.resolve(dispose(context ? { context } : undefined))\n } catch (error) {\n disposal = Promise.reject(error)\n }\n activeTrackingDisposal = disposal\n try {\n await disposal\n } finally {\n if (activeTrackingDisposal === disposal) {\n activeTrackingDisposal = null\n }\n }\n }\n\n function disposeTrackingAfterAbort(): Promise<void> {\n const disposal = (abortTrackingDisposal ??= safelyDisposeTracking())\n // Abort event dispatch does not observe a listener's returned promise.\n // The eager cleanup callback awaits this same promise below.\n void disposal.catch(() => undefined)\n return disposal\n }\n\n async function establishTracking(\n options: Parameters<typeof createDiffTrigger>[0],\n appliedReceipts: Array<SyncAppliedReceipt>,\n ): Promise<void> {\n const setup = (async () => {\n const dispose = await createDiffTrigger(options, appliedReceipts)\n disposeTracking = dispose\n })()\n trackingSetup = setup\n\n try {\n await setup\n } finally {\n if (trackingSetup === setup) {\n trackingSetup = null\n }\n }\n }\n\n async function createDiffTrigger(\n options: {\n setupContext?: LockContext\n immediate?: boolean\n when: Record<DiffTriggerOperation, string>\n writeType: (rowId: string) => OperationType\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) => Promise<Array<TableType>>\n },\n appliedReceipts: Array<SyncAppliedReceipt>,\n ) {\n const { setupContext, immediate, when, writeType, batchQuery } = options\n\n return await database.triggers.createDiffTrigger({\n source: viewName,\n destination: trackedTableName,\n columns: table.columns.map((column) => column.name),\n setupContext,\n when,\n hooks: {\n beforeCreate: async (context) => {\n let currentBatchCount = syncBatchSize\n let cursor = 0\n while (currentBatchCount == syncBatchSize) {\n begin(immediate ? { immediate: true } : undefined)\n\n const batchItems = await batchQuery(\n context,\n syncBatchSize,\n cursor,\n )\n currentBatchCount = batchItems.length\n cursor += currentBatchCount\n for (const row of batchItems) {\n write({\n type: writeType(row.id),\n value: deserializeSyncRow(row),\n })\n }\n appliedReceipts.push(commit())\n }\n database.logger.log({\n level: LogLevels.info,\n message: `Sync is ready for ${viewName} into ${trackedTableName}`,\n })\n },\n },\n })\n }\n\n async function flushDiffRecords(): Promise<void> {\n // PowerSync can notify after creating the tracking table but before its\n // create call returns. Preserve that notification until the disposer,\n // which proves the trigger is usable, has been published.\n const setup = trackingSetup\n if (setup) {\n await setup.catch(() => undefined)\n }\n if (!disposeTracking) {\n return\n }\n\n const ignoredReceipts: Array<SyncAppliedReceipt> = []\n await database\n .writeTransaction(async (context) => {\n await flushDiffRecordsWithContext(context, ignoredReceipts)\n })\n .catch((error) => {\n database.logger.log({\n level: LogLevels.error,\n message: `An error has been detected in the sync handler`,\n error,\n })\n })\n }\n\n // We can use this directly if we want to pair a flush with dispose+recreate diff trigger.\n async function flushDiffRecordsWithContext(\n context: LockContext,\n appliedReceipts: Array<SyncAppliedReceipt>,\n ): Promise<void> {\n // There is nothing to flush if no tracking table is currently active.\n if (!disposeTracking) {\n return\n }\n\n try {\n begin()\n const operations = await context.getAll<TriggerDiffRecord>(\n `SELECT * FROM ${trackedTableName} ORDER BY operation_id ASC`,\n )\n const pendingOperations: Array<PendingOperation> = []\n\n for (const op of operations) {\n const { id, operation, timestamp, value } = op\n const parsedValue = deserializeSyncRow({\n id,\n ...JSON.parse(value),\n })\n const parsedPreviousValue =\n op.operation == DiffTriggerOperation.UPDATE\n ? deserializeSyncRow({\n id,\n ...JSON.parse(op.previous_value),\n })\n : undefined\n write({\n type: mapOperation(operation),\n value: parsedValue,\n previousValue: parsedPreviousValue,\n })\n pendingOperations.push({\n id,\n operation,\n timestamp,\n tableName: viewName,\n })\n }\n\n // clear the current operations\n await context.execute(`DELETE FROM ${trackedTableName}`)\n\n const applied = commit()\n appliedReceipts.push(applied)\n // Mutation persistence is what releases the Collection's FIFO gate.\n // Confirm these local operations after the sync transaction is\n // staged; waiting for its applied receipt would deadlock the user\n // transaction that currently parks it.\n pendingOperationStore.resolvePendingFor(pendingOperations)\n } catch (error) {\n database.logger.log({\n level: LogLevels.error,\n message: `An error has been detected in the sync handler`,\n error,\n })\n }\n }\n\n // The sync function needs to be synchronous.\n async function start(afterOnChangeRegistered?: () => Promise<void>) {\n database.logger.log({\n level: LogLevels.info,\n message: `Sync is starting for ${viewName} into ${trackedTableName}`,\n })\n database.onChangeWithCallback(\n {\n onChange: async () => {\n await flushDiffRecords()\n },\n },\n {\n signal: abortController.signal,\n triggerImmediate: false,\n tables: [trackedTableName],\n },\n )\n\n await afterOnChangeRegistered?.()\n\n // If the abort controller was aborted while processing the request above\n if (abortController.signal.aborted) {\n await disposeTrackingAfterAbort()\n } else {\n abortController.signal.addEventListener(\n `abort`,\n () => {\n void disposeTrackingAfterAbort()\n },\n { once: true },\n )\n }\n }\n\n // Eager mode.\n // Registers a diff trigger for the entire table.\n function runEagerSync() {\n let onUnload: CleanupFn | void | null = null\n let onUnloadStarted = false\n\n const startup = start(async () => {\n const cleanup = await restConfig.onLoad?.()\n onUnload = cleanup\n if (abortController.signal.aborted) {\n return\n }\n\n const appliedReceipts: Array<SyncAppliedReceipt> = []\n await establishTracking(\n {\n // Initial eager hydration must make the source usable before\n // PowerSync can persist a mutation queued during startup.\n immediate: true,\n when: {\n [DiffTriggerOperation.INSERT]: `TRUE`,\n [DiffTriggerOperation.UPDATE]: `TRUE`,\n [DiffTriggerOperation.DELETE]: `TRUE`,\n },\n writeType: (_rowId: string) => `insert`,\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) =>\n lockContext.getAll<TableType>(\n sanitizeSQL`SELECT * FROM ${viewName} LIMIT ? OFFSET ?`,\n [batchSize, cursor],\n ),\n },\n appliedReceipts,\n )\n await Promise.all(appliedReceipts)\n markReady()\n }).catch((error) => {\n database.logger.log({\n level: LogLevels.error,\n message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,\n error,\n })\n if (collection.status === `loading`) {\n markError(error)\n }\n })\n\n const invokeOnUnload = (): void | Promise<void> => {\n if (onUnloadStarted || !onUnload) return\n onUnloadStarted = true\n const cleanup = onUnload\n onUnload = null\n return cleanup()\n }\n\n return async () => {\n database.logger.log({\n level: LogLevels.info,\n message: `Sync has been stopped for ${viewName} into ${trackedTableName}`,\n })\n abortController.abort()\n const trackingDisposal = disposeTrackingAfterAbort()\n\n let onUnloadFailure: { error: unknown } | undefined\n const settleOnUnload = async () => {\n try {\n await invokeOnUnload()\n } catch (error) {\n onUnloadFailure ??= { error }\n }\n }\n\n // Invoke an already-installed callback in this stack, but also wait\n // for startup to publish a callback acquired concurrently with abort.\n const settlements = await Promise.allSettled([\n startup,\n trackingDisposal,\n settleOnUnload(),\n ])\n const disposalSettlement = settlements[1]\n await settleOnUnload()\n if (disposalSettlement.status === `rejected` && onUnloadFailure) {\n throw new AggregateError(\n [disposalSettlement.reason, onUnloadFailure.error],\n `PowerSync tracking disposal and load-hook cleanup both failed`,\n { cause: disposalSettlement.reason },\n )\n }\n if (disposalSettlement.status === `rejected`) {\n throw disposalSettlement.reason\n }\n if (onUnloadFailure) throw onUnloadFailure.error\n }\n }\n\n // On-demand mode.\n // Registers a diff trigger for the active WHERE expressions.\n function runOnDemandSync() {\n type DemandRecord = {\n options: LoadSubsetOptions\n active: boolean\n cleanup?: CleanupFn\n }\n type PendingRelease = {\n options: LoadSubsetOptions\n failures: number\n }\n type DemandCleanupTask = {\n order: number\n promise: Promise<unknown>\n }\n type DemandCleanupFailure = {\n order: number\n error: unknown\n }\n type DemandCleanupCollector = {\n tasks: Array<DemandCleanupTask>\n ownedTasks: Set<Promise<unknown>>\n failures: Array<DemandCleanupFailure>\n nextOrder: number\n }\n\n const demands = new Map<LoadSubsetOptions, DemandRecord>()\n const releasedSubsets = new WeakSet<LoadSubsetOptions>()\n const pendingReleases: Array<PendingRelease> = []\n const pendingDemandLoads = new Set<Promise<void>>()\n const pendingDemandCleanups = new Set<Promise<unknown>>()\n let stopped = false\n let trackingRevision = 0\n let reconciledTrackingRevision = 0\n let rebuildPromise: Promise<void> | null = null\n let drainingReleases = false\n let releaseRetryTimer: ReturnType<typeof setTimeout> | undefined\n let demandCleanupCollector: DemandCleanupCollector | null = null\n const startup = start()\n void startup.catch((error) =>\n database.logger.log({\n level: LogLevels.error,\n message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,\n error,\n }),\n )\n\n const activeWhereExpressions = () =>\n Array.from(demands.values())\n .filter((demand) => demand.active)\n .map((demand) => demand.options.where)\n\n // One reconciliation owns every queued revision so callers cannot\n // settle against a stale trigger configuration.\n const reconcileTracking = async (): Promise<void> => {\n while (!stopped && reconciledTrackingRevision !== trackingRevision) {\n const revision = trackingRevision\n const isCurrent = () => !stopped && trackingRevision === revision\n const appliedReceipts: Array<SyncAppliedReceipt> = []\n\n await database.writeLock(async (ctx) => {\n if (!isCurrent()) return\n await flushDiffRecordsWithContext(ctx, appliedReceipts)\n if (!isCurrent()) return\n await safelyDisposeTracking(ctx)\n if (!isCurrent()) return\n\n const active = activeWhereExpressions()\n // Tracking was absent during an error. Positive baseline rows\n // alone cannot reveal deletes or predicate exits from that gap.\n const missing =\n collection.status === `error`\n ? new Set(collection.keys())\n : undefined\n if (active.length > 0) {\n const combinedWhere =\n active.length === 1\n ? active[0]\n : or(active[0], active[1], ...active.slice(2))\n const compiledNewData = compileSQLite(\n { where: combinedWhere },\n { jsonColumn: 'NEW.data' },\n )\n const compiledOldData = compileSQLite(\n { where: combinedWhere },\n { jsonColumn: 'OLD.data' },\n )\n const compiledView = compileSQLite({ where: combinedWhere })\n const newDataWhenClause = toInlinedWhereClause(compiledNewData)\n const oldDataWhenClause = toInlinedWhereClause(compiledOldData)\n const viewWhereClause = toInlinedWhereClause(compiledView)\n await establishTracking(\n {\n setupContext: ctx,\n when: {\n [DiffTriggerOperation.INSERT]: newDataWhenClause,\n [DiffTriggerOperation.UPDATE]: `(${newDataWhenClause}) OR (${oldDataWhenClause})`,\n [DiffTriggerOperation.DELETE]: oldDataWhenClause,\n },\n writeType: (rowId: string) =>\n collection.has(rowId) ? `update` : `insert`,\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) =>\n lockContext\n .getAll<TableType>(\n `SELECT * FROM ${viewName} WHERE ${viewWhereClause} LIMIT ? OFFSET ?`,\n [batchSize, cursor],\n )\n .then((rows) => {\n for (const row of rows) missing?.delete(row.id)\n return rows\n }),\n },\n appliedReceipts,\n )\n }\n if (!isCurrent()) await safelyDisposeTracking(ctx)\n else if (missing?.size) {\n begin()\n for (const key of missing) write({ type: `delete`, key })\n appliedReceipts.push(commit())\n }\n })\n await Promise.all(appliedReceipts)\n if (isCurrent()) {\n reconciledTrackingRevision = revision\n // Replacing the trigger alone is not recovery: its baseline\n // writes must also be applied before the source is ready again.\n if (collection.status === `error`) markReady()\n }\n }\n }\n\n const rebuildTracking = async (): Promise<void> => {\n // New demand can join after reconciliation exits but before its\n // shared promise clears. Each waiter must check its revision again.\n while (!stopped && reconciledTrackingRevision !== trackingRevision) {\n await (rebuildPromise ??= reconcileTracking()\n .catch((error) => {\n // A rebuild may already have removed every active diff trigger.\n // Do not leave healthy consumers ready against a stale source.\n if (!stopped) markError(error)\n throw error\n })\n .finally(() => {\n rebuildPromise = null\n }))\n }\n }\n\n const loadSubset = async (\n options: LoadSubsetOptions,\n ): Promise<void> => {\n if (stopped) return\n // Never create a trigger that has no observer to drain its diff table.\n await startup\n if (\n // Cleanup can run while startup is pending.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n stopped ||\n releasedSubsets.has(options) ||\n options.signal?.aborted\n ) {\n return\n }\n\n const demand: DemandRecord = { options, active: false }\n demands.set(options, demand)\n try {\n const cleanup = await restConfig.onLoadSubset?.(options)\n if (cleanup) demand.cleanup = cleanup\n } catch (error) {\n demands.delete(options)\n const collector = demandCleanupCollector\n if (collector) {\n collector.failures.push({\n order: collector.nextOrder++,\n error,\n })\n }\n throw error\n }\n\n if (\n // The user hook can reenter cleanup.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n stopped ||\n releasedSubsets.has(options) ||\n options.signal?.aborted ||\n demands.get(options) !== demand\n ) {\n cleanupDemand(demand)\n return\n }\n\n demand.active = true\n trackingRevision++\n await rebuildTracking()\n }\n\n const trackLoadSubset = (options: LoadSubsetOptions): Promise<void> => {\n const task = loadSubset(options)\n pendingDemandLoads.add(task)\n const finish = () => pendingDemandLoads.delete(task)\n void task.then(finish, finish)\n return task\n }\n\n const toInlinedWhereClause = (compiled: {\n where?: string\n params: Array<unknown>\n }): string => {\n if (!compiled.where) return 'TRUE'\n const sqlParts = compiled.where.split('?')\n return sanitizeSQL(\n sqlParts as unknown as TemplateStringsArray,\n ...compiled.params,\n )\n }\n\n const reportDemandCleanupFailure = (error: unknown): void => {\n database.logger.log({\n level: LogLevels.error,\n message: `Could not clean up subset hook for ${viewName}`,\n error,\n })\n }\n\n const cleanupDemand = (demand: DemandRecord): void => {\n demands.delete(demand.options)\n const collector = demandCleanupCollector\n const order = collector ? collector.nextOrder++ : undefined\n try {\n const cleanup = demand.cleanup\n demand.cleanup = undefined\n const result: unknown = cleanup?.()\n if (!isPromiseLike(result)) return\n\n const task = Promise.resolve(result)\n pendingDemandCleanups.add(task)\n const finish = () => pendingDemandCleanups.delete(task)\n void task.then(finish, finish)\n if (collector) {\n collector.tasks.push({ order: order!, promise: task })\n collector.ownedTasks.add(task)\n void task.catch(() => undefined)\n } else {\n void task.catch((error) => {\n if (!demandCleanupCollector?.ownedTasks.has(task)) {\n reportDemandCleanupFailure(error)\n }\n })\n }\n } catch (error) {\n if (collector) {\n collector.failures.push({ order: order!, error })\n } else {\n reportDemandCleanupFailure(error)\n }\n }\n }\n\n const throwDemandCleanupFailures = (\n collector: DemandCleanupCollector,\n primaryFailures: ReadonlyArray<unknown> = [],\n ): void => {\n const errors = [\n ...primaryFailures,\n ...collector.failures\n .slice()\n .sort((left, right) => left.order - right.order)\n .map(({ error }) => error),\n ]\n if (errors.length === 1) throw errors[0]\n if (errors.length > 1) {\n throw new AggregateError(errors, `PowerSync cleanup failed`, {\n cause: errors[0],\n })\n }\n }\n\n const performPhysicalRelease = async (\n options: LoadSubsetOptions,\n ): Promise<void> => {\n const compiledDeparting = compileSQLite({ where: options.where })\n const departingWhereSQL = toInlinedWhereClause(compiledDeparting)\n let rowsToEvict: Array<{ id: string }>\n for (;;) {\n if (stopped) return\n const revision = trackingRevision\n const active = activeWhereExpressions()\n let evictionSQL: string\n if (active.length === 0) {\n evictionSQL = `SELECT id FROM ${viewName} WHERE ${departingWhereSQL}`\n } else {\n const combinedRemaining =\n active.length === 1\n ? active[0]!\n : or(active[0], active[1], ...active.slice(2))\n const compiledRemaining = compileSQLite({\n where: combinedRemaining,\n })\n const remainingWhereSQL = toInlinedWhereClause(compiledRemaining)\n evictionSQL = `SELECT id FROM ${viewName} WHERE (${departingWhereSQL}) AND NOT (${remainingWhereSQL})`\n }\n\n rowsToEvict = await database.getAll<{ id: string }>(evictionSQL)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- cleanup can run during the query\n if (stopped) return\n if (trackingRevision === revision) break\n }\n if (rowsToEvict.length > 0) {\n begin()\n for (const { id } of rowsToEvict) {\n write({ type: `delete`, key: id })\n }\n void commit()\n }\n await rebuildTracking()\n }\n\n function scheduleReleaseDrain(delay = 0): void {\n if (stopped || drainingReleases || releaseRetryTimer) return\n if (delay > 0) {\n releaseRetryTimer = setTimeout(() => {\n releaseRetryTimer = undefined\n void drainReleases()\n }, delay)\n return\n }\n void drainReleases()\n }\n\n async function drainReleases(): Promise<void> {\n if (stopped || drainingReleases) return\n drainingReleases = true\n let retryDelay = 0\n try {\n const attempts = pendingReleases.length\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- each release can reenter cleanup\n for (let index = 0; !stopped && index < attempts; index++) {\n const pending = pendingReleases.shift()!\n try {\n await performPhysicalRelease(pending.options)\n } catch (error) {\n pending.failures++\n pendingReleases.push(pending)\n const delay = Math.min(\n 1000 * 2 ** (pending.failures - 1),\n 30000,\n )\n retryDelay =\n retryDelay === 0 ? delay : Math.min(retryDelay, delay)\n database.logger.log({\n level: LogLevels.error,\n message: `Could not release subset tracking for ${viewName}; retrying`,\n error,\n })\n }\n }\n } finally {\n drainingReleases = false\n }\n if (pendingReleases.length > 0) scheduleReleaseDrain(retryDelay)\n }\n\n const unloadSubset = (options: LoadSubsetOptions): void => {\n releasedSubsets.add(options)\n const demand = demands.get(options)\n if (!demand) return\n\n const wasActive = demand.active\n if (wasActive) trackingRevision++\n cleanupDemand(demand)\n\n if (wasActive) {\n pendingReleases.push({ options, failures: 0 })\n // New work must not wait for another release's backoff.\n clearTimeout(releaseRetryTimer)\n releaseRetryTimer = undefined\n scheduleReleaseDrain()\n }\n }\n\n markReady()\n\n return {\n cleanup: () => {\n stopped = true\n clearTimeout(releaseRetryTimer)\n releaseRetryTimer = undefined\n database.logger.log({\n level: LogLevels.info,\n message: `Sync has been stopped for ${viewName} into ${trackedTableName}`,\n })\n abortController.abort()\n const trackingDisposal = disposeTrackingAfterAbort()\n const pendingLoads = [...pendingDemandLoads]\n const pendingTasks = [...pendingDemandCleanups]\n const collector: DemandCleanupCollector = {\n tasks: pendingTasks.map((promise, order) => ({ order, promise })),\n ownedTasks: new Set(pendingTasks),\n failures: [],\n nextOrder: pendingTasks.length,\n }\n demandCleanupCollector = collector\n for (const demand of demands.values()) {\n cleanupDemand(demand)\n }\n pendingReleases.length = 0\n\n return (async () => {\n const ownershipSettlements = await Promise.allSettled([\n trackingDisposal,\n ...pendingLoads,\n ])\n const disposalSettlement = ownershipSettlements[0]\n\n let settledTasks = 0\n while (settledTasks < collector.tasks.length) {\n const tasks = collector.tasks.slice(settledTasks)\n settledTasks = collector.tasks.length\n const outcomes = await Promise.allSettled(\n tasks.map(({ promise }) => promise),\n )\n for (const [index, outcome] of outcomes.entries()) {\n if (outcome.status === `rejected`) {\n collector.failures.push({\n order: tasks[index]!.order,\n error: outcome.reason,\n })\n }\n }\n }\n throwDemandCleanupFailures(\n collector,\n disposalSettlement.status === `rejected`\n ? [disposalSettlement.reason]\n : [],\n )\n })().finally(() => {\n if (demandCleanupCollector === collector) {\n demandCleanupCollector = null\n }\n })\n },\n loadSubset: trackLoadSubset,\n unloadSubset,\n }\n }\n },\n // Expose the getSyncMetadata function\n getSyncMetadata: undefined,\n }\n\n const getKey = (record: OutputType) => asPowerSyncRecord(record).id\n\n const outputConfig: EnhancedPowerSyncCollectionConfig<\n TTable,\n OutputType,\n TSchema\n > = {\n ...restConfig,\n schema,\n getKey,\n // Syncing should start immediately since we need to monitor the changes for mutations\n startSync: true,\n syncMode,\n sync,\n onInsert: async (params) => {\n // The transaction here should only ever contain a single insert mutation\n return await transactor.applyTransaction(params.transaction)\n },\n onUpdate: async (params) => {\n // The transaction here should only ever contain a single update mutation\n return await transactor.applyTransaction(params.transaction)\n },\n onDelete: async (params) => {\n // The transaction here should only ever contain a single delete mutation\n return await transactor.applyTransaction(params.transaction)\n },\n utils: {\n getMeta: () => ({\n tableName: viewName,\n trackedTableName,\n metadataIsTracked,\n serializeValue: (value) =>\n serializeForSQLite<TTable>(value, table, serializer),\n }),\n },\n }\n return outputConfig\n}\n"],"names":[],"mappings":";;;;;;;;;AAmCA,SAAS,cAAc,OAA+C;AACpE,SACE,CAAC,CAAC,UACD,OAAO,UAAU,YAAY,OAAO,UAAU,eAC/C,OAAQ,MAA6B,SAAS;AAElD;AA8LO,SAAS,2BAId,QACqE;AACrE,QAAM,eAAe,gCAAgC,MAAM;AAC3D,SAAO;AAAA,IAA4B;AAAA,IAAc,MAC/C,gCAAgC,MAAM;AAAA,EAAA;AAE1C;AAEA,SAAS,gCAGP,QAAoD;AACpD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,GAAG;AAAA,EAAA,IACD;AAEJ,QAAM,wBACJ,2BAA2B,SAAS,OAAO,wBAAwB;AACrE,QAAM,aAAa,gBAAgB,SAAS,OAAO,aAAa;AAChE,QAAM,yBACJ,4BAA4B,SACxB,OAAO,yBACP;AAQN,QAAM,EAAE,UAAU,eAAe,kBAAA,IAAsB;AAKvD,QAAM,qBAAqB,CAAC,UAAiC;AAC3D,UAAM,mBAAmB,yBAAyB;AAClD,UAAM,aAAa,iBAAiB,WAAW,EAAE,SAAS,KAAK;AAC/D,QAAI,WAAW,YAAY;AACzB,aAAO,WAAW;AAAA,IACpB,WAAW,YAAY,YAAY;AACjC,YAAM,eAAe,wCAAwC,QAAQ,aAAa,WAAW,OAAO,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,MAAM,MAAM,OAAO,EAAE,CAAC;AACtJ,eAAS,OAAO,IAAI,EAAE,OAAO,UAAU,OAAO,SAAS,cAAc;AACrE,6BAAwB,UAAU;AAClC,YAAM,IAAI,MAAM,YAAY;AAAA,IAC9B,OAAO;AACL,YAAM,sBAAsB,qCAAqC,QAAQ;AACzE,eAAS,OAAO,IAAI;AAAA,QAClB,OAAO,UAAU;AAAA,QACjB,SAAS;AAAA,MAAA,CACV;AACD,6BAAwB,EAAE,QAAQ,CAAC,EAAE,SAAS,oBAAA,CAAqB,GAAG;AACtE,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAAA,EACF;AAGA,QAAM,SAAS,eAAgB,qBAAqB,KAAK;AAWzD,QAAM,wBAAwB,sBAAsB;AAEpD,QAAM,mBAAmB,KAAK,QAAQ,aAAa,KAAK;AAAA,IACtD,KAAK,WAAW;AAAA,EAAA,EAEf,SAAS,EAAE,EACX,SAAS,GAAG,GAAG,CAAC;AAEnB,QAAM,aAAa,IAAI,oBAAoB;AAAA,IACzC;AAAA,EAAA,CACD;AAOD,QAAM,OAAuC;AAAA,IAC3C,MAAM,CAAC,WAAW;AAChB,YAAM,EAAE,OAAO,OAAO,YAAY,QAAQ,WAAW,cAAc;AACnE,YAAM,kBAAkB,IAAI,gBAAA;AAE5B,UAAI,kBAEO;AACX,UAAI,gBAAsC;AAC1C,UAAI,yBAA+C;AACnD,UAAI,wBAA8C;AAElD,UAAI,aAAa,SAAS;AACxB,eAAO,aAAA;AAAA,MACT,OAAO;AACL,eAAO,gBAAA;AAAA,MACT;AAMA,qBAAe,sBACb,SACe;AAGf,cAAM,QAAQ;AACd,YAAI,OAAO;AACT,gBAAM,MAAM,MAAM,MAAM,MAAS;AAAA,QACnC;AAEA,cAAM,UAAU;AAChB,YAAI,CAAC,SAAS;AAGZ,gBAAM;AACN;AAAA,QACF;AAEA,0BAAkB;AAClB,YAAI;AACJ,YAAI;AACF,qBAAW,QAAQ,QAAQ,QAAQ,UAAU,EAAE,QAAA,IAAY,MAAS,CAAC;AAAA,QACvE,SAAS,OAAO;AACd,qBAAW,QAAQ,OAAO,KAAK;AAAA,QACjC;AACA,iCAAyB;AACzB,YAAI;AACF,gBAAM;AAAA,QACR,UAAA;AACE,cAAI,2BAA2B,UAAU;AACvC,qCAAyB;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAEA,eAAS,4BAA2C;AAClD,cAAM,WAAY,0BAA0B,sBAAA;AAG5C,aAAK,SAAS,MAAM,MAAM,MAAS;AACnC,eAAO;AAAA,MACT;AAEA,qBAAe,kBACb,SACA,iBACe;AACf,cAAM,SAAS,YAAY;AACzB,gBAAM,UAAU,MAAM,kBAAkB,SAAS,eAAe;AAChE,4BAAkB;AAAA,QACpB,GAAA;AACA,wBAAgB;AAEhB,YAAI;AACF,gBAAM;AAAA,QACR,UAAA;AACE,cAAI,kBAAkB,OAAO;AAC3B,4BAAgB;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AAEA,qBAAe,kBACb,SAWA,iBACA;AACA,cAAM,EAAE,cAAc,WAAW,MAAM,WAAW,eAAe;AAEjE,eAAO,MAAM,SAAS,SAAS,kBAAkB;AAAA,UAC/C,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,SAAS,MAAM,QAAQ,IAAI,CAAC,WAAW,OAAO,IAAI;AAAA,UAClD;AAAA,UACA;AAAA,UACA,OAAO;AAAA,YACL,cAAc,OAAO,YAAY;AAC/B,kBAAI,oBAAoB;AACxB,kBAAI,SAAS;AACb,qBAAO,qBAAqB,eAAe;AACzC,sBAAM,YAAY,EAAE,WAAW,KAAA,IAAS,MAAS;AAEjD,sBAAM,aAAa,MAAM;AAAA,kBACvB;AAAA,kBACA;AAAA,kBACA;AAAA,gBAAA;AAEF,oCAAoB,WAAW;AAC/B,0BAAU;AACV,2BAAW,OAAO,YAAY;AAC5B,wBAAM;AAAA,oBACJ,MAAM,UAAU,IAAI,EAAE;AAAA,oBACtB,OAAO,mBAAmB,GAAG;AAAA,kBAAA,CAC9B;AAAA,gBACH;AACA,gCAAgB,KAAK,QAAQ;AAAA,cAC/B;AACA,uBAAS,OAAO,IAAI;AAAA,gBAClB,OAAO,UAAU;AAAA,gBACjB,SAAS,qBAAqB,QAAQ,SAAS,gBAAgB;AAAA,cAAA,CAChE;AAAA,YACH;AAAA,UAAA;AAAA,QACF,CACD;AAAA,MACH;AAEA,qBAAe,mBAAkC;AAI/C,cAAM,QAAQ;AACd,YAAI,OAAO;AACT,gBAAM,MAAM,MAAM,MAAM,MAAS;AAAA,QACnC;AACA,YAAI,CAAC,iBAAiB;AACpB;AAAA,QACF;AAEA,cAAM,kBAA6C,CAAA;AACnD,cAAM,SACH,iBAAiB,OAAO,YAAY;AACnC,gBAAM,4BAA4B,SAAS,eAAe;AAAA,QAC5D,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,mBAAS,OAAO,IAAI;AAAA,YAClB,OAAO,UAAU;AAAA,YACjB,SAAS;AAAA,YACT;AAAA,UAAA,CACD;AAAA,QACH,CAAC;AAAA,MACL;AAGA,qBAAe,4BACb,SACA,iBACe;AAEf,YAAI,CAAC,iBAAiB;AACpB;AAAA,QACF;AAEA,YAAI;AACF,gBAAA;AACA,gBAAM,aAAa,MAAM,QAAQ;AAAA,YAC/B,iBAAiB,gBAAgB;AAAA,UAAA;AAEnC,gBAAM,oBAA6C,CAAA;AAEnD,qBAAW,MAAM,YAAY;AAC3B,kBAAM,EAAE,IAAI,WAAW,WAAW,UAAU;AAC5C,kBAAM,cAAc,mBAAmB;AAAA,cACrC;AAAA,cACA,GAAG,KAAK,MAAM,KAAK;AAAA,YAAA,CACpB;AACD,kBAAM,sBACJ,GAAG,aAAa,qBAAqB,SACjC,mBAAmB;AAAA,cACjB;AAAA,cACA,GAAG,KAAK,MAAM,GAAG,cAAc;AAAA,YAAA,CAChC,IACD;AACN,kBAAM;AAAA,cACJ,MAAM,aAAa,SAAS;AAAA,cAC5B,OAAO;AAAA,cACP,eAAe;AAAA,YAAA,CAChB;AACD,8BAAkB,KAAK;AAAA,cACrB;AAAA,cACA;AAAA,cACA;AAAA,cACA,WAAW;AAAA,YAAA,CACZ;AAAA,UACH;AAGA,gBAAM,QAAQ,QAAQ,eAAe,gBAAgB,EAAE;AAEvD,gBAAM,UAAU,OAAA;AAChB,0BAAgB,KAAK,OAAO;AAK5B,gCAAsB,kBAAkB,iBAAiB;AAAA,QAC3D,SAAS,OAAO;AACd,mBAAS,OAAO,IAAI;AAAA,YAClB,OAAO,UAAU;AAAA,YACjB,SAAS;AAAA,YACT;AAAA,UAAA,CACD;AAAA,QACH;AAAA,MACF;AAGA,qBAAe,MAAM,yBAA+C;AAClE,iBAAS,OAAO,IAAI;AAAA,UAClB,OAAO,UAAU;AAAA,UACjB,SAAS,wBAAwB,QAAQ,SAAS,gBAAgB;AAAA,QAAA,CACnE;AACD,iBAAS;AAAA,UACP;AAAA,YACE,UAAU,YAAY;AACpB,oBAAM,iBAAA;AAAA,YACR;AAAA,UAAA;AAAA,UAEF;AAAA,YACE,QAAQ,gBAAgB;AAAA,YACxB,kBAAkB;AAAA,YAClB,QAAQ,CAAC,gBAAgB;AAAA,UAAA;AAAA,QAC3B;AAGF,cAAM,0BAAA;AAGN,YAAI,gBAAgB,OAAO,SAAS;AAClC,gBAAM,0BAAA;AAAA,QACR,OAAO;AACL,0BAAgB,OAAO;AAAA,YACrB;AAAA,YACA,MAAM;AACJ,mBAAK,0BAAA;AAAA,YACP;AAAA,YACA,EAAE,MAAM,KAAA;AAAA,UAAK;AAAA,QAEjB;AAAA,MACF;AAIA,eAAS,eAAe;AACtB,YAAI,WAAoC;AACxC,YAAI,kBAAkB;AAEtB,cAAM,UAAU,MAAM,YAAY;AAChC,gBAAM,UAAU,MAAM,WAAW,SAAA;AACjC,qBAAW;AACX,cAAI,gBAAgB,OAAO,SAAS;AAClC;AAAA,UACF;AAEA,gBAAM,kBAA6C,CAAA;AACnD,gBAAM;AAAA,YACJ;AAAA;AAAA;AAAA,cAGE,WAAW;AAAA,cACX,MAAM;AAAA,gBACJ,CAAC,qBAAqB,MAAM,GAAG;AAAA,gBAC/B,CAAC,qBAAqB,MAAM,GAAG;AAAA,gBAC/B,CAAC,qBAAqB,MAAM,GAAG;AAAA,cAAA;AAAA,cAEjC,WAAW,CAAC,WAAmB;AAAA,cAC/B,YAAY,CACV,aACA,WACA,WAEA,YAAY;AAAA,gBACV,4BAA4B,QAAQ;AAAA,gBACpC,CAAC,WAAW,MAAM;AAAA,cAAA;AAAA,YACpB;AAAA,YAEJ;AAAA,UAAA;AAEF,gBAAM,QAAQ,IAAI,eAAe;AACjC,oBAAA;AAAA,QACF,CAAC,EAAE,MAAM,CAAC,UAAU;AAClB,mBAAS,OAAO,IAAI;AAAA,YAClB,OAAO,UAAU;AAAA,YACjB,SAAS,uCAAuC,QAAQ,SAAS,gBAAgB;AAAA,YACjF;AAAA,UAAA,CACD;AACD,cAAI,WAAW,WAAW,WAAW;AACnC,sBAAU,KAAK;AAAA,UACjB;AAAA,QACF,CAAC;AAED,cAAM,iBAAiB,MAA4B;AACjD,cAAI,mBAAmB,CAAC,SAAU;AAClC,4BAAkB;AAClB,gBAAM,UAAU;AAChB,qBAAW;AACX,iBAAO,QAAA;AAAA,QACT;AAEA,eAAO,YAAY;AACjB,mBAAS,OAAO,IAAI;AAAA,YAClB,OAAO,UAAU;AAAA,YACjB,SAAS,6BAA6B,QAAQ,SAAS,gBAAgB;AAAA,UAAA,CACxE;AACD,0BAAgB,MAAA;AAChB,gBAAM,mBAAmB,0BAAA;AAEzB,cAAI;AACJ,gBAAM,iBAAiB,YAAY;AACjC,gBAAI;AACF,oBAAM,eAAA;AAAA,YACR,SAAS,OAAO;AACd,kCAAoB,EAAE,MAAA;AAAA,YACxB;AAAA,UACF;AAIA,gBAAM,cAAc,MAAM,QAAQ,WAAW;AAAA,YAC3C;AAAA,YACA;AAAA,YACA,eAAA;AAAA,UAAe,CAChB;AACD,gBAAM,qBAAqB,YAAY,CAAC;AACxC,gBAAM,eAAA;AACN,cAAI,mBAAmB,WAAW,cAAc,iBAAiB;AAC/D,kBAAM,IAAI;AAAA,cACR,CAAC,mBAAmB,QAAQ,gBAAgB,KAAK;AAAA,cACjD;AAAA,cACA,EAAE,OAAO,mBAAmB,OAAA;AAAA,YAAO;AAAA,UAEvC;AACA,cAAI,mBAAmB,WAAW,YAAY;AAC5C,kBAAM,mBAAmB;AAAA,UAC3B;AACA,cAAI,uBAAuB,gBAAgB;AAAA,QAC7C;AAAA,MACF;AAIA,eAAS,kBAAkB;AAyBzB,cAAM,8BAAc,IAAA;AACpB,cAAM,sCAAsB,QAAA;AAC5B,cAAM,kBAAyC,CAAA;AAC/C,cAAM,yCAAyB,IAAA;AAC/B,cAAM,4CAA4B,IAAA;AAClC,YAAI,UAAU;AACd,YAAI,mBAAmB;AACvB,YAAI,6BAA6B;AACjC,YAAI,iBAAuC;AAC3C,YAAI,mBAAmB;AACvB,YAAI;AACJ,YAAI,yBAAwD;AAC5D,cAAM,UAAU,MAAA;AAChB,aAAK,QAAQ;AAAA,UAAM,CAAC,UAClB,SAAS,OAAO,IAAI;AAAA,YAClB,OAAO,UAAU;AAAA,YACjB,SAAS,uCAAuC,QAAQ,SAAS,gBAAgB;AAAA,YACjF;AAAA,UAAA,CACD;AAAA,QAAA;AAGH,cAAM,yBAAyB,MAC7B,MAAM,KAAK,QAAQ,OAAA,CAAQ,EACxB,OAAO,CAAC,WAAW,OAAO,MAAM,EAChC,IAAI,CAAC,WAAW,OAAO,QAAQ,KAAK;AAIzC,cAAM,oBAAoB,YAA2B;AACnD,iBAAO,CAAC,WAAW,+BAA+B,kBAAkB;AAClE,kBAAM,WAAW;AACjB,kBAAM,YAAY,MAAM,CAAC,WAAW,qBAAqB;AACzD,kBAAM,kBAA6C,CAAA;AAEnD,kBAAM,SAAS,UAAU,OAAO,QAAQ;AACtC,kBAAI,CAAC,YAAa;AAClB,oBAAM,4BAA4B,KAAK,eAAe;AACtD,kBAAI,CAAC,YAAa;AAClB,oBAAM,sBAAsB,GAAG;AAC/B,kBAAI,CAAC,YAAa;AAElB,oBAAM,SAAS,uBAAA;AAGf,oBAAM,UACJ,WAAW,WAAW,UAClB,IAAI,IAAI,WAAW,KAAA,CAAM,IACzB;AACN,kBAAI,OAAO,SAAS,GAAG;AACrB,sBAAM,gBACJ,OAAO,WAAW,IACd,OAAO,CAAC,IACR,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AACjD,sBAAM,kBAAkB;AAAA,kBACtB,EAAE,OAAO,cAAA;AAAA,kBACT,EAAE,YAAY,WAAA;AAAA,gBAAW;AAE3B,sBAAM,kBAAkB;AAAA,kBACtB,EAAE,OAAO,cAAA;AAAA,kBACT,EAAE,YAAY,WAAA;AAAA,gBAAW;AAE3B,sBAAM,eAAe,cAAc,EAAE,OAAO,eAAe;AAC3D,sBAAM,oBAAoB,qBAAqB,eAAe;AAC9D,sBAAM,oBAAoB,qBAAqB,eAAe;AAC9D,sBAAM,kBAAkB,qBAAqB,YAAY;AACzD,sBAAM;AAAA,kBACJ;AAAA,oBACE,cAAc;AAAA,oBACd,MAAM;AAAA,sBACJ,CAAC,qBAAqB,MAAM,GAAG;AAAA,sBAC/B,CAAC,qBAAqB,MAAM,GAAG,IAAI,iBAAiB,SAAS,iBAAiB;AAAA,sBAC9E,CAAC,qBAAqB,MAAM,GAAG;AAAA,oBAAA;AAAA,oBAEjC,WAAW,CAAC,UACV,WAAW,IAAI,KAAK,IAAI,WAAW;AAAA,oBACrC,YAAY,CACV,aACA,WACA,WAEA,YACG;AAAA,sBACC,iBAAiB,QAAQ,UAAU,eAAe;AAAA,sBAClD,CAAC,WAAW,MAAM;AAAA,oBAAA,EAEnB,KAAK,CAAC,SAAS;AACd,iCAAW,OAAO,KAAM,UAAS,OAAO,IAAI,EAAE;AAC9C,6BAAO;AAAA,oBACT,CAAC;AAAA,kBAAA;AAAA,kBAEP;AAAA,gBAAA;AAAA,cAEJ;AACA,kBAAI,CAAC,UAAA,EAAa,OAAM,sBAAsB,GAAG;AAAA,uBACxC,SAAS,MAAM;AACtB,sBAAA;AACA,2BAAW,OAAO,QAAS,OAAM,EAAE,MAAM,UAAU,KAAK;AACxD,gCAAgB,KAAK,QAAQ;AAAA,cAC/B;AAAA,YACF,CAAC;AACD,kBAAM,QAAQ,IAAI,eAAe;AACjC,gBAAI,aAAa;AACf,2CAA6B;AAG7B,kBAAI,WAAW,WAAW,QAAS,WAAA;AAAA,YACrC;AAAA,UACF;AAAA,QACF;AAEA,cAAM,kBAAkB,YAA2B;AAGjD,iBAAO,CAAC,WAAW,+BAA+B,kBAAkB;AAClE,mBAAO,mBAAmB,kBAAA,EACvB,MAAM,CAAC,UAAU;AAGhB,kBAAI,CAAC,QAAS,WAAU,KAAK;AAC7B,oBAAM;AAAA,YACR,CAAC,EACA,QAAQ,MAAM;AACb,+BAAiB;AAAA,YACnB,CAAC;AAAA,UACL;AAAA,QACF;AAEA,cAAM,aAAa,OACjB,YACkB;AAClB,cAAI,QAAS;AAEb,gBAAM;AACN;AAAA;AAAA;AAAA,YAGE,WACA,gBAAgB,IAAI,OAAO,KAC3B,QAAQ,QAAQ;AAAA,YAChB;AACA;AAAA,UACF;AAEA,gBAAM,SAAuB,EAAE,SAAS,QAAQ,MAAA;AAChD,kBAAQ,IAAI,SAAS,MAAM;AAC3B,cAAI;AACF,kBAAM,UAAU,MAAM,WAAW,eAAe,OAAO;AACvD,gBAAI,gBAAgB,UAAU;AAAA,UAChC,SAAS,OAAO;AACd,oBAAQ,OAAO,OAAO;AACtB,kBAAM,YAAY;AAClB,gBAAI,WAAW;AACb,wBAAU,SAAS,KAAK;AAAA,gBACtB,OAAO,UAAU;AAAA,gBACjB;AAAA,cAAA,CACD;AAAA,YACH;AACA,kBAAM;AAAA,UACR;AAEA;AAAA;AAAA;AAAA,YAGE,WACA,gBAAgB,IAAI,OAAO,KAC3B,QAAQ,QAAQ,WAChB,QAAQ,IAAI,OAAO,MAAM;AAAA,YACzB;AACA,0BAAc,MAAM;AACpB;AAAA,UACF;AAEA,iBAAO,SAAS;AAChB;AACA,gBAAM,gBAAA;AAAA,QACR;AAEA,cAAM,kBAAkB,CAAC,YAA8C;AACrE,gBAAM,OAAO,WAAW,OAAO;AAC/B,6BAAmB,IAAI,IAAI;AAC3B,gBAAM,SAAS,MAAM,mBAAmB,OAAO,IAAI;AACnD,eAAK,KAAK,KAAK,QAAQ,MAAM;AAC7B,iBAAO;AAAA,QACT;AAEA,cAAM,uBAAuB,CAAC,aAGhB;AACZ,cAAI,CAAC,SAAS,MAAO,QAAO;AAC5B,gBAAM,WAAW,SAAS,MAAM,MAAM,GAAG;AACzC,iBAAO;AAAA,YACL;AAAA,YACA,GAAG,SAAS;AAAA,UAAA;AAAA,QAEhB;AAEA,cAAM,6BAA6B,CAAC,UAAyB;AAC3D,mBAAS,OAAO,IAAI;AAAA,YAClB,OAAO,UAAU;AAAA,YACjB,SAAS,sCAAsC,QAAQ;AAAA,YACvD;AAAA,UAAA,CACD;AAAA,QACH;AAEA,cAAM,gBAAgB,CAAC,WAA+B;AACpD,kBAAQ,OAAO,OAAO,OAAO;AAC7B,gBAAM,YAAY;AAClB,gBAAM,QAAQ,YAAY,UAAU,cAAc;AAClD,cAAI;AACF,kBAAM,UAAU,OAAO;AACvB,mBAAO,UAAU;AACjB,kBAAM,SAAkB,UAAA;AACxB,gBAAI,CAAC,cAAc,MAAM,EAAG;AAE5B,kBAAM,OAAO,QAAQ,QAAQ,MAAM;AACnC,kCAAsB,IAAI,IAAI;AAC9B,kBAAM,SAAS,MAAM,sBAAsB,OAAO,IAAI;AACtD,iBAAK,KAAK,KAAK,QAAQ,MAAM;AAC7B,gBAAI,WAAW;AACb,wBAAU,MAAM,KAAK,EAAE,OAAe,SAAS,MAAM;AACrD,wBAAU,WAAW,IAAI,IAAI;AAC7B,mBAAK,KAAK,MAAM,MAAM,MAAS;AAAA,YACjC,OAAO;AACL,mBAAK,KAAK,MAAM,CAAC,UAAU;AACzB,oBAAI,CAAC,wBAAwB,WAAW,IAAI,IAAI,GAAG;AACjD,6CAA2B,KAAK;AAAA,gBAClC;AAAA,cACF,CAAC;AAAA,YACH;AAAA,UACF,SAAS,OAAO;AACd,gBAAI,WAAW;AACb,wBAAU,SAAS,KAAK,EAAE,OAAe,OAAO;AAAA,YAClD,OAAO;AACL,yCAA2B,KAAK;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAEA,cAAM,6BAA6B,CACjC,WACA,kBAA0C,CAAA,MACjC;AACT,gBAAM,SAAS;AAAA,YACb,GAAG;AAAA,YACH,GAAG,UAAU,SACV,QACA,KAAK,CAAC,MAAM,UAAU,KAAK,QAAQ,MAAM,KAAK,EAC9C,IAAI,CAAC,EAAE,MAAA,MAAY,KAAK;AAAA,UAAA;AAE7B,cAAI,OAAO,WAAW,EAAG,OAAM,OAAO,CAAC;AACvC,cAAI,OAAO,SAAS,GAAG;AACrB,kBAAM,IAAI,eAAe,QAAQ,4BAA4B;AAAA,cAC3D,OAAO,OAAO,CAAC;AAAA,YAAA,CAChB;AAAA,UACH;AAAA,QACF;AAEA,cAAM,yBAAyB,OAC7B,YACkB;AAClB,gBAAM,oBAAoB,cAAc,EAAE,OAAO,QAAQ,OAAO;AAChE,gBAAM,oBAAoB,qBAAqB,iBAAiB;AAChE,cAAI;AACJ,qBAAS;AACP,gBAAI,QAAS;AACb,kBAAM,WAAW;AACjB,kBAAM,SAAS,uBAAA;AACf,gBAAI;AACJ,gBAAI,OAAO,WAAW,GAAG;AACvB,4BAAc,kBAAkB,QAAQ,UAAU,iBAAiB;AAAA,YACrE,OAAO;AACL,oBAAM,oBACJ,OAAO,WAAW,IACd,OAAO,CAAC,IACR,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,GAAG,OAAO,MAAM,CAAC,CAAC;AACjD,oBAAM,oBAAoB,cAAc;AAAA,gBACtC,OAAO;AAAA,cAAA,CACR;AACD,oBAAM,oBAAoB,qBAAqB,iBAAiB;AAChE,4BAAc,kBAAkB,QAAQ,WAAW,iBAAiB,cAAc,iBAAiB;AAAA,YACrG;AAEA,0BAAc,MAAM,SAAS,OAAuB,WAAW;AAE/D,gBAAI,QAAS;AACb,gBAAI,qBAAqB,SAAU;AAAA,UACrC;AACA,cAAI,YAAY,SAAS,GAAG;AAC1B,kBAAA;AACA,uBAAW,EAAE,GAAA,KAAQ,aAAa;AAChC,oBAAM,EAAE,MAAM,UAAU,KAAK,IAAI;AAAA,YACnC;AACA,iBAAK,OAAA;AAAA,UACP;AACA,gBAAM,gBAAA;AAAA,QACR;AAEA,iBAAS,qBAAqB,QAAQ,GAAS;AAC7C,cAAI,WAAW,oBAAoB,kBAAmB;AACtD,cAAI,QAAQ,GAAG;AACb,gCAAoB,WAAW,MAAM;AACnC,kCAAoB;AACpB,mBAAK,cAAA;AAAA,YACP,GAAG,KAAK;AACR;AAAA,UACF;AACA,eAAK,cAAA;AAAA,QACP;AAEA,uBAAe,gBAA+B;AAC5C,cAAI,WAAW,iBAAkB;AACjC,6BAAmB;AACnB,cAAI,aAAa;AACjB,cAAI;AACF,kBAAM,WAAW,gBAAgB;AAEjC,qBAAS,QAAQ,GAAG,CAAC,WAAW,QAAQ,UAAU,SAAS;AACzD,oBAAM,UAAU,gBAAgB,MAAA;AAChC,kBAAI;AACF,sBAAM,uBAAuB,QAAQ,OAAO;AAAA,cAC9C,SAAS,OAAO;AACd,wBAAQ;AACR,gCAAgB,KAAK,OAAO;AAC5B,sBAAM,QAAQ,KAAK;AAAA,kBACjB,MAAO,MAAM,QAAQ,WAAW;AAAA,kBAChC;AAAA,gBAAA;AAEF,6BACE,eAAe,IAAI,QAAQ,KAAK,IAAI,YAAY,KAAK;AACvD,yBAAS,OAAO,IAAI;AAAA,kBAClB,OAAO,UAAU;AAAA,kBACjB,SAAS,yCAAyC,QAAQ;AAAA,kBAC1D;AAAA,gBAAA,CACD;AAAA,cACH;AAAA,YACF;AAAA,UACF,UAAA;AACE,+BAAmB;AAAA,UACrB;AACA,cAAI,gBAAgB,SAAS,EAAG,sBAAqB,UAAU;AAAA,QACjE;AAEA,cAAM,eAAe,CAAC,YAAqC;AACzD,0BAAgB,IAAI,OAAO;AAC3B,gBAAM,SAAS,QAAQ,IAAI,OAAO;AAClC,cAAI,CAAC,OAAQ;AAEb,gBAAM,YAAY,OAAO;AACzB,cAAI,UAAW;AACf,wBAAc,MAAM;AAEpB,cAAI,WAAW;AACb,4BAAgB,KAAK,EAAE,SAAS,UAAU,GAAG;AAE7C,yBAAa,iBAAiB;AAC9B,gCAAoB;AACpB,iCAAA;AAAA,UACF;AAAA,QACF;AAEA,kBAAA;AAEA,eAAO;AAAA,UACL,SAAS,MAAM;AACb,sBAAU;AACV,yBAAa,iBAAiB;AAC9B,gCAAoB;AACpB,qBAAS,OAAO,IAAI;AAAA,cAClB,OAAO,UAAU;AAAA,cACjB,SAAS,6BAA6B,QAAQ,SAAS,gBAAgB;AAAA,YAAA,CACxE;AACD,4BAAgB,MAAA;AAChB,kBAAM,mBAAmB,0BAAA;AACzB,kBAAM,eAAe,CAAC,GAAG,kBAAkB;AAC3C,kBAAM,eAAe,CAAC,GAAG,qBAAqB;AAC9C,kBAAM,YAAoC;AAAA,cACxC,OAAO,aAAa,IAAI,CAAC,SAAS,WAAW,EAAE,OAAO,QAAA,EAAU;AAAA,cAChE,YAAY,IAAI,IAAI,YAAY;AAAA,cAChC,UAAU,CAAA;AAAA,cACV,WAAW,aAAa;AAAA,YAAA;AAE1B,qCAAyB;AACzB,uBAAW,UAAU,QAAQ,UAAU;AACrC,4BAAc,MAAM;AAAA,YACtB;AACA,4BAAgB,SAAS;AAEzB,oBAAQ,YAAY;AAClB,oBAAM,uBAAuB,MAAM,QAAQ,WAAW;AAAA,gBACpD;AAAA,gBACA,GAAG;AAAA,cAAA,CACJ;AACD,oBAAM,qBAAqB,qBAAqB,CAAC;AAEjD,kBAAI,eAAe;AACnB,qBAAO,eAAe,UAAU,MAAM,QAAQ;AAC5C,sBAAM,QAAQ,UAAU,MAAM,MAAM,YAAY;AAChD,+BAAe,UAAU,MAAM;AAC/B,sBAAM,WAAW,MAAM,QAAQ;AAAA,kBAC7B,MAAM,IAAI,CAAC,EAAE,QAAA,MAAc,OAAO;AAAA,gBAAA;AAEpC,2BAAW,CAAC,OAAO,OAAO,KAAK,SAAS,WAAW;AACjD,sBAAI,QAAQ,WAAW,YAAY;AACjC,8BAAU,SAAS,KAAK;AAAA,sBACtB,OAAO,MAAM,KAAK,EAAG;AAAA,sBACrB,OAAO,QAAQ;AAAA,oBAAA,CAChB;AAAA,kBACH;AAAA,gBACF;AAAA,cACF;AACA;AAAA,gBACE;AAAA,gBACA,mBAAmB,WAAW,aAC1B,CAAC,mBAAmB,MAAM,IAC1B,CAAA;AAAA,cAAC;AAAA,YAET,GAAA,EAAK,QAAQ,MAAM;AACjB,kBAAI,2BAA2B,WAAW;AACxC,yCAAyB;AAAA,cAC3B;AAAA,YACF,CAAC;AAAA,UACH;AAAA,UACA,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF;AAAA;AAAA,IAEA,iBAAiB;AAAA,EAAA;AAGnB,QAAM,SAAS,CAAC,WAAuB,kBAAkB,MAAM,EAAE;AAEjE,QAAM,eAIF;AAAA,IACF,GAAG;AAAA,IACH;AAAA,IACA;AAAA;AAAA,IAEA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,UAAU,OAAO,WAAW;AAE1B,aAAO,MAAM,WAAW,iBAAiB,OAAO,WAAW;AAAA,IAC7D;AAAA,IACA,UAAU,OAAO,WAAW;AAE1B,aAAO,MAAM,WAAW,iBAAiB,OAAO,WAAW;AAAA,IAC7D;AAAA,IACA,UAAU,OAAO,WAAW;AAE1B,aAAO,MAAM,WAAW,iBAAiB,OAAO,WAAW;AAAA,IAC7D;AAAA,IACA,OAAO;AAAA,MACL,SAAS,OAAO;AAAA,QACd,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA,gBAAgB,CAAC,UACf,mBAA2B,OAAO,OAAO,UAAU;AAAA,MAAA;AAAA,IACvD;AAAA,EACF;AAEF,SAAO;AACT;"}
|
|
1
|
+
{"version":3,"file":"powersync.js","sources":["../../src/powersync.ts"],"sourcesContent":["import { DiffTriggerOperation, LogLevels, sanitizeSQL } from '@powersync/common'\nimport { or, whenSyncAccepted, withCollectionConfigFactory } from '@tanstack/db'\nimport { compileSQLite } from './sqlite-compiler'\nimport { PendingOperationStore } from './PendingOperationStore'\nimport { PowerSyncTransactor } from './PowerSyncTransactor'\nimport { DEFAULT_BATCH_SIZE } from './definitions'\nimport { asPowerSyncRecord, mapOperation } from './helpers'\nimport { convertTableToSchema } from './schema'\nimport { serializeForSQLite } from './serialization'\nimport type {\n CleanupFn,\n LoadSubsetOptions,\n OperationType,\n SyncAppliedReceipt,\n SyncConfig,\n} from '@tanstack/db'\nimport type {\n AnyTableColumnType,\n ExtractedTable,\n OptionalExtractedTable,\n} from './helpers'\nimport type {\n BasePowerSyncCollectionConfig,\n ConfigWithArbitraryCollectionTypes,\n ConfigWithSQLiteInputType,\n ConfigWithSQLiteTypes,\n EnhancedPowerSyncCollectionConfig,\n InferPowerSyncOutputType,\n PowerSyncCollectionConfig,\n PowerSyncCollectionUtils,\n} from './definitions'\nimport type { PendingOperation } from './PendingOperationStore'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { LockContext, Table, TriggerDiffRecord } from '@powersync/common'\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n !!value &&\n (typeof value === `object` || typeof value === `function`) &&\n typeof (value as { then?: unknown }).then === `function`\n )\n}\n\n/**\n * Creates PowerSync collection options for use with a standard Collection.\n *\n * @template TTable - The SQLite-based typing\n * @template TSchema - The validation schema type (optionally supports a custom input type)\n * @param config - Configuration options for the PowerSync collection\n * @returns Collection options with utilities\n */\n\n// Overload 1: No schema is provided\n\n/**\n * Creates a PowerSync collection configuration with basic default validation.\n * Input and Output types are the SQLite column types.\n *\n * @example\n * ```typescript\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * }),\n * })\n *\n * type Document = (typeof APP_SCHEMA)[\"types\"][\"documents\"]\n *\n * const db = new PowerSyncDatabase({\n * database: {\n * dbFilename: \"test.sqlite\",\n * },\n * schema: APP_SCHEMA,\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<TTable extends Table = Table>(\n config: BasePowerSyncCollectionConfig<TTable, never> & ConfigWithSQLiteTypes,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n OptionalExtractedTable<TTable>,\n never\n>\n\n// Overload 2: Schema is provided and the TInput matches SQLite types.\n\n/**\n * Creates a PowerSync collection configuration with schema validation.\n *\n * The input types satisfy the SQLite column types.\n *\n * The output types are defined by the provided schema. This schema can enforce additional\n * validation or type transforms.\n * Arbitrary output typed mutations are encoded to SQLite for persistence. We provide a basic standard\n * serialization implementation to serialize column values. Custom or advanced types require providing additional\n * serializer specifications. Partial column overrides can be supplied to `serializer`.\n *\n * @example\n * ```typescript\n * import { z } from \"zod\"\n *\n * // The PowerSync SQLite schema\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * // Dates are stored as ISO date strings in SQLite\n * created_at: column.text\n * }),\n * })\n *\n * // Advanced Zod validations. The output type of this schema\n * // is constrained to the SQLite schema of APP_SCHEMA\n * const schema = z.object({\n * id: z.string(),\n * // Notice that `name` is not nullable (is required) here and it has additional validation\n * name: z.string().min(3, { message: \"Should be at least 3 characters\" }).nullable(),\n * // The input type is still the SQLite string type. While collections will output smart Date instances.\n * created_at: z.string().transform(val => new Date(val))\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents,\n * schema,\n * serializer: {\n * // The default is toISOString, this is just to demonstrate custom overrides\n * created_at: (outputValue) => outputValue.toISOString(),\n * },\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<\n // TInput is the SQLite types. We can use the supplied schema to validate sync input\n OptionalExtractedTable<TTable>,\n AnyTableColumnType<TTable>\n >,\n>(\n config: BasePowerSyncCollectionConfig<TTable, TSchema> &\n ConfigWithSQLiteInputType<TTable, TSchema>,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n InferPowerSyncOutputType<TTable, TSchema>,\n TSchema\n> & {\n schema: TSchema\n}\n\n// Overload 3: Schema is provided with arbitrary TInput and TOutput\n/**\n * Creates a PowerSync collection configuration with schema validation.\n *\n * The input types are not linked to the internal SQLite table types. This can\n * give greater flexibility, e.g. by accepting rich types as input for `insert` or `update` operations.\n * An additional `deserializationSchema` is required in order to process incoming SQLite updates to the output type.\n *\n * The output types are defined by the provided schema. This schema can enforce additional\n * validation or type transforms.\n * Arbitrary output typed mutations are encoded to SQLite for persistence. We provide a basic standard\n * serialization implementation to serialize column values. Custom or advanced types require providing additional\n * serializer specifications. Partial column overrides can be supplied to `serializer`.\n *\n * @example\n * ```typescript\n * import { z } from \"zod\"\n *\n * // The PowerSync SQLite schema\n * const APP_SCHEMA = new Schema({\n * documents: new Table({\n * name: column.text,\n * // Booleans are represented as integers in SQLite\n * is_active: column.integer\n * }),\n * })\n *\n * // Advanced Zod validations.\n * // We accept boolean values as input for operations and expose Booleans in query results\n * const schema = z.object({\n * id: z.string(),\n * isActive: z.boolean(), // TInput and TOutput are boolean\n * })\n *\n * // The deserializationSchema converts the SQLite synced INTEGER (0/1) values to booleans.\n * const deserializationSchema = z.object({\n * id: z.string(),\n * isActive: z.number().nullable().transform((val) => val == null ? true : val > 0),\n * })\n *\n * const collection = createCollection(\n * powerSyncCollectionOptions({\n * database: db,\n * table: APP_SCHEMA.props.documents,\n * schema,\n * deserializationSchema,\n * })\n * )\n * ```\n */\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<\n // The input and output must have the same keys, the value types can be arbitrary\n AnyTableColumnType<TTable>,\n AnyTableColumnType<TTable>\n >,\n>(\n config: BasePowerSyncCollectionConfig<TTable, TSchema> &\n ConfigWithArbitraryCollectionTypes<TTable, TSchema>,\n): EnhancedPowerSyncCollectionConfig<\n TTable,\n InferPowerSyncOutputType<TTable, TSchema>,\n TSchema\n> & {\n utils: PowerSyncCollectionUtils<TTable>\n schema: TSchema\n}\n\n/**\n * Implementation of powerSyncCollectionOptions that handles both schema and non-schema configurations.\n */\n\nexport function powerSyncCollectionOptions<\n TTable extends Table,\n TSchema extends StandardSchemaV1<any> = never,\n>(\n config: PowerSyncCollectionConfig<TTable, TSchema>,\n): ReturnType<typeof createPowerSyncCollectionConfig<TTable, TSchema>> {\n const outputConfig = createPowerSyncCollectionConfig(config)\n return withCollectionConfigFactory(outputConfig, () =>\n createPowerSyncCollectionConfig(config),\n )\n}\n\nfunction createPowerSyncCollectionConfig<\n TTable extends Table,\n TSchema extends StandardSchemaV1<any> = never,\n>(config: PowerSyncCollectionConfig<TTable, TSchema>) {\n const {\n database,\n table,\n schema: inputSchema,\n syncBatchSize = DEFAULT_BATCH_SIZE,\n syncMode = 'eager',\n ...restConfig\n } = config\n\n const deserializationSchema =\n `deserializationSchema` in config ? config.deserializationSchema : null\n const serializer = `serializer` in config ? config.serializer : undefined\n const onDeserializationError =\n `onDeserializationError` in config\n ? config.onDeserializationError\n : undefined\n\n // The SQLite table type\n type TableType = ExtractedTable<TTable>\n\n // The collection output type\n type OutputType = InferPowerSyncOutputType<TTable, TSchema>\n\n const { viewName, trackMetadata: metadataIsTracked } = table\n\n /**\n * Deserializes data from the incoming sync stream\n */\n const deserializeSyncRow = (value: TableType): OutputType => {\n const validationSchema = deserializationSchema || schema\n const validation = validationSchema[`~standard`].validate(value)\n if (`value` in validation) {\n return validation.value\n } else if (`issues` in validation) {\n const issueMessage = `Failed to validate incoming data for ${viewName}. Issues: ${validation.issues.map((issue) => `${issue.path} - ${issue.message}`)}`\n database.logger.log({ level: LogLevels.error, message: issueMessage })\n onDeserializationError!(validation)\n throw new Error(issueMessage)\n } else {\n const unknownErrorMessage = `Unknown deserialization error for ${viewName}`\n database.logger.log({\n level: LogLevels.error,\n message: unknownErrorMessage,\n })\n onDeserializationError!({ issues: [{ message: unknownErrorMessage }] })\n throw new Error(unknownErrorMessage)\n }\n }\n\n // We can do basic runtime validations for columns if not explicit schema has been provided\n const schema = inputSchema ?? (convertTableToSchema(table) as TSchema)\n /**\n * The onInsert, onUpdate, and onDelete handlers should only return\n * after we have written the changes to TanStack DB.\n * We currently only write to TanStack DB from a diff trigger.\n * We wait for the diff trigger to observe the change,\n * and only then return from the on[X] handlers.\n * This ensures that when the transaction is reported as\n * complete to the caller, the in-memory state is already\n * consistent with the database.\n */\n const pendingOperationStore = PendingOperationStore.GLOBAL\n // Keep the tracked table unique in case of multiple tabs.\n const trackedTableName = `__${viewName}_tracking_${Math.floor(\n Math.random() * 0xffffffff,\n )\n .toString(16)\n .padStart(8, `0`)}`\n\n const transactor = new PowerSyncTransactor({\n database,\n })\n\n /**\n * \"sync\"\n * Notice that this describes the Sync between the local SQLite table\n * and the in-memory tanstack-db collection.\n */\n const sync: SyncConfig<OutputType, string> = {\n sync: (params) => {\n const { begin, write, collection, commit, markReady, markError } = params\n const abortController = new AbortController()\n\n let disposeTracking:\n ((options?: { context?: LockContext }) => Promise<void>) | null = null\n let trackingSetup: Promise<void> | null = null\n let activeTrackingDisposal: Promise<void> | null = null\n let abortTrackingDisposal: Promise<void> | null = null\n\n if (syncMode === `eager`) {\n return runEagerSync()\n } else {\n return runOnDemandSync()\n }\n\n /**\n * Disposes the current diff trigger, if one is active, and clears the\n * tracking state.\n */\n async function safelyDisposeTracking(\n context?: LockContext,\n ): Promise<void> {\n // Cleanup can race trigger creation. Wait until the disposer has been\n // published so an abort cannot strand a freshly-created trigger.\n const setup = trackingSetup\n if (setup) {\n await setup.catch(() => undefined)\n }\n\n const dispose = disposeTracking\n if (!dispose) {\n // Reconciliation may have claimed the disposer after this caller\n // observed setup. Join that invocation instead of settling early.\n await activeTrackingDisposal\n return\n }\n\n disposeTracking = null\n let disposal: Promise<void>\n try {\n disposal = Promise.resolve(dispose(context ? { context } : undefined))\n } catch (error) {\n disposal = Promise.reject(error)\n }\n activeTrackingDisposal = disposal\n try {\n await disposal\n } finally {\n if (activeTrackingDisposal === disposal) {\n activeTrackingDisposal = null\n }\n }\n }\n\n function disposeTrackingAfterAbort(): Promise<void> {\n const disposal = (abortTrackingDisposal ??= safelyDisposeTracking())\n // Abort event dispatch does not observe a listener's returned promise.\n // The eager cleanup callback awaits this same promise below.\n void disposal.catch(() => undefined)\n return disposal\n }\n\n async function establishTracking(\n options: Parameters<typeof createDiffTrigger>[0],\n appliedReceipts: Array<SyncAppliedReceipt>,\n ): Promise<void> {\n const setup = (async () => {\n const dispose = await createDiffTrigger(options, appliedReceipts)\n disposeTracking = dispose\n })()\n trackingSetup = setup\n\n try {\n await setup\n } finally {\n if (trackingSetup === setup) {\n trackingSetup = null\n }\n }\n }\n\n async function createDiffTrigger(\n options: {\n setupContext?: LockContext\n when: Record<DiffTriggerOperation, string>\n writeType: (rowId: string) => OperationType\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) => Promise<Array<TableType>>\n },\n appliedReceipts: Array<SyncAppliedReceipt>,\n ) {\n const { setupContext, when, writeType, batchQuery } = options\n\n return await database.triggers.createDiffTrigger({\n source: viewName,\n destination: trackedTableName,\n columns: table.columns.map((column) => column.name),\n setupContext,\n when,\n hooks: {\n beforeCreate: async (context) => {\n let currentBatchCount = syncBatchSize\n let cursor = 0\n while (currentBatchCount == syncBatchSize) {\n begin()\n\n const batchItems = await batchQuery(\n context,\n syncBatchSize,\n cursor,\n )\n currentBatchCount = batchItems.length\n cursor += currentBatchCount\n for (const row of batchItems) {\n write({\n type: writeType(row.id),\n value: deserializeSyncRow(row),\n })\n }\n appliedReceipts.push(commit())\n }\n database.logger.log({\n level: LogLevels.info,\n message: `Sync is ready for ${viewName} into ${trackedTableName}`,\n })\n },\n },\n })\n }\n\n async function flushDiffRecords(): Promise<void> {\n // PowerSync can notify after creating the tracking table but before its\n // create call returns. Preserve that notification until the disposer,\n // which proves the trigger is usable, has been published.\n const setup = trackingSetup\n if (setup) {\n await setup.catch(() => undefined)\n }\n if (!disposeTracking) {\n return\n }\n\n const ignoredReceipts: Array<SyncAppliedReceipt> = []\n await database\n .writeTransaction(async (context) => {\n await flushDiffRecordsWithContext(context, ignoredReceipts)\n })\n .catch((error) => {\n database.logger.log({\n level: LogLevels.error,\n message: `An error has been detected in the sync handler`,\n error,\n })\n })\n }\n\n // We can use this directly if we want to pair a flush with dispose+recreate diff trigger.\n async function flushDiffRecordsWithContext(\n context: LockContext,\n appliedReceipts: Array<SyncAppliedReceipt>,\n ): Promise<void> {\n // There is nothing to flush if no tracking table is currently active.\n if (!disposeTracking) {\n return\n }\n\n try {\n begin()\n const operations = await context.getAll<TriggerDiffRecord>(\n `SELECT * FROM ${trackedTableName} ORDER BY operation_id ASC`,\n )\n const pendingOperations: Array<PendingOperation> = []\n\n for (const op of operations) {\n const { id, operation, timestamp, value } = op\n const parsedValue = deserializeSyncRow({\n id,\n ...JSON.parse(value),\n })\n const parsedPreviousValue =\n op.operation == DiffTriggerOperation.UPDATE\n ? deserializeSyncRow({\n id,\n ...JSON.parse(op.previous_value),\n })\n : undefined\n write({\n type: mapOperation(operation),\n value: parsedValue,\n previousValue: parsedPreviousValue,\n })\n pendingOperations.push({\n id,\n operation,\n timestamp,\n tableName: viewName,\n })\n }\n\n // clear the current operations\n await context.execute(`DELETE FROM ${trackedTableName}`)\n\n const applied = commit()\n appliedReceipts.push(applied)\n // Mutation persistence is what releases the Collection's FIFO gate.\n // Confirm these local operations after the sync transaction is\n // staged; waiting for its applied receipt would deadlock the user\n // transaction that currently parks it.\n pendingOperationStore.resolvePendingFor(pendingOperations)\n } catch (error) {\n database.logger.log({\n level: LogLevels.error,\n message: `An error has been detected in the sync handler`,\n error,\n })\n }\n }\n\n // The sync function needs to be synchronous.\n async function start(afterOnChangeRegistered?: () => Promise<void>) {\n database.logger.log({\n level: LogLevels.info,\n message: `Sync is starting for ${viewName} into ${trackedTableName}`,\n })\n database.onChangeWithCallback(\n {\n onChange: async () => {\n await flushDiffRecords()\n },\n },\n {\n signal: abortController.signal,\n triggerImmediate: false,\n tables: [trackedTableName],\n },\n )\n\n await afterOnChangeRegistered?.()\n\n // If the abort controller was aborted while processing the request above\n if (abortController.signal.aborted) {\n await disposeTrackingAfterAbort()\n } else {\n abortController.signal.addEventListener(\n `abort`,\n () => {\n void disposeTrackingAfterAbort()\n },\n { once: true },\n )\n }\n }\n\n // Eager mode.\n // Registers a diff trigger for the entire table.\n function runEagerSync() {\n let onUnload: CleanupFn | void | null = null\n let onUnloadStarted = false\n\n const startup = start(async () => {\n const cleanup = await restConfig.onLoad?.()\n onUnload = cleanup\n if (abortController.signal.aborted) {\n return\n }\n\n const appliedReceipts: Array<SyncAppliedReceipt> = []\n await establishTracking(\n {\n when: {\n [DiffTriggerOperation.INSERT]: `TRUE`,\n [DiffTriggerOperation.UPDATE]: `TRUE`,\n [DiffTriggerOperation.DELETE]: `TRUE`,\n },\n writeType: (_rowId: string) => `insert`,\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) =>\n lockContext.getAll<TableType>(\n sanitizeSQL`SELECT * FROM ${viewName} LIMIT ? OFFSET ?`,\n [batchSize, cursor],\n ),\n },\n appliedReceipts,\n )\n // Readiness counts accepted rows. A mutation handler waits for it,\n // and its own persisting transaction holds the startup rows.\n await Promise.all(appliedReceipts.map(whenSyncAccepted))\n markReady()\n }).catch((error) => {\n database.logger.log({\n level: LogLevels.error,\n message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,\n error,\n })\n if (collection.status === `loading`) {\n markError(error)\n }\n })\n\n const invokeOnUnload = (): void | Promise<void> => {\n if (onUnloadStarted || !onUnload) return\n onUnloadStarted = true\n const cleanup = onUnload\n onUnload = null\n return cleanup()\n }\n\n return async () => {\n database.logger.log({\n level: LogLevels.info,\n message: `Sync has been stopped for ${viewName} into ${trackedTableName}`,\n })\n abortController.abort()\n const trackingDisposal = disposeTrackingAfterAbort()\n\n let onUnloadFailure: { error: unknown } | undefined\n const settleOnUnload = async () => {\n try {\n await invokeOnUnload()\n } catch (error) {\n onUnloadFailure ??= { error }\n }\n }\n\n // Invoke an already-installed callback in this stack, but also wait\n // for startup to publish a callback acquired concurrently with abort.\n const settlements = await Promise.allSettled([\n startup,\n trackingDisposal,\n settleOnUnload(),\n ])\n const disposalSettlement = settlements[1]\n await settleOnUnload()\n if (disposalSettlement.status === `rejected` && onUnloadFailure) {\n throw new AggregateError(\n [disposalSettlement.reason, onUnloadFailure.error],\n `PowerSync tracking disposal and load-hook cleanup both failed`,\n { cause: disposalSettlement.reason },\n )\n }\n if (disposalSettlement.status === `rejected`) {\n throw disposalSettlement.reason\n }\n if (onUnloadFailure) throw onUnloadFailure.error\n }\n }\n\n // On-demand mode.\n // Registers a diff trigger for the active WHERE expressions.\n function runOnDemandSync() {\n type DemandRecord = {\n options: LoadSubsetOptions\n active: boolean\n cleanup?: CleanupFn\n }\n type PendingRelease = {\n options: LoadSubsetOptions\n failures: number\n }\n type DemandCleanupTask = {\n order: number\n promise: Promise<unknown>\n }\n type DemandCleanupFailure = {\n order: number\n error: unknown\n }\n type DemandCleanupCollector = {\n tasks: Array<DemandCleanupTask>\n ownedTasks: Set<Promise<unknown>>\n failures: Array<DemandCleanupFailure>\n nextOrder: number\n }\n\n const demands = new Map<LoadSubsetOptions, DemandRecord>()\n const releasedSubsets = new WeakSet<LoadSubsetOptions>()\n const pendingReleases: Array<PendingRelease> = []\n const pendingDemandLoads = new Set<Promise<void>>()\n const pendingDemandCleanups = new Set<Promise<unknown>>()\n let stopped = false\n let trackingRevision = 0\n let reconciledTrackingRevision = 0\n let rebuildPromise: Promise<void> | null = null\n let drainingReleases = false\n let releaseRetryTimer: ReturnType<typeof setTimeout> | undefined\n let demandCleanupCollector: DemandCleanupCollector | null = null\n const startup = start()\n void startup.catch((error) =>\n database.logger.log({\n level: LogLevels.error,\n message: `Could not start syncing process for ${viewName} into ${trackedTableName}`,\n error,\n }),\n )\n\n const activeWhereExpressions = () =>\n Array.from(demands.values())\n .filter((demand) => demand.active)\n .map((demand) => demand.options.where)\n\n // One reconciliation owns every queued revision so callers cannot\n // settle against a stale trigger configuration.\n const reconcileTracking = async (): Promise<void> => {\n while (!stopped && reconciledTrackingRevision !== trackingRevision) {\n const revision = trackingRevision\n const isCurrent = () => !stopped && trackingRevision === revision\n const appliedReceipts: Array<SyncAppliedReceipt> = []\n\n await database.writeLock(async (ctx) => {\n if (!isCurrent()) return\n await flushDiffRecordsWithContext(ctx, appliedReceipts)\n if (!isCurrent()) return\n await safelyDisposeTracking(ctx)\n if (!isCurrent()) return\n\n const active = activeWhereExpressions()\n // Tracking was absent during an error. Positive baseline rows\n // alone cannot reveal deletes or predicate exits from that gap.\n const missing =\n collection.status === `error`\n ? new Set(collection.keys())\n : undefined\n if (active.length > 0) {\n const combinedWhere =\n active.length === 1\n ? active[0]\n : or(active[0], active[1], ...active.slice(2))\n const compiledNewData = compileSQLite(\n { where: combinedWhere },\n { jsonColumn: 'NEW.data' },\n )\n const compiledOldData = compileSQLite(\n { where: combinedWhere },\n { jsonColumn: 'OLD.data' },\n )\n const compiledView = compileSQLite({ where: combinedWhere })\n const newDataWhenClause = toInlinedWhereClause(compiledNewData)\n const oldDataWhenClause = toInlinedWhereClause(compiledOldData)\n const viewWhereClause = toInlinedWhereClause(compiledView)\n await establishTracking(\n {\n setupContext: ctx,\n when: {\n [DiffTriggerOperation.INSERT]: newDataWhenClause,\n [DiffTriggerOperation.UPDATE]: `(${newDataWhenClause}) OR (${oldDataWhenClause})`,\n [DiffTriggerOperation.DELETE]: oldDataWhenClause,\n },\n writeType: (rowId: string) =>\n collection.has(rowId) ? `update` : `insert`,\n batchQuery: (\n lockContext: LockContext,\n batchSize: number,\n cursor: number,\n ) =>\n lockContext\n .getAll<TableType>(\n `SELECT * FROM ${viewName} WHERE ${viewWhereClause} LIMIT ? OFFSET ?`,\n [batchSize, cursor],\n )\n .then((rows) => {\n for (const row of rows) missing?.delete(row.id)\n return rows\n }),\n },\n appliedReceipts,\n )\n }\n if (!isCurrent()) await safelyDisposeTracking(ctx)\n else if (missing?.size) {\n begin()\n for (const key of missing) write({ type: `delete`, key })\n appliedReceipts.push(commit())\n }\n })\n await Promise.all(appliedReceipts)\n if (isCurrent()) {\n reconciledTrackingRevision = revision\n // Replacing the trigger alone is not recovery: its baseline\n // writes must also be applied before the source is ready again.\n if (collection.status === `error`) markReady()\n }\n }\n }\n\n const rebuildTracking = async (): Promise<void> => {\n // New demand can join after reconciliation exits but before its\n // shared promise clears. Each waiter must check its revision again.\n while (!stopped && reconciledTrackingRevision !== trackingRevision) {\n await (rebuildPromise ??= reconcileTracking()\n .catch((error) => {\n // A rebuild may already have removed every active diff trigger.\n // Do not leave healthy consumers ready against a stale source.\n if (!stopped) markError(error)\n throw error\n })\n .finally(() => {\n rebuildPromise = null\n }))\n }\n }\n\n const loadSubset = async (\n options: LoadSubsetOptions,\n ): Promise<void> => {\n if (stopped) return\n // Never create a trigger that has no observer to drain its diff table.\n await startup\n if (\n // Cleanup can run while startup is pending.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n stopped ||\n releasedSubsets.has(options) ||\n options.signal?.aborted\n ) {\n return\n }\n\n const demand: DemandRecord = { options, active: false }\n demands.set(options, demand)\n try {\n const cleanup = await restConfig.onLoadSubset?.(options)\n if (cleanup) demand.cleanup = cleanup\n } catch (error) {\n demands.delete(options)\n const collector = demandCleanupCollector\n if (collector) {\n collector.failures.push({\n order: collector.nextOrder++,\n error,\n })\n }\n throw error\n }\n\n if (\n // The user hook can reenter cleanup.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n stopped ||\n releasedSubsets.has(options) ||\n options.signal?.aborted ||\n demands.get(options) !== demand\n ) {\n cleanupDemand(demand)\n return\n }\n\n demand.active = true\n trackingRevision++\n await rebuildTracking()\n }\n\n const trackLoadSubset = (options: LoadSubsetOptions): Promise<void> => {\n const task = loadSubset(options)\n pendingDemandLoads.add(task)\n const finish = () => pendingDemandLoads.delete(task)\n void task.then(finish, finish)\n return task\n }\n\n const toInlinedWhereClause = (compiled: {\n where?: string\n params: Array<unknown>\n }): string => {\n if (!compiled.where) return 'TRUE'\n const sqlParts = compiled.where.split('?')\n return sanitizeSQL(\n sqlParts as unknown as TemplateStringsArray,\n ...compiled.params,\n )\n }\n\n const reportDemandCleanupFailure = (error: unknown): void => {\n database.logger.log({\n level: LogLevels.error,\n message: `Could not clean up subset hook for ${viewName}`,\n error,\n })\n }\n\n const cleanupDemand = (demand: DemandRecord): void => {\n demands.delete(demand.options)\n const collector = demandCleanupCollector\n const order = collector ? collector.nextOrder++ : undefined\n try {\n const cleanup = demand.cleanup\n demand.cleanup = undefined\n const result: unknown = cleanup?.()\n if (!isPromiseLike(result)) return\n\n const task = Promise.resolve(result)\n pendingDemandCleanups.add(task)\n const finish = () => pendingDemandCleanups.delete(task)\n void task.then(finish, finish)\n if (collector) {\n collector.tasks.push({ order: order!, promise: task })\n collector.ownedTasks.add(task)\n void task.catch(() => undefined)\n } else {\n void task.catch((error) => {\n if (!demandCleanupCollector?.ownedTasks.has(task)) {\n reportDemandCleanupFailure(error)\n }\n })\n }\n } catch (error) {\n if (collector) {\n collector.failures.push({ order: order!, error })\n } else {\n reportDemandCleanupFailure(error)\n }\n }\n }\n\n const throwDemandCleanupFailures = (\n collector: DemandCleanupCollector,\n primaryFailures: ReadonlyArray<unknown> = [],\n ): void => {\n const errors = [\n ...primaryFailures,\n ...collector.failures\n .slice()\n .sort((left, right) => left.order - right.order)\n .map(({ error }) => error),\n ]\n if (errors.length === 1) throw errors[0]\n if (errors.length > 1) {\n throw new AggregateError(errors, `PowerSync cleanup failed`, {\n cause: errors[0],\n })\n }\n }\n\n const performPhysicalRelease = async (\n options: LoadSubsetOptions,\n ): Promise<void> => {\n const compiledDeparting = compileSQLite({ where: options.where })\n const departingWhereSQL = toInlinedWhereClause(compiledDeparting)\n let rowsToEvict: Array<{ id: string }>\n for (;;) {\n if (stopped) return\n const revision = trackingRevision\n const active = activeWhereExpressions()\n let evictionSQL: string\n if (active.length === 0) {\n evictionSQL = `SELECT id FROM ${viewName} WHERE ${departingWhereSQL}`\n } else {\n const combinedRemaining =\n active.length === 1\n ? active[0]!\n : or(active[0], active[1], ...active.slice(2))\n const compiledRemaining = compileSQLite({\n where: combinedRemaining,\n })\n const remainingWhereSQL = toInlinedWhereClause(compiledRemaining)\n evictionSQL = `SELECT id FROM ${viewName} WHERE (${departingWhereSQL}) AND NOT (${remainingWhereSQL})`\n }\n\n rowsToEvict = await database.getAll<{ id: string }>(evictionSQL)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- cleanup can run during the query\n if (stopped) return\n if (trackingRevision === revision) break\n }\n if (rowsToEvict.length > 0) {\n begin()\n for (const { id } of rowsToEvict) {\n write({ type: `delete`, key: id })\n }\n void commit()\n }\n await rebuildTracking()\n }\n\n function scheduleReleaseDrain(delay = 0): void {\n if (stopped || drainingReleases || releaseRetryTimer) return\n if (delay > 0) {\n releaseRetryTimer = setTimeout(() => {\n releaseRetryTimer = undefined\n void drainReleases()\n }, delay)\n return\n }\n void drainReleases()\n }\n\n async function drainReleases(): Promise<void> {\n if (stopped || drainingReleases) return\n drainingReleases = true\n let retryDelay = 0\n try {\n const attempts = pendingReleases.length\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- each release can reenter cleanup\n for (let index = 0; !stopped && index < attempts; index++) {\n const pending = pendingReleases.shift()!\n try {\n await performPhysicalRelease(pending.options)\n } catch (error) {\n pending.failures++\n pendingReleases.push(pending)\n const delay = Math.min(\n 1000 * 2 ** (pending.failures - 1),\n 30000,\n )\n retryDelay =\n retryDelay === 0 ? delay : Math.min(retryDelay, delay)\n database.logger.log({\n level: LogLevels.error,\n message: `Could not release subset tracking for ${viewName}; retrying`,\n error,\n })\n }\n }\n } finally {\n drainingReleases = false\n }\n if (pendingReleases.length > 0) scheduleReleaseDrain(retryDelay)\n }\n\n const unloadSubset = (options: LoadSubsetOptions): void => {\n releasedSubsets.add(options)\n const demand = demands.get(options)\n if (!demand) return\n\n const wasActive = demand.active\n if (wasActive) trackingRevision++\n cleanupDemand(demand)\n\n if (wasActive) {\n pendingReleases.push({ options, failures: 0 })\n // New work must not wait for another release's backoff.\n clearTimeout(releaseRetryTimer)\n releaseRetryTimer = undefined\n scheduleReleaseDrain()\n }\n }\n\n markReady()\n\n return {\n cleanup: () => {\n stopped = true\n clearTimeout(releaseRetryTimer)\n releaseRetryTimer = undefined\n database.logger.log({\n level: LogLevels.info,\n message: `Sync has been stopped for ${viewName} into ${trackedTableName}`,\n })\n abortController.abort()\n const trackingDisposal = disposeTrackingAfterAbort()\n const pendingLoads = [...pendingDemandLoads]\n const pendingTasks = [...pendingDemandCleanups]\n const collector: DemandCleanupCollector = {\n tasks: pendingTasks.map((promise, order) => ({ order, promise })),\n ownedTasks: new Set(pendingTasks),\n failures: [],\n nextOrder: pendingTasks.length,\n }\n demandCleanupCollector = collector\n for (const demand of demands.values()) {\n cleanupDemand(demand)\n }\n pendingReleases.length = 0\n\n return (async () => {\n const ownershipSettlements = await Promise.allSettled([\n trackingDisposal,\n ...pendingLoads,\n ])\n const disposalSettlement = ownershipSettlements[0]\n\n let settledTasks = 0\n while (settledTasks < collector.tasks.length) {\n const tasks = collector.tasks.slice(settledTasks)\n settledTasks = collector.tasks.length\n const outcomes = await Promise.allSettled(\n tasks.map(({ promise }) => promise),\n )\n for (const [index, outcome] of outcomes.entries()) {\n if (outcome.status === `rejected`) {\n collector.failures.push({\n order: tasks[index]!.order,\n error: outcome.reason,\n })\n }\n }\n }\n throwDemandCleanupFailures(\n collector,\n disposalSettlement.status === `rejected`\n ? [disposalSettlement.reason]\n : [],\n )\n })().finally(() => {\n if (demandCleanupCollector === collector) {\n demandCleanupCollector = null\n }\n })\n },\n loadSubset: trackLoadSubset,\n unloadSubset,\n }\n }\n },\n // Expose the getSyncMetadata function\n getSyncMetadata: undefined,\n }\n\n const getKey = (record: OutputType) => asPowerSyncRecord(record).id\n\n // Keep adapter hooks and serialization options out of the core config.\n const {\n onLoad: _onLoad,\n onLoadSubset: _onLoadSubset,\n deserializationSchema: _deserializationSchema,\n serializer: _serializer,\n onDeserializationError: _onDeserializationError,\n ...collectionConfig\n } = {\n deserializationSchema,\n serializer,\n onDeserializationError,\n ...restConfig,\n }\n\n const outputConfig: EnhancedPowerSyncCollectionConfig<\n TTable,\n OutputType,\n TSchema\n > = {\n ...collectionConfig,\n schema,\n getKey,\n // Syncing should start immediately since we need to monitor the changes for mutations\n startSync: true,\n syncMode,\n sync,\n onInsert: async (params) => {\n // The transaction here should only ever contain a single insert mutation\n return await transactor.applyTransaction(params.transaction)\n },\n onUpdate: async (params) => {\n // The transaction here should only ever contain a single update mutation\n return await transactor.applyTransaction(params.transaction)\n },\n onDelete: async (params) => {\n // The transaction here should only ever contain a single delete mutation\n return await transactor.applyTransaction(params.transaction)\n },\n utils: {\n getMeta: () => ({\n tableName: viewName,\n trackedTableName,\n metadataIsTracked,\n serializeValue: (value) =>\n serializeForSQLite<TTable>(value, table, serializer),\n }),\n },\n }\n return outputConfig\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/powersync-db-collection",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "PowerSync collection for TanStack DB",
|
|
5
5
|
"author": "POWERSYNC",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@tanstack/store": "^0.9.2",
|
|
51
51
|
"debug": "^4.4.3",
|
|
52
52
|
"p-defer": "^4.0.1",
|
|
53
|
-
"@tanstack/db": "0.
|
|
53
|
+
"@tanstack/db": "0.12.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@powersync/common": "^2.0.0"
|
package/src/powersync.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DiffTriggerOperation, LogLevels, sanitizeSQL } from '@powersync/common'
|
|
2
|
-
import { or, withCollectionConfigFactory } from '@tanstack/db'
|
|
2
|
+
import { or, whenSyncAccepted, withCollectionConfigFactory } from '@tanstack/db'
|
|
3
3
|
import { compileSQLite } from './sqlite-compiler'
|
|
4
4
|
import { PendingOperationStore } from './PendingOperationStore'
|
|
5
5
|
import { PowerSyncTransactor } from './PowerSyncTransactor'
|
|
@@ -329,8 +329,7 @@ function createPowerSyncCollectionConfig<
|
|
|
329
329
|
const abortController = new AbortController()
|
|
330
330
|
|
|
331
331
|
let disposeTracking:
|
|
332
|
-
|
|
333
|
-
| null = null
|
|
332
|
+
((options?: { context?: LockContext }) => Promise<void>) | null = null
|
|
334
333
|
let trackingSetup: Promise<void> | null = null
|
|
335
334
|
let activeTrackingDisposal: Promise<void> | null = null
|
|
336
335
|
let abortTrackingDisposal: Promise<void> | null = null
|
|
@@ -410,7 +409,6 @@ function createPowerSyncCollectionConfig<
|
|
|
410
409
|
async function createDiffTrigger(
|
|
411
410
|
options: {
|
|
412
411
|
setupContext?: LockContext
|
|
413
|
-
immediate?: boolean
|
|
414
412
|
when: Record<DiffTriggerOperation, string>
|
|
415
413
|
writeType: (rowId: string) => OperationType
|
|
416
414
|
batchQuery: (
|
|
@@ -421,7 +419,7 @@ function createPowerSyncCollectionConfig<
|
|
|
421
419
|
},
|
|
422
420
|
appliedReceipts: Array<SyncAppliedReceipt>,
|
|
423
421
|
) {
|
|
424
|
-
const { setupContext,
|
|
422
|
+
const { setupContext, when, writeType, batchQuery } = options
|
|
425
423
|
|
|
426
424
|
return await database.triggers.createDiffTrigger({
|
|
427
425
|
source: viewName,
|
|
@@ -434,7 +432,7 @@ function createPowerSyncCollectionConfig<
|
|
|
434
432
|
let currentBatchCount = syncBatchSize
|
|
435
433
|
let cursor = 0
|
|
436
434
|
while (currentBatchCount == syncBatchSize) {
|
|
437
|
-
begin(
|
|
435
|
+
begin()
|
|
438
436
|
|
|
439
437
|
const batchItems = await batchQuery(
|
|
440
438
|
context,
|
|
@@ -599,9 +597,6 @@ function createPowerSyncCollectionConfig<
|
|
|
599
597
|
const appliedReceipts: Array<SyncAppliedReceipt> = []
|
|
600
598
|
await establishTracking(
|
|
601
599
|
{
|
|
602
|
-
// Initial eager hydration must make the source usable before
|
|
603
|
-
// PowerSync can persist a mutation queued during startup.
|
|
604
|
-
immediate: true,
|
|
605
600
|
when: {
|
|
606
601
|
[DiffTriggerOperation.INSERT]: `TRUE`,
|
|
607
602
|
[DiffTriggerOperation.UPDATE]: `TRUE`,
|
|
@@ -620,7 +615,9 @@ function createPowerSyncCollectionConfig<
|
|
|
620
615
|
},
|
|
621
616
|
appliedReceipts,
|
|
622
617
|
)
|
|
623
|
-
|
|
618
|
+
// Readiness counts accepted rows. A mutation handler waits for it,
|
|
619
|
+
// and its own persisting transaction holds the startup rows.
|
|
620
|
+
await Promise.all(appliedReceipts.map(whenSyncAccepted))
|
|
624
621
|
markReady()
|
|
625
622
|
}).catch((error) => {
|
|
626
623
|
database.logger.log({
|
|
@@ -1142,12 +1139,27 @@ function createPowerSyncCollectionConfig<
|
|
|
1142
1139
|
|
|
1143
1140
|
const getKey = (record: OutputType) => asPowerSyncRecord(record).id
|
|
1144
1141
|
|
|
1142
|
+
// Keep adapter hooks and serialization options out of the core config.
|
|
1143
|
+
const {
|
|
1144
|
+
onLoad: _onLoad,
|
|
1145
|
+
onLoadSubset: _onLoadSubset,
|
|
1146
|
+
deserializationSchema: _deserializationSchema,
|
|
1147
|
+
serializer: _serializer,
|
|
1148
|
+
onDeserializationError: _onDeserializationError,
|
|
1149
|
+
...collectionConfig
|
|
1150
|
+
} = {
|
|
1151
|
+
deserializationSchema,
|
|
1152
|
+
serializer,
|
|
1153
|
+
onDeserializationError,
|
|
1154
|
+
...restConfig,
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1145
1157
|
const outputConfig: EnhancedPowerSyncCollectionConfig<
|
|
1146
1158
|
TTable,
|
|
1147
1159
|
OutputType,
|
|
1148
1160
|
TSchema
|
|
1149
1161
|
> = {
|
|
1150
|
-
...
|
|
1162
|
+
...collectionConfig,
|
|
1151
1163
|
schema,
|
|
1152
1164
|
getKey,
|
|
1153
1165
|
// Syncing should start immediately since we need to monitor the changes for mutations
|