@tanstack/db 0.5.33 → 0.6.1
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/collection/change-events.cjs.map +1 -1
- package/dist/cjs/collection/change-events.d.cts +3 -2
- package/dist/cjs/collection/changes.cjs +13 -4
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/changes.d.cts +10 -1
- package/dist/cjs/collection/cleanup-queue.cjs +89 -0
- package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
- package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
- package/dist/cjs/collection/events.cjs +14 -0
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/events.d.cts +39 -1
- package/dist/cjs/collection/index.cjs +66 -28
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +49 -36
- package/dist/cjs/collection/indexes.cjs +211 -62
- package/dist/cjs/collection/indexes.cjs.map +1 -1
- package/dist/cjs/collection/indexes.d.cts +27 -17
- package/dist/cjs/collection/lifecycle.cjs +5 -22
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/lifecycle.d.cts +0 -1
- package/dist/cjs/collection/mutations.cjs +18 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/mutations.d.cts +1 -0
- package/dist/cjs/collection/state.cjs +381 -53
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +65 -1
- package/dist/cjs/collection/subscription.cjs +6 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +4 -0
- package/dist/cjs/collection/sync.cjs +108 -1
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/collection/sync.d.cts +2 -0
- package/dist/cjs/collection/transaction-metadata.cjs +5 -0
- package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
- package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
- package/dist/cjs/errors.cjs +8 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/index.cjs +22 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +11 -3
- package/dist/cjs/indexes/auto-index.cjs +13 -6
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.cjs +0 -3
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -6
- package/dist/cjs/indexes/basic-index.cjs +361 -0
- package/dist/cjs/indexes/basic-index.cjs.map +1 -0
- package/dist/cjs/indexes/basic-index.d.cts +102 -0
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/indexes/index-options.d.cts +8 -9
- package/dist/cjs/indexes/index-registry.cjs +89 -0
- package/dist/cjs/indexes/index-registry.cjs.map +1 -0
- package/dist/cjs/indexes/index-registry.d.cts +61 -0
- package/dist/cjs/local-only.cjs +5 -0
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +27 -11
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +25 -3
- package/dist/cjs/query/builder/index.cjs +200 -39
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +4 -3
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
- package/dist/cjs/query/builder/types.d.cts +84 -19
- package/dist/cjs/query/compiler/evaluators.cjs +51 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +100 -28
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.d.cts +4 -2
- package/dist/cjs/query/compiler/index.cjs +283 -11
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +30 -2
- package/dist/cjs/query/compiler/order-by.cjs +29 -10
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -1
- package/dist/cjs/query/compiler/select.cjs +8 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/index.d.cts +2 -1
- package/dist/cjs/query/ir.cjs +18 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +21 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +501 -5
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
- package/dist/cjs/query/live/types.d.cts +3 -3
- package/dist/cjs/query/live/utils.cjs +43 -3
- package/dist/cjs/query/live/utils.cjs.map +1 -1
- package/dist/cjs/query/live/utils.d.cts +1 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +9 -6
- package/dist/cjs/query/query-once.cjs.map +1 -1
- package/dist/cjs/query/query-once.d.cts +7 -5
- package/dist/cjs/query/subset-dedupe.cjs +9 -3
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/cjs/types.d.cts +42 -8
- package/dist/cjs/utils/array-utils.cjs +27 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +16 -0
- package/dist/cjs/utils/comparison.cjs +11 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.cjs +4 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils.cjs +7 -9
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/cjs/virtual-props.cjs +33 -0
- package/dist/cjs/virtual-props.cjs.map +1 -0
- package/dist/cjs/virtual-props.d.cts +196 -0
- package/dist/esm/collection/change-events.d.ts +3 -2
- package/dist/esm/collection/change-events.js.map +1 -1
- package/dist/esm/collection/changes.d.ts +10 -1
- package/dist/esm/collection/changes.js +13 -4
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/cleanup-queue.d.ts +30 -0
- package/dist/esm/collection/cleanup-queue.js +89 -0
- package/dist/esm/collection/cleanup-queue.js.map +1 -0
- package/dist/esm/collection/events.d.ts +39 -1
- package/dist/esm/collection/events.js +14 -0
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.d.ts +49 -36
- package/dist/esm/collection/index.js +67 -29
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/indexes.d.ts +27 -17
- package/dist/esm/collection/indexes.js +211 -62
- package/dist/esm/collection/indexes.js.map +1 -1
- package/dist/esm/collection/lifecycle.d.ts +0 -1
- package/dist/esm/collection/lifecycle.js +5 -22
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/mutations.d.ts +1 -0
- package/dist/esm/collection/mutations.js +18 -0
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.d.ts +65 -1
- package/dist/esm/collection/state.js +381 -53
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +4 -0
- package/dist/esm/collection/subscription.js +6 -0
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.d.ts +2 -0
- package/dist/esm/collection/sync.js +108 -1
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/collection/transaction-metadata.d.ts +1 -0
- package/dist/esm/collection/transaction-metadata.js +5 -0
- package/dist/esm/collection/transaction-metadata.js.map +1 -0
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +8 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +11 -3
- package/dist/esm/index.js +25 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.js +13 -6
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -6
- package/dist/esm/indexes/base-index.js +1 -4
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/basic-index.d.ts +102 -0
- package/dist/esm/indexes/basic-index.js +361 -0
- package/dist/esm/indexes/basic-index.js.map +1 -0
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/index-options.d.ts +8 -9
- package/dist/esm/indexes/index-registry.d.ts +61 -0
- package/dist/esm/indexes/index-registry.js +89 -0
- package/dist/esm/indexes/index-registry.js.map +1 -0
- package/dist/esm/local-only.js +5 -0
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +25 -3
- package/dist/esm/query/builder/functions.js +27 -11
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +4 -3
- package/dist/esm/query/builder/index.js +201 -40
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +84 -19
- package/dist/esm/query/compiler/evaluators.js +51 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.d.ts +4 -2
- package/dist/esm/query/compiler/group-by.js +101 -29
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +30 -2
- package/dist/esm/query/compiler/index.js +285 -13
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js +30 -11
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js +8 -0
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/index.d.ts +2 -1
- package/dist/esm/query/ir.d.ts +21 -1
- package/dist/esm/query/ir.js +18 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
- package/dist/esm/query/live/collection-config-builder.js +503 -7
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/types.d.ts +3 -3
- package/dist/esm/query/live/utils.d.ts +1 -0
- package/dist/esm/query/live/utils.js +43 -3
- package/dist/esm/query/live/utils.js.map +1 -1
- package/dist/esm/query/live-query-collection.d.ts +9 -6
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/query-once.d.ts +7 -5
- package/dist/esm/query/query-once.js.map +1 -1
- package/dist/esm/query/subset-dedupe.js +9 -3
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/dist/esm/types.d.ts +42 -8
- package/dist/esm/utils/array-utils.d.ts +16 -0
- package/dist/esm/utils/array-utils.js +27 -0
- package/dist/esm/utils/array-utils.js.map +1 -0
- package/dist/esm/utils/comparison.js +11 -0
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/index-optimization.js +4 -0
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +7 -9
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/virtual-props.d.ts +196 -0
- package/dist/esm/virtual-props.js +33 -0
- package/dist/esm/virtual-props.js.map +1 -0
- package/package.json +4 -3
- package/skills/db-core/SKILL.md +4 -2
- package/skills/db-core/collection-setup/SKILL.md +30 -11
- package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
- package/skills/db-core/collection-setup/references/powersync-adapter.md +4 -0
- package/skills/db-core/collection-setup/references/query-adapter.md +32 -0
- package/skills/db-core/custom-adapter/SKILL.md +58 -9
- package/skills/db-core/live-queries/SKILL.md +162 -2
- package/skills/db-core/mutations-optimistic/SKILL.md +1 -1
- package/skills/db-core/persistence/SKILL.md +241 -0
- package/skills/meta-framework/SKILL.md +1 -1
- package/src/collection/change-events.ts +13 -9
- package/src/collection/changes.ts +30 -7
- package/src/collection/cleanup-queue.ts +105 -0
- package/src/collection/events.ts +65 -0
- package/src/collection/index.ts +110 -45
- package/src/collection/indexes.ts +283 -76
- package/src/collection/lifecycle.ts +5 -26
- package/src/collection/mutations.ts +21 -0
- package/src/collection/state.ts +545 -71
- package/src/collection/subscription.ts +7 -0
- package/src/collection/sync.ts +137 -0
- package/src/collection/transaction-metadata.ts +1 -0
- package/src/errors.ts +9 -0
- package/src/index.ts +46 -3
- package/src/indexes/auto-index.ts +18 -8
- package/src/indexes/base-index.ts +2 -10
- package/src/indexes/basic-index.ts +507 -0
- package/src/indexes/btree-index.ts +1 -1
- package/src/indexes/index-options.ts +17 -37
- package/src/indexes/index-registry.ts +174 -0
- package/src/local-only.ts +7 -0
- package/src/query/builder/functions.ts +84 -7
- package/src/query/builder/index.ts +329 -9
- package/src/query/builder/ref-proxy.ts +22 -4
- package/src/query/builder/types.ts +257 -62
- package/src/query/compiler/evaluators.ts +57 -0
- package/src/query/compiler/group-by.ts +156 -35
- package/src/query/compiler/index.ts +445 -15
- package/src/query/compiler/order-by.ts +51 -12
- package/src/query/compiler/select.ts +9 -0
- package/src/query/index.ts +7 -0
- package/src/query/ir.ts +23 -2
- package/src/query/live/collection-config-builder.ts +809 -9
- package/src/query/live/types.ts +10 -4
- package/src/query/live/utils.ts +64 -3
- package/src/query/live-query-collection.ts +43 -18
- package/src/query/query-once.ts +31 -12
- package/src/query/subset-dedupe.ts +11 -7
- package/src/types.ts +49 -9
- package/src/utils/array-utils.ts +49 -0
- package/src/utils/comparison.ts +14 -0
- package/src/utils/index-optimization.ts +4 -0
- package/src/utils.ts +12 -9
- package/src/virtual-props.ts +282 -0
- package/dist/cjs/indexes/lazy-index.cjs +0 -190
- package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
- package/dist/cjs/indexes/lazy-index.d.cts +0 -96
- package/dist/esm/indexes/lazy-index.d.ts +0 -96
- package/dist/esm/indexes/lazy-index.js +0 -190
- package/dist/esm/indexes/lazy-index.js.map +0 -1
- package/src/indexes/lazy-index.ts +0 -251
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.cjs","sources":["../../../src/collection/state.ts"],"sourcesContent":["import { deepEquals } from '../utils'\nimport { SortedMap } from '../SortedMap'\nimport type { Transaction } from '../transactions'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type {\n ChangeMessage,\n CollectionConfig,\n OptimisticChangeMessage,\n} from '../types'\nimport type { CollectionImpl } from './index.js'\nimport type { CollectionLifecycleManager } from './lifecycle'\nimport type { CollectionChangesManager } from './changes'\nimport type { CollectionIndexesManager } from './indexes'\nimport type { CollectionEventsManager } from './events'\n\ninterface PendingSyncedTransaction<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n> {\n committed: boolean\n operations: Array<OptimisticChangeMessage<T>>\n truncate?: boolean\n deletedKeys: Set<string | number>\n optimisticSnapshot?: {\n upserts: Map<TKey, T>\n deletes: Set<TKey>\n }\n /**\n * When true, this transaction should be processed immediately even if there\n * are persisting user transactions. Used by manual write operations (writeInsert,\n * writeUpdate, writeDelete, writeUpsert) which need synchronous updates to syncedData.\n */\n immediate?: boolean\n}\n\nexport class CollectionStateManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n public config!: CollectionConfig<TOutput, TKey, TSchema>\n public collection!: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n public lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n public changes!: CollectionChangesManager<TOutput, TKey, TSchema, TInput>\n public indexes!: CollectionIndexesManager<TOutput, TKey, TSchema, TInput>\n private _events!: CollectionEventsManager\n\n // Core state - make public for testing\n public transactions: SortedMap<string, Transaction<any>>\n public pendingSyncedTransactions: Array<\n PendingSyncedTransaction<TOutput, TKey>\n > = []\n public syncedData: SortedMap<TKey, TOutput>\n public syncedMetadata = new Map<TKey, unknown>()\n\n // Optimistic state tracking - make public for testing\n public optimisticUpserts = new Map<TKey, TOutput>()\n public optimisticDeletes = new Set<TKey>()\n\n // Cached size for performance\n public size = 0\n\n // State used for computing the change events\n public syncedKeys = new Set<TKey>()\n public preSyncVisibleState = new Map<TKey, TOutput>()\n public recentlySyncedKeys = new Set<TKey>()\n public hasReceivedFirstCommit = false\n public isCommittingSyncTransactions = false\n\n /**\n * Creates a new CollectionState manager\n */\n constructor(config: CollectionConfig<TOutput, TKey, TSchema>) {\n this.config = config\n this.transactions = new SortedMap<string, Transaction<any>>((a, b) =>\n a.compareCreatedAt(b),\n )\n\n // Set up data storage - always use SortedMap for deterministic iteration.\n // If a custom compare function is provided, use it; otherwise entries are sorted by key only.\n this.syncedData = new SortedMap<TKey, TOutput>(config.compare)\n }\n\n setDeps(deps: {\n collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n changes: CollectionChangesManager<TOutput, TKey, TSchema, TInput>\n indexes: CollectionIndexesManager<TOutput, TKey, TSchema, TInput>\n events: CollectionEventsManager\n }) {\n this.collection = deps.collection\n this.lifecycle = deps.lifecycle\n this.changes = deps.changes\n this.indexes = deps.indexes\n this._events = deps.events\n }\n\n /**\n * Get the current value for a key (virtual derived state)\n */\n public get(key: TKey): TOutput | undefined {\n const { optimisticDeletes, optimisticUpserts, syncedData } = this\n // Check if optimistically deleted\n if (optimisticDeletes.has(key)) {\n return undefined\n }\n\n // Check optimistic upserts first\n if (optimisticUpserts.has(key)) {\n return optimisticUpserts.get(key)\n }\n\n // Fall back to synced data\n return syncedData.get(key)\n }\n\n /**\n * Check if a key exists in the collection (virtual derived state)\n */\n public has(key: TKey): boolean {\n const { optimisticDeletes, optimisticUpserts, syncedData } = this\n // Check if optimistically deleted\n if (optimisticDeletes.has(key)) {\n return false\n }\n\n // Check optimistic upserts first\n if (optimisticUpserts.has(key)) {\n return true\n }\n\n // Fall back to synced data\n return syncedData.has(key)\n }\n\n /**\n * Get all keys (virtual derived state)\n */\n public *keys(): IterableIterator<TKey> {\n const { syncedData, optimisticDeletes, optimisticUpserts } = this\n // Yield keys from synced data, skipping any that are deleted.\n for (const key of syncedData.keys()) {\n if (!optimisticDeletes.has(key)) {\n yield key\n }\n }\n // Yield keys from upserts that were not already in synced data.\n for (const key of optimisticUpserts.keys()) {\n if (!syncedData.has(key) && !optimisticDeletes.has(key)) {\n // The optimisticDeletes check is technically redundant if inserts/updates always remove from deletes,\n // but it's safer to keep it.\n yield key\n }\n }\n }\n\n /**\n * Get all values (virtual derived state)\n */\n public *values(): IterableIterator<TOutput> {\n for (const key of this.keys()) {\n const value = this.get(key)\n if (value !== undefined) {\n yield value\n }\n }\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *entries(): IterableIterator<[TKey, TOutput]> {\n for (const key of this.keys()) {\n const value = this.get(key)\n if (value !== undefined) {\n yield [key, value]\n }\n }\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *[Symbol.iterator](): IterableIterator<[TKey, TOutput]> {\n for (const [key, value] of this.entries()) {\n yield [key, value]\n }\n }\n\n /**\n * Execute a callback for each entry in the collection\n */\n public forEach(\n callbackfn: (value: TOutput, key: TKey, index: number) => void,\n ): void {\n let index = 0\n for (const [key, value] of this.entries()) {\n callbackfn(value, key, index++)\n }\n }\n\n /**\n * Create a new array with the results of calling a function for each entry in the collection\n */\n public map<U>(\n callbackfn: (value: TOutput, key: TKey, index: number) => U,\n ): Array<U> {\n const result: Array<U> = []\n let index = 0\n for (const [key, value] of this.entries()) {\n result.push(callbackfn(value, key, index++))\n }\n return result\n }\n\n /**\n * Check if the given collection is this collection\n * @param collection The collection to check\n * @returns True if the given collection is this collection, false otherwise\n */\n private isThisCollection(\n collection: CollectionImpl<any, any, any, any, any>,\n ): boolean {\n return collection === this.collection\n }\n\n /**\n * Recompute optimistic state from active transactions\n */\n public recomputeOptimisticState(\n triggeredByUserAction: boolean = false,\n ): void {\n // Skip redundant recalculations when we're in the middle of committing sync transactions\n // While the sync pipeline is replaying a large batch we still want to honour\n // fresh optimistic mutations from the UI. Only skip recompute for the\n // internal sync-driven redraws; user-triggered work (triggeredByUserAction)\n // must run so live queries stay responsive during long commits.\n if (this.isCommittingSyncTransactions && !triggeredByUserAction) {\n return\n }\n\n const previousState = new Map(this.optimisticUpserts)\n const previousDeletes = new Set(this.optimisticDeletes)\n\n // Clear current optimistic state\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n\n const activeTransactions: Array<Transaction<any>> = []\n\n for (const transaction of this.transactions.values()) {\n if (![`completed`, `failed`].includes(transaction.state)) {\n activeTransactions.push(transaction)\n }\n }\n\n // Apply active transactions only (completed transactions are handled by sync operations)\n for (const transaction of activeTransactions) {\n for (const mutation of transaction.mutations) {\n if (this.isThisCollection(mutation.collection) && mutation.optimistic) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.optimisticUpserts.set(\n mutation.key,\n mutation.modified as TOutput,\n )\n this.optimisticDeletes.delete(mutation.key)\n break\n case `delete`:\n this.optimisticUpserts.delete(mutation.key)\n this.optimisticDeletes.add(mutation.key)\n break\n }\n }\n }\n }\n\n // Update cached size\n this.size = this.calculateSize()\n\n // Collect events for changes\n const events: Array<ChangeMessage<TOutput, TKey>> = []\n this.collectOptimisticChanges(previousState, previousDeletes, events)\n\n // Filter out events for recently synced keys to prevent duplicates\n // BUT: Only filter out events that are actually from sync operations\n // New user transactions should NOT be filtered even if the key was recently synced\n const filteredEventsBySyncStatus = events.filter((event) => {\n if (!this.recentlySyncedKeys.has(event.key)) {\n return true // Key not recently synced, allow event through\n }\n\n // Key was recently synced - allow if this is a user-triggered action\n if (triggeredByUserAction) {\n return true\n }\n\n // Otherwise filter out duplicate sync events\n return false\n })\n\n // Filter out redundant delete events if there are pending sync transactions\n // that will immediately restore the same data, but only for completed transactions\n // IMPORTANT: Skip complex filtering for user-triggered actions to prevent UI blocking\n if (this.pendingSyncedTransactions.length > 0 && !triggeredByUserAction) {\n const pendingSyncKeys = new Set<TKey>()\n\n // Collect keys from pending sync operations\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n pendingSyncKeys.add(operation.key as TKey)\n }\n }\n\n // Only filter out delete events for keys that:\n // 1. Have pending sync operations AND\n // 2. Are from completed transactions (being cleaned up)\n const filteredEvents = filteredEventsBySyncStatus.filter((event) => {\n if (event.type === `delete` && pendingSyncKeys.has(event.key)) {\n // Check if this delete is from clearing optimistic state of completed transactions\n // We can infer this by checking if we have no remaining optimistic mutations for this key\n const hasActiveOptimisticMutation = activeTransactions.some((tx) =>\n tx.mutations.some(\n (m) => this.isThisCollection(m.collection) && m.key === event.key,\n ),\n )\n\n if (!hasActiveOptimisticMutation) {\n return false // Skip this delete event as sync will restore the data\n }\n }\n return true\n })\n\n // Update indexes for the filtered events\n if (filteredEvents.length > 0) {\n this.indexes.updateIndexes(filteredEvents)\n }\n this.changes.emitEvents(filteredEvents, triggeredByUserAction)\n } else {\n // Update indexes for all events\n if (filteredEventsBySyncStatus.length > 0) {\n this.indexes.updateIndexes(filteredEventsBySyncStatus)\n }\n // Emit all events if no pending sync transactions\n this.changes.emitEvents(filteredEventsBySyncStatus, triggeredByUserAction)\n }\n }\n\n /**\n * Calculate the current size based on synced data and optimistic changes\n */\n private calculateSize(): number {\n const syncedSize = this.syncedData.size\n const deletesFromSynced = Array.from(this.optimisticDeletes).filter(\n (key) => this.syncedData.has(key) && !this.optimisticUpserts.has(key),\n ).length\n const upsertsNotInSynced = Array.from(this.optimisticUpserts.keys()).filter(\n (key) => !this.syncedData.has(key),\n ).length\n\n return syncedSize - deletesFromSynced + upsertsNotInSynced\n }\n\n /**\n * Collect events for optimistic changes\n */\n private collectOptimisticChanges(\n previousUpserts: Map<TKey, TOutput>,\n previousDeletes: Set<TKey>,\n events: Array<ChangeMessage<TOutput, TKey>>,\n ): void {\n const allKeys = new Set([\n ...previousUpserts.keys(),\n ...this.optimisticUpserts.keys(),\n ...previousDeletes,\n ...this.optimisticDeletes,\n ])\n\n for (const key of allKeys) {\n const currentValue = this.get(key)\n const previousValue = this.getPreviousValue(\n key,\n previousUpserts,\n previousDeletes,\n )\n\n if (previousValue !== undefined && currentValue === undefined) {\n events.push({ type: `delete`, key, value: previousValue })\n } else if (previousValue === undefined && currentValue !== undefined) {\n events.push({ type: `insert`, key, value: currentValue })\n } else if (\n previousValue !== undefined &&\n currentValue !== undefined &&\n previousValue !== currentValue\n ) {\n events.push({\n type: `update`,\n key,\n value: currentValue,\n previousValue,\n })\n }\n }\n }\n\n /**\n * Get the previous value for a key given previous optimistic state\n */\n private getPreviousValue(\n key: TKey,\n previousUpserts: Map<TKey, TOutput>,\n previousDeletes: Set<TKey>,\n ): TOutput | undefined {\n if (previousDeletes.has(key)) {\n return undefined\n }\n if (previousUpserts.has(key)) {\n return previousUpserts.get(key)\n }\n return this.syncedData.get(key)\n }\n\n /**\n * Attempts to commit pending synced transactions if there are no active transactions\n * This method processes operations from pending transactions and applies them to the synced data\n */\n commitPendingTransactions = () => {\n // Check if there are any persisting transaction\n let hasPersistingTransaction = false\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `persisting`) {\n hasPersistingTransaction = true\n break\n }\n }\n\n // pending synced transactions could be either `committed` or still open.\n // we only want to process `committed` transactions here\n const {\n committedSyncedTransactions,\n uncommittedSyncedTransactions,\n hasTruncateSync,\n hasImmediateSync,\n } = this.pendingSyncedTransactions.reduce(\n (acc, t) => {\n if (t.committed) {\n acc.committedSyncedTransactions.push(t)\n if (t.truncate) {\n acc.hasTruncateSync = true\n }\n if (t.immediate) {\n acc.hasImmediateSync = true\n }\n } else {\n acc.uncommittedSyncedTransactions.push(t)\n }\n return acc\n },\n {\n committedSyncedTransactions: [] as Array<\n PendingSyncedTransaction<TOutput, TKey>\n >,\n uncommittedSyncedTransactions: [] as Array<\n PendingSyncedTransaction<TOutput, TKey>\n >,\n hasTruncateSync: false,\n hasImmediateSync: false,\n },\n )\n\n // Process committed transactions if:\n // 1. No persisting user transaction (normal sync flow), OR\n // 2. There's a truncate operation (must be processed immediately), OR\n // 3. There's an immediate transaction (manual writes must be processed synchronously)\n //\n // Note: When hasImmediateSync or hasTruncateSync is true, we process ALL committed\n // sync transactions (not just the immediate/truncate ones). This is intentional for\n // ordering correctness: if we only processed the immediate transaction, earlier\n // non-immediate transactions would be applied later and could overwrite newer state.\n // Processing all committed transactions together preserves causal ordering.\n if (!hasPersistingTransaction || hasTruncateSync || hasImmediateSync) {\n // Set flag to prevent redundant optimistic state recalculations\n this.isCommittingSyncTransactions = true\n\n // Get the optimistic snapshot from the truncate transaction (captured when truncate() was called)\n const truncateOptimisticSnapshot = hasTruncateSync\n ? committedSyncedTransactions.find((t) => t.truncate)\n ?.optimisticSnapshot\n : null\n\n // First collect all keys that will be affected by sync operations\n const changedKeys = new Set<TKey>()\n for (const transaction of committedSyncedTransactions) {\n for (const operation of transaction.operations) {\n changedKeys.add(operation.key as TKey)\n }\n }\n\n // Use pre-captured state if available (from optimistic scenarios),\n // otherwise capture current state (for pure sync scenarios)\n let currentVisibleState = this.preSyncVisibleState\n if (currentVisibleState.size === 0) {\n // No pre-captured state, capture it now for pure sync operations\n currentVisibleState = new Map<TKey, TOutput>()\n for (const key of changedKeys) {\n const currentValue = this.get(key)\n if (currentValue !== undefined) {\n currentVisibleState.set(key, currentValue)\n }\n }\n }\n\n const events: Array<ChangeMessage<TOutput, TKey>> = []\n const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`\n\n for (const transaction of committedSyncedTransactions) {\n // Handle truncate operations first\n if (transaction.truncate) {\n // TRUNCATE PHASE\n // 1) Emit a delete for every visible key (synced + optimistic) so downstream listeners/indexes\n // observe a clear-before-rebuild. We intentionally skip keys already in\n // optimisticDeletes because their delete was previously emitted by the user.\n // Use the snapshot to ensure we emit deletes for all items that existed at truncate start.\n const visibleKeys = new Set([\n ...this.syncedData.keys(),\n ...(truncateOptimisticSnapshot?.upserts.keys() || []),\n ])\n for (const key of visibleKeys) {\n if (truncateOptimisticSnapshot?.deletes.has(key)) continue\n const previousValue =\n truncateOptimisticSnapshot?.upserts.get(key) ||\n this.syncedData.get(key)\n if (previousValue !== undefined) {\n events.push({ type: `delete`, key, value: previousValue })\n }\n }\n\n // 2) Clear the authoritative synced base. Subsequent server ops in this\n // same commit will rebuild the base atomically.\n this.syncedData.clear()\n this.syncedMetadata.clear()\n this.syncedKeys.clear()\n\n // 3) Clear currentVisibleState for truncated keys to ensure subsequent operations\n // are compared against the post-truncate state (undefined) rather than pre-truncate state\n // This ensures that re-inserted keys are emitted as INSERT events, not UPDATE events\n for (const key of changedKeys) {\n currentVisibleState.delete(key)\n }\n\n // 4) Emit truncate event so subscriptions can reset their cursor tracking state\n this._events.emit(`truncate`, {\n type: `truncate`,\n collection: this.collection,\n })\n }\n\n for (const operation of transaction.operations) {\n const key = operation.key as TKey\n this.syncedKeys.add(key)\n\n // Update metadata\n switch (operation.type) {\n case `insert`:\n this.syncedMetadata.set(key, operation.metadata)\n break\n case `update`:\n this.syncedMetadata.set(\n key,\n Object.assign(\n {},\n this.syncedMetadata.get(key),\n operation.metadata,\n ),\n )\n break\n case `delete`:\n this.syncedMetadata.delete(key)\n break\n }\n\n // Update synced data\n switch (operation.type) {\n case `insert`:\n this.syncedData.set(key, operation.value)\n break\n case `update`: {\n if (rowUpdateMode === `partial`) {\n const updatedValue = Object.assign(\n {},\n this.syncedData.get(key),\n operation.value,\n )\n this.syncedData.set(key, updatedValue)\n } else {\n this.syncedData.set(key, operation.value)\n }\n break\n }\n case `delete`:\n this.syncedData.delete(key)\n break\n }\n }\n }\n\n // After applying synced operations, if this commit included a truncate,\n // re-apply optimistic mutations on top of the fresh synced base. This ensures\n // the UI preserves local intent while respecting server rebuild semantics.\n // Ordering: deletes (above) -> server ops (just applied) -> optimistic upserts.\n if (hasTruncateSync) {\n // Avoid duplicating keys that were inserted/updated by synced operations in this commit\n const syncedInsertedOrUpdatedKeys = new Set<TKey>()\n for (const t of committedSyncedTransactions) {\n for (const op of t.operations) {\n if (op.type === `insert` || op.type === `update`) {\n syncedInsertedOrUpdatedKeys.add(op.key as TKey)\n }\n }\n }\n\n // Build re-apply sets from the snapshot taken at the start of this function.\n // This prevents losing optimistic state if transactions complete during truncate processing.\n const reapplyUpserts = new Map<TKey, TOutput>(\n truncateOptimisticSnapshot!.upserts,\n )\n const reapplyDeletes = new Set<TKey>(\n truncateOptimisticSnapshot!.deletes,\n )\n\n // Emit inserts for re-applied upserts, skipping any keys that have an optimistic delete.\n // If the server also inserted/updated the same key in this batch, override that value\n // with the optimistic value to preserve local intent.\n for (const [key, value] of reapplyUpserts) {\n if (reapplyDeletes.has(key)) continue\n if (syncedInsertedOrUpdatedKeys.has(key)) {\n let foundInsert = false\n for (let i = events.length - 1; i >= 0; i--) {\n const evt = events[i]!\n if (evt.key === key && evt.type === `insert`) {\n evt.value = value\n foundInsert = true\n break\n }\n }\n if (!foundInsert) {\n events.push({ type: `insert`, key, value })\n }\n } else {\n events.push({ type: `insert`, key, value })\n }\n }\n\n // Finally, ensure we do NOT insert keys that have an outstanding optimistic delete.\n if (events.length > 0 && reapplyDeletes.size > 0) {\n const filtered: Array<ChangeMessage<TOutput, TKey>> = []\n for (const evt of events) {\n if (evt.type === `insert` && reapplyDeletes.has(evt.key)) {\n continue\n }\n filtered.push(evt)\n }\n events.length = 0\n events.push(...filtered)\n }\n\n // Ensure listeners are active before emitting this critical batch\n if (this.lifecycle.status !== `ready`) {\n this.lifecycle.markReady()\n }\n }\n\n // Maintain optimistic state appropriately\n // Clear optimistic state since sync operations will now provide the authoritative data.\n // Any still-active user transactions will be re-applied below in recompute.\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n\n // Reset flag and recompute optimistic state for any remaining active transactions\n this.isCommittingSyncTransactions = false\n\n // If we had a truncate, restore the preserved optimistic state from the snapshot\n // This includes items from transactions that may have completed during processing\n if (hasTruncateSync && truncateOptimisticSnapshot) {\n for (const [key, value] of truncateOptimisticSnapshot.upserts) {\n this.optimisticUpserts.set(key, value)\n }\n for (const key of truncateOptimisticSnapshot.deletes) {\n this.optimisticDeletes.add(key)\n }\n }\n\n // Always overlay any still-active optimistic transactions so mutations that started\n // after the truncate snapshot are preserved.\n for (const transaction of this.transactions.values()) {\n if (![`completed`, `failed`].includes(transaction.state)) {\n for (const mutation of transaction.mutations) {\n if (\n this.isThisCollection(mutation.collection) &&\n mutation.optimistic\n ) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.optimisticUpserts.set(\n mutation.key,\n mutation.modified as TOutput,\n )\n this.optimisticDeletes.delete(mutation.key)\n break\n case `delete`:\n this.optimisticUpserts.delete(mutation.key)\n this.optimisticDeletes.add(mutation.key)\n break\n }\n }\n }\n }\n }\n\n // Check for redundant sync operations that match completed optimistic operations\n const completedOptimisticOps = new Map<TKey, any>()\n\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `completed`) {\n for (const mutation of transaction.mutations) {\n if (\n mutation.optimistic &&\n this.isThisCollection(mutation.collection) &&\n changedKeys.has(mutation.key)\n ) {\n completedOptimisticOps.set(mutation.key, {\n type: mutation.type,\n value: mutation.modified,\n })\n }\n }\n }\n }\n\n // Now check what actually changed in the final visible state\n for (const key of changedKeys) {\n const previousVisibleValue = currentVisibleState.get(key)\n const newVisibleValue = this.get(key) // This returns the new derived state\n\n // Check if this sync operation is redundant with a completed optimistic operation\n const completedOp = completedOptimisticOps.get(key)\n let isRedundantSync = false\n\n if (completedOp) {\n if (\n completedOp.type === `delete` &&\n previousVisibleValue !== undefined &&\n newVisibleValue === undefined &&\n deepEquals(completedOp.value, previousVisibleValue)\n ) {\n isRedundantSync = true\n } else if (\n newVisibleValue !== undefined &&\n deepEquals(completedOp.value, newVisibleValue)\n ) {\n isRedundantSync = true\n }\n }\n\n if (!isRedundantSync) {\n if (\n previousVisibleValue === undefined &&\n newVisibleValue !== undefined\n ) {\n events.push({\n type: `insert`,\n key,\n value: newVisibleValue,\n })\n } else if (\n previousVisibleValue !== undefined &&\n newVisibleValue === undefined\n ) {\n events.push({\n type: `delete`,\n key,\n value: previousVisibleValue,\n })\n } else if (\n previousVisibleValue !== undefined &&\n newVisibleValue !== undefined &&\n !deepEquals(previousVisibleValue, newVisibleValue)\n ) {\n events.push({\n type: `update`,\n key,\n value: newVisibleValue,\n previousValue: previousVisibleValue,\n })\n }\n }\n }\n\n // Update cached size after synced data changes\n this.size = this.calculateSize()\n\n // Update indexes for all events before emitting\n if (events.length > 0) {\n this.indexes.updateIndexes(events)\n }\n\n // End batching and emit all events (combines any batched events with sync events)\n this.changes.emitEvents(events, true)\n\n this.pendingSyncedTransactions = uncommittedSyncedTransactions\n\n // Clear the pre-sync state since sync operations are complete\n this.preSyncVisibleState.clear()\n\n // Clear recently synced keys after a microtask to allow recomputeOptimisticState to see them\n Promise.resolve().then(() => {\n this.recentlySyncedKeys.clear()\n })\n\n // Mark that we've received the first commit (for tracking purposes)\n if (!this.hasReceivedFirstCommit) {\n this.hasReceivedFirstCommit = true\n }\n }\n }\n\n /**\n * Schedule cleanup of a transaction when it completes\n */\n public scheduleTransactionCleanup(transaction: Transaction<any>): void {\n // Only schedule cleanup for transactions that aren't already completed\n if (transaction.state === `completed`) {\n this.transactions.delete(transaction.id)\n return\n }\n\n // Schedule cleanup when the transaction completes\n transaction.isPersisted.promise\n .then(() => {\n // Transaction completed successfully, remove it immediately\n this.transactions.delete(transaction.id)\n })\n .catch(() => {\n // Transaction failed, but we want to keep failed transactions for reference\n // so don't remove it.\n // This empty catch block is necessary to prevent unhandled promise rejections.\n })\n }\n\n /**\n * Capture visible state for keys that will be affected by pending sync operations\n * This must be called BEFORE onTransactionStateChange clears optimistic state\n */\n public capturePreSyncVisibleState(): void {\n if (this.pendingSyncedTransactions.length === 0) return\n\n // Get all keys that will be affected by sync operations\n const syncedKeys = new Set<TKey>()\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n syncedKeys.add(operation.key as TKey)\n }\n }\n\n // Mark keys as about to be synced to suppress intermediate events from recomputeOptimisticState\n for (const key of syncedKeys) {\n this.recentlySyncedKeys.add(key)\n }\n\n // Only capture current visible state for keys that will be affected by sync operations\n // This is much more efficient than capturing the entire collection state\n // Only capture keys that haven't been captured yet to preserve earlier captures\n for (const key of syncedKeys) {\n if (!this.preSyncVisibleState.has(key)) {\n const currentValue = this.get(key)\n if (currentValue !== undefined) {\n this.preSyncVisibleState.set(key, currentValue)\n }\n }\n }\n }\n\n /**\n * Trigger a recomputation when transactions change\n * This method should be called by the Transaction class when state changes\n */\n public onTransactionStateChange(): void {\n // Check if commitPendingTransactions will be called after this\n // by checking if there are pending sync transactions (same logic as in transactions.ts)\n this.changes.shouldBatchEvents = this.pendingSyncedTransactions.length > 0\n\n // CRITICAL: Capture visible state BEFORE clearing optimistic state\n this.capturePreSyncVisibleState()\n\n this.recomputeOptimisticState(false)\n }\n\n /**\n * Clean up the collection by stopping sync and clearing data\n * This can be called manually or automatically by garbage collection\n */\n public cleanup(): void {\n this.syncedData.clear()\n this.syncedMetadata.clear()\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n this.size = 0\n this.pendingSyncedTransactions = []\n this.syncedKeys.clear()\n this.hasReceivedFirstCommit = false\n }\n}\n"],"names":["deepEquals","SortedMap"],"mappings":";;;;AAmCO,MAAM,uBAKX;AAAA;AAAA;AAAA;AAAA,EAiCA,YAAY,QAAkD;AAvB9D,SAAO,4BAEH,CAAA;AAEJ,SAAO,qCAAqB,IAAA;AAG5B,SAAO,wCAAwB,IAAA;AAC/B,SAAO,wCAAwB,IAAA;AAG/B,SAAO,OAAO;AAGd,SAAO,iCAAiB,IAAA;AACxB,SAAO,0CAA0B,IAAA;AACjC,SAAO,yCAAyB,IAAA;AAChC,SAAO,yBAAyB;AAChC,SAAO,+BAA+B;AAyWtC,SAAA,4BAA4B,MAAM;AAEhC,UAAI,2BAA2B;AAC/B,iBAAW,eAAe,KAAK,aAAa,OAAA,GAAU;AACpD,YAAI,YAAY,UAAU,cAAc;AACtC,qCAA2B;AAC3B;AAAA,QACF;AAAA,MACF;AAIA,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA,IACE,KAAK,0BAA0B;AAAA,QACjC,CAAC,KAAK,MAAM;AACV,cAAI,EAAE,WAAW;AACf,gBAAI,4BAA4B,KAAK,CAAC;AACtC,gBAAI,EAAE,UAAU;AACd,kBAAI,kBAAkB;AAAA,YACxB;AACA,gBAAI,EAAE,WAAW;AACf,kBAAI,mBAAmB;AAAA,YACzB;AAAA,UACF,OAAO;AACL,gBAAI,8BAA8B,KAAK,CAAC;AAAA,UAC1C;AACA,iBAAO;AAAA,QACT;AAAA,QACA;AAAA,UACE,6BAA6B,CAAA;AAAA,UAG7B,+BAA+B,CAAA;AAAA,UAG/B,iBAAiB;AAAA,UACjB,kBAAkB;AAAA,QAAA;AAAA,MACpB;AAaF,UAAI,CAAC,4BAA4B,mBAAmB,kBAAkB;AAEpE,aAAK,+BAA+B;AAGpC,cAAM,6BAA6B,kBAC/B,4BAA4B,KAAK,CAAC,MAAM,EAAE,QAAQ,GAC9C,qBACJ;AAGJ,cAAM,kCAAkB,IAAA;AACxB,mBAAW,eAAe,6BAA6B;AACrD,qBAAW,aAAa,YAAY,YAAY;AAC9C,wBAAY,IAAI,UAAU,GAAW;AAAA,UACvC;AAAA,QACF;AAIA,YAAI,sBAAsB,KAAK;AAC/B,YAAI,oBAAoB,SAAS,GAAG;AAElC,oDAA0B,IAAA;AAC1B,qBAAW,OAAO,aAAa;AAC7B,kBAAM,eAAe,KAAK,IAAI,GAAG;AACjC,gBAAI,iBAAiB,QAAW;AAC9B,kCAAoB,IAAI,KAAK,YAAY;AAAA,YAC3C;AAAA,UACF;AAAA,QACF;AAEA,cAAM,SAA8C,CAAA;AACpD,cAAM,gBAAgB,KAAK,OAAO,KAAK,iBAAiB;AAExD,mBAAW,eAAe,6BAA6B;AAErD,cAAI,YAAY,UAAU;AAMxB,kBAAM,kCAAkB,IAAI;AAAA,cAC1B,GAAG,KAAK,WAAW,KAAA;AAAA,cACnB,GAAI,4BAA4B,QAAQ,UAAU,CAAA;AAAA,YAAC,CACpD;AACD,uBAAW,OAAO,aAAa;AAC7B,kBAAI,4BAA4B,QAAQ,IAAI,GAAG,EAAG;AAClD,oBAAM,gBACJ,4BAA4B,QAAQ,IAAI,GAAG,KAC3C,KAAK,WAAW,IAAI,GAAG;AACzB,kBAAI,kBAAkB,QAAW;AAC/B,uBAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,eAAe;AAAA,cAC3D;AAAA,YACF;AAIA,iBAAK,WAAW,MAAA;AAChB,iBAAK,eAAe,MAAA;AACpB,iBAAK,WAAW,MAAA;AAKhB,uBAAW,OAAO,aAAa;AAC7B,kCAAoB,OAAO,GAAG;AAAA,YAChC;AAGA,iBAAK,QAAQ,KAAK,YAAY;AAAA,cAC5B,MAAM;AAAA,cACN,YAAY,KAAK;AAAA,YAAA,CAClB;AAAA,UACH;AAEA,qBAAW,aAAa,YAAY,YAAY;AAC9C,kBAAM,MAAM,UAAU;AACtB,iBAAK,WAAW,IAAI,GAAG;AAGvB,oBAAQ,UAAU,MAAA;AAAA,cAChB,KAAK;AACH,qBAAK,eAAe,IAAI,KAAK,UAAU,QAAQ;AAC/C;AAAA,cACF,KAAK;AACH,qBAAK,eAAe;AAAA,kBAClB;AAAA,kBACA,OAAO;AAAA,oBACL,CAAA;AAAA,oBACA,KAAK,eAAe,IAAI,GAAG;AAAA,oBAC3B,UAAU;AAAA,kBAAA;AAAA,gBACZ;AAEF;AAAA,cACF,KAAK;AACH,qBAAK,eAAe,OAAO,GAAG;AAC9B;AAAA,YAAA;AAIJ,oBAAQ,UAAU,MAAA;AAAA,cAChB,KAAK;AACH,qBAAK,WAAW,IAAI,KAAK,UAAU,KAAK;AACxC;AAAA,cACF,KAAK,UAAU;AACb,oBAAI,kBAAkB,WAAW;AAC/B,wBAAM,eAAe,OAAO;AAAA,oBAC1B,CAAA;AAAA,oBACA,KAAK,WAAW,IAAI,GAAG;AAAA,oBACvB,UAAU;AAAA,kBAAA;AAEZ,uBAAK,WAAW,IAAI,KAAK,YAAY;AAAA,gBACvC,OAAO;AACL,uBAAK,WAAW,IAAI,KAAK,UAAU,KAAK;AAAA,gBAC1C;AACA;AAAA,cACF;AAAA,cACA,KAAK;AACH,qBAAK,WAAW,OAAO,GAAG;AAC1B;AAAA,YAAA;AAAA,UAEN;AAAA,QACF;AAMA,YAAI,iBAAiB;AAEnB,gBAAM,kDAAkC,IAAA;AACxC,qBAAW,KAAK,6BAA6B;AAC3C,uBAAW,MAAM,EAAE,YAAY;AAC7B,kBAAI,GAAG,SAAS,YAAY,GAAG,SAAS,UAAU;AAChD,4CAA4B,IAAI,GAAG,GAAW;AAAA,cAChD;AAAA,YACF;AAAA,UACF;AAIA,gBAAM,iBAAiB,IAAI;AAAA,YACzB,2BAA4B;AAAA,UAAA;AAE9B,gBAAM,iBAAiB,IAAI;AAAA,YACzB,2BAA4B;AAAA,UAAA;AAM9B,qBAAW,CAAC,KAAK,KAAK,KAAK,gBAAgB;AACzC,gBAAI,eAAe,IAAI,GAAG,EAAG;AAC7B,gBAAI,4BAA4B,IAAI,GAAG,GAAG;AACxC,kBAAI,cAAc;AAClB,uBAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,sBAAM,MAAM,OAAO,CAAC;AACpB,oBAAI,IAAI,QAAQ,OAAO,IAAI,SAAS,UAAU;AAC5C,sBAAI,QAAQ;AACZ,gCAAc;AACd;AAAA,gBACF;AAAA,cACF;AACA,kBAAI,CAAC,aAAa;AAChB,uBAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO;AAAA,cAC5C;AAAA,YACF,OAAO;AACL,qBAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO;AAAA,YAC5C;AAAA,UACF;AAGA,cAAI,OAAO,SAAS,KAAK,eAAe,OAAO,GAAG;AAChD,kBAAM,WAAgD,CAAA;AACtD,uBAAW,OAAO,QAAQ;AACxB,kBAAI,IAAI,SAAS,YAAY,eAAe,IAAI,IAAI,GAAG,GAAG;AACxD;AAAA,cACF;AACA,uBAAS,KAAK,GAAG;AAAA,YACnB;AACA,mBAAO,SAAS;AAChB,mBAAO,KAAK,GAAG,QAAQ;AAAA,UACzB;AAGA,cAAI,KAAK,UAAU,WAAW,SAAS;AACrC,iBAAK,UAAU,UAAA;AAAA,UACjB;AAAA,QACF;AAKA,aAAK,kBAAkB,MAAA;AACvB,aAAK,kBAAkB,MAAA;AAGvB,aAAK,+BAA+B;AAIpC,YAAI,mBAAmB,4BAA4B;AACjD,qBAAW,CAAC,KAAK,KAAK,KAAK,2BAA2B,SAAS;AAC7D,iBAAK,kBAAkB,IAAI,KAAK,KAAK;AAAA,UACvC;AACA,qBAAW,OAAO,2BAA2B,SAAS;AACpD,iBAAK,kBAAkB,IAAI,GAAG;AAAA,UAChC;AAAA,QACF;AAIA,mBAAW,eAAe,KAAK,aAAa,OAAA,GAAU;AACpD,cAAI,CAAC,CAAC,aAAa,QAAQ,EAAE,SAAS,YAAY,KAAK,GAAG;AACxD,uBAAW,YAAY,YAAY,WAAW;AAC5C,kBACE,KAAK,iBAAiB,SAAS,UAAU,KACzC,SAAS,YACT;AACA,wBAAQ,SAAS,MAAA;AAAA,kBACf,KAAK;AAAA,kBACL,KAAK;AACH,yBAAK,kBAAkB;AAAA,sBACrB,SAAS;AAAA,sBACT,SAAS;AAAA,oBAAA;AAEX,yBAAK,kBAAkB,OAAO,SAAS,GAAG;AAC1C;AAAA,kBACF,KAAK;AACH,yBAAK,kBAAkB,OAAO,SAAS,GAAG;AAC1C,yBAAK,kBAAkB,IAAI,SAAS,GAAG;AACvC;AAAA,gBAAA;AAAA,cAEN;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,cAAM,6CAA6B,IAAA;AAEnC,mBAAW,eAAe,KAAK,aAAa,OAAA,GAAU;AACpD,cAAI,YAAY,UAAU,aAAa;AACrC,uBAAW,YAAY,YAAY,WAAW;AAC5C,kBACE,SAAS,cACT,KAAK,iBAAiB,SAAS,UAAU,KACzC,YAAY,IAAI,SAAS,GAAG,GAC5B;AACA,uCAAuB,IAAI,SAAS,KAAK;AAAA,kBACvC,MAAM,SAAS;AAAA,kBACf,OAAO,SAAS;AAAA,gBAAA,CACjB;AAAA,cACH;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,mBAAW,OAAO,aAAa;AAC7B,gBAAM,uBAAuB,oBAAoB,IAAI,GAAG;AACxD,gBAAM,kBAAkB,KAAK,IAAI,GAAG;AAGpC,gBAAM,cAAc,uBAAuB,IAAI,GAAG;AAClD,cAAI,kBAAkB;AAEtB,cAAI,aAAa;AACf,gBACE,YAAY,SAAS,YACrB,yBAAyB,UACzB,oBAAoB,UACpBA,MAAAA,WAAW,YAAY,OAAO,oBAAoB,GAClD;AACA,gCAAkB;AAAA,YACpB,WACE,oBAAoB,UACpBA,MAAAA,WAAW,YAAY,OAAO,eAAe,GAC7C;AACA,gCAAkB;AAAA,YACpB;AAAA,UACF;AAEA,cAAI,CAAC,iBAAiB;AACpB,gBACE,yBAAyB,UACzB,oBAAoB,QACpB;AACA,qBAAO,KAAK;AAAA,gBACV,MAAM;AAAA,gBACN;AAAA,gBACA,OAAO;AAAA,cAAA,CACR;AAAA,YACH,WACE,yBAAyB,UACzB,oBAAoB,QACpB;AACA,qBAAO,KAAK;AAAA,gBACV,MAAM;AAAA,gBACN;AAAA,gBACA,OAAO;AAAA,cAAA,CACR;AAAA,YACH,WACE,yBAAyB,UACzB,oBAAoB,UACpB,CAACA,MAAAA,WAAW,sBAAsB,eAAe,GACjD;AACA,qBAAO,KAAK;AAAA,gBACV,MAAM;AAAA,gBACN;AAAA,gBACA,OAAO;AAAA,gBACP,eAAe;AAAA,cAAA,CAChB;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAGA,aAAK,OAAO,KAAK,cAAA;AAGjB,YAAI,OAAO,SAAS,GAAG;AACrB,eAAK,QAAQ,cAAc,MAAM;AAAA,QACnC;AAGA,aAAK,QAAQ,WAAW,QAAQ,IAAI;AAEpC,aAAK,4BAA4B;AAGjC,aAAK,oBAAoB,MAAA;AAGzB,gBAAQ,UAAU,KAAK,MAAM;AAC3B,eAAK,mBAAmB,MAAA;AAAA,QAC1B,CAAC;AAGD,YAAI,CAAC,KAAK,wBAAwB;AAChC,eAAK,yBAAyB;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAlvBE,SAAK,SAAS;AACd,SAAK,eAAe,IAAIC,UAAAA;AAAAA,MAAoC,CAAC,GAAG,MAC9D,EAAE,iBAAiB,CAAC;AAAA,IAAA;AAKtB,SAAK,aAAa,IAAIA,oBAAyB,OAAO,OAAO;AAAA,EAC/D;AAAA,EAEA,QAAQ,MAML;AACD,SAAK,aAAa,KAAK;AACvB,SAAK,YAAY,KAAK;AACtB,SAAK,UAAU,KAAK;AACpB,SAAK,UAAU,KAAK;AACpB,SAAK,UAAU,KAAK;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKO,IAAI,KAAgC;AACzC,UAAM,EAAE,mBAAmB,mBAAmB,WAAA,IAAe;AAE7D,QAAI,kBAAkB,IAAI,GAAG,GAAG;AAC9B,aAAO;AAAA,IACT;AAGA,QAAI,kBAAkB,IAAI,GAAG,GAAG;AAC9B,aAAO,kBAAkB,IAAI,GAAG;AAAA,IAClC;AAGA,WAAO,WAAW,IAAI,GAAG;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKO,IAAI,KAAoB;AAC7B,UAAM,EAAE,mBAAmB,mBAAmB,WAAA,IAAe;AAE7D,QAAI,kBAAkB,IAAI,GAAG,GAAG;AAC9B,aAAO;AAAA,IACT;AAGA,QAAI,kBAAkB,IAAI,GAAG,GAAG;AAC9B,aAAO;AAAA,IACT;AAGA,WAAO,WAAW,IAAI,GAAG;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,CAAQ,OAA+B;AACrC,UAAM,EAAE,YAAY,mBAAmB,kBAAA,IAAsB;AAE7D,eAAW,OAAO,WAAW,QAAQ;AACnC,UAAI,CAAC,kBAAkB,IAAI,GAAG,GAAG;AAC/B,cAAM;AAAA,MACR;AAAA,IACF;AAEA,eAAW,OAAO,kBAAkB,QAAQ;AAC1C,UAAI,CAAC,WAAW,IAAI,GAAG,KAAK,CAAC,kBAAkB,IAAI,GAAG,GAAG;AAGvD,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,CAAQ,SAAoC;AAC1C,eAAW,OAAO,KAAK,QAAQ;AAC7B,YAAM,QAAQ,KAAK,IAAI,GAAG;AAC1B,UAAI,UAAU,QAAW;AACvB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,CAAQ,UAA6C;AACnD,eAAW,OAAO,KAAK,QAAQ;AAC7B,YAAM,QAAQ,KAAK,IAAI,GAAG;AAC1B,UAAI,UAAU,QAAW;AACvB,cAAM,CAAC,KAAK,KAAK;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,EAAS,OAAO,QAAQ,IAAuC;AAC7D,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,WAAW;AACzC,YAAM,CAAC,KAAK,KAAK;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,QACL,YACM;AACN,QAAI,QAAQ;AACZ,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,WAAW;AACzC,iBAAW,OAAO,KAAK,OAAO;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,IACL,YACU;AACV,UAAM,SAAmB,CAAA;AACzB,QAAI,QAAQ;AACZ,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,WAAW;AACzC,aAAO,KAAK,WAAW,OAAO,KAAK,OAAO,CAAC;AAAA,IAC7C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBACN,YACS;AACT,WAAO,eAAe,KAAK;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKO,yBACL,wBAAiC,OAC3B;AAMN,QAAI,KAAK,gCAAgC,CAAC,uBAAuB;AAC/D;AAAA,IACF;AAEA,UAAM,gBAAgB,IAAI,IAAI,KAAK,iBAAiB;AACpD,UAAM,kBAAkB,IAAI,IAAI,KAAK,iBAAiB;AAGtD,SAAK,kBAAkB,MAAA;AACvB,SAAK,kBAAkB,MAAA;AAEvB,UAAM,qBAA8C,CAAA;AAEpD,eAAW,eAAe,KAAK,aAAa,OAAA,GAAU;AACpD,UAAI,CAAC,CAAC,aAAa,QAAQ,EAAE,SAAS,YAAY,KAAK,GAAG;AACxD,2BAAmB,KAAK,WAAW;AAAA,MACrC;AAAA,IACF;AAGA,eAAW,eAAe,oBAAoB;AAC5C,iBAAW,YAAY,YAAY,WAAW;AAC5C,YAAI,KAAK,iBAAiB,SAAS,UAAU,KAAK,SAAS,YAAY;AACrE,kBAAQ,SAAS,MAAA;AAAA,YACf,KAAK;AAAA,YACL,KAAK;AACH,mBAAK,kBAAkB;AAAA,gBACrB,SAAS;AAAA,gBACT,SAAS;AAAA,cAAA;AAEX,mBAAK,kBAAkB,OAAO,SAAS,GAAG;AAC1C;AAAA,YACF,KAAK;AACH,mBAAK,kBAAkB,OAAO,SAAS,GAAG;AAC1C,mBAAK,kBAAkB,IAAI,SAAS,GAAG;AACvC;AAAA,UAAA;AAAA,QAEN;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO,KAAK,cAAA;AAGjB,UAAM,SAA8C,CAAA;AACpD,SAAK,yBAAyB,eAAe,iBAAiB,MAAM;AAKpE,UAAM,6BAA6B,OAAO,OAAO,CAAC,UAAU;AAC1D,UAAI,CAAC,KAAK,mBAAmB,IAAI,MAAM,GAAG,GAAG;AAC3C,eAAO;AAAA,MACT;AAGA,UAAI,uBAAuB;AACzB,eAAO;AAAA,MACT;AAGA,aAAO;AAAA,IACT,CAAC;AAKD,QAAI,KAAK,0BAA0B,SAAS,KAAK,CAAC,uBAAuB;AACvE,YAAM,sCAAsB,IAAA;AAG5B,iBAAW,eAAe,KAAK,2BAA2B;AACxD,mBAAW,aAAa,YAAY,YAAY;AAC9C,0BAAgB,IAAI,UAAU,GAAW;AAAA,QAC3C;AAAA,MACF;AAKA,YAAM,iBAAiB,2BAA2B,OAAO,CAAC,UAAU;AAClE,YAAI,MAAM,SAAS,YAAY,gBAAgB,IAAI,MAAM,GAAG,GAAG;AAG7D,gBAAM,8BAA8B,mBAAmB;AAAA,YAAK,CAAC,OAC3D,GAAG,UAAU;AAAA,cACX,CAAC,MAAM,KAAK,iBAAiB,EAAE,UAAU,KAAK,EAAE,QAAQ,MAAM;AAAA,YAAA;AAAA,UAChE;AAGF,cAAI,CAAC,6BAA6B;AAChC,mBAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AAGD,UAAI,eAAe,SAAS,GAAG;AAC7B,aAAK,QAAQ,cAAc,cAAc;AAAA,MAC3C;AACA,WAAK,QAAQ,WAAW,gBAAgB,qBAAqB;AAAA,IAC/D,OAAO;AAEL,UAAI,2BAA2B,SAAS,GAAG;AACzC,aAAK,QAAQ,cAAc,0BAA0B;AAAA,MACvD;AAEA,WAAK,QAAQ,WAAW,4BAA4B,qBAAqB;AAAA,IAC3E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAwB;AAC9B,UAAM,aAAa,KAAK,WAAW;AACnC,UAAM,oBAAoB,MAAM,KAAK,KAAK,iBAAiB,EAAE;AAAA,MAC3D,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,KAAK,CAAC,KAAK,kBAAkB,IAAI,GAAG;AAAA,IAAA,EACpE;AACF,UAAM,qBAAqB,MAAM,KAAK,KAAK,kBAAkB,KAAA,CAAM,EAAE;AAAA,MACnE,CAAC,QAAQ,CAAC,KAAK,WAAW,IAAI,GAAG;AAAA,IAAA,EACjC;AAEF,WAAO,aAAa,oBAAoB;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKQ,yBACN,iBACA,iBACA,QACM;AACN,UAAM,8BAAc,IAAI;AAAA,MACtB,GAAG,gBAAgB,KAAA;AAAA,MACnB,GAAG,KAAK,kBAAkB,KAAA;AAAA,MAC1B,GAAG;AAAA,MACH,GAAG,KAAK;AAAA,IAAA,CACT;AAED,eAAW,OAAO,SAAS;AACzB,YAAM,eAAe,KAAK,IAAI,GAAG;AACjC,YAAM,gBAAgB,KAAK;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAGF,UAAI,kBAAkB,UAAa,iBAAiB,QAAW;AAC7D,eAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,eAAe;AAAA,MAC3D,WAAW,kBAAkB,UAAa,iBAAiB,QAAW;AACpE,eAAO,KAAK,EAAE,MAAM,UAAU,KAAK,OAAO,cAAc;AAAA,MAC1D,WACE,kBAAkB,UAClB,iBAAiB,UACjB,kBAAkB,cAClB;AACA,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN;AAAA,UACA,OAAO;AAAA,UACP;AAAA,QAAA,CACD;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBACN,KACA,iBACA,iBACqB;AACrB,QAAI,gBAAgB,IAAI,GAAG,GAAG;AAC5B,aAAO;AAAA,IACT;AACA,QAAI,gBAAgB,IAAI,GAAG,GAAG;AAC5B,aAAO,gBAAgB,IAAI,GAAG;AAAA,IAChC;AACA,WAAO,KAAK,WAAW,IAAI,GAAG;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EA0ZO,2BAA2B,aAAqC;AAErE,QAAI,YAAY,UAAU,aAAa;AACrC,WAAK,aAAa,OAAO,YAAY,EAAE;AACvC;AAAA,IACF;AAGA,gBAAY,YAAY,QACrB,KAAK,MAAM;AAEV,WAAK,aAAa,OAAO,YAAY,EAAE;AAAA,IACzC,CAAC,EACA,MAAM,MAAM;AAAA,IAIb,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,6BAAmC;AACxC,QAAI,KAAK,0BAA0B,WAAW,EAAG;AAGjD,UAAM,iCAAiB,IAAA;AACvB,eAAW,eAAe,KAAK,2BAA2B;AACxD,iBAAW,aAAa,YAAY,YAAY;AAC9C,mBAAW,IAAI,UAAU,GAAW;AAAA,MACtC;AAAA,IACF;AAGA,eAAW,OAAO,YAAY;AAC5B,WAAK,mBAAmB,IAAI,GAAG;AAAA,IACjC;AAKA,eAAW,OAAO,YAAY;AAC5B,UAAI,CAAC,KAAK,oBAAoB,IAAI,GAAG,GAAG;AACtC,cAAM,eAAe,KAAK,IAAI,GAAG;AACjC,YAAI,iBAAiB,QAAW;AAC9B,eAAK,oBAAoB,IAAI,KAAK,YAAY;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,2BAAiC;AAGtC,SAAK,QAAQ,oBAAoB,KAAK,0BAA0B,SAAS;AAGzE,SAAK,2BAAA;AAEL,SAAK,yBAAyB,KAAK;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAgB;AACrB,SAAK,WAAW,MAAA;AAChB,SAAK,eAAe,MAAA;AACpB,SAAK,kBAAkB,MAAA;AACvB,SAAK,kBAAkB,MAAA;AACvB,SAAK,OAAO;AACZ,SAAK,4BAA4B,CAAA;AACjC,SAAK,WAAW,MAAA;AAChB,SAAK,yBAAyB;AAAA,EAChC;AACF;;"}
|
|
1
|
+
{"version":3,"file":"state.cjs","sources":["../../../src/collection/state.ts"],"sourcesContent":["import { deepEquals } from '../utils'\nimport { SortedMap } from '../SortedMap'\nimport { enrichRowWithVirtualProps } from '../virtual-props.js'\nimport { DIRECT_TRANSACTION_METADATA_KEY } from './transaction-metadata.js'\nimport type {\n VirtualOrigin,\n VirtualRowProps,\n WithVirtualProps,\n} from '../virtual-props.js'\nimport type { Transaction } from '../transactions'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type {\n ChangeMessage,\n CollectionConfig,\n OptimisticChangeMessage,\n} from '../types'\nimport type { CollectionImpl } from './index.js'\nimport type { CollectionLifecycleManager } from './lifecycle'\nimport type { CollectionChangesManager } from './changes'\nimport type { CollectionIndexesManager } from './indexes'\nimport type { CollectionEventsManager } from './events'\n\ninterface PendingSyncedTransaction<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n> {\n committed: boolean\n operations: Array<OptimisticChangeMessage<T>>\n truncate?: boolean\n deletedKeys: Set<string | number>\n rowMetadataWrites: Map<TKey, PendingMetadataWrite>\n collectionMetadataWrites: Map<string, PendingMetadataWrite>\n optimisticSnapshot?: {\n upserts: Map<TKey, T>\n deletes: Set<TKey>\n }\n /**\n * When true, this transaction should be processed immediately even if there\n * are persisting user transactions. Used by manual write operations (writeInsert,\n * writeUpdate, writeDelete, writeUpsert) which need synchronous updates to syncedData.\n */\n immediate?: boolean\n}\n\ntype PendingMetadataWrite = { type: `set`; value: unknown } | { type: `delete` }\n\ntype InternalChangeMessage<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n> = ChangeMessage<T, TKey> & {\n __virtualProps?: {\n value?: VirtualRowProps<TKey>\n previousValue?: VirtualRowProps<TKey>\n }\n}\n\nexport class CollectionStateManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n public config!: CollectionConfig<TOutput, TKey, TSchema>\n public collection!: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n public lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n public changes!: CollectionChangesManager<TOutput, TKey, TSchema, TInput>\n public indexes!: CollectionIndexesManager<TOutput, TKey, TSchema, TInput>\n private _events!: CollectionEventsManager\n\n // Core state - make public for testing\n public transactions: SortedMap<string, Transaction<any>>\n public pendingSyncedTransactions: Array<\n PendingSyncedTransaction<TOutput, TKey>\n > = []\n public syncedData: SortedMap<TKey, TOutput>\n public syncedMetadata = new Map<TKey, unknown>()\n public syncedCollectionMetadata = new Map<string, unknown>()\n\n // Optimistic state tracking - make public for testing\n public optimisticUpserts = new Map<TKey, TOutput>()\n public optimisticDeletes = new Set<TKey>()\n public pendingOptimisticUpserts = new Map<TKey, TOutput>()\n public pendingOptimisticDeletes = new Set<TKey>()\n public pendingOptimisticDirectUpserts = new Set<TKey>()\n public pendingOptimisticDirectDeletes = new Set<TKey>()\n\n /**\n * Tracks the origin of confirmed changes for each row.\n * 'local' = change originated from this client\n * 'remote' = change was received via sync\n *\n * This is used for the $origin virtual property.\n * Note: This only tracks *confirmed* changes, not optimistic ones.\n * Optimistic changes are always considered 'local' for $origin.\n */\n public rowOrigins = new Map<TKey, VirtualOrigin>()\n\n /**\n * Tracks keys that have pending local changes.\n * Used to determine whether sync-confirmed data should have 'local' or 'remote' origin.\n * When sync confirms data for a key with pending local changes, it keeps 'local' origin.\n */\n public pendingLocalChanges = new Set<TKey>()\n public pendingLocalOrigins = new Set<TKey>()\n\n private virtualPropsCache = new WeakMap<\n object,\n {\n synced: boolean\n origin: VirtualOrigin\n key: TKey\n collectionId: string\n enriched: WithVirtualProps<TOutput, TKey>\n }\n >()\n\n // Cached size for performance\n public size = 0\n\n // State used for computing the change events\n public syncedKeys = new Set<TKey>()\n public preSyncVisibleState = new Map<TKey, TOutput>()\n public recentlySyncedKeys = new Set<TKey>()\n public hasReceivedFirstCommit = false\n public isCommittingSyncTransactions = false\n public isLocalOnly = false\n\n /**\n * Creates a new CollectionState manager\n */\n constructor(config: CollectionConfig<TOutput, TKey, TSchema>) {\n this.config = config\n this.transactions = new SortedMap<string, Transaction<any>>((a, b) =>\n a.compareCreatedAt(b),\n )\n\n // Set up data storage - always use SortedMap for deterministic iteration.\n // If a custom compare function is provided, use it; otherwise entries are sorted by key only.\n this.syncedData = new SortedMap<TKey, TOutput>(config.compare)\n }\n\n setDeps(deps: {\n collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n changes: CollectionChangesManager<TOutput, TKey, TSchema, TInput>\n indexes: CollectionIndexesManager<TOutput, TKey, TSchema, TInput>\n events: CollectionEventsManager\n }) {\n this.collection = deps.collection\n this.lifecycle = deps.lifecycle\n this.changes = deps.changes\n this.indexes = deps.indexes\n this._events = deps.events\n }\n\n /**\n * Checks if a row has pending optimistic mutations (not yet confirmed by sync).\n * Used to compute the $synced virtual property.\n */\n public isRowSynced(key: TKey): boolean {\n if (this.isLocalOnly) {\n return true\n }\n return !this.optimisticUpserts.has(key) && !this.optimisticDeletes.has(key)\n }\n\n /**\n * Gets the origin of the last confirmed change to a row.\n * Returns 'local' if the row has optimistic mutations (optimistic changes are local).\n * Used to compute the $origin virtual property.\n */\n public getRowOrigin(key: TKey): VirtualOrigin {\n if (this.isLocalOnly) {\n return 'local'\n }\n // If there are optimistic changes, they're local\n if (this.optimisticUpserts.has(key) || this.optimisticDeletes.has(key)) {\n return 'local'\n }\n // Otherwise, return the confirmed origin (defaults to 'remote' for synced data)\n return this.rowOrigins.get(key) ?? 'remote'\n }\n\n private createVirtualPropsSnapshot(\n key: TKey,\n overrides?: Partial<VirtualRowProps<TKey>>,\n ): VirtualRowProps<TKey> {\n return {\n $synced: overrides?.$synced ?? this.isRowSynced(key),\n $origin: overrides?.$origin ?? this.getRowOrigin(key),\n $key: overrides?.$key ?? key,\n $collectionId: overrides?.$collectionId ?? this.collection.id,\n }\n }\n\n private getVirtualPropsSnapshotForState(\n key: TKey,\n options?: {\n rowOrigins?: ReadonlyMap<TKey, VirtualOrigin>\n optimisticUpserts?: Pick<Map<TKey, unknown>, 'has'>\n optimisticDeletes?: Pick<Set<TKey>, 'has'>\n completedOptimisticKeys?: Pick<Map<TKey, unknown>, 'has'>\n },\n ): VirtualRowProps<TKey> {\n if (this.isLocalOnly) {\n return this.createVirtualPropsSnapshot(key, {\n $synced: true,\n $origin: 'local',\n })\n }\n\n const optimisticUpserts =\n options?.optimisticUpserts ?? this.optimisticUpserts\n const optimisticDeletes =\n options?.optimisticDeletes ?? this.optimisticDeletes\n const hasOptimisticChange =\n optimisticUpserts.has(key) ||\n optimisticDeletes.has(key) ||\n options?.completedOptimisticKeys?.has(key) === true\n\n return this.createVirtualPropsSnapshot(key, {\n $synced: !hasOptimisticChange,\n $origin: hasOptimisticChange\n ? 'local'\n : ((options?.rowOrigins ?? this.rowOrigins).get(key) ?? 'remote'),\n })\n }\n\n private enrichWithVirtualPropsSnapshot(\n row: TOutput,\n virtualProps: VirtualRowProps<TKey>,\n ): WithVirtualProps<TOutput, TKey> {\n const existingRow = row as Partial<WithVirtualProps<TOutput, TKey>>\n const synced = existingRow.$synced ?? virtualProps.$synced\n const origin = existingRow.$origin ?? virtualProps.$origin\n const resolvedKey = existingRow.$key ?? virtualProps.$key\n const collectionId = existingRow.$collectionId ?? virtualProps.$collectionId\n\n const cached = this.virtualPropsCache.get(row as object)\n if (\n cached &&\n cached.synced === synced &&\n cached.origin === origin &&\n cached.key === resolvedKey &&\n cached.collectionId === collectionId\n ) {\n return cached.enriched\n }\n\n const enriched = {\n ...row,\n $synced: synced,\n $origin: origin,\n $key: resolvedKey,\n $collectionId: collectionId,\n } as WithVirtualProps<TOutput, TKey>\n\n this.virtualPropsCache.set(row as object, {\n synced,\n origin,\n key: resolvedKey,\n collectionId,\n enriched,\n })\n\n return enriched\n }\n\n private clearOriginTrackingState(): void {\n this.rowOrigins.clear()\n this.pendingLocalChanges.clear()\n this.pendingLocalOrigins.clear()\n }\n\n /**\n * Enriches a row with virtual properties using the \"add-if-missing\" pattern.\n * If the row already has virtual properties (from an upstream collection),\n * they are preserved. Otherwise, new values are computed.\n */\n public enrichWithVirtualProps(\n row: TOutput,\n key: TKey,\n ): WithVirtualProps<TOutput, TKey> {\n return this.enrichWithVirtualPropsSnapshot(\n row,\n this.createVirtualPropsSnapshot(key),\n )\n }\n\n /**\n * Creates a change message with virtual properties.\n * Uses the \"add-if-missing\" pattern so that pass-through from upstream\n * collections works correctly.\n */\n public enrichChangeMessage(\n change: ChangeMessage<TOutput, TKey>,\n ): ChangeMessage<WithVirtualProps<TOutput, TKey>, TKey> {\n const { __virtualProps } = change as InternalChangeMessage<TOutput, TKey>\n const enrichedValue = __virtualProps?.value\n ? this.enrichWithVirtualPropsSnapshot(change.value, __virtualProps.value)\n : this.enrichWithVirtualProps(change.value, change.key)\n const enrichedPreviousValue = change.previousValue\n ? __virtualProps?.previousValue\n ? this.enrichWithVirtualPropsSnapshot(\n change.previousValue,\n __virtualProps.previousValue,\n )\n : this.enrichWithVirtualProps(change.previousValue, change.key)\n : undefined\n\n return {\n key: change.key,\n type: change.type,\n value: enrichedValue,\n previousValue: enrichedPreviousValue,\n metadata: change.metadata,\n } as ChangeMessage<WithVirtualProps<TOutput, TKey>, TKey>\n }\n\n /**\n * Get the current value for a key enriched with virtual properties.\n */\n public getWithVirtualProps(\n key: TKey,\n ): WithVirtualProps<TOutput, TKey> | undefined {\n const value = this.get(key)\n if (value === undefined) {\n return undefined\n }\n return this.enrichWithVirtualProps(value, key)\n }\n\n /**\n * Get the current value for a key (virtual derived state)\n */\n public get(key: TKey): TOutput | undefined {\n const { optimisticDeletes, optimisticUpserts, syncedData } = this\n // Check if optimistically deleted\n if (optimisticDeletes.has(key)) {\n return undefined\n }\n\n // Check optimistic upserts first\n if (optimisticUpserts.has(key)) {\n return optimisticUpserts.get(key)\n }\n\n // Fall back to synced data\n return syncedData.get(key)\n }\n\n /**\n * Check if a key exists in the collection (virtual derived state)\n */\n public has(key: TKey): boolean {\n const { optimisticDeletes, optimisticUpserts, syncedData } = this\n // Check if optimistically deleted\n if (optimisticDeletes.has(key)) {\n return false\n }\n\n // Check optimistic upserts first\n if (optimisticUpserts.has(key)) {\n return true\n }\n\n // Fall back to synced data\n return syncedData.has(key)\n }\n\n /**\n * Get all keys (virtual derived state)\n */\n public *keys(): IterableIterator<TKey> {\n const { syncedData, optimisticDeletes, optimisticUpserts } = this\n // Yield keys from synced data, skipping any that are deleted.\n for (const key of syncedData.keys()) {\n if (!optimisticDeletes.has(key)) {\n yield key\n }\n }\n // Yield keys from upserts that were not already in synced data.\n for (const key of optimisticUpserts.keys()) {\n if (!syncedData.has(key) && !optimisticDeletes.has(key)) {\n // The optimisticDeletes check is technically redundant if inserts/updates always remove from deletes,\n // but it's safer to keep it.\n yield key\n }\n }\n }\n\n /**\n * Get all values (virtual derived state)\n */\n public *values(): IterableIterator<TOutput> {\n for (const key of this.keys()) {\n const value = this.get(key)\n if (value !== undefined) {\n yield value\n }\n }\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *entries(): IterableIterator<[TKey, TOutput]> {\n for (const key of this.keys()) {\n const value = this.get(key)\n if (value !== undefined) {\n yield [key, value]\n }\n }\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *[Symbol.iterator](): IterableIterator<[TKey, TOutput]> {\n for (const [key, value] of this.entries()) {\n yield [key, value]\n }\n }\n\n /**\n * Execute a callback for each entry in the collection\n */\n public forEach(\n callbackfn: (value: TOutput, key: TKey, index: number) => void,\n ): void {\n let index = 0\n for (const [key, value] of this.entries()) {\n callbackfn(value, key, index++)\n }\n }\n\n /**\n * Create a new array with the results of calling a function for each entry in the collection\n */\n public map<U>(\n callbackfn: (value: TOutput, key: TKey, index: number) => U,\n ): Array<U> {\n const result: Array<U> = []\n let index = 0\n for (const [key, value] of this.entries()) {\n result.push(callbackfn(value, key, index++))\n }\n return result\n }\n\n /**\n * Check if the given collection is this collection\n * @param collection The collection to check\n * @returns True if the given collection is this collection, false otherwise\n */\n private isThisCollection(\n collection: CollectionImpl<any, any, any, any, any>,\n ): boolean {\n return collection === this.collection\n }\n\n /**\n * Recompute optimistic state from active transactions\n */\n public recomputeOptimisticState(\n triggeredByUserAction: boolean = false,\n ): void {\n // Skip redundant recalculations when we're in the middle of committing sync transactions\n // While the sync pipeline is replaying a large batch we still want to honour\n // fresh optimistic mutations from the UI. Only skip recompute for the\n // internal sync-driven redraws; user-triggered work (triggeredByUserAction)\n // must run so live queries stay responsive during long commits.\n if (this.isCommittingSyncTransactions && !triggeredByUserAction) {\n return\n }\n\n const previousState = new Map(this.optimisticUpserts)\n const previousDeletes = new Set(this.optimisticDeletes)\n const previousRowOrigins = new Map(this.rowOrigins)\n\n // Update pending optimistic state for completed/failed transactions\n for (const transaction of this.transactions.values()) {\n const isDirectTransaction =\n transaction.metadata[DIRECT_TRANSACTION_METADATA_KEY] === true\n if (transaction.state === `completed`) {\n for (const mutation of transaction.mutations) {\n if (!this.isThisCollection(mutation.collection)) {\n continue\n }\n this.pendingLocalOrigins.add(mutation.key)\n if (!mutation.optimistic) {\n continue\n }\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.pendingOptimisticUpserts.set(\n mutation.key,\n mutation.modified as TOutput,\n )\n this.pendingOptimisticDeletes.delete(mutation.key)\n if (isDirectTransaction) {\n this.pendingOptimisticDirectUpserts.add(mutation.key)\n this.pendingOptimisticDirectDeletes.delete(mutation.key)\n } else {\n this.pendingOptimisticDirectUpserts.delete(mutation.key)\n this.pendingOptimisticDirectDeletes.delete(mutation.key)\n }\n break\n case `delete`:\n this.pendingOptimisticUpserts.delete(mutation.key)\n this.pendingOptimisticDeletes.add(mutation.key)\n if (isDirectTransaction) {\n this.pendingOptimisticDirectUpserts.delete(mutation.key)\n this.pendingOptimisticDirectDeletes.add(mutation.key)\n } else {\n this.pendingOptimisticDirectUpserts.delete(mutation.key)\n this.pendingOptimisticDirectDeletes.delete(mutation.key)\n }\n break\n }\n }\n } else if (transaction.state === `failed`) {\n for (const mutation of transaction.mutations) {\n if (!this.isThisCollection(mutation.collection)) {\n continue\n }\n this.pendingLocalOrigins.delete(mutation.key)\n if (mutation.optimistic) {\n this.pendingOptimisticUpserts.delete(mutation.key)\n this.pendingOptimisticDeletes.delete(mutation.key)\n this.pendingOptimisticDirectUpserts.delete(mutation.key)\n this.pendingOptimisticDirectDeletes.delete(mutation.key)\n }\n }\n }\n }\n\n // Clear current optimistic state\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n this.pendingLocalChanges.clear()\n\n // Seed optimistic state with pending optimistic mutations only when a sync is pending\n const pendingSyncKeys = new Set<TKey>()\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n pendingSyncKeys.add(operation.key as TKey)\n }\n }\n const staleOptimisticUpserts: Array<TKey> = []\n for (const [key, value] of this.pendingOptimisticUpserts) {\n if (\n pendingSyncKeys.has(key) ||\n this.pendingOptimisticDirectUpserts.has(key)\n ) {\n this.optimisticUpserts.set(key, value)\n } else {\n staleOptimisticUpserts.push(key)\n }\n }\n for (const key of staleOptimisticUpserts) {\n this.pendingOptimisticUpserts.delete(key)\n this.pendingLocalOrigins.delete(key)\n }\n const staleOptimisticDeletes: Array<TKey> = []\n for (const key of this.pendingOptimisticDeletes) {\n if (\n pendingSyncKeys.has(key) ||\n this.pendingOptimisticDirectDeletes.has(key)\n ) {\n this.optimisticDeletes.add(key)\n } else {\n staleOptimisticDeletes.push(key)\n }\n }\n for (const key of staleOptimisticDeletes) {\n this.pendingOptimisticDeletes.delete(key)\n this.pendingLocalOrigins.delete(key)\n }\n\n const activeTransactions: Array<Transaction<any>> = []\n\n for (const transaction of this.transactions.values()) {\n if (![`completed`, `failed`].includes(transaction.state)) {\n activeTransactions.push(transaction)\n }\n }\n\n // Apply active transactions only (completed transactions are handled by sync operations)\n for (const transaction of activeTransactions) {\n for (const mutation of transaction.mutations) {\n if (!this.isThisCollection(mutation.collection)) {\n continue\n }\n\n // Track that this key has pending local changes for $origin tracking\n this.pendingLocalChanges.add(mutation.key)\n\n if (mutation.optimistic) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.optimisticUpserts.set(\n mutation.key,\n mutation.modified as TOutput,\n )\n this.optimisticDeletes.delete(mutation.key)\n break\n case `delete`:\n this.optimisticUpserts.delete(mutation.key)\n this.optimisticDeletes.add(mutation.key)\n break\n }\n }\n }\n }\n\n // Update cached size\n this.size = this.calculateSize()\n\n // Collect events for changes\n const events: Array<InternalChangeMessage<TOutput, TKey>> = []\n this.collectOptimisticChanges(\n previousState,\n previousDeletes,\n previousRowOrigins,\n events,\n )\n\n // Filter out events for recently synced keys to prevent duplicates\n // BUT: Only filter out events that are actually from sync operations\n // New user transactions should NOT be filtered even if the key was recently synced\n const filteredEventsBySyncStatus = events.filter((event) => {\n if (!this.recentlySyncedKeys.has(event.key)) {\n return true // Key not recently synced, allow event through\n }\n\n // Key was recently synced - allow if this is a user-triggered action\n if (triggeredByUserAction) {\n return true\n }\n\n // Otherwise filter out duplicate sync events\n return false\n })\n\n // Filter out redundant delete events if there are pending sync transactions\n // that will immediately restore the same data, but only for completed transactions\n // IMPORTANT: Skip complex filtering for user-triggered actions to prevent UI blocking\n if (this.pendingSyncedTransactions.length > 0 && !triggeredByUserAction) {\n const pendingSyncKeysForFilter = new Set<TKey>()\n\n // Collect keys from pending sync operations\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n pendingSyncKeysForFilter.add(operation.key as TKey)\n }\n }\n\n // Only filter out delete events for keys that:\n // 1. Have pending sync operations AND\n // 2. Are from completed transactions (being cleaned up)\n const filteredEvents = filteredEventsBySyncStatus.filter((event) => {\n if (\n event.type === `delete` &&\n pendingSyncKeysForFilter.has(event.key)\n ) {\n // Check if this delete is from clearing optimistic state of completed transactions\n // We can infer this by checking if we have no remaining optimistic mutations for this key\n const hasActiveOptimisticMutation = activeTransactions.some((tx) =>\n tx.mutations.some(\n (m) => this.isThisCollection(m.collection) && m.key === event.key,\n ),\n )\n\n if (!hasActiveOptimisticMutation) {\n return false // Skip this delete event as sync will restore the data\n }\n }\n return true\n })\n\n // Update indexes for the filtered events\n if (filteredEvents.length > 0) {\n this.indexes.updateIndexes(filteredEvents)\n }\n this.changes.emitEvents(filteredEvents, triggeredByUserAction)\n } else {\n // Update indexes for all events\n if (filteredEventsBySyncStatus.length > 0) {\n this.indexes.updateIndexes(filteredEventsBySyncStatus)\n }\n // Emit all events if no pending sync transactions\n this.changes.emitEvents(filteredEventsBySyncStatus, triggeredByUserAction)\n }\n }\n\n /**\n * Calculate the current size based on synced data and optimistic changes\n */\n private calculateSize(): number {\n const syncedSize = this.syncedData.size\n const deletesFromSynced = Array.from(this.optimisticDeletes).filter(\n (key) => this.syncedData.has(key) && !this.optimisticUpserts.has(key),\n ).length\n const upsertsNotInSynced = Array.from(this.optimisticUpserts.keys()).filter(\n (key) => !this.syncedData.has(key),\n ).length\n\n return syncedSize - deletesFromSynced + upsertsNotInSynced\n }\n\n /**\n * Collect events for optimistic changes\n */\n private collectOptimisticChanges(\n previousUpserts: Map<TKey, TOutput>,\n previousDeletes: Set<TKey>,\n previousRowOrigins: ReadonlyMap<TKey, VirtualOrigin>,\n events: Array<InternalChangeMessage<TOutput, TKey>>,\n ): void {\n const allKeys = new Set([\n ...previousUpserts.keys(),\n ...this.optimisticUpserts.keys(),\n ...previousDeletes,\n ...this.optimisticDeletes,\n ])\n\n for (const key of allKeys) {\n const currentValue = this.get(key)\n const previousValue = this.getPreviousValue(\n key,\n previousUpserts,\n previousDeletes,\n )\n const previousVirtualProps = this.getVirtualPropsSnapshotForState(key, {\n rowOrigins: previousRowOrigins,\n optimisticUpserts: previousUpserts,\n optimisticDeletes: previousDeletes,\n })\n const nextVirtualProps = this.getVirtualPropsSnapshotForState(key)\n\n if (previousValue !== undefined && currentValue === undefined) {\n events.push({\n type: `delete`,\n key,\n value: previousValue,\n __virtualProps: {\n value: previousVirtualProps,\n },\n })\n } else if (previousValue === undefined && currentValue !== undefined) {\n events.push({\n type: `insert`,\n key,\n value: currentValue,\n __virtualProps: {\n value: nextVirtualProps,\n },\n })\n } else if (\n previousValue !== undefined &&\n currentValue !== undefined &&\n previousValue !== currentValue\n ) {\n events.push({\n type: `update`,\n key,\n value: currentValue,\n previousValue,\n __virtualProps: {\n value: nextVirtualProps,\n previousValue: previousVirtualProps,\n },\n })\n }\n }\n }\n\n /**\n * Get the previous value for a key given previous optimistic state\n */\n private getPreviousValue(\n key: TKey,\n previousUpserts: Map<TKey, TOutput>,\n previousDeletes: Set<TKey>,\n ): TOutput | undefined {\n if (previousDeletes.has(key)) {\n return undefined\n }\n if (previousUpserts.has(key)) {\n return previousUpserts.get(key)\n }\n return this.syncedData.get(key)\n }\n\n /**\n * Attempts to commit pending synced transactions if there are no active transactions\n * This method processes operations from pending transactions and applies them to the synced data\n */\n commitPendingTransactions = () => {\n // Check if there are any persisting transaction\n let hasPersistingTransaction = false\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `persisting`) {\n hasPersistingTransaction = true\n break\n }\n }\n\n // pending synced transactions could be either `committed` or still open.\n // we only want to process `committed` transactions here\n const {\n committedSyncedTransactions,\n uncommittedSyncedTransactions,\n hasTruncateSync,\n hasImmediateSync,\n } = this.pendingSyncedTransactions.reduce(\n (acc, t) => {\n if (t.committed) {\n acc.committedSyncedTransactions.push(t)\n if (t.truncate) {\n acc.hasTruncateSync = true\n }\n if (t.immediate) {\n acc.hasImmediateSync = true\n }\n } else {\n acc.uncommittedSyncedTransactions.push(t)\n }\n return acc\n },\n {\n committedSyncedTransactions: [] as Array<\n PendingSyncedTransaction<TOutput, TKey>\n >,\n uncommittedSyncedTransactions: [] as Array<\n PendingSyncedTransaction<TOutput, TKey>\n >,\n hasTruncateSync: false,\n hasImmediateSync: false,\n },\n )\n\n // Process committed transactions if:\n // 1. No persisting user transaction (normal sync flow), OR\n // 2. There's a truncate operation (must be processed immediately), OR\n // 3. There's an immediate transaction (manual writes must be processed synchronously)\n //\n // Note: When hasImmediateSync or hasTruncateSync is true, we process ALL committed\n // sync transactions (not just the immediate/truncate ones). This is intentional for\n // ordering correctness: if we only processed the immediate transaction, earlier\n // non-immediate transactions would be applied later and could overwrite newer state.\n // Processing all committed transactions together preserves causal ordering.\n if (!hasPersistingTransaction || hasTruncateSync || hasImmediateSync) {\n // Set flag to prevent redundant optimistic state recalculations\n this.isCommittingSyncTransactions = true\n\n const previousRowOrigins = new Map(this.rowOrigins)\n const previousOptimisticUpserts = new Map(this.optimisticUpserts)\n const previousOptimisticDeletes = new Set(this.optimisticDeletes)\n\n // Get the optimistic snapshot from the truncate transaction (captured when truncate() was called)\n const truncateOptimisticSnapshot = hasTruncateSync\n ? committedSyncedTransactions.find((t) => t.truncate)\n ?.optimisticSnapshot\n : null\n let truncatePendingLocalChanges: Set<TKey> | undefined\n let truncatePendingLocalOrigins: Set<TKey> | undefined\n\n // First collect all keys that will be affected by sync operations\n const changedKeys = new Set<TKey>()\n for (const transaction of committedSyncedTransactions) {\n for (const operation of transaction.operations) {\n changedKeys.add(operation.key as TKey)\n }\n for (const [key] of transaction.rowMetadataWrites) {\n changedKeys.add(key)\n }\n }\n\n // Use pre-captured state if available (from optimistic scenarios),\n // otherwise capture current state (for pure sync scenarios)\n let currentVisibleState = this.preSyncVisibleState\n if (currentVisibleState.size === 0) {\n // No pre-captured state, capture it now for pure sync operations\n currentVisibleState = new Map<TKey, TOutput>()\n for (const key of changedKeys) {\n const currentValue = this.get(key)\n if (currentValue !== undefined) {\n currentVisibleState.set(key, currentValue)\n }\n }\n }\n\n const events: Array<ChangeMessage<TOutput, TKey>> = []\n const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`\n const completedOptimisticOps = new Map<\n TKey,\n { type: string; value: TOutput }\n >()\n\n for (const transaction of this.transactions.values()) {\n if (transaction.state === `completed`) {\n for (const mutation of transaction.mutations) {\n if (this.isThisCollection(mutation.collection)) {\n if (mutation.optimistic) {\n completedOptimisticOps.set(mutation.key, {\n type: mutation.type,\n value: mutation.modified as TOutput,\n })\n }\n }\n }\n }\n }\n\n for (const transaction of committedSyncedTransactions) {\n // Handle truncate operations first\n if (transaction.truncate) {\n // TRUNCATE PHASE\n // 1) Emit a delete for every visible key (synced + optimistic) so downstream listeners/indexes\n // observe a clear-before-rebuild. We intentionally skip keys already in\n // optimisticDeletes because their delete was previously emitted by the user.\n // Use the snapshot to ensure we emit deletes for all items that existed at truncate start.\n const visibleKeys = new Set([\n ...this.syncedData.keys(),\n ...(truncateOptimisticSnapshot?.upserts.keys() || []),\n ])\n for (const key of visibleKeys) {\n if (truncateOptimisticSnapshot?.deletes.has(key)) continue\n const previousValue =\n truncateOptimisticSnapshot?.upserts.get(key) ||\n this.syncedData.get(key)\n if (previousValue !== undefined) {\n events.push({ type: `delete`, key, value: previousValue })\n }\n }\n\n // 2) Clear the authoritative synced base. Subsequent server ops in this\n // same commit will rebuild the base atomically.\n // Preserve pending local tracking just long enough for operations in this\n // truncate batch to retain correct local origin semantics.\n truncatePendingLocalChanges = new Set(this.pendingLocalChanges)\n truncatePendingLocalOrigins = new Set(this.pendingLocalOrigins)\n this.syncedData.clear()\n this.syncedMetadata.clear()\n this.syncedKeys.clear()\n this.clearOriginTrackingState()\n\n // 3) Clear currentVisibleState for truncated keys to ensure subsequent operations\n // are compared against the post-truncate state (undefined) rather than pre-truncate state\n // This ensures that re-inserted keys are emitted as INSERT events, not UPDATE events\n for (const key of changedKeys) {\n currentVisibleState.delete(key)\n }\n\n // 4) Emit truncate event so subscriptions can reset their cursor tracking state\n this._events.emit(`truncate`, {\n type: `truncate`,\n collection: this.collection,\n })\n }\n\n for (const operation of transaction.operations) {\n const key = operation.key as TKey\n this.syncedKeys.add(key)\n\n // Determine origin: 'local' for local-only collections or pending local changes\n const origin: VirtualOrigin =\n this.isLocalOnly ||\n this.pendingLocalChanges.has(key) ||\n this.pendingLocalOrigins.has(key) ||\n truncatePendingLocalChanges?.has(key) === true ||\n truncatePendingLocalOrigins?.has(key) === true\n ? 'local'\n : 'remote'\n\n // Update synced data\n switch (operation.type) {\n case `insert`:\n this.syncedData.set(key, operation.value)\n this.rowOrigins.set(key, origin)\n // Clear pending local changes now that sync has confirmed\n this.pendingLocalChanges.delete(key)\n this.pendingLocalOrigins.delete(key)\n this.pendingOptimisticUpserts.delete(key)\n this.pendingOptimisticDeletes.delete(key)\n this.pendingOptimisticDirectUpserts.delete(key)\n this.pendingOptimisticDirectDeletes.delete(key)\n break\n case `update`: {\n if (rowUpdateMode === `partial`) {\n const updatedValue = Object.assign(\n {},\n this.syncedData.get(key),\n operation.value,\n )\n this.syncedData.set(key, updatedValue)\n } else {\n this.syncedData.set(key, operation.value)\n }\n this.rowOrigins.set(key, origin)\n // Clear pending local changes now that sync has confirmed\n this.pendingLocalChanges.delete(key)\n this.pendingLocalOrigins.delete(key)\n this.pendingOptimisticUpserts.delete(key)\n this.pendingOptimisticDeletes.delete(key)\n this.pendingOptimisticDirectUpserts.delete(key)\n this.pendingOptimisticDirectDeletes.delete(key)\n break\n }\n case `delete`:\n this.syncedData.delete(key)\n this.syncedMetadata.delete(key)\n // Clean up origin and pending tracking for deleted rows\n this.rowOrigins.delete(key)\n this.pendingLocalChanges.delete(key)\n this.pendingLocalOrigins.delete(key)\n this.pendingOptimisticUpserts.delete(key)\n this.pendingOptimisticDeletes.delete(key)\n this.pendingOptimisticDirectUpserts.delete(key)\n this.pendingOptimisticDirectDeletes.delete(key)\n break\n }\n }\n\n for (const [key, metadataWrite] of transaction.rowMetadataWrites) {\n if (metadataWrite.type === `delete`) {\n this.syncedMetadata.delete(key)\n continue\n }\n this.syncedMetadata.set(key, metadataWrite.value)\n }\n\n for (const [\n key,\n metadataWrite,\n ] of transaction.collectionMetadataWrites) {\n if (metadataWrite.type === `delete`) {\n this.syncedCollectionMetadata.delete(key)\n continue\n }\n this.syncedCollectionMetadata.set(key, metadataWrite.value)\n }\n }\n\n // After applying synced operations, if this commit included a truncate,\n // re-apply optimistic mutations on top of the fresh synced base. This ensures\n // the UI preserves local intent while respecting server rebuild semantics.\n // Ordering: deletes (above) -> server ops (just applied) -> optimistic upserts.\n if (hasTruncateSync) {\n // Avoid duplicating keys that were inserted/updated by synced operations in this commit\n const syncedInsertedOrUpdatedKeys = new Set<TKey>()\n for (const t of committedSyncedTransactions) {\n for (const op of t.operations) {\n if (op.type === `insert` || op.type === `update`) {\n syncedInsertedOrUpdatedKeys.add(op.key as TKey)\n }\n }\n }\n\n // Build re-apply sets from the snapshot taken at the start of this function.\n // This prevents losing optimistic state if transactions complete during truncate processing.\n const reapplyUpserts = new Map<TKey, TOutput>(\n truncateOptimisticSnapshot!.upserts,\n )\n const reapplyDeletes = new Set<TKey>(\n truncateOptimisticSnapshot!.deletes,\n )\n\n // Emit inserts for re-applied upserts, skipping any keys that have an optimistic delete.\n // If the server also inserted/updated the same key in this batch, override that value\n // with the optimistic value to preserve local intent.\n for (const [key, value] of reapplyUpserts) {\n if (reapplyDeletes.has(key)) continue\n if (syncedInsertedOrUpdatedKeys.has(key)) {\n let foundInsert = false\n for (let i = events.length - 1; i >= 0; i--) {\n const evt = events[i]!\n if (evt.key === key && evt.type === `insert`) {\n evt.value = value\n foundInsert = true\n break\n }\n }\n if (!foundInsert) {\n events.push({ type: `insert`, key, value })\n }\n } else {\n events.push({ type: `insert`, key, value })\n }\n }\n\n // Finally, ensure we do NOT insert keys that have an outstanding optimistic delete.\n if (events.length > 0 && reapplyDeletes.size > 0) {\n const filtered: Array<ChangeMessage<TOutput, TKey>> = []\n for (const evt of events) {\n if (evt.type === `insert` && reapplyDeletes.has(evt.key)) {\n continue\n }\n filtered.push(evt)\n }\n events.length = 0\n events.push(...filtered)\n }\n\n // Ensure listeners are active before emitting this critical batch\n if (this.lifecycle.status !== `ready`) {\n this.lifecycle.markReady()\n }\n }\n\n // Maintain optimistic state appropriately\n // Clear optimistic state since sync operations will now provide the authoritative data.\n // Any still-active user transactions will be re-applied below in recompute.\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n\n // Reset flag and recompute optimistic state for any remaining active transactions\n this.isCommittingSyncTransactions = false\n\n // If we had a truncate, restore the preserved optimistic state from the snapshot\n // This includes items from transactions that may have completed during processing\n if (hasTruncateSync && truncateOptimisticSnapshot) {\n for (const [key, value] of truncateOptimisticSnapshot.upserts) {\n this.optimisticUpserts.set(key, value)\n }\n for (const key of truncateOptimisticSnapshot.deletes) {\n this.optimisticDeletes.add(key)\n }\n }\n\n // Always overlay any still-active optimistic transactions so mutations that started\n // after the truncate snapshot are preserved.\n for (const transaction of this.transactions.values()) {\n if (![`completed`, `failed`].includes(transaction.state)) {\n for (const mutation of transaction.mutations) {\n if (\n this.isThisCollection(mutation.collection) &&\n mutation.optimistic\n ) {\n switch (mutation.type) {\n case `insert`:\n case `update`:\n this.optimisticUpserts.set(\n mutation.key,\n mutation.modified as TOutput,\n )\n this.optimisticDeletes.delete(mutation.key)\n break\n case `delete`:\n this.optimisticUpserts.delete(mutation.key)\n this.optimisticDeletes.add(mutation.key)\n break\n }\n }\n }\n }\n }\n\n // Now check what actually changed in the final visible state\n for (const key of changedKeys) {\n const previousVisibleValue = currentVisibleState.get(key)\n const newVisibleValue = this.get(key) // This returns the new derived state\n const previousVirtualProps = this.getVirtualPropsSnapshotForState(key, {\n rowOrigins: previousRowOrigins,\n optimisticUpserts: previousOptimisticUpserts,\n optimisticDeletes: previousOptimisticDeletes,\n completedOptimisticKeys: completedOptimisticOps,\n })\n const nextVirtualProps = this.getVirtualPropsSnapshotForState(key)\n const virtualChanged =\n previousVirtualProps.$synced !== nextVirtualProps.$synced ||\n previousVirtualProps.$origin !== nextVirtualProps.$origin\n const previousValueWithVirtual =\n previousVisibleValue !== undefined\n ? enrichRowWithVirtualProps(\n previousVisibleValue,\n key,\n this.collection.id,\n () => previousVirtualProps.$synced,\n () => previousVirtualProps.$origin,\n )\n : undefined\n\n // Check if this sync operation is redundant with a completed optimistic operation\n const completedOp = completedOptimisticOps.get(key)\n let isRedundantSync = false\n\n if (completedOp) {\n if (\n completedOp.type === `delete` &&\n previousVisibleValue !== undefined &&\n newVisibleValue === undefined &&\n deepEquals(completedOp.value, previousVisibleValue)\n ) {\n isRedundantSync = true\n } else if (\n newVisibleValue !== undefined &&\n deepEquals(completedOp.value, newVisibleValue)\n ) {\n isRedundantSync = true\n }\n }\n\n const shouldEmitVirtualUpdate =\n virtualChanged &&\n previousVisibleValue !== undefined &&\n newVisibleValue !== undefined &&\n deepEquals(previousVisibleValue, newVisibleValue)\n\n if (isRedundantSync && !shouldEmitVirtualUpdate) {\n continue\n }\n\n if (\n previousVisibleValue === undefined &&\n newVisibleValue !== undefined\n ) {\n const completedOptimisticOp = completedOptimisticOps.get(key)\n if (completedOptimisticOp) {\n const previousValueFromCompleted = completedOptimisticOp.value\n const previousValueWithVirtualFromCompleted =\n enrichRowWithVirtualProps(\n previousValueFromCompleted,\n key,\n this.collection.id,\n () => previousVirtualProps.$synced,\n () => previousVirtualProps.$origin,\n )\n events.push({\n type: `update`,\n key,\n value: newVisibleValue,\n previousValue: previousValueWithVirtualFromCompleted,\n })\n } else {\n events.push({\n type: `insert`,\n key,\n value: newVisibleValue,\n })\n }\n } else if (\n previousVisibleValue !== undefined &&\n newVisibleValue === undefined\n ) {\n events.push({\n type: `delete`,\n key,\n value: previousValueWithVirtual ?? previousVisibleValue,\n })\n } else if (\n previousVisibleValue !== undefined &&\n newVisibleValue !== undefined &&\n (!deepEquals(previousVisibleValue, newVisibleValue) ||\n shouldEmitVirtualUpdate)\n ) {\n events.push({\n type: `update`,\n key,\n value: newVisibleValue,\n previousValue: previousValueWithVirtual ?? previousVisibleValue,\n })\n }\n }\n\n // Update cached size after synced data changes\n this.size = this.calculateSize()\n\n // Update indexes for all events before emitting\n if (events.length > 0) {\n this.indexes.updateIndexes(events)\n }\n\n // End batching and emit all events (combines any batched events with sync events)\n this.changes.emitEvents(events, true)\n\n this.pendingSyncedTransactions = uncommittedSyncedTransactions\n\n // Clear the pre-sync state since sync operations are complete\n this.preSyncVisibleState.clear()\n\n // Clear recently synced keys after a microtask to allow recomputeOptimisticState to see them\n Promise.resolve().then(() => {\n this.recentlySyncedKeys.clear()\n })\n\n // Mark that we've received the first commit (for tracking purposes)\n if (!this.hasReceivedFirstCommit) {\n this.hasReceivedFirstCommit = true\n }\n }\n }\n\n /**\n * Schedule cleanup of a transaction when it completes\n */\n public scheduleTransactionCleanup(transaction: Transaction<any>): void {\n // Only schedule cleanup for transactions that aren't already completed\n if (transaction.state === `completed`) {\n this.transactions.delete(transaction.id)\n return\n }\n\n // Schedule cleanup when the transaction completes\n transaction.isPersisted.promise\n .then(() => {\n // Transaction completed successfully, remove it immediately\n this.transactions.delete(transaction.id)\n })\n .catch(() => {\n // Transaction failed, but we want to keep failed transactions for reference\n // so don't remove it.\n // Rollback already triggers state recomputation via touchCollection().\n })\n }\n\n /**\n * Capture visible state for keys that will be affected by pending sync operations\n * This must be called BEFORE onTransactionStateChange clears optimistic state\n */\n public capturePreSyncVisibleState(): void {\n if (this.pendingSyncedTransactions.length === 0) return\n\n // Get all keys that will be affected by sync operations\n const syncedKeys = new Set<TKey>()\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n syncedKeys.add(operation.key as TKey)\n }\n }\n\n // Mark keys as about to be synced to suppress intermediate events from recomputeOptimisticState\n for (const key of syncedKeys) {\n this.recentlySyncedKeys.add(key)\n }\n\n // Only capture current visible state for keys that will be affected by sync operations\n // This is much more efficient than capturing the entire collection state\n // Only capture keys that haven't been captured yet to preserve earlier captures\n for (const key of syncedKeys) {\n if (!this.preSyncVisibleState.has(key)) {\n const currentValue = this.get(key)\n if (currentValue !== undefined) {\n this.preSyncVisibleState.set(key, currentValue)\n }\n }\n }\n }\n\n /**\n * Trigger a recomputation when transactions change\n * This method should be called by the Transaction class when state changes\n */\n public onTransactionStateChange(): void {\n // Check if commitPendingTransactions will be called after this\n // by checking if there are pending sync transactions (same logic as in transactions.ts)\n this.changes.shouldBatchEvents = this.pendingSyncedTransactions.length > 0\n\n // CRITICAL: Capture visible state BEFORE clearing optimistic state\n this.capturePreSyncVisibleState()\n\n this.recomputeOptimisticState(false)\n }\n\n /**\n * Clean up the collection by stopping sync and clearing data\n * This can be called manually or automatically by garbage collection\n */\n public cleanup(): void {\n this.syncedData.clear()\n this.syncedMetadata.clear()\n this.syncedCollectionMetadata.clear()\n this.optimisticUpserts.clear()\n this.optimisticDeletes.clear()\n this.pendingOptimisticUpserts.clear()\n this.pendingOptimisticDeletes.clear()\n this.pendingOptimisticDirectUpserts.clear()\n this.pendingOptimisticDirectDeletes.clear()\n this.clearOriginTrackingState()\n this.isLocalOnly = false\n this.size = 0\n this.pendingSyncedTransactions = []\n this.syncedKeys.clear()\n this.hasReceivedFirstCommit = false\n }\n}\n"],"names":["enrichRowWithVirtualProps","deepEquals","SortedMap","virtualProps","DIRECT_TRANSACTION_METADATA_KEY"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { SortedMap } from '../SortedMap.cjs';
|
|
2
|
+
import { VirtualOrigin, WithVirtualProps } from '../virtual-props.js';
|
|
2
3
|
import { Transaction } from '../transactions.cjs';
|
|
3
4
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
4
|
-
import { CollectionConfig, OptimisticChangeMessage } from '../types.cjs';
|
|
5
|
+
import { ChangeMessage, CollectionConfig, OptimisticChangeMessage } from '../types.cjs';
|
|
5
6
|
import { CollectionImpl } from './index.js';
|
|
6
7
|
import { CollectionLifecycleManager } from './lifecycle.cjs';
|
|
7
8
|
import { CollectionChangesManager } from './changes.cjs';
|
|
@@ -12,6 +13,8 @@ interface PendingSyncedTransaction<T extends object = Record<string, unknown>, T
|
|
|
12
13
|
operations: Array<OptimisticChangeMessage<T>>;
|
|
13
14
|
truncate?: boolean;
|
|
14
15
|
deletedKeys: Set<string | number>;
|
|
16
|
+
rowMetadataWrites: Map<TKey, PendingMetadataWrite>;
|
|
17
|
+
collectionMetadataWrites: Map<string, PendingMetadataWrite>;
|
|
15
18
|
optimisticSnapshot?: {
|
|
16
19
|
upserts: Map<TKey, T>;
|
|
17
20
|
deletes: Set<TKey>;
|
|
@@ -23,6 +26,12 @@ interface PendingSyncedTransaction<T extends object = Record<string, unknown>, T
|
|
|
23
26
|
*/
|
|
24
27
|
immediate?: boolean;
|
|
25
28
|
}
|
|
29
|
+
type PendingMetadataWrite = {
|
|
30
|
+
type: `set`;
|
|
31
|
+
value: unknown;
|
|
32
|
+
} | {
|
|
33
|
+
type: `delete`;
|
|
34
|
+
};
|
|
26
35
|
export declare class CollectionStateManager<TOutput extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInput extends object = TOutput> {
|
|
27
36
|
config: CollectionConfig<TOutput, TKey, TSchema>;
|
|
28
37
|
collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>;
|
|
@@ -34,14 +43,38 @@ export declare class CollectionStateManager<TOutput extends object = Record<stri
|
|
|
34
43
|
pendingSyncedTransactions: Array<PendingSyncedTransaction<TOutput, TKey>>;
|
|
35
44
|
syncedData: SortedMap<TKey, TOutput>;
|
|
36
45
|
syncedMetadata: Map<TKey, unknown>;
|
|
46
|
+
syncedCollectionMetadata: Map<string, unknown>;
|
|
37
47
|
optimisticUpserts: Map<TKey, TOutput>;
|
|
38
48
|
optimisticDeletes: Set<TKey>;
|
|
49
|
+
pendingOptimisticUpserts: Map<TKey, TOutput>;
|
|
50
|
+
pendingOptimisticDeletes: Set<TKey>;
|
|
51
|
+
pendingOptimisticDirectUpserts: Set<TKey>;
|
|
52
|
+
pendingOptimisticDirectDeletes: Set<TKey>;
|
|
53
|
+
/**
|
|
54
|
+
* Tracks the origin of confirmed changes for each row.
|
|
55
|
+
* 'local' = change originated from this client
|
|
56
|
+
* 'remote' = change was received via sync
|
|
57
|
+
*
|
|
58
|
+
* This is used for the $origin virtual property.
|
|
59
|
+
* Note: This only tracks *confirmed* changes, not optimistic ones.
|
|
60
|
+
* Optimistic changes are always considered 'local' for $origin.
|
|
61
|
+
*/
|
|
62
|
+
rowOrigins: Map<TKey, VirtualOrigin>;
|
|
63
|
+
/**
|
|
64
|
+
* Tracks keys that have pending local changes.
|
|
65
|
+
* Used to determine whether sync-confirmed data should have 'local' or 'remote' origin.
|
|
66
|
+
* When sync confirms data for a key with pending local changes, it keeps 'local' origin.
|
|
67
|
+
*/
|
|
68
|
+
pendingLocalChanges: Set<TKey>;
|
|
69
|
+
pendingLocalOrigins: Set<TKey>;
|
|
70
|
+
private virtualPropsCache;
|
|
39
71
|
size: number;
|
|
40
72
|
syncedKeys: Set<TKey>;
|
|
41
73
|
preSyncVisibleState: Map<TKey, TOutput>;
|
|
42
74
|
recentlySyncedKeys: Set<TKey>;
|
|
43
75
|
hasReceivedFirstCommit: boolean;
|
|
44
76
|
isCommittingSyncTransactions: boolean;
|
|
77
|
+
isLocalOnly: boolean;
|
|
45
78
|
/**
|
|
46
79
|
* Creates a new CollectionState manager
|
|
47
80
|
*/
|
|
@@ -53,6 +86,37 @@ export declare class CollectionStateManager<TOutput extends object = Record<stri
|
|
|
53
86
|
indexes: CollectionIndexesManager<TOutput, TKey, TSchema, TInput>;
|
|
54
87
|
events: CollectionEventsManager;
|
|
55
88
|
}): void;
|
|
89
|
+
/**
|
|
90
|
+
* Checks if a row has pending optimistic mutations (not yet confirmed by sync).
|
|
91
|
+
* Used to compute the $synced virtual property.
|
|
92
|
+
*/
|
|
93
|
+
isRowSynced(key: TKey): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Gets the origin of the last confirmed change to a row.
|
|
96
|
+
* Returns 'local' if the row has optimistic mutations (optimistic changes are local).
|
|
97
|
+
* Used to compute the $origin virtual property.
|
|
98
|
+
*/
|
|
99
|
+
getRowOrigin(key: TKey): VirtualOrigin;
|
|
100
|
+
private createVirtualPropsSnapshot;
|
|
101
|
+
private getVirtualPropsSnapshotForState;
|
|
102
|
+
private enrichWithVirtualPropsSnapshot;
|
|
103
|
+
private clearOriginTrackingState;
|
|
104
|
+
/**
|
|
105
|
+
* Enriches a row with virtual properties using the "add-if-missing" pattern.
|
|
106
|
+
* If the row already has virtual properties (from an upstream collection),
|
|
107
|
+
* they are preserved. Otherwise, new values are computed.
|
|
108
|
+
*/
|
|
109
|
+
enrichWithVirtualProps(row: TOutput, key: TKey): WithVirtualProps<TOutput, TKey>;
|
|
110
|
+
/**
|
|
111
|
+
* Creates a change message with virtual properties.
|
|
112
|
+
* Uses the "add-if-missing" pattern so that pass-through from upstream
|
|
113
|
+
* collections works correctly.
|
|
114
|
+
*/
|
|
115
|
+
enrichChangeMessage(change: ChangeMessage<TOutput, TKey>): ChangeMessage<WithVirtualProps<TOutput, TKey>, TKey>;
|
|
116
|
+
/**
|
|
117
|
+
* Get the current value for a key enriched with virtual properties.
|
|
118
|
+
*/
|
|
119
|
+
getWithVirtualProps(key: TKey): WithVirtualProps<TOutput, TKey> | undefined;
|
|
56
120
|
/**
|
|
57
121
|
* Get the current value for a key (virtual derived state)
|
|
58
122
|
*/
|
|
@@ -113,6 +113,12 @@ class CollectionSubscription extends eventEmitter.EventEmitter {
|
|
|
113
113
|
setOrderByIndex(index) {
|
|
114
114
|
this.orderByIndex = index;
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Check if an orderBy index has been set for this subscription
|
|
118
|
+
*/
|
|
119
|
+
hasOrderByIndex() {
|
|
120
|
+
return this.orderByIndex !== void 0;
|
|
121
|
+
}
|
|
116
122
|
/**
|
|
117
123
|
* Set subscription status and emit events if changed
|
|
118
124
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription.cjs","sources":["../../../src/collection/subscription.ts"],"sourcesContent":["import { ensureIndexForExpression } from '../indexes/auto-index.js'\nimport { and, eq, gte, lt } from '../query/builder/functions.js'\nimport { PropRef, Value } from '../query/ir.js'\nimport { EventEmitter } from '../event-emitter.js'\nimport { compileExpression } from '../query/compiler/evaluators.js'\nimport { buildCursor } from '../utils/cursor.js'\nimport {\n createFilterFunctionFromExpression,\n createFilteredCallback,\n} from './change-events.js'\nimport type { BasicExpression, OrderBy } from '../query/ir.js'\nimport type { IndexInterface } from '../indexes/base-index.js'\nimport type {\n ChangeMessage,\n LoadSubsetOptions,\n Subscription,\n SubscriptionEvents,\n SubscriptionStatus,\n SubscriptionUnsubscribedEvent,\n} from '../types.js'\nimport type { CollectionImpl } from './index.js'\n\ntype RequestSnapshotOptions = {\n where?: BasicExpression<boolean>\n optimizedOnly?: boolean\n trackLoadSubsetPromise?: boolean\n /** Optional orderBy to pass to loadSubset for backend optimization */\n orderBy?: OrderBy\n /** Optional limit to pass to loadSubset for backend optimization */\n limit?: number\n /** Callback that receives the raw loadSubset result for external tracking */\n onLoadSubsetResult?: (result: Promise<void> | true) => void\n}\n\ntype RequestLimitedSnapshotOptions = {\n orderBy: OrderBy\n limit: number\n /** All column values for cursor (first value used for local index, all values for sync layer) */\n minValues?: Array<unknown>\n /** Row offset for offset-based pagination (passed to sync layer) */\n offset?: number\n /** Whether to track the loadSubset promise on this subscription (default: true) */\n trackLoadSubsetPromise?: boolean\n /** Callback that receives the raw loadSubset result for external tracking */\n onLoadSubsetResult?: (result: Promise<void> | true) => void\n}\n\ntype CollectionSubscriptionOptions = {\n includeInitialState?: boolean\n /** Pre-compiled expression for filtering changes */\n whereExpression?: BasicExpression<boolean>\n /** Callback to call when the subscription is unsubscribed */\n onUnsubscribe?: (event: SubscriptionUnsubscribedEvent) => void\n}\n\nexport class CollectionSubscription\n extends EventEmitter<SubscriptionEvents>\n implements Subscription\n{\n private loadedInitialState = false\n\n // Flag to skip filtering in filterAndFlipChanges.\n // This is separate from loadedInitialState because we want to allow\n // requestSnapshot to still work even when filtering is skipped.\n private skipFiltering = false\n\n // Flag to indicate that we have sent at least 1 snapshot.\n // While `snapshotSent` is false we filter out all changes from subscription to the collection.\n private snapshotSent = false\n\n /**\n * Track all loadSubset calls made by this subscription so we can unload them on cleanup.\n * We store the exact LoadSubsetOptions we passed to loadSubset to ensure symmetric unload.\n */\n private loadedSubsets: Array<LoadSubsetOptions> = []\n\n // Keep track of the keys we've sent (needed for join and orderBy optimizations)\n private sentKeys = new Set<string | number>()\n\n // Track the count of rows sent via requestLimitedSnapshot for offset-based pagination\n private limitedSnapshotRowCount = 0\n\n // Track the last key sent via requestLimitedSnapshot for cursor-based pagination\n private lastSentKey: string | number | undefined\n\n private filteredCallback: (changes: Array<ChangeMessage<any, any>>) => void\n\n private orderByIndex: IndexInterface<string | number> | undefined\n\n // Status tracking\n private _status: SubscriptionStatus = `ready`\n private pendingLoadSubsetPromises: Set<Promise<void>> = new Set()\n\n // Cleanup function for truncate event listener\n private truncateCleanup: (() => void) | undefined\n\n // Truncate buffering state\n // When a truncate occurs, we buffer changes until all loadSubset refetches complete\n // This prevents a flash of missing content between deletes and new inserts\n private isBufferingForTruncate = false\n private truncateBuffer: Array<Array<ChangeMessage<any, any>>> = []\n private pendingTruncateRefetches: Set<Promise<void>> = new Set()\n\n public get status(): SubscriptionStatus {\n return this._status\n }\n\n constructor(\n private collection: CollectionImpl<any, any, any, any, any>,\n private callback: (changes: Array<ChangeMessage<any, any>>) => void,\n private options: CollectionSubscriptionOptions,\n ) {\n super()\n if (options.onUnsubscribe) {\n this.on(`unsubscribed`, (event) => options.onUnsubscribe!(event))\n }\n\n // Auto-index for where expressions if enabled\n if (options.whereExpression) {\n ensureIndexForExpression(options.whereExpression, this.collection)\n }\n\n const callbackWithSentKeysTracking = (\n changes: Array<ChangeMessage<any, any>>,\n ) => {\n callback(changes)\n this.trackSentKeys(changes)\n }\n\n this.callback = callbackWithSentKeysTracking\n\n // Create a filtered callback if where clause is provided\n this.filteredCallback = options.whereExpression\n ? createFilteredCallback(this.callback, options)\n : this.callback\n\n // Listen for truncate events to re-request data after must-refetch\n // When a truncate happens (e.g., from a 409 must-refetch), all collection data is cleared.\n // We need to re-request all previously loaded subsets to repopulate the data.\n this.truncateCleanup = this.collection.on(`truncate`, () => {\n this.handleTruncate()\n })\n }\n\n /**\n * Handle collection truncate event by resetting state and re-requesting subsets.\n * This is called when the sync layer receives a must-refetch and clears all data.\n *\n * To prevent a flash of missing content, we buffer all changes (deletes from truncate\n * and inserts from refetch) until all loadSubset promises resolve, then emit them together.\n */\n private handleTruncate() {\n // Copy the loaded subsets before clearing (we'll re-request them)\n const subsetsToReload = [...this.loadedSubsets]\n\n // Only buffer if there's an actual loadSubset handler that can do async work.\n // Without a loadSubset handler, there's nothing to re-request and no reason to buffer.\n // This prevents unnecessary buffering in eager sync mode or when loadSubset isn't implemented.\n const hasLoadSubsetHandler = this.collection._sync.syncLoadSubsetFn !== null\n\n // If there are no subsets to reload OR no loadSubset handler, just reset state\n if (subsetsToReload.length === 0 || !hasLoadSubsetHandler) {\n this.snapshotSent = false\n this.loadedInitialState = false\n this.limitedSnapshotRowCount = 0\n this.lastSentKey = undefined\n this.loadedSubsets = []\n return\n }\n\n // Start buffering BEFORE we receive the delete events from the truncate commit\n // This ensures we capture both the deletes and subsequent inserts\n this.isBufferingForTruncate = true\n this.truncateBuffer = []\n this.pendingTruncateRefetches.clear()\n\n // Reset snapshot/pagination tracking state\n // Note: We don't need to populate sentKeys here because filterAndFlipChanges\n // will skip the delete filter when isBufferingForTruncate is true\n this.snapshotSent = false\n this.loadedInitialState = false\n this.limitedSnapshotRowCount = 0\n this.lastSentKey = undefined\n\n // Clear the loadedSubsets array since we're re-requesting fresh\n this.loadedSubsets = []\n\n // Defer the loadSubset calls to a microtask so the truncate commit's delete events\n // are buffered BEFORE the loadSubset calls potentially trigger nested commits.\n // This ensures correct event ordering: deletes first, then inserts.\n queueMicrotask(() => {\n // Check if we were unsubscribed while waiting\n if (!this.isBufferingForTruncate) {\n return\n }\n\n // Re-request all previously loaded subsets and track their promises\n for (const options of subsetsToReload) {\n const syncResult = this.collection._sync.loadSubset(options)\n\n // Track this loadSubset call so we can unload it later\n this.loadedSubsets.push(options)\n this.trackLoadSubsetPromise(syncResult)\n\n // Track the promise for buffer flushing\n if (syncResult instanceof Promise) {\n this.pendingTruncateRefetches.add(syncResult)\n syncResult\n .catch(() => {\n // Ignore errors - we still want to flush the buffer even if some requests fail\n })\n .finally(() => {\n this.pendingTruncateRefetches.delete(syncResult)\n this.checkTruncateRefetchComplete()\n })\n }\n }\n\n // If all loadSubset calls were synchronous (returned true), flush now\n // At this point, delete events have already been buffered from the truncate commit\n if (this.pendingTruncateRefetches.size === 0) {\n this.flushTruncateBuffer()\n }\n })\n }\n\n /**\n * Check if all truncate refetch promises have completed and flush buffer if so\n */\n private checkTruncateRefetchComplete() {\n if (\n this.pendingTruncateRefetches.size === 0 &&\n this.isBufferingForTruncate\n ) {\n this.flushTruncateBuffer()\n }\n }\n\n /**\n * Flush the truncate buffer, emitting all buffered changes to the callback\n */\n private flushTruncateBuffer() {\n this.isBufferingForTruncate = false\n\n // Flatten all buffered changes into a single array for atomic emission\n // This ensures consumers see all truncate changes (deletes + inserts) in one callback\n const merged = this.truncateBuffer.flat()\n if (merged.length > 0) {\n this.filteredCallback(merged)\n }\n\n this.truncateBuffer = []\n }\n\n setOrderByIndex(index: IndexInterface<any>) {\n this.orderByIndex = index\n }\n\n /**\n * Set subscription status and emit events if changed\n */\n private setStatus(newStatus: SubscriptionStatus) {\n if (this._status === newStatus) {\n return // No change\n }\n\n const previousStatus = this._status\n this._status = newStatus\n\n // Emit status:change event\n this.emitInner(`status:change`, {\n type: `status:change`,\n subscription: this,\n previousStatus,\n status: newStatus,\n })\n\n // Emit specific status event\n const eventKey: `status:${SubscriptionStatus}` = `status:${newStatus}`\n this.emitInner(eventKey, {\n type: eventKey,\n subscription: this,\n previousStatus,\n status: newStatus,\n } as SubscriptionEvents[typeof eventKey])\n }\n\n /**\n * Track a loadSubset promise and manage loading status\n */\n private trackLoadSubsetPromise(syncResult: Promise<void> | true) {\n // Track the promise if it's actually a promise (async work)\n if (syncResult instanceof Promise) {\n this.pendingLoadSubsetPromises.add(syncResult)\n this.setStatus(`loadingSubset`)\n\n syncResult.finally(() => {\n this.pendingLoadSubsetPromises.delete(syncResult)\n if (this.pendingLoadSubsetPromises.size === 0) {\n this.setStatus(`ready`)\n }\n })\n }\n }\n\n hasLoadedInitialState() {\n return this.loadedInitialState\n }\n\n hasSentAtLeastOneSnapshot() {\n return this.snapshotSent\n }\n\n emitEvents(changes: Array<ChangeMessage<any, any>>) {\n const newChanges = this.filterAndFlipChanges(changes)\n\n if (this.isBufferingForTruncate) {\n // Buffer the changes instead of emitting immediately\n // This prevents a flash of missing content during truncate/refetch\n if (newChanges.length > 0) {\n this.truncateBuffer.push(newChanges)\n }\n } else {\n this.filteredCallback(newChanges)\n }\n }\n\n /**\n * Sends the snapshot to the callback.\n * Returns a boolean indicating if it succeeded.\n * It can only fail if there is no index to fulfill the request\n * and the optimizedOnly option is set to true,\n * or, the entire state was already loaded.\n */\n requestSnapshot(opts?: RequestSnapshotOptions): boolean {\n if (this.loadedInitialState) {\n // Subscription was deoptimized so we already sent the entire initial state\n return false\n }\n\n const stateOpts: RequestSnapshotOptions = {\n where: this.options.whereExpression,\n optimizedOnly: opts?.optimizedOnly ?? false,\n }\n\n if (opts) {\n if (`where` in opts) {\n const snapshotWhereExp = opts.where\n if (stateOpts.where) {\n // Combine the two where expressions\n const subWhereExp = stateOpts.where\n const combinedWhereExp = and(subWhereExp, snapshotWhereExp)\n stateOpts.where = combinedWhereExp\n } else {\n stateOpts.where = snapshotWhereExp\n }\n }\n } else {\n // No options provided so it's loading the entire initial state\n this.loadedInitialState = true\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n const loadOptions: LoadSubsetOptions = {\n where: stateOpts.where,\n subscription: this,\n // Include orderBy and limit if provided so sync layer can optimize the query\n orderBy: opts?.orderBy,\n limit: opts?.limit,\n }\n const syncResult = this.collection._sync.loadSubset(loadOptions)\n\n // Pass the raw loadSubset result to the caller for external tracking\n opts?.onLoadSubsetResult?.(syncResult)\n\n // Track this loadSubset call so we can unload it later\n this.loadedSubsets.push(loadOptions)\n\n const trackLoadSubsetPromise = opts?.trackLoadSubsetPromise ?? true\n if (trackLoadSubsetPromise) {\n this.trackLoadSubsetPromise(syncResult)\n }\n\n // Also load data immediately from the collection\n const snapshot = this.collection.currentStateAsChanges(stateOpts)\n\n if (snapshot === undefined) {\n // Couldn't load from indexes\n return false\n }\n\n // Only send changes that have not been sent yet\n const filteredSnapshot = snapshot.filter(\n (change) => !this.sentKeys.has(change.key),\n )\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of filteredSnapshot) {\n this.sentKeys.add(change.key)\n }\n\n this.snapshotSent = true\n this.callback(filteredSnapshot)\n return true\n }\n\n /**\n * Sends a snapshot that fulfills the `where` clause and all rows are bigger or equal to the cursor.\n * Requires a range index to be set with `setOrderByIndex` prior to calling this method.\n * It uses that range index to load the items in the order of the index.\n *\n * For multi-column orderBy:\n * - Uses first value from `minValues` for LOCAL index operations (wide bounds, ensures no missed rows)\n * - Uses all `minValues` to build a precise composite cursor for SYNC layer loadSubset\n *\n * Note 1: it may load more rows than the provided LIMIT because it loads all values equal to the first cursor value + limit values greater.\n * This is needed to ensure that it does not accidentally skip duplicate values when the limit falls in the middle of some duplicated values.\n * Note 2: it does not send keys that have already been sent before.\n */\n requestLimitedSnapshot({\n orderBy,\n limit,\n minValues,\n offset,\n trackLoadSubsetPromise: shouldTrackLoadSubsetPromise = true,\n onLoadSubsetResult,\n }: RequestLimitedSnapshotOptions) {\n if (!limit) throw new Error(`limit is required`)\n\n if (!this.orderByIndex) {\n throw new Error(\n `Ordered snapshot was requested but no index was found. You have to call setOrderByIndex before requesting an ordered snapshot.`,\n )\n }\n\n // Check if minValues has a first element (regardless of its value)\n // This distinguishes between \"no min value provided\" vs \"min value is undefined\"\n const hasMinValue = minValues !== undefined && minValues.length > 0\n // Derive first column value from minValues (used for local index operations)\n const minValue = minValues?.[0]\n // Cast for index operations (index expects string | number)\n const minValueForIndex = minValue as string | number | undefined\n\n const index = this.orderByIndex\n const where = this.options.whereExpression\n const whereFilterFn = where\n ? createFilterFunctionFromExpression(where)\n : undefined\n\n const filterFn = (key: string | number | undefined): boolean => {\n if (key !== undefined && this.sentKeys.has(key)) {\n return false\n }\n\n const value = this.collection.get(key)\n if (value === undefined) {\n return false\n }\n\n return whereFilterFn?.(value) ?? true\n }\n\n let biggestObservedValue = minValueForIndex\n const changes: Array<ChangeMessage<any, string | number>> = []\n\n // If we have a minValue we need to handle the case\n // where there might be duplicate values equal to minValue that we need to include\n // because we can have data like this: [1, 2, 3, 3, 3, 4, 5]\n // so if minValue is 3 then the previous snapshot may not have included all 3s\n // e.g. if it was offset 0 and limit 3 it would only have loaded the first 3\n // so we load all rows equal to minValue first, to be sure we don't skip any duplicate values\n //\n // For multi-column orderBy, we use the first column value for index operations (wide bounds)\n // This may load some duplicates but ensures we never miss any rows.\n let keys: Array<string | number> = []\n if (hasMinValue) {\n // First, get all items with the same FIRST COLUMN value as minValue\n // This provides wide bounds for the local index\n const { expression } = orderBy[0]!\n const allRowsWithMinValue = this.collection.currentStateAsChanges({\n where: eq(expression, new Value(minValueForIndex)),\n })\n\n if (allRowsWithMinValue) {\n const keysWithMinValue = allRowsWithMinValue\n .map((change) => change.key)\n .filter((key) => !this.sentKeys.has(key) && filterFn(key))\n\n // Add items with the minValue first\n keys.push(...keysWithMinValue)\n\n // Then get items greater than minValue\n const keysGreaterThanMin = index.take(\n limit - keys.length,\n minValueForIndex!,\n filterFn,\n )\n keys.push(...keysGreaterThanMin)\n } else {\n keys = index.take(limit, minValueForIndex!, filterFn)\n }\n } else {\n // No min value provided, start from the beginning\n keys = index.takeFromStart(limit, filterFn)\n }\n\n const valuesNeeded = () => Math.max(limit - changes.length, 0)\n const collectionExhausted = () => keys.length === 0\n\n // Create a value extractor for the orderBy field to properly track the biggest indexed value\n const orderByExpression = orderBy[0]!.expression\n const valueExtractor =\n orderByExpression.type === `ref`\n ? compileExpression(new PropRef(orderByExpression.path), true)\n : null\n\n while (valuesNeeded() > 0 && !collectionExhausted()) {\n const insertedKeys = new Set<string | number>() // Track keys we add to `changes` in this iteration\n\n for (const key of keys) {\n const value = this.collection.get(key)!\n changes.push({\n type: `insert`,\n key,\n value,\n })\n // Extract the indexed value (e.g., salary) from the row, not the full row\n // This is needed for index.take() to work correctly with the BTree comparator\n biggestObservedValue = valueExtractor ? valueExtractor(value) : value\n insertedKeys.add(key) // Track this key\n }\n\n keys = index.take(valuesNeeded(), biggestObservedValue!, filterFn)\n }\n\n // Track row count for offset-based pagination (before sending to callback)\n // Use the current count as the offset for this load\n const currentOffset = this.limitedSnapshotRowCount\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of changes) {\n this.sentKeys.add(change.key)\n }\n\n this.callback(changes)\n\n // Update the row count and last key after sending (for next call's offset/cursor)\n this.limitedSnapshotRowCount += changes.length\n if (changes.length > 0) {\n this.lastSentKey = changes[changes.length - 1]!.key\n }\n\n // Build cursor expressions for sync layer loadSubset\n // The cursor expressions are separate from the main where clause\n // so the sync layer can choose cursor-based or offset-based pagination\n let cursorExpressions:\n | {\n whereFrom: BasicExpression<boolean>\n whereCurrent: BasicExpression<boolean>\n lastKey?: string | number\n }\n | undefined\n\n if (minValues !== undefined && minValues.length > 0) {\n const whereFromCursor = buildCursor(orderBy, minValues)\n\n if (whereFromCursor) {\n const { expression } = orderBy[0]!\n const cursorMinValue = minValues[0]\n\n // Build the whereCurrent expression for the first orderBy column\n // For Date values, we need to handle precision differences between JS (ms) and backends (μs)\n // A JS Date represents a 1ms range, so we query for all values within that range\n let whereCurrentCursor: BasicExpression<boolean>\n if (cursorMinValue instanceof Date) {\n const cursorMinValuePlus1ms = new Date(cursorMinValue.getTime() + 1)\n whereCurrentCursor = and(\n gte(expression, new Value(cursorMinValue)),\n lt(expression, new Value(cursorMinValuePlus1ms)),\n )\n } else {\n whereCurrentCursor = eq(expression, new Value(cursorMinValue))\n }\n\n cursorExpressions = {\n whereFrom: whereFromCursor,\n whereCurrent: whereCurrentCursor,\n lastKey: this.lastSentKey,\n }\n }\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n // Note: `where` does NOT include cursor expressions - they are passed separately\n // The sync layer can choose to use cursor-based or offset-based pagination\n const loadOptions: LoadSubsetOptions = {\n where, // Main filter only, no cursor\n limit,\n orderBy,\n cursor: cursorExpressions, // Cursor expressions passed separately\n offset: offset ?? currentOffset, // Use provided offset, or auto-tracked offset\n subscription: this,\n }\n const syncResult = this.collection._sync.loadSubset(loadOptions)\n\n // Pass the raw loadSubset result to the caller for external tracking\n onLoadSubsetResult?.(syncResult)\n\n // Track this loadSubset call\n this.loadedSubsets.push(loadOptions)\n if (shouldTrackLoadSubsetPromise) {\n this.trackLoadSubsetPromise(syncResult)\n }\n }\n\n // TODO: also add similar test but that checks that it can also load it from the collection's loadSubset function\n // and that that also works properly (i.e. does not skip duplicate values)\n\n /**\n * Filters and flips changes for keys that have not been sent yet.\n * Deletes are filtered out for keys that have not been sent yet.\n * Updates are flipped into inserts for keys that have not been sent yet.\n * Duplicate inserts are filtered out to prevent D2 multiplicity > 1.\n */\n private filterAndFlipChanges(changes: Array<ChangeMessage<any, any>>) {\n if (this.loadedInitialState || this.skipFiltering) {\n // We loaded the entire initial state or filtering is explicitly skipped\n // so no need to filter or flip changes\n return changes\n }\n\n // When buffering for truncate, we need all changes (including deletes) to pass through.\n // This is important because:\n // 1. If loadedInitialState was previously true, sentKeys will be empty\n // (trackSentKeys early-returns when loadedInitialState is true)\n // 2. The truncate deletes are for keys that WERE sent to the subscriber\n // 3. We're collecting all changes atomically, so filtering doesn't make sense\n const skipDeleteFilter = this.isBufferingForTruncate\n\n const newChanges = []\n for (const change of changes) {\n let newChange = change\n const keyInSentKeys = this.sentKeys.has(change.key)\n\n if (!keyInSentKeys) {\n if (change.type === `update`) {\n newChange = { ...change, type: `insert`, previousValue: undefined }\n } else if (change.type === `delete`) {\n // Filter out deletes for keys that have not been sent,\n // UNLESS we're buffering for truncate (where all deletes should pass through)\n if (!skipDeleteFilter) {\n continue\n }\n }\n this.sentKeys.add(change.key)\n } else {\n // Key was already sent - handle based on change type\n if (change.type === `insert`) {\n // Filter out duplicate inserts - the key was already inserted.\n // This prevents D2 multiplicity from going above 1, which would\n // cause deletes to not properly remove items (multiplicity would\n // go from 2 to 1 instead of 1 to 0).\n continue\n } else if (change.type === `delete`) {\n // Remove from sentKeys so future inserts for this key are allowed\n // (e.g., after truncate + reinsert)\n this.sentKeys.delete(change.key)\n }\n }\n newChanges.push(newChange)\n }\n return newChanges\n }\n\n private trackSentKeys(changes: Array<ChangeMessage<any, string | number>>) {\n if (this.loadedInitialState || this.skipFiltering) {\n // No need to track sent keys if we loaded the entire state or filtering is skipped.\n // Since filtering won't be applied, all keys are effectively \"observed\".\n return\n }\n\n for (const change of changes) {\n if (change.type === `delete`) {\n this.sentKeys.delete(change.key)\n } else {\n this.sentKeys.add(change.key)\n }\n }\n\n // Keep the limited snapshot offset in sync with keys we've actually sent.\n // This matters when loadSubset resolves asynchronously and requestLimitedSnapshot\n // didn't have local rows to count yet.\n if (this.orderByIndex) {\n this.limitedSnapshotRowCount = Math.max(\n this.limitedSnapshotRowCount,\n this.sentKeys.size,\n )\n }\n }\n\n /**\n * Mark that the subscription should not filter any changes.\n * This is used when includeInitialState is explicitly set to false,\n * meaning the caller doesn't want initial state but does want ALL future changes.\n */\n markAllStateAsSeen() {\n this.skipFiltering = true\n }\n\n unsubscribe() {\n // Clean up truncate event listener\n this.truncateCleanup?.()\n this.truncateCleanup = undefined\n\n // Clean up truncate buffer state\n this.isBufferingForTruncate = false\n this.truncateBuffer = []\n this.pendingTruncateRefetches.clear()\n\n // Unload all subsets that this subscription loaded\n // We pass the exact same LoadSubsetOptions we used for loadSubset\n for (const options of this.loadedSubsets) {\n this.collection._sync.unloadSubset(options)\n }\n this.loadedSubsets = []\n\n this.emitInner(`unsubscribed`, {\n type: `unsubscribed`,\n subscription: this,\n })\n // Clear all event listeners to prevent memory leaks\n this.clearListeners()\n }\n}\n"],"names":["EventEmitter","ensureIndexForExpression","createFilteredCallback","and","createFilterFunctionFromExpression","eq","Value","compileExpression","PropRef","buildCursor","gte","lt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"subscription.cjs","sources":["../../../src/collection/subscription.ts"],"sourcesContent":["import { ensureIndexForExpression } from '../indexes/auto-index.js'\nimport { and, eq, gte, lt } from '../query/builder/functions.js'\nimport { PropRef, Value } from '../query/ir.js'\nimport { EventEmitter } from '../event-emitter.js'\nimport { compileExpression } from '../query/compiler/evaluators.js'\nimport { buildCursor } from '../utils/cursor.js'\nimport {\n createFilterFunctionFromExpression,\n createFilteredCallback,\n} from './change-events.js'\nimport type { BasicExpression, OrderBy } from '../query/ir.js'\nimport type { IndexInterface } from '../indexes/base-index.js'\nimport type {\n ChangeMessage,\n LoadSubsetOptions,\n Subscription,\n SubscriptionEvents,\n SubscriptionStatus,\n SubscriptionUnsubscribedEvent,\n} from '../types.js'\nimport type { CollectionImpl } from './index.js'\n\ntype RequestSnapshotOptions = {\n where?: BasicExpression<boolean>\n optimizedOnly?: boolean\n trackLoadSubsetPromise?: boolean\n /** Optional orderBy to pass to loadSubset for backend optimization */\n orderBy?: OrderBy\n /** Optional limit to pass to loadSubset for backend optimization */\n limit?: number\n /** Callback that receives the raw loadSubset result for external tracking */\n onLoadSubsetResult?: (result: Promise<void> | true) => void\n}\n\ntype RequestLimitedSnapshotOptions = {\n orderBy: OrderBy\n limit: number\n /** All column values for cursor (first value used for local index, all values for sync layer) */\n minValues?: Array<unknown>\n /** Row offset for offset-based pagination (passed to sync layer) */\n offset?: number\n /** Whether to track the loadSubset promise on this subscription (default: true) */\n trackLoadSubsetPromise?: boolean\n /** Callback that receives the raw loadSubset result for external tracking */\n onLoadSubsetResult?: (result: Promise<void> | true) => void\n}\n\ntype CollectionSubscriptionOptions = {\n includeInitialState?: boolean\n /** Pre-compiled expression for filtering changes */\n whereExpression?: BasicExpression<boolean>\n /** Callback to call when the subscription is unsubscribed */\n onUnsubscribe?: (event: SubscriptionUnsubscribedEvent) => void\n}\n\nexport class CollectionSubscription\n extends EventEmitter<SubscriptionEvents>\n implements Subscription\n{\n private loadedInitialState = false\n\n // Flag to skip filtering in filterAndFlipChanges.\n // This is separate from loadedInitialState because we want to allow\n // requestSnapshot to still work even when filtering is skipped.\n private skipFiltering = false\n\n // Flag to indicate that we have sent at least 1 snapshot.\n // While `snapshotSent` is false we filter out all changes from subscription to the collection.\n private snapshotSent = false\n\n /**\n * Track all loadSubset calls made by this subscription so we can unload them on cleanup.\n * We store the exact LoadSubsetOptions we passed to loadSubset to ensure symmetric unload.\n */\n private loadedSubsets: Array<LoadSubsetOptions> = []\n\n // Keep track of the keys we've sent (needed for join and orderBy optimizations)\n private sentKeys = new Set<string | number>()\n\n // Track the count of rows sent via requestLimitedSnapshot for offset-based pagination\n private limitedSnapshotRowCount = 0\n\n // Track the last key sent via requestLimitedSnapshot for cursor-based pagination\n private lastSentKey: string | number | undefined\n\n private filteredCallback: (changes: Array<ChangeMessage<any, any>>) => void\n\n private orderByIndex: IndexInterface<string | number> | undefined\n\n // Status tracking\n private _status: SubscriptionStatus = `ready`\n private pendingLoadSubsetPromises: Set<Promise<void>> = new Set()\n\n // Cleanup function for truncate event listener\n private truncateCleanup: (() => void) | undefined\n\n // Truncate buffering state\n // When a truncate occurs, we buffer changes until all loadSubset refetches complete\n // This prevents a flash of missing content between deletes and new inserts\n private isBufferingForTruncate = false\n private truncateBuffer: Array<Array<ChangeMessage<any, any>>> = []\n private pendingTruncateRefetches: Set<Promise<void>> = new Set()\n\n public get status(): SubscriptionStatus {\n return this._status\n }\n\n constructor(\n private collection: CollectionImpl<any, any, any, any, any>,\n private callback: (changes: Array<ChangeMessage<any, any>>) => void,\n private options: CollectionSubscriptionOptions,\n ) {\n super()\n if (options.onUnsubscribe) {\n this.on(`unsubscribed`, (event) => options.onUnsubscribe!(event))\n }\n\n // Auto-index for where expressions if enabled\n if (options.whereExpression) {\n ensureIndexForExpression(options.whereExpression, this.collection)\n }\n\n const callbackWithSentKeysTracking = (\n changes: Array<ChangeMessage<any, any>>,\n ) => {\n callback(changes)\n this.trackSentKeys(changes)\n }\n\n this.callback = callbackWithSentKeysTracking\n\n // Create a filtered callback if where clause is provided\n this.filteredCallback = options.whereExpression\n ? createFilteredCallback(this.callback, options)\n : this.callback\n\n // Listen for truncate events to re-request data after must-refetch\n // When a truncate happens (e.g., from a 409 must-refetch), all collection data is cleared.\n // We need to re-request all previously loaded subsets to repopulate the data.\n this.truncateCleanup = this.collection.on(`truncate`, () => {\n this.handleTruncate()\n })\n }\n\n /**\n * Handle collection truncate event by resetting state and re-requesting subsets.\n * This is called when the sync layer receives a must-refetch and clears all data.\n *\n * To prevent a flash of missing content, we buffer all changes (deletes from truncate\n * and inserts from refetch) until all loadSubset promises resolve, then emit them together.\n */\n private handleTruncate() {\n // Copy the loaded subsets before clearing (we'll re-request them)\n const subsetsToReload = [...this.loadedSubsets]\n\n // Only buffer if there's an actual loadSubset handler that can do async work.\n // Without a loadSubset handler, there's nothing to re-request and no reason to buffer.\n // This prevents unnecessary buffering in eager sync mode or when loadSubset isn't implemented.\n const hasLoadSubsetHandler = this.collection._sync.syncLoadSubsetFn !== null\n\n // If there are no subsets to reload OR no loadSubset handler, just reset state\n if (subsetsToReload.length === 0 || !hasLoadSubsetHandler) {\n this.snapshotSent = false\n this.loadedInitialState = false\n this.limitedSnapshotRowCount = 0\n this.lastSentKey = undefined\n this.loadedSubsets = []\n return\n }\n\n // Start buffering BEFORE we receive the delete events from the truncate commit\n // This ensures we capture both the deletes and subsequent inserts\n this.isBufferingForTruncate = true\n this.truncateBuffer = []\n this.pendingTruncateRefetches.clear()\n\n // Reset snapshot/pagination tracking state\n // Note: We don't need to populate sentKeys here because filterAndFlipChanges\n // will skip the delete filter when isBufferingForTruncate is true\n this.snapshotSent = false\n this.loadedInitialState = false\n this.limitedSnapshotRowCount = 0\n this.lastSentKey = undefined\n\n // Clear the loadedSubsets array since we're re-requesting fresh\n this.loadedSubsets = []\n\n // Defer the loadSubset calls to a microtask so the truncate commit's delete events\n // are buffered BEFORE the loadSubset calls potentially trigger nested commits.\n // This ensures correct event ordering: deletes first, then inserts.\n queueMicrotask(() => {\n // Check if we were unsubscribed while waiting\n if (!this.isBufferingForTruncate) {\n return\n }\n\n // Re-request all previously loaded subsets and track their promises\n for (const options of subsetsToReload) {\n const syncResult = this.collection._sync.loadSubset(options)\n\n // Track this loadSubset call so we can unload it later\n this.loadedSubsets.push(options)\n this.trackLoadSubsetPromise(syncResult)\n\n // Track the promise for buffer flushing\n if (syncResult instanceof Promise) {\n this.pendingTruncateRefetches.add(syncResult)\n syncResult\n .catch(() => {\n // Ignore errors - we still want to flush the buffer even if some requests fail\n })\n .finally(() => {\n this.pendingTruncateRefetches.delete(syncResult)\n this.checkTruncateRefetchComplete()\n })\n }\n }\n\n // If all loadSubset calls were synchronous (returned true), flush now\n // At this point, delete events have already been buffered from the truncate commit\n if (this.pendingTruncateRefetches.size === 0) {\n this.flushTruncateBuffer()\n }\n })\n }\n\n /**\n * Check if all truncate refetch promises have completed and flush buffer if so\n */\n private checkTruncateRefetchComplete() {\n if (\n this.pendingTruncateRefetches.size === 0 &&\n this.isBufferingForTruncate\n ) {\n this.flushTruncateBuffer()\n }\n }\n\n /**\n * Flush the truncate buffer, emitting all buffered changes to the callback\n */\n private flushTruncateBuffer() {\n this.isBufferingForTruncate = false\n\n // Flatten all buffered changes into a single array for atomic emission\n // This ensures consumers see all truncate changes (deletes + inserts) in one callback\n const merged = this.truncateBuffer.flat()\n if (merged.length > 0) {\n this.filteredCallback(merged)\n }\n\n this.truncateBuffer = []\n }\n\n setOrderByIndex(index: IndexInterface<any>) {\n this.orderByIndex = index\n }\n\n /**\n * Check if an orderBy index has been set for this subscription\n */\n hasOrderByIndex(): boolean {\n return this.orderByIndex !== undefined\n }\n\n /**\n * Set subscription status and emit events if changed\n */\n private setStatus(newStatus: SubscriptionStatus) {\n if (this._status === newStatus) {\n return // No change\n }\n\n const previousStatus = this._status\n this._status = newStatus\n\n // Emit status:change event\n this.emitInner(`status:change`, {\n type: `status:change`,\n subscription: this,\n previousStatus,\n status: newStatus,\n })\n\n // Emit specific status event\n const eventKey: `status:${SubscriptionStatus}` = `status:${newStatus}`\n this.emitInner(eventKey, {\n type: eventKey,\n subscription: this,\n previousStatus,\n status: newStatus,\n } as SubscriptionEvents[typeof eventKey])\n }\n\n /**\n * Track a loadSubset promise and manage loading status\n */\n private trackLoadSubsetPromise(syncResult: Promise<void> | true) {\n // Track the promise if it's actually a promise (async work)\n if (syncResult instanceof Promise) {\n this.pendingLoadSubsetPromises.add(syncResult)\n this.setStatus(`loadingSubset`)\n\n syncResult.finally(() => {\n this.pendingLoadSubsetPromises.delete(syncResult)\n if (this.pendingLoadSubsetPromises.size === 0) {\n this.setStatus(`ready`)\n }\n })\n }\n }\n\n hasLoadedInitialState() {\n return this.loadedInitialState\n }\n\n hasSentAtLeastOneSnapshot() {\n return this.snapshotSent\n }\n\n emitEvents(changes: Array<ChangeMessage<any, any>>) {\n const newChanges = this.filterAndFlipChanges(changes)\n\n if (this.isBufferingForTruncate) {\n // Buffer the changes instead of emitting immediately\n // This prevents a flash of missing content during truncate/refetch\n if (newChanges.length > 0) {\n this.truncateBuffer.push(newChanges)\n }\n } else {\n this.filteredCallback(newChanges)\n }\n }\n\n /**\n * Sends the snapshot to the callback.\n * Returns a boolean indicating if it succeeded.\n * It can only fail if there is no index to fulfill the request\n * and the optimizedOnly option is set to true,\n * or, the entire state was already loaded.\n */\n requestSnapshot(opts?: RequestSnapshotOptions): boolean {\n if (this.loadedInitialState) {\n // Subscription was deoptimized so we already sent the entire initial state\n return false\n }\n\n const stateOpts: RequestSnapshotOptions = {\n where: this.options.whereExpression,\n optimizedOnly: opts?.optimizedOnly ?? false,\n }\n\n if (opts) {\n if (`where` in opts) {\n const snapshotWhereExp = opts.where\n if (stateOpts.where) {\n // Combine the two where expressions\n const subWhereExp = stateOpts.where\n const combinedWhereExp = and(subWhereExp, snapshotWhereExp)\n stateOpts.where = combinedWhereExp\n } else {\n stateOpts.where = snapshotWhereExp\n }\n }\n } else {\n // No options provided so it's loading the entire initial state\n this.loadedInitialState = true\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n const loadOptions: LoadSubsetOptions = {\n where: stateOpts.where,\n subscription: this,\n // Include orderBy and limit if provided so sync layer can optimize the query\n orderBy: opts?.orderBy,\n limit: opts?.limit,\n }\n const syncResult = this.collection._sync.loadSubset(loadOptions)\n\n // Pass the raw loadSubset result to the caller for external tracking\n opts?.onLoadSubsetResult?.(syncResult)\n\n // Track this loadSubset call so we can unload it later\n this.loadedSubsets.push(loadOptions)\n\n const trackLoadSubsetPromise = opts?.trackLoadSubsetPromise ?? true\n if (trackLoadSubsetPromise) {\n this.trackLoadSubsetPromise(syncResult)\n }\n\n // Also load data immediately from the collection\n const snapshot = this.collection.currentStateAsChanges(stateOpts)\n\n if (snapshot === undefined) {\n // Couldn't load from indexes\n return false\n }\n\n // Only send changes that have not been sent yet\n const filteredSnapshot = snapshot.filter(\n (change) => !this.sentKeys.has(change.key),\n )\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of filteredSnapshot) {\n this.sentKeys.add(change.key)\n }\n\n this.snapshotSent = true\n this.callback(filteredSnapshot)\n return true\n }\n\n /**\n * Sends a snapshot that fulfills the `where` clause and all rows are bigger or equal to the cursor.\n * Requires a range index to be set with `setOrderByIndex` prior to calling this method.\n * It uses that range index to load the items in the order of the index.\n *\n * For multi-column orderBy:\n * - Uses first value from `minValues` for LOCAL index operations (wide bounds, ensures no missed rows)\n * - Uses all `minValues` to build a precise composite cursor for SYNC layer loadSubset\n *\n * Note 1: it may load more rows than the provided LIMIT because it loads all values equal to the first cursor value + limit values greater.\n * This is needed to ensure that it does not accidentally skip duplicate values when the limit falls in the middle of some duplicated values.\n * Note 2: it does not send keys that have already been sent before.\n */\n requestLimitedSnapshot({\n orderBy,\n limit,\n minValues,\n offset,\n trackLoadSubsetPromise: shouldTrackLoadSubsetPromise = true,\n onLoadSubsetResult,\n }: RequestLimitedSnapshotOptions) {\n if (!limit) throw new Error(`limit is required`)\n\n if (!this.orderByIndex) {\n throw new Error(\n `Ordered snapshot was requested but no index was found. You have to call setOrderByIndex before requesting an ordered snapshot.`,\n )\n }\n\n // Check if minValues has a first element (regardless of its value)\n // This distinguishes between \"no min value provided\" vs \"min value is undefined\"\n const hasMinValue = minValues !== undefined && minValues.length > 0\n // Derive first column value from minValues (used for local index operations)\n const minValue = minValues?.[0]\n // Cast for index operations (index expects string | number)\n const minValueForIndex = minValue as string | number | undefined\n\n const index = this.orderByIndex\n const where = this.options.whereExpression\n const whereFilterFn = where\n ? createFilterFunctionFromExpression(where)\n : undefined\n\n const filterFn = (key: string | number | undefined): boolean => {\n if (key !== undefined && this.sentKeys.has(key)) {\n return false\n }\n\n const value = this.collection.get(key)\n if (value === undefined) {\n return false\n }\n\n return whereFilterFn?.(value) ?? true\n }\n\n let biggestObservedValue = minValueForIndex\n const changes: Array<ChangeMessage<any, string | number>> = []\n\n // If we have a minValue we need to handle the case\n // where there might be duplicate values equal to minValue that we need to include\n // because we can have data like this: [1, 2, 3, 3, 3, 4, 5]\n // so if minValue is 3 then the previous snapshot may not have included all 3s\n // e.g. if it was offset 0 and limit 3 it would only have loaded the first 3\n // so we load all rows equal to minValue first, to be sure we don't skip any duplicate values\n //\n // For multi-column orderBy, we use the first column value for index operations (wide bounds)\n // This may load some duplicates but ensures we never miss any rows.\n let keys: Array<string | number> = []\n if (hasMinValue) {\n // First, get all items with the same FIRST COLUMN value as minValue\n // This provides wide bounds for the local index\n const { expression } = orderBy[0]!\n const allRowsWithMinValue = this.collection.currentStateAsChanges({\n where: eq(expression, new Value(minValueForIndex)),\n })\n\n if (allRowsWithMinValue) {\n const keysWithMinValue = allRowsWithMinValue\n .map((change) => change.key)\n .filter((key) => !this.sentKeys.has(key) && filterFn(key))\n\n // Add items with the minValue first\n keys.push(...keysWithMinValue)\n\n // Then get items greater than minValue\n const keysGreaterThanMin = index.take(\n limit - keys.length,\n minValueForIndex!,\n filterFn,\n )\n keys.push(...keysGreaterThanMin)\n } else {\n keys = index.take(limit, minValueForIndex!, filterFn)\n }\n } else {\n // No min value provided, start from the beginning\n keys = index.takeFromStart(limit, filterFn)\n }\n\n const valuesNeeded = () => Math.max(limit - changes.length, 0)\n const collectionExhausted = () => keys.length === 0\n\n // Create a value extractor for the orderBy field to properly track the biggest indexed value\n const orderByExpression = orderBy[0]!.expression\n const valueExtractor =\n orderByExpression.type === `ref`\n ? compileExpression(new PropRef(orderByExpression.path), true)\n : null\n\n while (valuesNeeded() > 0 && !collectionExhausted()) {\n const insertedKeys = new Set<string | number>() // Track keys we add to `changes` in this iteration\n\n for (const key of keys) {\n const value = this.collection.get(key)!\n changes.push({\n type: `insert`,\n key,\n value,\n })\n // Extract the indexed value (e.g., salary) from the row, not the full row\n // This is needed for index.take() to work correctly with the BTree comparator\n biggestObservedValue = valueExtractor ? valueExtractor(value) : value\n insertedKeys.add(key) // Track this key\n }\n\n keys = index.take(valuesNeeded(), biggestObservedValue!, filterFn)\n }\n\n // Track row count for offset-based pagination (before sending to callback)\n // Use the current count as the offset for this load\n const currentOffset = this.limitedSnapshotRowCount\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of changes) {\n this.sentKeys.add(change.key)\n }\n\n this.callback(changes)\n\n // Update the row count and last key after sending (for next call's offset/cursor)\n this.limitedSnapshotRowCount += changes.length\n if (changes.length > 0) {\n this.lastSentKey = changes[changes.length - 1]!.key\n }\n\n // Build cursor expressions for sync layer loadSubset\n // The cursor expressions are separate from the main where clause\n // so the sync layer can choose cursor-based or offset-based pagination\n let cursorExpressions:\n | {\n whereFrom: BasicExpression<boolean>\n whereCurrent: BasicExpression<boolean>\n lastKey?: string | number\n }\n | undefined\n\n if (minValues !== undefined && minValues.length > 0) {\n const whereFromCursor = buildCursor(orderBy, minValues)\n\n if (whereFromCursor) {\n const { expression } = orderBy[0]!\n const cursorMinValue = minValues[0]\n\n // Build the whereCurrent expression for the first orderBy column\n // For Date values, we need to handle precision differences between JS (ms) and backends (μs)\n // A JS Date represents a 1ms range, so we query for all values within that range\n let whereCurrentCursor: BasicExpression<boolean>\n if (cursorMinValue instanceof Date) {\n const cursorMinValuePlus1ms = new Date(cursorMinValue.getTime() + 1)\n whereCurrentCursor = and(\n gte(expression, new Value(cursorMinValue)),\n lt(expression, new Value(cursorMinValuePlus1ms)),\n )\n } else {\n whereCurrentCursor = eq(expression, new Value(cursorMinValue))\n }\n\n cursorExpressions = {\n whereFrom: whereFromCursor,\n whereCurrent: whereCurrentCursor,\n lastKey: this.lastSentKey,\n }\n }\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n // Note: `where` does NOT include cursor expressions - they are passed separately\n // The sync layer can choose to use cursor-based or offset-based pagination\n const loadOptions: LoadSubsetOptions = {\n where, // Main filter only, no cursor\n limit,\n orderBy,\n cursor: cursorExpressions, // Cursor expressions passed separately\n offset: offset ?? currentOffset, // Use provided offset, or auto-tracked offset\n subscription: this,\n }\n const syncResult = this.collection._sync.loadSubset(loadOptions)\n\n // Pass the raw loadSubset result to the caller for external tracking\n onLoadSubsetResult?.(syncResult)\n\n // Track this loadSubset call\n this.loadedSubsets.push(loadOptions)\n if (shouldTrackLoadSubsetPromise) {\n this.trackLoadSubsetPromise(syncResult)\n }\n }\n\n // TODO: also add similar test but that checks that it can also load it from the collection's loadSubset function\n // and that that also works properly (i.e. does not skip duplicate values)\n\n /**\n * Filters and flips changes for keys that have not been sent yet.\n * Deletes are filtered out for keys that have not been sent yet.\n * Updates are flipped into inserts for keys that have not been sent yet.\n * Duplicate inserts are filtered out to prevent D2 multiplicity > 1.\n */\n private filterAndFlipChanges(changes: Array<ChangeMessage<any, any>>) {\n if (this.loadedInitialState || this.skipFiltering) {\n // We loaded the entire initial state or filtering is explicitly skipped\n // so no need to filter or flip changes\n return changes\n }\n\n // When buffering for truncate, we need all changes (including deletes) to pass through.\n // This is important because:\n // 1. If loadedInitialState was previously true, sentKeys will be empty\n // (trackSentKeys early-returns when loadedInitialState is true)\n // 2. The truncate deletes are for keys that WERE sent to the subscriber\n // 3. We're collecting all changes atomically, so filtering doesn't make sense\n const skipDeleteFilter = this.isBufferingForTruncate\n\n const newChanges = []\n for (const change of changes) {\n let newChange = change\n const keyInSentKeys = this.sentKeys.has(change.key)\n\n if (!keyInSentKeys) {\n if (change.type === `update`) {\n newChange = { ...change, type: `insert`, previousValue: undefined }\n } else if (change.type === `delete`) {\n // Filter out deletes for keys that have not been sent,\n // UNLESS we're buffering for truncate (where all deletes should pass through)\n if (!skipDeleteFilter) {\n continue\n }\n }\n this.sentKeys.add(change.key)\n } else {\n // Key was already sent - handle based on change type\n if (change.type === `insert`) {\n // Filter out duplicate inserts - the key was already inserted.\n // This prevents D2 multiplicity from going above 1, which would\n // cause deletes to not properly remove items (multiplicity would\n // go from 2 to 1 instead of 1 to 0).\n continue\n } else if (change.type === `delete`) {\n // Remove from sentKeys so future inserts for this key are allowed\n // (e.g., after truncate + reinsert)\n this.sentKeys.delete(change.key)\n }\n }\n newChanges.push(newChange)\n }\n return newChanges\n }\n\n private trackSentKeys(changes: Array<ChangeMessage<any, string | number>>) {\n if (this.loadedInitialState || this.skipFiltering) {\n // No need to track sent keys if we loaded the entire state or filtering is skipped.\n // Since filtering won't be applied, all keys are effectively \"observed\".\n return\n }\n\n for (const change of changes) {\n if (change.type === `delete`) {\n this.sentKeys.delete(change.key)\n } else {\n this.sentKeys.add(change.key)\n }\n }\n\n // Keep the limited snapshot offset in sync with keys we've actually sent.\n // This matters when loadSubset resolves asynchronously and requestLimitedSnapshot\n // didn't have local rows to count yet.\n if (this.orderByIndex) {\n this.limitedSnapshotRowCount = Math.max(\n this.limitedSnapshotRowCount,\n this.sentKeys.size,\n )\n }\n }\n\n /**\n * Mark that the subscription should not filter any changes.\n * This is used when includeInitialState is explicitly set to false,\n * meaning the caller doesn't want initial state but does want ALL future changes.\n */\n markAllStateAsSeen() {\n this.skipFiltering = true\n }\n\n unsubscribe() {\n // Clean up truncate event listener\n this.truncateCleanup?.()\n this.truncateCleanup = undefined\n\n // Clean up truncate buffer state\n this.isBufferingForTruncate = false\n this.truncateBuffer = []\n this.pendingTruncateRefetches.clear()\n\n // Unload all subsets that this subscription loaded\n // We pass the exact same LoadSubsetOptions we used for loadSubset\n for (const options of this.loadedSubsets) {\n this.collection._sync.unloadSubset(options)\n }\n this.loadedSubsets = []\n\n this.emitInner(`unsubscribed`, {\n type: `unsubscribed`,\n subscription: this,\n })\n // Clear all event listeners to prevent memory leaks\n this.clearListeners()\n }\n}\n"],"names":["EventEmitter","ensureIndexForExpression","createFilteredCallback","and","createFilterFunctionFromExpression","eq","Value","compileExpression","PropRef","buildCursor","gte","lt"],"mappings":"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"}
|
|
@@ -75,6 +75,10 @@ export declare class CollectionSubscription extends EventEmitter<SubscriptionEve
|
|
|
75
75
|
*/
|
|
76
76
|
private flushTruncateBuffer;
|
|
77
77
|
setOrderByIndex(index: IndexInterface<any>): void;
|
|
78
|
+
/**
|
|
79
|
+
* Check if an orderBy index has been set for this subscription
|
|
80
|
+
*/
|
|
81
|
+
hasOrderByIndex(): boolean;
|
|
78
82
|
/**
|
|
79
83
|
* Set subscription status and emit events if changed
|
|
80
84
|
*/
|