{"version":3,"file":"indexes.cjs","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":["createSingleRowRefProxy","toExpression","CollectionConfigurationError"],"mappings":"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"}