@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":"index.js","sources":["../../../src/collection/index.ts"],"sourcesContent":["import {\n CollectionRequiresConfigError,\n CollectionRequiresSyncConfigError,\n} from '../errors'\nimport { currentStateAsChanges } from './change-events'\n\nimport { CollectionStateManager } from './state'\nimport { CollectionChangesManager } from './changes'\nimport { CollectionLifecycleManager } from './lifecycle.js'\nimport { CollectionSyncManager } from './sync'\nimport { CollectionIndexesManager } from './indexes'\nimport { CollectionMutationsManager } from './mutations'\nimport { CollectionEventsManager } from './events.js'\nimport type { CollectionSubscription } from './subscription'\nimport type { AllCollectionEvents, CollectionEventHandler } from './events.js'\nimport type { BaseIndex, IndexResolver } from '../indexes/base-index.js'\nimport type { IndexOptions } from '../indexes/index-options.js'\nimport type {\n ChangeMessage,\n CollectionConfig,\n CollectionStatus,\n CurrentStateAsChangesOptions,\n Fn,\n InferSchemaInput,\n InferSchemaOutput,\n InsertConfig,\n NonSingleResult,\n OperationConfig,\n SingleResult,\n StringCollationConfig,\n SubscribeChangesOptions,\n Transaction as TransactionType,\n UtilsRecord,\n WritableDeep,\n} from '../types'\nimport type { SingleRowRefProxy } from '../query/builder/ref-proxy'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { BTreeIndex } from '../indexes/btree-index.js'\nimport type { IndexProxy } from '../indexes/lazy-index.js'\n\n/**\n * Enhanced Collection interface that includes both data type T and utilities TUtils\n * @template T - The type of items in the collection\n * @template TKey - The type of the key for the collection\n * @template TUtils - The utilities record type\n * @template TInsertInput - The type for insert operations (can be different from T for schemas with defaults)\n */\nexport interface Collection<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = UtilsRecord,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInsertInput extends object = T,\n> extends CollectionImpl<T, TKey, TUtils, TSchema, TInsertInput> {\n readonly utils: TUtils\n readonly singleResult?: true\n}\n\n/**\n * Creates a new Collection instance with the given configuration\n *\n * @template T - The schema type if a schema is provided, otherwise the type of items in the collection\n * @template TKey - The type of the key for the collection\n * @template TUtils - The utilities record type\n * @param options - Collection options with optional utilities\n * @returns A new Collection with utilities exposed both at top level and under .utils\n *\n * @example\n * // Pattern 1: With operation handlers (direct collection calls)\n * const todos = createCollection({\n * id: \"todos\",\n * getKey: (todo) => todo.id,\n * schema,\n * onInsert: async ({ transaction, collection }) => {\n * // Send to API\n * await api.createTodo(transaction.mutations[0].modified)\n * },\n * onUpdate: async ({ transaction, collection }) => {\n * await api.updateTodo(transaction.mutations[0].modified)\n * },\n * onDelete: async ({ transaction, collection }) => {\n * await api.deleteTodo(transaction.mutations[0].key)\n * },\n * sync: { sync: () => {} }\n * })\n *\n * // Direct usage (handlers manage transactions)\n * const tx = todos.insert({ id: \"1\", text: \"Buy milk\", completed: false })\n * await tx.isPersisted.promise\n *\n * @example\n * // Pattern 2: Manual transaction management\n * const todos = createCollection({\n * getKey: (todo) => todo.id,\n * schema: todoSchema,\n * sync: { sync: () => {} }\n * })\n *\n * // Explicit transaction usage\n * const tx = createTransaction({\n * mutationFn: async ({ transaction }) => {\n * // Handle all mutations in transaction\n * await api.saveChanges(transaction.mutations)\n * }\n * })\n *\n * tx.mutate(() => {\n * todos.insert({ id: \"1\", text: \"Buy milk\" })\n * todos.update(\"2\", draft => { draft.completed = true })\n * })\n *\n * await tx.isPersisted.promise\n *\n * @example\n * // Using schema for type inference (preferred as it also gives you client side validation)\n * const todoSchema = z.object({\n * id: z.string(),\n * title: z.string(),\n * completed: z.boolean()\n * })\n *\n * const todos = createCollection({\n * schema: todoSchema,\n * getKey: (todo) => todo.id,\n * sync: { sync: () => {} }\n * })\n *\n */\n\n// Overload for when schema is provided and utils is required (not optional)\n// We can't infer the Utils type from the CollectionConfig because it will always be optional\n// So we omit it from that type and instead infer it from the extension `& { utils: TUtils }`\n// such that we have the real, non-optional Utils type\nexport function createCollection<\n T extends StandardSchemaV1,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: Omit<\n CollectionConfig<InferSchemaOutput<T>, TKey, T, TUtils>,\n `utils`\n > & {\n schema: T\n utils: TUtils // Required utils\n } & NonSingleResult,\n): Collection<InferSchemaOutput<T>, TKey, TUtils, T, InferSchemaInput<T>> &\n NonSingleResult\n\n// Overload for when schema is provided and utils is optional\n// In this case we can simply infer the Utils type from the CollectionConfig type\nexport function createCollection<\n T extends StandardSchemaV1,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: CollectionConfig<InferSchemaOutput<T>, TKey, T, TUtils> & {\n schema: T\n } & NonSingleResult,\n): Collection<\n InferSchemaOutput<T>,\n TKey,\n Exclude<TUtils, undefined>,\n T,\n InferSchemaInput<T>\n> &\n NonSingleResult\n\n// Overload for when schema is provided, singleResult is true, and utils is required\nexport function createCollection<\n T extends StandardSchemaV1,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: Omit<\n CollectionConfig<InferSchemaOutput<T>, TKey, T, TUtils>,\n `utils`\n > & {\n schema: T\n utils: TUtils // Required utils\n } & SingleResult,\n): Collection<InferSchemaOutput<T>, TKey, TUtils, T, InferSchemaInput<T>> &\n SingleResult\n\n// Overload for when schema is provided and singleResult is true\nexport function createCollection<\n T extends StandardSchemaV1,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: CollectionConfig<InferSchemaOutput<T>, TKey, T, TUtils> & {\n schema: T\n } & SingleResult,\n): Collection<InferSchemaOutput<T>, TKey, TUtils, T, InferSchemaInput<T>> &\n SingleResult\n\n// Overload for when no schema is provided and utils is required\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function createCollection<\n T extends object,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: Omit<CollectionConfig<T, TKey, never, TUtils>, `utils`> & {\n schema?: never // prohibit schema if an explicit type is provided\n utils: TUtils // Required utils\n } & NonSingleResult,\n): Collection<T, TKey, TUtils, never, T> & NonSingleResult\n\n// Overload for when no schema is provided\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function createCollection<\n T extends object,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = UtilsRecord,\n>(\n options: CollectionConfig<T, TKey, never, TUtils> & {\n schema?: never // prohibit schema if an explicit type is provided\n } & NonSingleResult,\n): Collection<T, TKey, TUtils, never, T> & NonSingleResult\n\n// Overload for when no schema is provided, singleResult is true, and utils is required\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function createCollection<\n T extends object,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = UtilsRecord,\n>(\n options: Omit<CollectionConfig<T, TKey, never, TUtils>, `utils`> & {\n schema?: never // prohibit schema if an explicit type is provided\n utils: TUtils // Required utils\n } & SingleResult,\n): Collection<T, TKey, TUtils, never, T> & SingleResult\n\n// Overload for when no schema is provided and singleResult is true\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function createCollection<\n T extends object,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = UtilsRecord,\n>(\n options: CollectionConfig<T, TKey, never, TUtils> & {\n schema?: never // prohibit schema if an explicit type is provided\n } & SingleResult,\n): Collection<T, TKey, TUtils, never, T> & SingleResult\n\n// Implementation\nexport function createCollection(\n options: CollectionConfig<any, string | number, any, UtilsRecord> & {\n schema?: StandardSchemaV1\n },\n): Collection<any, string | number, UtilsRecord, any, any> {\n const collection = new CollectionImpl<any, string | number, any, any, any>(\n options,\n )\n\n // Attach utils to collection\n if (options.utils) {\n collection.utils = options.utils\n } else {\n collection.utils = {}\n }\n\n return collection\n}\n\nexport class CollectionImpl<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n public id: string\n public config: CollectionConfig<TOutput, TKey, TSchema>\n\n // Utilities namespace\n // This is populated by createCollection\n public utils: Record<string, Fn> = {}\n\n // Managers\n private _events: CollectionEventsManager\n private _changes: CollectionChangesManager<TOutput, TKey, TSchema, TInput>\n public _lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n public _sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n private _indexes: CollectionIndexesManager<TOutput, TKey, TSchema, TInput>\n private _mutations: CollectionMutationsManager<\n TOutput,\n TKey,\n TUtils,\n TSchema,\n TInput\n >\n // The core state of the collection is \"public\" so that is accessible in tests\n // and for debugging\n public _state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n\n private comparisonOpts: StringCollationConfig\n\n /**\n * Creates a new Collection instance\n *\n * @param config - Configuration object for the collection\n * @throws Error if sync config is missing\n */\n constructor(config: CollectionConfig<TOutput, TKey, TSchema>) {\n // eslint-disable-next-line\n if (!config) {\n throw new CollectionRequiresConfigError()\n }\n\n // eslint-disable-next-line\n if (!config.sync) {\n throw new CollectionRequiresSyncConfigError()\n }\n\n if (config.id) {\n this.id = config.id\n } else {\n this.id = crypto.randomUUID()\n }\n\n // Set default values for optional config properties\n this.config = {\n ...config,\n autoIndex: config.autoIndex ?? `eager`,\n }\n\n this._changes = new CollectionChangesManager()\n this._events = new CollectionEventsManager()\n this._indexes = new CollectionIndexesManager()\n this._lifecycle = new CollectionLifecycleManager(config, this.id)\n this._mutations = new CollectionMutationsManager(config, this.id)\n this._state = new CollectionStateManager(config)\n this._sync = new CollectionSyncManager(config, this.id)\n\n this.comparisonOpts = buildCompareOptionsFromConfig(config)\n\n this._changes.setDeps({\n collection: this, // Required for passing to CollectionSubscription\n lifecycle: this._lifecycle,\n sync: this._sync,\n events: this._events,\n })\n this._events.setDeps({\n collection: this, // Required for adding to emitted events\n })\n this._indexes.setDeps({\n state: this._state,\n lifecycle: this._lifecycle,\n })\n this._lifecycle.setDeps({\n changes: this._changes,\n events: this._events,\n indexes: this._indexes,\n state: this._state,\n sync: this._sync,\n })\n this._mutations.setDeps({\n collection: this, // Required for passing to config.onInsert/onUpdate/onDelete and annotating mutations\n lifecycle: this._lifecycle,\n state: this._state,\n })\n this._state.setDeps({\n collection: this, // Required for filtering events to only include this collection\n lifecycle: this._lifecycle,\n changes: this._changes,\n indexes: this._indexes,\n events: this._events,\n })\n this._sync.setDeps({\n collection: this, // Required for passing to config.sync callback\n state: this._state,\n lifecycle: this._lifecycle,\n events: this._events,\n })\n\n // Only start sync immediately if explicitly enabled\n if (config.startSync === true) {\n this._sync.startSync()\n }\n }\n\n /**\n * Gets the current status of the collection\n */\n public get status(): CollectionStatus {\n return this._lifecycle.status\n }\n\n /**\n * Get the number of subscribers to the collection\n */\n public get subscriberCount(): number {\n return this._changes.activeSubscribersCount\n }\n\n /**\n * Register a callback to be executed when the collection first becomes ready\n * Useful for preloading collections\n * @param callback Function to call when the collection first becomes ready\n * @example\n * collection.onFirstReady(() => {\n * console.log('Collection is ready for the first time')\n * // Safe to access collection.state now\n * })\n */\n public onFirstReady(callback: () => void): void {\n return this._lifecycle.onFirstReady(callback)\n }\n\n /**\n * Check if the collection is ready for use\n * Returns true if the collection has been marked as ready by its sync implementation\n * @returns true if the collection is ready, false otherwise\n * @example\n * if (collection.isReady()) {\n * console.log('Collection is ready, data is available')\n * // Safe to access collection.state\n * } else {\n * console.log('Collection is still loading')\n * }\n */\n public isReady(): boolean {\n return this._lifecycle.status === `ready`\n }\n\n /**\n * Check if the collection is currently loading more data\n * @returns true if the collection has pending load more operations, false otherwise\n */\n public get isLoadingSubset(): boolean {\n return this._sync.isLoadingSubset\n }\n\n /**\n * Start sync immediately - internal method for compiled queries\n * This bypasses lazy loading for special cases like live query results\n */\n public startSyncImmediate(): void {\n this._sync.startSync()\n }\n\n /**\n * Preload the collection data by starting sync if not already started\n * Multiple concurrent calls will share the same promise\n */\n public preload(): Promise<void> {\n return this._sync.preload()\n }\n\n /**\n * Get the current value for a key (virtual derived state)\n */\n public get(key: TKey): TOutput | undefined {\n return this._state.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 return this._state.has(key)\n }\n\n /**\n * Get the current size of the collection (cached)\n */\n public get size(): number {\n return this._state.size\n }\n\n /**\n * Get all keys (virtual derived state)\n */\n public *keys(): IterableIterator<TKey> {\n yield* this._state.keys()\n }\n\n /**\n * Get all values (virtual derived state)\n */\n public *values(): IterableIterator<TOutput> {\n yield* this._state.values()\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *entries(): IterableIterator<[TKey, TOutput]> {\n yield* this._state.entries()\n }\n\n /**\n * Get all entries (virtual derived state)\n */\n public *[Symbol.iterator](): IterableIterator<[TKey, TOutput]> {\n yield* this._state[Symbol.iterator]()\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 return this._state.forEach(callbackfn)\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 return this._state.map(callbackfn)\n }\n\n public getKeyFromItem(item: TOutput): TKey {\n return this.config.getKey(item)\n }\n\n /**\n * Creates an index on a collection for faster queries.\n * Indexes significantly improve query performance by allowing constant time lookups\n * and logarithmic time range queries instead of full scans.\n *\n * @template TResolver - The type of the index resolver (constructor or async loader)\n * @param indexCallback - Function that extracts the indexed value from each item\n * @param config - Configuration including index type and type-specific options\n * @returns An index proxy that provides access to the index when ready\n *\n * @example\n * // Create a default B+ tree index\n * const ageIndex = collection.createIndex((row) => row.age)\n *\n * // Create a ordered index with custom options\n * const ageIndex = collection.createIndex((row) => row.age, {\n * indexType: BTreeIndex,\n * options: {\n * compareFn: customComparator,\n * compareOptions: { direction: 'asc', nulls: 'first', stringSort: 'lexical' }\n * },\n * name: 'age_btree'\n * })\n *\n * // Create an async-loaded index\n * const textIndex = collection.createIndex((row) => row.content, {\n * indexType: async () => {\n * const { FullTextIndex } = await import('./indexes/fulltext.js')\n * return FullTextIndex\n * },\n * options: { language: 'en' }\n * })\n */\n public createIndex<TResolver extends IndexResolver<TKey> = typeof BTreeIndex>(\n indexCallback: (row: SingleRowRefProxy<TOutput>) => any,\n config: IndexOptions<TResolver> = {},\n ): IndexProxy<TKey> {\n return this._indexes.createIndex(indexCallback, config)\n }\n\n /**\n * Get resolved indexes for query optimization\n */\n get indexes(): Map<number, BaseIndex<TKey>> {\n return this._indexes.indexes\n }\n\n /**\n * Validates the data against the schema\n */\n public validateData(\n data: unknown,\n type: `insert` | `update`,\n key?: TKey,\n ): TOutput | never {\n return this._mutations.validateData(data, type, key)\n }\n\n get compareOptions(): StringCollationConfig {\n // return a copy such that no one can mutate the internal comparison object\n return { ...this.comparisonOpts }\n }\n\n /**\n * Inserts one or more items into the collection\n * @param items - Single item or array of items to insert\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the insert operation(s)\n * @throws {SchemaValidationError} If the data fails schema validation\n * @example\n * // Insert a single todo (requires onInsert handler)\n * const tx = collection.insert({ id: \"1\", text: \"Buy milk\", completed: false })\n * await tx.isPersisted.promise\n *\n * @example\n * // Insert multiple todos at once\n * const tx = collection.insert([\n * { id: \"1\", text: \"Buy milk\", completed: false },\n * { id: \"2\", text: \"Walk dog\", completed: true }\n * ])\n * await tx.isPersisted.promise\n *\n * @example\n * // Insert with metadata\n * const tx = collection.insert({ id: \"1\", text: \"Buy groceries\" },\n * { metadata: { source: \"mobile-app\" } }\n * )\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.insert({ id: \"1\", text: \"New item\" })\n * await tx.isPersisted.promise\n * console.log('Insert successful')\n * } catch (error) {\n * console.log('Insert failed:', error)\n * }\n */\n insert = (data: TInput | Array<TInput>, config?: InsertConfig) => {\n return this._mutations.insert(data, config)\n }\n\n /**\n * Updates one or more items in the collection using a callback function\n * @param keys - Single key or array of keys to update\n * @param configOrCallback - Either update configuration or update callback\n * @param maybeCallback - Update callback if config was provided\n * @returns A Transaction object representing the update operation(s)\n * @throws {SchemaValidationError} If the updated data fails schema validation\n * @example\n * // Update single item by key\n * const tx = collection.update(\"todo-1\", (draft) => {\n * draft.completed = true\n * })\n * await tx.isPersisted.promise\n *\n * @example\n * // Update multiple items\n * const tx = collection.update([\"todo-1\", \"todo-2\"], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n * await tx.isPersisted.promise\n *\n * @example\n * // Update with metadata\n * const tx = collection.update(\"todo-1\",\n * { metadata: { reason: \"user update\" } },\n * (draft) => { draft.text = \"Updated text\" }\n * )\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.update(\"item-1\", draft => { draft.value = \"new\" })\n * await tx.isPersisted.promise\n * console.log('Update successful')\n * } catch (error) {\n * console.log('Update failed:', error)\n * }\n */\n\n // Overload 1: Update multiple items with a callback\n update(\n key: Array<TKey | unknown>,\n callback: (drafts: Array<WritableDeep<TInput>>) => void,\n ): TransactionType\n\n // Overload 2: Update multiple items with config and a callback\n update(\n keys: Array<TKey | unknown>,\n config: OperationConfig,\n callback: (drafts: Array<WritableDeep<TInput>>) => void,\n ): TransactionType\n\n // Overload 3: Update a single item with a callback\n update(\n id: TKey | unknown,\n callback: (draft: WritableDeep<TInput>) => void,\n ): TransactionType\n\n // Overload 4: Update a single item with config and a callback\n update(\n id: TKey | unknown,\n config: OperationConfig,\n callback: (draft: WritableDeep<TInput>) => void,\n ): TransactionType\n\n update(\n keys: (TKey | unknown) | Array<TKey | unknown>,\n configOrCallback:\n | ((draft: WritableDeep<TInput>) => void)\n | ((drafts: Array<WritableDeep<TInput>>) => void)\n | OperationConfig,\n maybeCallback?:\n | ((draft: WritableDeep<TInput>) => void)\n | ((drafts: Array<WritableDeep<TInput>>) => void),\n ) {\n return this._mutations.update(keys, configOrCallback, maybeCallback)\n }\n\n /**\n * Deletes one or more items from the collection\n * @param keys - Single key or array of keys to delete\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the delete operation(s)\n * @example\n * // Delete a single item\n * const tx = collection.delete(\"todo-1\")\n * await tx.isPersisted.promise\n *\n * @example\n * // Delete multiple items\n * const tx = collection.delete([\"todo-1\", \"todo-2\"])\n * await tx.isPersisted.promise\n *\n * @example\n * // Delete with metadata\n * const tx = collection.delete(\"todo-1\", { metadata: { reason: \"completed\" } })\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.delete(\"item-1\")\n * await tx.isPersisted.promise\n * console.log('Delete successful')\n * } catch (error) {\n * console.log('Delete failed:', error)\n * }\n */\n delete = (\n keys: Array<TKey> | TKey,\n config?: OperationConfig,\n ): TransactionType<any> => {\n return this._mutations.delete(keys, config)\n }\n\n /**\n * Gets the current state of the collection as a Map\n * @returns Map containing all items in the collection, with keys as identifiers\n * @example\n * const itemsMap = collection.state\n * console.log(`Collection has ${itemsMap.size} items`)\n *\n * for (const [key, item] of itemsMap) {\n * console.log(`${key}: ${item.title}`)\n * }\n *\n * // Check if specific item exists\n * if (itemsMap.has(\"todo-1\")) {\n * console.log(\"Todo 1 exists:\", itemsMap.get(\"todo-1\"))\n * }\n */\n get state() {\n const result = new Map<TKey, TOutput>()\n for (const [key, value] of this.entries()) {\n result.set(key, value)\n }\n return result\n }\n\n /**\n * Gets the current state of the collection as a Map, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to a Map containing all items in the collection\n */\n stateWhenReady(): Promise<Map<TKey, TOutput>> {\n // If we already have data or collection is ready, resolve immediately\n if (this.size > 0 || this.isReady()) {\n return Promise.resolve(this.state)\n }\n\n // Use preload to ensure the collection starts loading, then return the state\n return this.preload().then(() => this.state)\n }\n\n /**\n * Gets the current state of the collection as an Array\n *\n * @returns An Array containing all items in the collection\n */\n get toArray() {\n return Array.from(this.values())\n }\n\n /**\n * Gets the current state of the collection as an Array, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to an Array containing all items in the collection\n */\n toArrayWhenReady(): Promise<Array<TOutput>> {\n // If we already have data or collection is ready, resolve immediately\n if (this.size > 0 || this.isReady()) {\n return Promise.resolve(this.toArray)\n }\n\n // Use preload to ensure the collection starts loading, then return the array\n return this.preload().then(() => this.toArray)\n }\n\n /**\n * Returns the current state of the collection as an array of changes\n * @param options - Options including optional where filter\n * @returns An array of changes\n * @example\n * // Get all items as changes\n * const allChanges = collection.currentStateAsChanges()\n *\n * // Get only items matching a condition\n * const activeChanges = collection.currentStateAsChanges({\n * where: (row) => row.status === 'active'\n * })\n *\n * // Get only items using a pre-compiled expression\n * const activeChanges = collection.currentStateAsChanges({\n * whereExpression: eq(row.status, 'active')\n * })\n */\n public currentStateAsChanges(\n options: CurrentStateAsChangesOptions = {},\n ): Array<ChangeMessage<TOutput>> | void {\n return currentStateAsChanges(this, options)\n }\n\n /**\n * Subscribe to changes in the collection\n * @param callback - Function called when items change\n * @param options - Subscription options including includeInitialState and where filter\n * @returns Unsubscribe function - Call this to stop listening for changes\n * @example\n * // Basic subscription\n * const subscription = collection.subscribeChanges((changes) => {\n * changes.forEach(change => {\n * console.log(`${change.type}: ${change.key}`, change.value)\n * })\n * })\n *\n * // Later: subscription.unsubscribe()\n *\n * @example\n * // Include current state immediately\n * const subscription = collection.subscribeChanges((changes) => {\n * updateUI(changes)\n * }, { includeInitialState: true })\n *\n * @example\n * // Subscribe only to changes matching a condition using where callback\n * import { eq } from \"@tanstack/db\"\n *\n * const subscription = collection.subscribeChanges((changes) => {\n * updateUI(changes)\n * }, {\n * includeInitialState: true,\n * where: (row) => eq(row.status, \"active\")\n * })\n *\n * @example\n * // Using multiple conditions with and()\n * import { and, eq, gt } from \"@tanstack/db\"\n *\n * const subscription = collection.subscribeChanges((changes) => {\n * updateUI(changes)\n * }, {\n * where: (row) => and(eq(row.status, \"active\"), gt(row.priority, 5))\n * })\n */\n public subscribeChanges(\n callback: (changes: Array<ChangeMessage<TOutput>>) => void,\n options: SubscribeChangesOptions<TOutput> = {},\n ): CollectionSubscription {\n return this._changes.subscribeChanges(callback, options)\n }\n\n /**\n * Subscribe to a collection event\n */\n public on<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>,\n ) {\n return this._events.on(event, callback)\n }\n\n /**\n * Subscribe to a collection event once\n */\n public once<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>,\n ) {\n return this._events.once(event, callback)\n }\n\n /**\n * Unsubscribe from a collection event\n */\n public off<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>,\n ) {\n this._events.off(event, callback)\n }\n\n /**\n * Wait for a collection event\n */\n public waitFor<T extends keyof AllCollectionEvents>(\n event: T,\n timeout?: number,\n ) {\n return this._events.waitFor(event, timeout)\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 async cleanup(): Promise<void> {\n this._lifecycle.cleanup()\n return Promise.resolve()\n }\n}\n\nfunction buildCompareOptionsFromConfig(\n config: CollectionConfig<any, any, any>,\n): StringCollationConfig {\n if (config.defaultStringCollation) {\n const options = config.defaultStringCollation\n return {\n stringSort: options.stringSort ?? `locale`,\n locale: options.stringSort === `locale` ? options.locale : undefined,\n localeOptions:\n options.stringSort === `locale` ? options.localeOptions : undefined,\n }\n } else {\n return {\n stringSort: `locale`,\n }\n }\n}\n"],"names":["config"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/collection/index.ts"],"sourcesContent":["import {\n CollectionConfigurationError,\n CollectionRequiresConfigError,\n CollectionRequiresSyncConfigError,\n} from '../errors'\nimport { currentStateAsChanges } from './change-events'\n\nimport { CollectionStateManager } from './state'\nimport { CollectionChangesManager } from './changes'\nimport { CollectionLifecycleManager } from './lifecycle.js'\nimport { CollectionSyncManager } from './sync'\nimport { CollectionIndexesManager } from './indexes'\nimport { CollectionMutationsManager } from './mutations'\nimport { CollectionEventsManager } from './events.js'\nimport type { CollectionSubscription } from './subscription'\nimport type {\n AllCollectionEvents,\n CollectionEventHandler,\n CollectionIndexMetadata,\n} from './events.js'\nimport type { BaseIndex, IndexConstructor } from '../indexes/base-index.js'\nimport type { IndexOptions } from '../indexes/index-options.js'\nimport type {\n ChangeMessage,\n CollectionConfig,\n CollectionStatus,\n CurrentStateAsChangesOptions,\n Fn,\n InferSchemaInput,\n InferSchemaOutput,\n InsertConfig,\n NonSingleResult,\n OperationConfig,\n SingleResult,\n StringCollationConfig,\n SubscribeChangesOptions,\n Transaction as TransactionType,\n UtilsRecord,\n WritableDeep,\n} from '../types'\nimport type { SingleRowRefProxy } from '../query/builder/ref-proxy'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { WithVirtualProps } from '../virtual-props.js'\n\nexport type { CollectionIndexMetadata } from './events.js'\n\n/**\n * Enhanced Collection interface that includes both data type T and utilities TUtils\n * @template T - The type of items in the collection\n * @template TKey - The type of the key for the collection\n * @template TUtils - The utilities record type\n * @template TInsertInput - The type for insert operations (can be different from T for schemas with defaults)\n */\nexport interface Collection<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = UtilsRecord,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInsertInput extends object = T,\n> extends CollectionImpl<T, TKey, TUtils, TSchema, TInsertInput> {\n readonly utils: TUtils\n readonly singleResult?: true\n}\n\n/**\n * Creates a new Collection instance with the given configuration\n *\n * @template T - The schema type if a schema is provided, otherwise the type of items in the collection\n * @template TKey - The type of the key for the collection\n * @template TUtils - The utilities record type\n * @param options - Collection options with optional utilities\n * @returns A new Collection with utilities exposed both at top level and under .utils\n *\n * @example\n * // Pattern 1: With operation handlers (direct collection calls)\n * const todos = createCollection({\n * id: \"todos\",\n * getKey: (todo) => todo.id,\n * schema,\n * onInsert: async ({ transaction, collection }) => {\n * // Send to API\n * await api.createTodo(transaction.mutations[0].modified)\n * },\n * onUpdate: async ({ transaction, collection }) => {\n * await api.updateTodo(transaction.mutations[0].modified)\n * },\n * onDelete: async ({ transaction, collection }) => {\n * await api.deleteTodo(transaction.mutations[0].key)\n * },\n * sync: { sync: () => {} }\n * })\n *\n * // Direct usage (handlers manage transactions)\n * const tx = todos.insert({ id: \"1\", text: \"Buy milk\", completed: false })\n * await tx.isPersisted.promise\n *\n * @example\n * // Pattern 2: Manual transaction management\n * const todos = createCollection({\n * getKey: (todo) => todo.id,\n * schema: todoSchema,\n * sync: { sync: () => {} }\n * })\n *\n * // Explicit transaction usage\n * const tx = createTransaction({\n * mutationFn: async ({ transaction }) => {\n * // Handle all mutations in transaction\n * await api.saveChanges(transaction.mutations)\n * }\n * })\n *\n * tx.mutate(() => {\n * todos.insert({ id: \"1\", text: \"Buy milk\" })\n * todos.update(\"2\", draft => { draft.completed = true })\n * })\n *\n * await tx.isPersisted.promise\n *\n * @example\n * // Using schema for type inference (preferred as it also gives you client side validation)\n * const todoSchema = z.object({\n * id: z.string(),\n * title: z.string(),\n * completed: z.boolean()\n * })\n *\n * const todos = createCollection({\n * schema: todoSchema,\n * getKey: (todo) => todo.id,\n * sync: { sync: () => {} }\n * })\n *\n */\n\n// Overload for when schema is provided and utils is required (not optional)\n// We can't infer the Utils type from the CollectionConfig because it will always be optional\n// So we omit it from that type and instead infer it from the extension `& { utils: TUtils }`\n// such that we have the real, non-optional Utils type\nexport function createCollection<\n T extends StandardSchemaV1,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: Omit<\n CollectionConfig<InferSchemaOutput<T>, TKey, T, TUtils>,\n `utils`\n > & {\n schema: T\n utils: TUtils // Required utils\n } & NonSingleResult,\n): Collection<InferSchemaOutput<T>, TKey, TUtils, T, InferSchemaInput<T>> &\n NonSingleResult\n\n// Overload for when schema is provided and utils is optional\n// In this case we can simply infer the Utils type from the CollectionConfig type\nexport function createCollection<\n T extends StandardSchemaV1,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: CollectionConfig<InferSchemaOutput<T>, TKey, T, TUtils> & {\n schema: T\n } & NonSingleResult,\n): Collection<\n InferSchemaOutput<T>,\n TKey,\n Exclude<TUtils, undefined>,\n T,\n InferSchemaInput<T>\n> &\n NonSingleResult\n\n// Overload for when schema is provided, singleResult is true, and utils is required\nexport function createCollection<\n T extends StandardSchemaV1,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: Omit<\n CollectionConfig<InferSchemaOutput<T>, TKey, T, TUtils>,\n `utils`\n > & {\n schema: T\n utils: TUtils // Required utils\n } & SingleResult,\n): Collection<InferSchemaOutput<T>, TKey, TUtils, T, InferSchemaInput<T>> &\n SingleResult\n\n// Overload for when schema is provided and singleResult is true\nexport function createCollection<\n T extends StandardSchemaV1,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: CollectionConfig<InferSchemaOutput<T>, TKey, T, TUtils> & {\n schema: T\n } & SingleResult,\n): Collection<InferSchemaOutput<T>, TKey, TUtils, T, InferSchemaInput<T>> &\n SingleResult\n\n// Overload for when no schema is provided and utils is required\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function createCollection<\n T extends object,\n TKey extends string | number,\n TUtils extends UtilsRecord,\n>(\n options: Omit<CollectionConfig<T, TKey, never, TUtils>, `utils`> & {\n schema?: never // prohibit schema if an explicit type is provided\n utils: TUtils // Required utils\n } & NonSingleResult,\n): Collection<T, TKey, TUtils, never, T> & NonSingleResult\n\n// Overload for when no schema is provided\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function createCollection<\n T extends object,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = UtilsRecord,\n>(\n options: CollectionConfig<T, TKey, never, TUtils> & {\n schema?: never // prohibit schema if an explicit type is provided\n } & NonSingleResult,\n): Collection<T, TKey, TUtils, never, T> & NonSingleResult\n\n// Overload for when no schema is provided, singleResult is true, and utils is required\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function createCollection<\n T extends object,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = UtilsRecord,\n>(\n options: Omit<CollectionConfig<T, TKey, never, TUtils>, `utils`> & {\n schema?: never // prohibit schema if an explicit type is provided\n utils: TUtils // Required utils\n } & SingleResult,\n): Collection<T, TKey, TUtils, never, T> & SingleResult\n\n// Overload for when no schema is provided and singleResult is true\n// the type T needs to be passed explicitly unless it can be inferred from the getKey function in the config\nexport function createCollection<\n T extends object,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = UtilsRecord,\n>(\n options: CollectionConfig<T, TKey, never, TUtils> & {\n schema?: never // prohibit schema if an explicit type is provided\n } & SingleResult,\n): Collection<T, TKey, TUtils, never, T> & SingleResult\n\n// Implementation\nexport function createCollection(\n options: CollectionConfig<any, string | number, any, UtilsRecord> & {\n schema?: StandardSchemaV1\n },\n): Collection<any, string | number, UtilsRecord, any, any> {\n const collection = new CollectionImpl<any, string | number, any, any, any>(\n options,\n )\n\n // Attach utils to collection\n if (options.utils) {\n collection.utils = options.utils\n } else {\n collection.utils = {}\n }\n\n return collection\n}\n\nexport class CollectionImpl<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TUtils extends UtilsRecord = {},\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n public id: string\n public config: CollectionConfig<TOutput, TKey, TSchema>\n\n // Utilities namespace\n // This is populated by createCollection\n public utils: Record<string, Fn> = {}\n\n // Managers\n private _events: CollectionEventsManager\n private _changes: CollectionChangesManager<TOutput, TKey, TSchema, TInput>\n public _lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n public _sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n private _indexes: CollectionIndexesManager<TOutput, TKey, TSchema, TInput>\n private _mutations: CollectionMutationsManager<\n TOutput,\n TKey,\n TUtils,\n TSchema,\n TInput\n >\n // The core state of the collection is \"public\" so that is accessible in tests\n // and for debugging\n public _state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n\n /**\n * When set, collection consumers should defer processing incoming data\n * refreshes until this promise resolves. This prevents stale data from\n * overwriting optimistic state while pending writes are being applied.\n */\n public deferDataRefresh: Promise<void> | null = null\n\n private comparisonOpts: StringCollationConfig\n\n /**\n * Creates a new Collection instance\n *\n * @param config - Configuration object for the collection\n * @throws Error if sync config is missing\n */\n constructor(config: CollectionConfig<TOutput, TKey, TSchema>) {\n // eslint-disable-next-line\n if (!config) {\n throw new CollectionRequiresConfigError()\n }\n\n // eslint-disable-next-line\n if (!config.sync) {\n throw new CollectionRequiresSyncConfigError()\n }\n\n if (config.id) {\n this.id = config.id\n } else {\n this.id = crypto.randomUUID()\n }\n\n // Set default values for optional config properties\n this.config = {\n ...config,\n autoIndex: config.autoIndex ?? `off`,\n }\n\n if (this.config.autoIndex === `eager` && !config.defaultIndexType) {\n throw new CollectionConfigurationError(\n `autoIndex: 'eager' requires defaultIndexType to be set. ` +\n `Import an index type and set it:\\n` +\n ` import { BasicIndex } from '@tanstack/db'\\n` +\n ` createCollection({ defaultIndexType: BasicIndex, autoIndex: 'eager', ... })`,\n )\n }\n\n this._changes = new CollectionChangesManager()\n this._events = new CollectionEventsManager()\n this._indexes = new CollectionIndexesManager()\n this._lifecycle = new CollectionLifecycleManager(config, this.id)\n this._mutations = new CollectionMutationsManager(config, this.id)\n this._state = new CollectionStateManager(config)\n this._sync = new CollectionSyncManager(config, this.id)\n\n this.comparisonOpts = buildCompareOptionsFromConfig(config)\n\n this._changes.setDeps({\n collection: this, // Required for passing to CollectionSubscription\n lifecycle: this._lifecycle,\n sync: this._sync,\n events: this._events,\n state: this._state, // Required for enriching changes with virtual properties\n })\n this._events.setDeps({\n collection: this, // Required for adding to emitted events\n })\n this._indexes.setDeps({\n state: this._state,\n lifecycle: this._lifecycle,\n defaultIndexType: config.defaultIndexType,\n events: this._events,\n })\n this._lifecycle.setDeps({\n changes: this._changes,\n events: this._events,\n indexes: this._indexes,\n state: this._state,\n sync: this._sync,\n })\n this._mutations.setDeps({\n collection: this, // Required for passing to config.onInsert/onUpdate/onDelete and annotating mutations\n lifecycle: this._lifecycle,\n state: this._state,\n })\n this._state.setDeps({\n collection: this, // Required for filtering events to only include this collection\n lifecycle: this._lifecycle,\n changes: this._changes,\n indexes: this._indexes,\n events: this._events,\n })\n this._sync.setDeps({\n collection: this, // Required for passing to config.sync callback\n state: this._state,\n lifecycle: this._lifecycle,\n events: this._events,\n })\n\n // Only start sync immediately if explicitly enabled\n if (config.startSync === true) {\n this._sync.startSync()\n }\n }\n\n /**\n * Gets the current status of the collection\n */\n public get status(): CollectionStatus {\n return this._lifecycle.status\n }\n\n /**\n * Get the number of subscribers to the collection\n */\n public get subscriberCount(): number {\n return this._changes.activeSubscribersCount\n }\n\n /**\n * Register a callback to be executed when the collection first becomes ready\n * Useful for preloading collections\n * @param callback Function to call when the collection first becomes ready\n * @example\n * collection.onFirstReady(() => {\n * console.log('Collection is ready for the first time')\n * // Safe to access collection.state now\n * })\n */\n public onFirstReady(callback: () => void): void {\n return this._lifecycle.onFirstReady(callback)\n }\n\n /**\n * Check if the collection is ready for use\n * Returns true if the collection has been marked as ready by its sync implementation\n * @returns true if the collection is ready, false otherwise\n * @example\n * if (collection.isReady()) {\n * console.log('Collection is ready, data is available')\n * // Safe to access collection.state\n * } else {\n * console.log('Collection is still loading')\n * }\n */\n public isReady(): boolean {\n return this._lifecycle.status === `ready`\n }\n\n /**\n * Check if the collection is currently loading more data\n * @returns true if the collection has pending load more operations, false otherwise\n */\n public get isLoadingSubset(): boolean {\n return this._sync.isLoadingSubset\n }\n\n /**\n * Start sync immediately - internal method for compiled queries\n * This bypasses lazy loading for special cases like live query results\n */\n public startSyncImmediate(): void {\n this._sync.startSync()\n }\n\n /**\n * Preload the collection data by starting sync if not already started\n * Multiple concurrent calls will share the same promise\n */\n public preload(): Promise<void> {\n return this._sync.preload()\n }\n\n /**\n * Get the current value for a key (virtual derived state)\n */\n public get(key: TKey): WithVirtualProps<TOutput, TKey> | undefined {\n return this._state.getWithVirtualProps(key)\n }\n\n /**\n * Check if a key exists in the collection (virtual derived state)\n */\n public has(key: TKey): boolean {\n return this._state.has(key)\n }\n\n /**\n * Get the current size of the collection (cached)\n */\n public get size(): number {\n return this._state.size\n }\n\n /**\n * Get all keys (virtual derived state)\n */\n public *keys(): IterableIterator<TKey> {\n yield* this._state.keys()\n }\n\n /**\n * Get all values (virtual derived state)\n */\n public *values(): IterableIterator<WithVirtualProps<TOutput, TKey>> {\n for (const key of this._state.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, WithVirtualProps<TOutput, TKey>]> {\n for (const key of this._state.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<\n [TKey, WithVirtualProps<TOutput, TKey>]\n > {\n yield* this.entries()\n }\n\n /**\n * Execute a callback for each entry in the collection\n */\n public forEach(\n callbackfn: (\n value: WithVirtualProps<TOutput, TKey>,\n key: TKey,\n index: number,\n ) => 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: (\n value: WithVirtualProps<TOutput, TKey>,\n key: TKey,\n index: number,\n ) => 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 public getKeyFromItem(item: TOutput): TKey {\n return this.config.getKey(item)\n }\n\n /**\n * Creates an index on a collection for faster queries.\n * Indexes significantly improve query performance by allowing constant time lookups\n * and logarithmic time range queries instead of full scans.\n *\n * @param indexCallback - Function that extracts the indexed value from each item\n * @param config - Configuration including index type and type-specific options\n * @returns The created index\n *\n * @example\n * ```ts\n * import { BasicIndex } from '@tanstack/db'\n *\n * // Create an index with explicit type\n * const ageIndex = collection.createIndex((row) => row.age, {\n * indexType: BasicIndex\n * })\n *\n * // Create an index with collection's default type\n * const nameIndex = collection.createIndex((row) => row.name)\n * ```\n */\n public createIndex<TIndexType extends IndexConstructor<TKey>>(\n indexCallback: (row: SingleRowRefProxy<TOutput>) => any,\n config: IndexOptions<TIndexType> = {},\n ): BaseIndex<TKey> {\n return this._indexes.createIndex(indexCallback, config)\n }\n\n /**\n * Removes an index created with createIndex.\n * Returns true when an index existed and was removed.\n *\n * Best-effort semantics: removing an index guarantees it is detached from\n * collection query planning. Existing index proxy references should be treated\n * as invalid after removal.\n */\n public removeIndex(indexOrId: BaseIndex<TKey> | number): boolean {\n return this._indexes.removeIndex(indexOrId)\n }\n\n /**\n * Returns a snapshot of current index metadata sorted by indexId.\n * Persistence wrappers can use this to bootstrap index state if indexes were\n * created before event listeners were attached.\n */\n public getIndexMetadata(): Array<CollectionIndexMetadata> {\n return this._indexes.getIndexMetadataSnapshot()\n }\n\n /**\n * Get resolved indexes for query optimization\n */\n get indexes(): Map<number, BaseIndex<TKey>> {\n return this._indexes.indexes\n }\n\n /**\n * Validates the data against the schema\n */\n public validateData(\n data: unknown,\n type: `insert` | `update`,\n key?: TKey,\n ): TOutput | never {\n return this._mutations.validateData(data, type, key)\n }\n\n get compareOptions(): StringCollationConfig {\n // return a copy such that no one can mutate the internal comparison object\n return { ...this.comparisonOpts }\n }\n\n /**\n * Inserts one or more items into the collection\n * @param items - Single item or array of items to insert\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the insert operation(s)\n * @throws {SchemaValidationError} If the data fails schema validation\n * @example\n * // Insert a single todo (requires onInsert handler)\n * const tx = collection.insert({ id: \"1\", text: \"Buy milk\", completed: false })\n * await tx.isPersisted.promise\n *\n * @example\n * // Insert multiple todos at once\n * const tx = collection.insert([\n * { id: \"1\", text: \"Buy milk\", completed: false },\n * { id: \"2\", text: \"Walk dog\", completed: true }\n * ])\n * await tx.isPersisted.promise\n *\n * @example\n * // Insert with metadata\n * const tx = collection.insert({ id: \"1\", text: \"Buy groceries\" },\n * { metadata: { source: \"mobile-app\" } }\n * )\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.insert({ id: \"1\", text: \"New item\" })\n * await tx.isPersisted.promise\n * console.log('Insert successful')\n * } catch (error) {\n * console.log('Insert failed:', error)\n * }\n */\n insert = (data: TInput | Array<TInput>, config?: InsertConfig) => {\n return this._mutations.insert(data, config)\n }\n\n /**\n * Updates one or more items in the collection using a callback function\n * @param keys - Single key or array of keys to update\n * @param configOrCallback - Either update configuration or update callback\n * @param maybeCallback - Update callback if config was provided\n * @returns A Transaction object representing the update operation(s)\n * @throws {SchemaValidationError} If the updated data fails schema validation\n * @example\n * // Update single item by key\n * const tx = collection.update(\"todo-1\", (draft) => {\n * draft.completed = true\n * })\n * await tx.isPersisted.promise\n *\n * @example\n * // Update multiple items\n * const tx = collection.update([\"todo-1\", \"todo-2\"], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n * await tx.isPersisted.promise\n *\n * @example\n * // Update with metadata\n * const tx = collection.update(\"todo-1\",\n * { metadata: { reason: \"user update\" } },\n * (draft) => { draft.text = \"Updated text\" }\n * )\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.update(\"item-1\", draft => { draft.value = \"new\" })\n * await tx.isPersisted.promise\n * console.log('Update successful')\n * } catch (error) {\n * console.log('Update failed:', error)\n * }\n */\n\n // Overload 1: Update multiple items with a callback\n update(\n key: Array<TKey | unknown>,\n callback: (drafts: Array<WritableDeep<TInput>>) => void,\n ): TransactionType\n\n // Overload 2: Update multiple items with config and a callback\n update(\n keys: Array<TKey | unknown>,\n config: OperationConfig,\n callback: (drafts: Array<WritableDeep<TInput>>) => void,\n ): TransactionType\n\n // Overload 3: Update a single item with a callback\n update(\n id: TKey | unknown,\n callback: (draft: WritableDeep<TInput>) => void,\n ): TransactionType\n\n // Overload 4: Update a single item with config and a callback\n update(\n id: TKey | unknown,\n config: OperationConfig,\n callback: (draft: WritableDeep<TInput>) => void,\n ): TransactionType\n\n update(\n keys: (TKey | unknown) | Array<TKey | unknown>,\n configOrCallback:\n | ((draft: WritableDeep<TInput>) => void)\n | ((drafts: Array<WritableDeep<TInput>>) => void)\n | OperationConfig,\n maybeCallback?:\n | ((draft: WritableDeep<TInput>) => void)\n | ((drafts: Array<WritableDeep<TInput>>) => void),\n ) {\n return this._mutations.update(keys, configOrCallback, maybeCallback)\n }\n\n /**\n * Deletes one or more items from the collection\n * @param keys - Single key or array of keys to delete\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the delete operation(s)\n * @example\n * // Delete a single item\n * const tx = collection.delete(\"todo-1\")\n * await tx.isPersisted.promise\n *\n * @example\n * // Delete multiple items\n * const tx = collection.delete([\"todo-1\", \"todo-2\"])\n * await tx.isPersisted.promise\n *\n * @example\n * // Delete with metadata\n * const tx = collection.delete(\"todo-1\", { metadata: { reason: \"completed\" } })\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle errors\n * try {\n * const tx = collection.delete(\"item-1\")\n * await tx.isPersisted.promise\n * console.log('Delete successful')\n * } catch (error) {\n * console.log('Delete failed:', error)\n * }\n */\n delete = (\n keys: Array<TKey> | TKey,\n config?: OperationConfig,\n ): TransactionType<any> => {\n return this._mutations.delete(keys, config)\n }\n\n /**\n * Gets the current state of the collection as a Map\n * @returns Map containing all items in the collection, with keys as identifiers\n * @example\n * const itemsMap = collection.state\n * console.log(`Collection has ${itemsMap.size} items`)\n *\n * for (const [key, item] of itemsMap) {\n * console.log(`${key}: ${item.title}`)\n * }\n *\n * // Check if specific item exists\n * if (itemsMap.has(\"todo-1\")) {\n * console.log(\"Todo 1 exists:\", itemsMap.get(\"todo-1\"))\n * }\n */\n get state() {\n const result = new Map<TKey, WithVirtualProps<TOutput, TKey>>()\n for (const [key, value] of this.entries()) {\n result.set(key, value)\n }\n return result\n }\n\n /**\n * Gets the current state of the collection as a Map, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to a Map containing all items in the collection\n */\n stateWhenReady(): Promise<Map<TKey, WithVirtualProps<TOutput, TKey>>> {\n // If we already have data or collection is ready, resolve immediately\n if (this.size > 0 || this.isReady()) {\n return Promise.resolve(this.state)\n }\n\n // Use preload to ensure the collection starts loading, then return the state\n return this.preload().then(() => this.state)\n }\n\n /**\n * Gets the current state of the collection as an Array\n *\n * @returns An Array containing all items in the collection\n */\n get toArray() {\n return Array.from(this.values())\n }\n\n /**\n * Gets the current state of the collection as an Array, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to an Array containing all items in the collection\n */\n toArrayWhenReady(): Promise<Array<WithVirtualProps<TOutput, TKey>>> {\n // If we already have data or collection is ready, resolve immediately\n if (this.size > 0 || this.isReady()) {\n return Promise.resolve(this.toArray)\n }\n\n // Use preload to ensure the collection starts loading, then return the array\n return this.preload().then(() => this.toArray)\n }\n\n /**\n * Returns the current state of the collection as an array of changes\n * @param options - Options including optional where filter\n * @returns An array of changes\n * @example\n * // Get all items as changes\n * const allChanges = collection.currentStateAsChanges()\n *\n * // Get only items matching a condition\n * const activeChanges = collection.currentStateAsChanges({\n * where: (row) => row.status === 'active'\n * })\n *\n * // Get only items using a pre-compiled expression\n * const activeChanges = collection.currentStateAsChanges({\n * whereExpression: eq(row.status, 'active')\n * })\n */\n public currentStateAsChanges(\n options: CurrentStateAsChangesOptions = {},\n ): Array<ChangeMessage<WithVirtualProps<TOutput, TKey>>> | void {\n return currentStateAsChanges(this, options)\n }\n\n /**\n * Subscribe to changes in the collection\n * @param callback - Function called when items change\n * @param options - Subscription options including includeInitialState and where filter\n * @returns Unsubscribe function - Call this to stop listening for changes\n * @example\n * // Basic subscription\n * const subscription = collection.subscribeChanges((changes) => {\n * changes.forEach(change => {\n * console.log(`${change.type}: ${change.key}`, change.value)\n * })\n * })\n *\n * // Later: subscription.unsubscribe()\n *\n * @example\n * // Include current state immediately\n * const subscription = collection.subscribeChanges((changes) => {\n * updateUI(changes)\n * }, { includeInitialState: true })\n *\n * @example\n * // Subscribe only to changes matching a condition using where callback\n * import { eq } from \"@tanstack/db\"\n *\n * const subscription = collection.subscribeChanges((changes) => {\n * updateUI(changes)\n * }, {\n * includeInitialState: true,\n * where: (row) => eq(row.status, \"active\")\n * })\n *\n * @example\n * // Using multiple conditions with and()\n * import { and, eq, gt } from \"@tanstack/db\"\n *\n * const subscription = collection.subscribeChanges((changes) => {\n * updateUI(changes)\n * }, {\n * where: (row) => and(eq(row.status, \"active\"), gt(row.priority, 5))\n * })\n */\n public subscribeChanges(\n callback: (\n changes: Array<ChangeMessage<WithVirtualProps<TOutput, TKey>>>,\n ) => void,\n options: SubscribeChangesOptions<TOutput, TKey> = {},\n ): CollectionSubscription {\n return this._changes.subscribeChanges(callback, options)\n }\n\n /**\n * Subscribe to a collection event\n */\n public on<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>,\n ) {\n return this._events.on(event, callback)\n }\n\n /**\n * Subscribe to a collection event once\n */\n public once<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>,\n ) {\n return this._events.once(event, callback)\n }\n\n /**\n * Unsubscribe from a collection event\n */\n public off<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>,\n ) {\n this._events.off(event, callback)\n }\n\n /**\n * Wait for a collection event\n */\n public waitFor<T extends keyof AllCollectionEvents>(\n event: T,\n timeout?: number,\n ) {\n return this._events.waitFor(event, timeout)\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 async cleanup(): Promise<void> {\n this._lifecycle.cleanup()\n return Promise.resolve()\n }\n}\n\nfunction buildCompareOptionsFromConfig(\n config: CollectionConfig<any, any, any>,\n): StringCollationConfig {\n if (config.defaultStringCollation) {\n const options = config.defaultStringCollation\n return {\n stringSort: options.stringSort ?? `locale`,\n locale: options.stringSort === `locale` ? options.locale : undefined,\n localeOptions:\n options.stringSort === `locale` ? options.localeOptions : undefined,\n }\n } else {\n return {\n stringSort: `locale`,\n }\n }\n}\n"],"names":["config"],"mappings":"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"}
|
|
@@ -1,47 +1,57 @@
|
|
|
1
|
-
import { IndexProxy, LazyIndexWrapper } from '../indexes/lazy-index.js';
|
|
2
|
-
import { BTreeIndex } from '../indexes/btree-index.js';
|
|
3
1
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
4
|
-
import { BaseIndex,
|
|
2
|
+
import { BaseIndex, IndexConstructor } from '../indexes/base-index.js';
|
|
5
3
|
import { ChangeMessage } from '../types.js';
|
|
6
4
|
import { IndexOptions } from '../indexes/index-options.js';
|
|
7
5
|
import { SingleRowRefProxy } from '../query/builder/ref-proxy.js';
|
|
8
6
|
import { CollectionLifecycleManager } from './lifecycle.js';
|
|
9
7
|
import { CollectionStateManager } from './state.js';
|
|
8
|
+
import { CollectionEventsManager, CollectionIndexMetadata } from './events.js';
|
|
10
9
|
export declare class CollectionIndexesManager<TOutput extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInput extends object = TOutput> {
|
|
11
10
|
private lifecycle;
|
|
12
11
|
private state;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
private defaultIndexType;
|
|
13
|
+
private events;
|
|
14
|
+
indexes: Map<number, BaseIndex<TKey>>;
|
|
15
|
+
indexMetadata: Map<number, CollectionIndexMetadata>;
|
|
16
16
|
indexCounter: number;
|
|
17
17
|
constructor();
|
|
18
18
|
setDeps(deps: {
|
|
19
19
|
state: CollectionStateManager<TOutput, TKey, TSchema, TInput>;
|
|
20
20
|
lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>;
|
|
21
|
+
defaultIndexType?: IndexConstructor<TKey>;
|
|
22
|
+
events: CollectionEventsManager;
|
|
21
23
|
}): void;
|
|
22
24
|
/**
|
|
23
25
|
* Creates an index on a collection for faster queries.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* // With explicit index type (recommended for tree-shaking)
|
|
30
|
+
* import { BasicIndex } from '@tanstack/db'
|
|
31
|
+
* collection.createIndex((row) => row.userId, { indexType: BasicIndex })
|
|
32
|
+
*
|
|
33
|
+
* // With collection's default index type
|
|
34
|
+
* collection.createIndex((row) => row.userId)
|
|
35
|
+
* ```
|
|
24
36
|
*/
|
|
25
|
-
createIndex<
|
|
37
|
+
createIndex<TIndexType extends IndexConstructor<TKey>>(indexCallback: (row: SingleRowRefProxy<TOutput>) => any, config?: IndexOptions<TIndexType>): BaseIndex<TKey>;
|
|
26
38
|
/**
|
|
27
|
-
*
|
|
39
|
+
* Removes an index from this collection.
|
|
40
|
+
* Returns true when an index existed and was removed, false otherwise.
|
|
28
41
|
*/
|
|
29
|
-
|
|
42
|
+
removeIndex(indexOrId: BaseIndex<TKey> | number): boolean;
|
|
30
43
|
/**
|
|
31
|
-
*
|
|
44
|
+
* Returns a sorted snapshot of index metadata.
|
|
45
|
+
* This allows persisted wrappers to bootstrap from indexes that were created
|
|
46
|
+
* before they attached lifecycle listeners.
|
|
32
47
|
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Get resolved indexes for query optimization
|
|
36
|
-
*/
|
|
37
|
-
get indexes(): Map<number, BaseIndex<TKey>>;
|
|
48
|
+
getIndexMetadataSnapshot(): Array<CollectionIndexMetadata>;
|
|
38
49
|
/**
|
|
39
50
|
* Updates all indexes when the collection changes
|
|
40
51
|
*/
|
|
41
52
|
updateIndexes(changes: Array<ChangeMessage<TOutput, TKey>>): void;
|
|
42
53
|
/**
|
|
43
|
-
* Clean up
|
|
44
|
-
* This can be called manually or automatically by garbage collection
|
|
54
|
+
* Clean up indexes
|
|
45
55
|
*/
|
|
46
56
|
cleanup(): void;
|
|
47
57
|
}
|
|
@@ -1,19 +1,171 @@
|
|
|
1
|
-
import { LazyIndexWrapper, IndexProxy } from "../indexes/lazy-index.js";
|
|
2
1
|
import { createSingleRowRefProxy, toExpression } from "../query/builder/ref-proxy.js";
|
|
3
|
-
import {
|
|
2
|
+
import { CollectionConfigurationError } from "../errors.js";
|
|
3
|
+
const INDEX_SIGNATURE_VERSION = 1;
|
|
4
|
+
function compareStringsCodePoint(left, right) {
|
|
5
|
+
if (left === right) {
|
|
6
|
+
return 0;
|
|
7
|
+
}
|
|
8
|
+
return left < right ? -1 : 1;
|
|
9
|
+
}
|
|
10
|
+
function resolveResolverMetadata(resolver) {
|
|
11
|
+
return {
|
|
12
|
+
kind: `constructor`,
|
|
13
|
+
...resolver.name ? { name: resolver.name } : {}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function toSerializableIndexValue(value) {
|
|
17
|
+
if (value == null) {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
switch (typeof value) {
|
|
21
|
+
case `string`:
|
|
22
|
+
case `boolean`:
|
|
23
|
+
return value;
|
|
24
|
+
case `number`:
|
|
25
|
+
return Number.isFinite(value) ? value : null;
|
|
26
|
+
case `bigint`:
|
|
27
|
+
return { __type: `bigint`, value: value.toString() };
|
|
28
|
+
case `function`:
|
|
29
|
+
case `symbol`:
|
|
30
|
+
return void 0;
|
|
31
|
+
case `undefined`:
|
|
32
|
+
return void 0;
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
return value.map((entry) => toSerializableIndexValue(entry) ?? null);
|
|
36
|
+
}
|
|
37
|
+
if (value instanceof Date) {
|
|
38
|
+
return {
|
|
39
|
+
__type: `date`,
|
|
40
|
+
value: value.toISOString()
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (value instanceof Set) {
|
|
44
|
+
const serializedValues = Array.from(value).map((entry) => toSerializableIndexValue(entry) ?? null).sort(
|
|
45
|
+
(a, b) => compareStringsCodePoint(
|
|
46
|
+
stableStringifyCollectionIndexValue(a),
|
|
47
|
+
stableStringifyCollectionIndexValue(b)
|
|
48
|
+
)
|
|
49
|
+
);
|
|
50
|
+
return {
|
|
51
|
+
__type: `set`,
|
|
52
|
+
values: serializedValues
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (value instanceof Map) {
|
|
56
|
+
const serializedEntries = Array.from(value.entries()).map(([mapKey, mapValue]) => ({
|
|
57
|
+
key: toSerializableIndexValue(mapKey) ?? null,
|
|
58
|
+
value: toSerializableIndexValue(mapValue) ?? null
|
|
59
|
+
})).sort(
|
|
60
|
+
(a, b) => compareStringsCodePoint(
|
|
61
|
+
stableStringifyCollectionIndexValue(a.key),
|
|
62
|
+
stableStringifyCollectionIndexValue(b.key)
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
return {
|
|
66
|
+
__type: `map`,
|
|
67
|
+
entries: serializedEntries
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (value instanceof RegExp) {
|
|
71
|
+
return {
|
|
72
|
+
__type: `regexp`,
|
|
73
|
+
value: value.toString()
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const serializedObject = {};
|
|
77
|
+
const entries = Object.entries(value).sort(
|
|
78
|
+
([leftKey], [rightKey]) => compareStringsCodePoint(leftKey, rightKey)
|
|
79
|
+
);
|
|
80
|
+
for (const [key, entryValue] of entries) {
|
|
81
|
+
const serializedEntry = toSerializableIndexValue(entryValue);
|
|
82
|
+
if (serializedEntry !== void 0) {
|
|
83
|
+
serializedObject[key] = serializedEntry;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return serializedObject;
|
|
87
|
+
}
|
|
88
|
+
function stableStringifyCollectionIndexValue(value) {
|
|
89
|
+
if (value === null) {
|
|
90
|
+
return `null`;
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(value)) {
|
|
93
|
+
return `[${value.map(stableStringifyCollectionIndexValue).join(`,`)}]`;
|
|
94
|
+
}
|
|
95
|
+
if (typeof value !== `object`) {
|
|
96
|
+
return JSON.stringify(value);
|
|
97
|
+
}
|
|
98
|
+
const sortedKeys = Object.keys(value).sort(
|
|
99
|
+
(left, right) => compareStringsCodePoint(left, right)
|
|
100
|
+
);
|
|
101
|
+
const serializedEntries = sortedKeys.map(
|
|
102
|
+
(key) => `${JSON.stringify(key)}:${stableStringifyCollectionIndexValue(value[key])}`
|
|
103
|
+
);
|
|
104
|
+
return `{${serializedEntries.join(`,`)}}`;
|
|
105
|
+
}
|
|
106
|
+
function createCollectionIndexMetadata(indexId, expression, name, resolver, options) {
|
|
107
|
+
const resolverMetadata = resolveResolverMetadata(resolver);
|
|
108
|
+
const serializedExpression = toSerializableIndexValue(expression) ?? null;
|
|
109
|
+
const serializedOptions = toSerializableIndexValue(options);
|
|
110
|
+
const signatureInput = toSerializableIndexValue({
|
|
111
|
+
signatureVersion: INDEX_SIGNATURE_VERSION,
|
|
112
|
+
expression: serializedExpression,
|
|
113
|
+
options: serializedOptions ?? null
|
|
114
|
+
});
|
|
115
|
+
const normalizedSignatureInput = signatureInput ?? null;
|
|
116
|
+
const signature = stableStringifyCollectionIndexValue(
|
|
117
|
+
normalizedSignatureInput
|
|
118
|
+
);
|
|
119
|
+
return {
|
|
120
|
+
signatureVersion: INDEX_SIGNATURE_VERSION,
|
|
121
|
+
signature,
|
|
122
|
+
indexId,
|
|
123
|
+
name,
|
|
124
|
+
expression,
|
|
125
|
+
resolver: resolverMetadata,
|
|
126
|
+
...serializedOptions === void 0 ? {} : { options: serializedOptions }
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function cloneSerializableIndexValue(value) {
|
|
130
|
+
if (value === null || typeof value !== `object`) {
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
if (Array.isArray(value)) {
|
|
134
|
+
return value.map((entry) => cloneSerializableIndexValue(entry));
|
|
135
|
+
}
|
|
136
|
+
const cloned = {};
|
|
137
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
138
|
+
cloned[key] = cloneSerializableIndexValue(entryValue);
|
|
139
|
+
}
|
|
140
|
+
return cloned;
|
|
141
|
+
}
|
|
142
|
+
function cloneExpression(expression) {
|
|
143
|
+
return JSON.parse(JSON.stringify(expression));
|
|
144
|
+
}
|
|
4
145
|
class CollectionIndexesManager {
|
|
5
146
|
constructor() {
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
8
|
-
this.isIndexesResolved = false;
|
|
147
|
+
this.indexes = /* @__PURE__ */ new Map();
|
|
148
|
+
this.indexMetadata = /* @__PURE__ */ new Map();
|
|
9
149
|
this.indexCounter = 0;
|
|
10
150
|
}
|
|
11
151
|
setDeps(deps) {
|
|
12
152
|
this.state = deps.state;
|
|
13
153
|
this.lifecycle = deps.lifecycle;
|
|
154
|
+
this.defaultIndexType = deps.defaultIndexType;
|
|
155
|
+
this.events = deps.events;
|
|
14
156
|
}
|
|
15
157
|
/**
|
|
16
158
|
* Creates an index on a collection for faster queries.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* // With explicit index type (recommended for tree-shaking)
|
|
163
|
+
* import { BasicIndex } from '@tanstack/db'
|
|
164
|
+
* collection.createIndex((row) => row.userId, { indexType: BasicIndex })
|
|
165
|
+
*
|
|
166
|
+
* // With collection's default index type
|
|
167
|
+
* collection.createIndex((row) => row.userId)
|
|
168
|
+
* ```
|
|
17
169
|
*/
|
|
18
170
|
createIndex(indexCallback, config = {}) {
|
|
19
171
|
this.lifecycle.validateCollectionUsable(`createIndex`);
|
|
@@ -21,75 +173,73 @@ class CollectionIndexesManager {
|
|
|
21
173
|
const singleRowRefProxy = createSingleRowRefProxy();
|
|
22
174
|
const indexExpression = indexCallback(singleRowRefProxy);
|
|
23
175
|
const expression = toExpression(indexExpression);
|
|
24
|
-
const
|
|
25
|
-
|
|
176
|
+
const IndexType = config.indexType ?? this.defaultIndexType;
|
|
177
|
+
if (!IndexType) {
|
|
178
|
+
throw new CollectionConfigurationError(
|
|
179
|
+
`No index type specified and no defaultIndexType set on collection. Either pass indexType in config, or set defaultIndexType on the collection:
|
|
180
|
+
import { BasicIndex } from '@tanstack/db'
|
|
181
|
+
createCollection({ defaultIndexType: BasicIndex, ... })`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const index = new IndexType(
|
|
26
185
|
indexId,
|
|
27
186
|
expression,
|
|
28
187
|
config.name,
|
|
29
|
-
|
|
30
|
-
config.options,
|
|
31
|
-
this.state.entries()
|
|
188
|
+
config.options
|
|
32
189
|
);
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} else if (typeof resolver === `function` && resolver.prototype) {
|
|
42
|
-
try {
|
|
43
|
-
const resolvedIndex = lazyIndex.getResolved();
|
|
44
|
-
this.resolvedIndexes.set(indexId, resolvedIndex);
|
|
45
|
-
} catch {
|
|
46
|
-
this.resolveSingleIndex(indexId, lazyIndex).catch((error) => {
|
|
47
|
-
console.warn(`Failed to resolve single index:`, error);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
} else if (this.isIndexesResolved) {
|
|
51
|
-
this.resolveSingleIndex(indexId, lazyIndex).catch((error) => {
|
|
52
|
-
console.warn(`Failed to resolve single index:`, error);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
return new IndexProxy(indexId, lazyIndex);
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Resolve all lazy indexes (called when collection first syncs)
|
|
59
|
-
*/
|
|
60
|
-
async resolveAllIndexes() {
|
|
61
|
-
if (this.isIndexesResolved) return;
|
|
62
|
-
const resolutionPromises = Array.from(this.lazyIndexes.entries()).map(
|
|
63
|
-
async ([indexId, lazyIndex]) => {
|
|
64
|
-
const resolvedIndex = await lazyIndex.resolve();
|
|
65
|
-
resolvedIndex.build(this.state.entries());
|
|
66
|
-
this.resolvedIndexes.set(indexId, resolvedIndex);
|
|
67
|
-
return { indexId, resolvedIndex };
|
|
68
|
-
}
|
|
190
|
+
index.build(this.state.entries());
|
|
191
|
+
this.indexes.set(indexId, index);
|
|
192
|
+
const metadata = createCollectionIndexMetadata(
|
|
193
|
+
indexId,
|
|
194
|
+
expression,
|
|
195
|
+
config.name,
|
|
196
|
+
IndexType,
|
|
197
|
+
config.options
|
|
69
198
|
);
|
|
70
|
-
|
|
71
|
-
this.
|
|
199
|
+
this.indexMetadata.set(indexId, metadata);
|
|
200
|
+
this.events.emitIndexAdded(metadata);
|
|
201
|
+
return index;
|
|
72
202
|
}
|
|
73
203
|
/**
|
|
74
|
-
*
|
|
204
|
+
* Removes an index from this collection.
|
|
205
|
+
* Returns true when an index existed and was removed, false otherwise.
|
|
75
206
|
*/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
this.
|
|
80
|
-
|
|
207
|
+
removeIndex(indexOrId) {
|
|
208
|
+
this.lifecycle.validateCollectionUsable(`removeIndex`);
|
|
209
|
+
const indexId = typeof indexOrId === `number` ? indexOrId : indexOrId.id;
|
|
210
|
+
const index = this.indexes.get(indexId);
|
|
211
|
+
if (!index) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
if (typeof indexOrId !== `number` && index !== indexOrId) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
this.indexes.delete(indexId);
|
|
218
|
+
const metadata = this.indexMetadata.get(indexId);
|
|
219
|
+
this.indexMetadata.delete(indexId);
|
|
220
|
+
if (metadata) {
|
|
221
|
+
this.events.emitIndexRemoved(metadata);
|
|
222
|
+
}
|
|
223
|
+
return true;
|
|
81
224
|
}
|
|
82
225
|
/**
|
|
83
|
-
*
|
|
226
|
+
* Returns a sorted snapshot of index metadata.
|
|
227
|
+
* This allows persisted wrappers to bootstrap from indexes that were created
|
|
228
|
+
* before they attached lifecycle listeners.
|
|
84
229
|
*/
|
|
85
|
-
|
|
86
|
-
return this.
|
|
230
|
+
getIndexMetadataSnapshot() {
|
|
231
|
+
return Array.from(this.indexMetadata.values()).sort((left, right) => left.indexId - right.indexId).map((metadata) => ({
|
|
232
|
+
...metadata,
|
|
233
|
+
expression: cloneExpression(metadata.expression),
|
|
234
|
+
resolver: { ...metadata.resolver },
|
|
235
|
+
...metadata.options === void 0 ? {} : { options: cloneSerializableIndexValue(metadata.options) }
|
|
236
|
+
}));
|
|
87
237
|
}
|
|
88
238
|
/**
|
|
89
239
|
* Updates all indexes when the collection changes
|
|
90
240
|
*/
|
|
91
241
|
updateIndexes(changes) {
|
|
92
|
-
for (const index of this.
|
|
242
|
+
for (const index of this.indexes.values()) {
|
|
93
243
|
for (const change of changes) {
|
|
94
244
|
switch (change.type) {
|
|
95
245
|
case `insert`:
|
|
@@ -110,12 +260,11 @@ class CollectionIndexesManager {
|
|
|
110
260
|
}
|
|
111
261
|
}
|
|
112
262
|
/**
|
|
113
|
-
* Clean up
|
|
114
|
-
* This can be called manually or automatically by garbage collection
|
|
263
|
+
* Clean up indexes
|
|
115
264
|
*/
|
|
116
265
|
cleanup() {
|
|
117
|
-
this.
|
|
118
|
-
this.
|
|
266
|
+
this.indexes.clear();
|
|
267
|
+
this.indexMetadata.clear();
|
|
119
268
|
}
|
|
120
269
|
}
|
|
121
270
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexes.js","sources":["../../../src/collection/indexes.ts"],"sourcesContent":["import { IndexProxy, LazyIndexWrapper } from '../indexes/lazy-index'\nimport {\n createSingleRowRefProxy,\n toExpression,\n} from '../query/builder/ref-proxy'\nimport { BTreeIndex } from '../indexes/btree-index'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { BaseIndex, IndexResolver } from '../indexes/base-index'\nimport type { ChangeMessage } from '../types'\nimport type { IndexOptions } from '../indexes/index-options'\nimport type { SingleRowRefProxy } from '../query/builder/ref-proxy'\nimport type { CollectionLifecycleManager } from './lifecycle'\nimport type { CollectionStateManager } from './state'\n\nexport class CollectionIndexesManager<\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 private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private state!: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n\n public lazyIndexes = new Map<number, LazyIndexWrapper<TKey>>()\n public resolvedIndexes = new Map<number, BaseIndex<TKey>>()\n public isIndexesResolved = false\n public indexCounter = 0\n\n constructor() {}\n\n setDeps(deps: {\n state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n }) {\n this.state = deps.state\n this.lifecycle = deps.lifecycle\n }\n\n /**\n * Creates an index on a collection for faster queries.\n */\n public createIndex<TResolver extends IndexResolver<TKey> = typeof BTreeIndex>(\n indexCallback: (row: SingleRowRefProxy<TOutput>) => any,\n config: IndexOptions<TResolver> = {},\n ): IndexProxy<TKey> {\n this.lifecycle.validateCollectionUsable(`createIndex`)\n\n const indexId = ++this.indexCounter\n const singleRowRefProxy = createSingleRowRefProxy<TOutput>()\n const indexExpression = indexCallback(singleRowRefProxy)\n const expression = toExpression(indexExpression)\n\n // Default to BTreeIndex if no type specified\n const resolver = config.indexType ?? (BTreeIndex as unknown as TResolver)\n\n // Create lazy wrapper\n const lazyIndex = new LazyIndexWrapper<TKey>(\n indexId,\n expression,\n config.name,\n resolver,\n config.options,\n this.state.entries(),\n )\n\n this.lazyIndexes.set(indexId, lazyIndex)\n\n // For BTreeIndex, resolve immediately and synchronously\n if ((resolver as unknown) === BTreeIndex) {\n try {\n const resolvedIndex = lazyIndex.getResolved()\n this.resolvedIndexes.set(indexId, resolvedIndex)\n } catch (error) {\n console.warn(`Failed to resolve BTreeIndex:`, error)\n }\n } else if (typeof resolver === `function` && resolver.prototype) {\n // Other synchronous constructors - resolve immediately\n try {\n const resolvedIndex = lazyIndex.getResolved()\n this.resolvedIndexes.set(indexId, resolvedIndex)\n } catch {\n // Fallback to async resolution\n this.resolveSingleIndex(indexId, lazyIndex).catch((error) => {\n console.warn(`Failed to resolve single index:`, error)\n })\n }\n } else if (this.isIndexesResolved) {\n // Async loader but indexes are already resolved - resolve this one\n this.resolveSingleIndex(indexId, lazyIndex).catch((error) => {\n console.warn(`Failed to resolve single index:`, error)\n })\n }\n\n return new IndexProxy(indexId, lazyIndex)\n }\n\n /**\n * Resolve all lazy indexes (called when collection first syncs)\n */\n public async resolveAllIndexes(): Promise<void> {\n if (this.isIndexesResolved) return\n\n const resolutionPromises = Array.from(this.lazyIndexes.entries()).map(\n async ([indexId, lazyIndex]) => {\n const resolvedIndex = await lazyIndex.resolve()\n\n // Build index with current data\n resolvedIndex.build(this.state.entries())\n\n this.resolvedIndexes.set(indexId, resolvedIndex)\n return { indexId, resolvedIndex }\n },\n )\n\n await Promise.all(resolutionPromises)\n this.isIndexesResolved = true\n }\n\n /**\n * Resolve a single index immediately\n */\n private async resolveSingleIndex(\n indexId: number,\n lazyIndex: LazyIndexWrapper<TKey>,\n ): Promise<BaseIndex<TKey>> {\n const resolvedIndex = await lazyIndex.resolve()\n resolvedIndex.build(this.state.entries())\n this.resolvedIndexes.set(indexId, resolvedIndex)\n return resolvedIndex\n }\n\n /**\n * Get resolved indexes for query optimization\n */\n get indexes(): Map<number, BaseIndex<TKey>> {\n return this.resolvedIndexes\n }\n\n /**\n * Updates all indexes when the collection changes\n */\n public updateIndexes(changes: Array<ChangeMessage<TOutput, TKey>>): void {\n for (const index of this.resolvedIndexes.values()) {\n for (const change of changes) {\n switch (change.type) {\n case `insert`:\n index.add(change.key, change.value)\n break\n case `update`:\n if (change.previousValue) {\n index.update(change.key, change.previousValue, change.value)\n } else {\n index.add(change.key, change.value)\n }\n break\n case `delete`:\n index.remove(change.key, change.value)\n break\n }\n }\n }\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.lazyIndexes.clear()\n this.resolvedIndexes.clear()\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"indexes.js","sources":["../../../src/collection/indexes.ts"],"sourcesContent":["import {\n createSingleRowRefProxy,\n toExpression,\n} from '../query/builder/ref-proxy'\nimport { CollectionConfigurationError } from '../errors'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { BaseIndex, IndexConstructor } from '../indexes/base-index'\nimport type { ChangeMessage } from '../types'\nimport type { IndexOptions } from '../indexes/index-options'\nimport type { SingleRowRefProxy } from '../query/builder/ref-proxy'\nimport type { CollectionLifecycleManager } from './lifecycle'\nimport type { CollectionStateManager } from './state'\nimport type { BasicExpression } from '../query/ir'\nimport type {\n CollectionEventsManager,\n CollectionIndexMetadata,\n CollectionIndexResolverMetadata,\n CollectionIndexSerializableValue,\n} from './events'\n\nconst INDEX_SIGNATURE_VERSION = 1 as const\n\nfunction compareStringsCodePoint(left: string, right: string): number {\n if (left === right) {\n return 0\n }\n\n return left < right ? -1 : 1\n}\n\nfunction resolveResolverMetadata<TKey extends string | number>(\n resolver: IndexConstructor<TKey>,\n): CollectionIndexResolverMetadata {\n return {\n kind: `constructor`,\n ...(resolver.name ? { name: resolver.name } : {}),\n }\n}\n\nfunction toSerializableIndexValue(\n value: unknown,\n): CollectionIndexSerializableValue | undefined {\n if (value == null) {\n return value\n }\n\n switch (typeof value) {\n case `string`:\n case `boolean`:\n return value\n case `number`:\n return Number.isFinite(value) ? value : null\n case `bigint`:\n return { __type: `bigint`, value: value.toString() }\n case `function`:\n case `symbol`:\n // Function and symbol identity are process-local and not stable across runtimes.\n // Dropping them keeps signatures deterministic; we may skip index reuse, which is acceptable.\n return undefined\n case `undefined`:\n return undefined\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => toSerializableIndexValue(entry) ?? null)\n }\n\n if (value instanceof Date) {\n return {\n __type: `date`,\n value: value.toISOString(),\n }\n }\n\n if (value instanceof Set) {\n const serializedValues = Array.from(value)\n .map((entry) => toSerializableIndexValue(entry) ?? null)\n .sort((a, b) =>\n compareStringsCodePoint(\n stableStringifyCollectionIndexValue(a),\n stableStringifyCollectionIndexValue(b),\n ),\n )\n return {\n __type: `set`,\n values: serializedValues,\n }\n }\n\n if (value instanceof Map) {\n const serializedEntries = Array.from(value.entries())\n .map(([mapKey, mapValue]) => ({\n key: toSerializableIndexValue(mapKey) ?? null,\n value: toSerializableIndexValue(mapValue) ?? null,\n }))\n .sort((a, b) =>\n compareStringsCodePoint(\n stableStringifyCollectionIndexValue(a.key),\n stableStringifyCollectionIndexValue(b.key),\n ),\n )\n\n return {\n __type: `map`,\n entries: serializedEntries,\n }\n }\n\n if (value instanceof RegExp) {\n return {\n __type: `regexp`,\n value: value.toString(),\n }\n }\n\n const serializedObject: Record<string, CollectionIndexSerializableValue> = {}\n const entries = Object.entries(value as Record<string, unknown>).sort(\n ([leftKey], [rightKey]) => compareStringsCodePoint(leftKey, rightKey),\n )\n\n for (const [key, entryValue] of entries) {\n const serializedEntry = toSerializableIndexValue(entryValue)\n if (serializedEntry !== undefined) {\n serializedObject[key] = serializedEntry\n }\n }\n\n return serializedObject\n}\n\nfunction stableStringifyCollectionIndexValue(\n value: CollectionIndexSerializableValue,\n): string {\n if (value === null) {\n return `null`\n }\n\n if (Array.isArray(value)) {\n return `[${value.map(stableStringifyCollectionIndexValue).join(`,`)}]`\n }\n\n if (typeof value !== `object`) {\n return JSON.stringify(value)\n }\n\n const sortedKeys = Object.keys(value).sort((left, right) =>\n compareStringsCodePoint(left, right),\n )\n const serializedEntries = sortedKeys.map(\n (key) =>\n `${JSON.stringify(key)}:${stableStringifyCollectionIndexValue(value[key]!)}`,\n )\n return `{${serializedEntries.join(`,`)}}`\n}\n\nfunction createCollectionIndexMetadata<TKey extends string | number>(\n indexId: number,\n expression: BasicExpression,\n name: string | undefined,\n resolver: IndexConstructor<TKey>,\n options: unknown,\n): CollectionIndexMetadata {\n const resolverMetadata = resolveResolverMetadata(resolver)\n const serializedExpression = toSerializableIndexValue(expression) ?? null\n const serializedOptions = toSerializableIndexValue(options)\n const signatureInput = toSerializableIndexValue({\n signatureVersion: INDEX_SIGNATURE_VERSION,\n expression: serializedExpression,\n options: serializedOptions ?? null,\n })\n const normalizedSignatureInput = signatureInput ?? null\n const signature = stableStringifyCollectionIndexValue(\n normalizedSignatureInput,\n )\n\n return {\n signatureVersion: INDEX_SIGNATURE_VERSION,\n signature,\n indexId,\n name,\n expression,\n resolver: resolverMetadata,\n ...(serializedOptions === undefined ? {} : { options: serializedOptions }),\n }\n}\n\nfunction cloneSerializableIndexValue(\n value: CollectionIndexSerializableValue,\n): CollectionIndexSerializableValue {\n if (value === null || typeof value !== `object`) {\n return value\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => cloneSerializableIndexValue(entry))\n }\n\n const cloned: Record<string, CollectionIndexSerializableValue> = {}\n for (const [key, entryValue] of Object.entries(value)) {\n cloned[key] = cloneSerializableIndexValue(entryValue)\n }\n return cloned\n}\n\nfunction cloneExpression(expression: BasicExpression): BasicExpression {\n return JSON.parse(JSON.stringify(expression)) as BasicExpression\n}\n\nexport class CollectionIndexesManager<\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 private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private state!: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n private defaultIndexType: IndexConstructor<TKey> | undefined\n private events!: CollectionEventsManager\n\n public indexes = new Map<number, BaseIndex<TKey>>()\n public indexMetadata = new Map<number, CollectionIndexMetadata>()\n public indexCounter = 0\n\n constructor() {}\n\n setDeps(deps: {\n state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n defaultIndexType?: IndexConstructor<TKey>\n events: CollectionEventsManager\n }) {\n this.state = deps.state\n this.lifecycle = deps.lifecycle\n this.defaultIndexType = deps.defaultIndexType\n this.events = deps.events\n }\n\n /**\n * Creates an index on a collection for faster queries.\n *\n * @example\n * ```ts\n * // With explicit index type (recommended for tree-shaking)\n * import { BasicIndex } from '@tanstack/db'\n * collection.createIndex((row) => row.userId, { indexType: BasicIndex })\n *\n * // With collection's default index type\n * collection.createIndex((row) => row.userId)\n * ```\n */\n public createIndex<TIndexType extends IndexConstructor<TKey>>(\n indexCallback: (row: SingleRowRefProxy<TOutput>) => any,\n config: IndexOptions<TIndexType> = {},\n ): BaseIndex<TKey> {\n this.lifecycle.validateCollectionUsable(`createIndex`)\n\n const indexId = ++this.indexCounter\n const singleRowRefProxy = createSingleRowRefProxy<TOutput>()\n const indexExpression = indexCallback(singleRowRefProxy)\n const expression = toExpression(indexExpression)\n\n // Use provided index type, or fall back to collection's default\n const IndexType = config.indexType ?? this.defaultIndexType\n if (!IndexType) {\n throw new CollectionConfigurationError(\n `No index type specified and no defaultIndexType set on collection. ` +\n `Either pass indexType in config, or set defaultIndexType on the collection:\\n` +\n ` import { BasicIndex } from '@tanstack/db'\\n` +\n ` createCollection({ defaultIndexType: BasicIndex, ... })`,\n )\n }\n\n // Create index synchronously\n const index = new IndexType(\n indexId,\n expression,\n config.name,\n config.options,\n )\n\n // Build with current data\n index.build(this.state.entries())\n\n this.indexes.set(indexId, index)\n\n // Track metadata and emit event\n const metadata = createCollectionIndexMetadata(\n indexId,\n expression,\n config.name,\n IndexType,\n config.options,\n )\n this.indexMetadata.set(indexId, metadata)\n this.events.emitIndexAdded(metadata)\n\n return index\n }\n\n /**\n * Removes an index from this collection.\n * Returns true when an index existed and was removed, false otherwise.\n */\n public removeIndex(indexOrId: BaseIndex<TKey> | number): boolean {\n this.lifecycle.validateCollectionUsable(`removeIndex`)\n\n const indexId = typeof indexOrId === `number` ? indexOrId : indexOrId.id\n const index = this.indexes.get(indexId)\n if (!index) {\n return false\n }\n\n if (typeof indexOrId !== `number` && index !== indexOrId) {\n // Passed a different index instance with the same id — do not remove.\n return false\n }\n\n this.indexes.delete(indexId)\n\n const metadata = this.indexMetadata.get(indexId)\n this.indexMetadata.delete(indexId)\n if (metadata) {\n this.events.emitIndexRemoved(metadata)\n }\n\n return true\n }\n\n /**\n * Returns a sorted snapshot of index metadata.\n * This allows persisted wrappers to bootstrap from indexes that were created\n * before they attached lifecycle listeners.\n */\n public getIndexMetadataSnapshot(): Array<CollectionIndexMetadata> {\n return Array.from(this.indexMetadata.values())\n .sort((left, right) => left.indexId - right.indexId)\n .map((metadata) => ({\n ...metadata,\n expression: cloneExpression(metadata.expression),\n resolver: { ...metadata.resolver },\n ...(metadata.options === undefined\n ? {}\n : { options: cloneSerializableIndexValue(metadata.options) }),\n }))\n }\n\n /**\n * Updates all indexes when the collection changes\n */\n public updateIndexes(changes: Array<ChangeMessage<TOutput, TKey>>): void {\n for (const index of this.indexes.values()) {\n for (const change of changes) {\n switch (change.type) {\n case `insert`:\n index.add(change.key, change.value)\n break\n case `update`:\n if (change.previousValue) {\n index.update(change.key, change.previousValue, change.value)\n } else {\n index.add(change.key, change.value)\n }\n break\n case `delete`:\n index.remove(change.key, change.value)\n break\n }\n }\n }\n }\n\n /**\n * Clean up indexes\n */\n public cleanup(): void {\n this.indexes.clear()\n this.indexMetadata.clear()\n }\n}\n"],"names":[],"mappings":"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"}
|