{"version":3,"file":"effect.cjs","sources":["../../../src/query/effect.ts"],"sourcesContent":["import { D2, output } from '@tanstack/db-ivm'\nimport { transactionScopedScheduler } from '../scheduler.js'\nimport { getActiveTransaction } from '../transactions.js'\nimport { compileQuery } from './compiler/index.js'\nimport {\n  normalizeExpressionPaths,\n  normalizeOrderByPaths,\n} from './compiler/expressions.js'\nimport { getCollectionBuilder } from './live/collection-registry.js'\nimport {\n  buildQueryFromConfig,\n  computeOrderedLoadCursor,\n  computeSubscriptionOrderByHints,\n  extractCollectionAliases,\n  extractCollectionsFromQuery,\n  filterDuplicateInserts,\n  sendChangesToInput,\n  splitUpdates,\n  trackBiggestSentValue,\n} from './live/utils.js'\nimport type { RootStreamBuilder } from '@tanstack/db-ivm'\nimport type { Collection } from '../collection/index.js'\nimport type { CollectionSubscription } from '../collection/subscription.js'\nimport type { InitialQueryBuilder, QueryBuilder } from './builder/index.js'\nimport type { Context } from './builder/types.js'\nimport type { BasicExpression, QueryIR } from './ir.js'\nimport type { OrderByOptimizationInfo } from './compiler/order-by.js'\nimport type { ChangeMessage, KeyedStream, ResultStream } from '../types.js'\n\n// ---------------------------------------------------------------------------\n// Public Types\n// ---------------------------------------------------------------------------\n\n/** Event types for query result deltas */\nexport type DeltaType = 'enter' | 'exit' | 'update'\n\n/** Delta event emitted when a row enters, exits, or updates within a query result */\nexport type DeltaEvent<\n  TRow extends object = Record<string, unknown>,\n  TKey extends string | number = string | number,\n> =\n  | {\n      type: 'enter'\n      key: TKey\n      /** Current value for the entering row */\n      value: TRow\n      metadata?: Record<string, unknown>\n    }\n  | {\n      type: 'exit'\n      key: TKey\n      /** Current value for the exiting row */\n      value: TRow\n      metadata?: Record<string, unknown>\n    }\n  | {\n      type: 'update'\n      key: TKey\n      /** Current value after the update */\n      value: TRow\n      /** Previous value before the batch */\n      previousValue: TRow\n      metadata?: Record<string, unknown>\n    }\n\n/** Context passed to effect handlers */\nexport interface EffectContext {\n  /** ID of this effect (auto-generated if not provided) */\n  effectId: string\n  /** Aborted when effect.dispose() is called */\n  signal: AbortSignal\n}\n\n/** Query input - can be a builder function or a prebuilt query */\nexport type EffectQueryInput<TContext extends Context> =\n  | ((q: InitialQueryBuilder) => QueryBuilder<TContext>)\n  | QueryBuilder<TContext>\n\ntype EffectEventHandler<\n  TRow extends object = Record<string, unknown>,\n  TKey extends string | number = string | number,\n> = (event: DeltaEvent<TRow, TKey>, ctx: EffectContext) => void | Promise<void>\n\ntype EffectBatchHandler<\n  TRow extends object = Record<string, unknown>,\n  TKey extends string | number = string | number,\n> = (\n  events: Array<DeltaEvent<TRow, TKey>>,\n  ctx: EffectContext,\n) => void | Promise<void>\n\n/** Effect configuration */\nexport interface EffectConfig<\n  TRow extends object = Record<string, unknown>,\n  TKey extends string | number = string | number,\n> {\n  /** Optional ID for debugging/tracing */\n  id?: string\n\n  /** Query to watch for deltas */\n  query: EffectQueryInput<any>\n\n  /** Called once for each row entering the query result */\n  onEnter?: EffectEventHandler<TRow, TKey>\n\n  /** Called once for each row updating within the query result */\n  onUpdate?: EffectEventHandler<TRow, TKey>\n\n  /** Called once for each row exiting the query result */\n  onExit?: EffectEventHandler<TRow, TKey>\n\n  /** Called once per graph run with all delta events from that batch */\n  onBatch?: EffectBatchHandler<TRow, TKey>\n\n  /** Error handler for exceptions thrown by effect callbacks */\n  onError?: (error: Error, event: DeltaEvent<TRow, TKey>) => void\n\n  /**\n   * Called when a source collection enters an error or cleaned-up state.\n   * The effect is automatically disposed after this callback fires.\n   * If not provided, the error is logged to console.error.\n   */\n  onSourceError?: (error: Error) => void\n\n  /**\n   * Skip deltas during initial collection load.\n   * Defaults to false (process all deltas including initial sync).\n   * Set to true for effects that should only process new changes.\n   */\n  skipInitial?: boolean\n}\n\n/** Handle returned by createEffect */\nexport interface Effect {\n  /** Dispose the effect. Returns a promise that resolves when in-flight handlers complete. */\n  dispose: () => Promise<void>\n  /** Whether this effect has been disposed */\n  readonly disposed: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Internal Types\n// ---------------------------------------------------------------------------\n\n/** Accumulated changes for a single key within a graph run */\ninterface EffectChanges<T> {\n  deletes: number\n  inserts: number\n  /** Value from the latest insert (the newest/current value) */\n  insertValue?: T\n  /** Value from the first delete (the oldest/previous value before the batch) */\n  deleteValue?: T\n}\n\n// ---------------------------------------------------------------------------\n// Global Counter\n// ---------------------------------------------------------------------------\n\nlet effectCounter = 0\n\n// ---------------------------------------------------------------------------\n// createEffect\n// ---------------------------------------------------------------------------\n\n/**\n * Creates a reactive effect that fires handlers when rows enter, exit, or\n * update within a query result. Effects process deltas only — they do not\n * maintain or require the full materialised query result.\n *\n * @example\n * ```typescript\n * const effect = createEffect({\n *   query: (q) => q.from({ msg: messagesCollection })\n *     .where(({ msg }) => eq(msg.role, 'user')),\n *   onEnter: async (event) => {\n *     await generateResponse(event.value)\n *   },\n * })\n *\n * // Later: stop the effect\n * await effect.dispose()\n * ```\n */\nexport function createEffect<\n  TRow extends object = Record<string, unknown>,\n  TKey extends string | number = string | number,\n>(config: EffectConfig<TRow, TKey>): Effect {\n  const id = config.id ?? `live-query-effect-${++effectCounter}`\n\n  // AbortController for signalling disposal to handlers\n  const abortController = new AbortController()\n\n  const ctx: EffectContext = {\n    effectId: id,\n    signal: abortController.signal,\n  }\n\n  // Track in-flight async handler promises so dispose() can await them\n  const inFlightHandlers = new Set<Promise<void>>()\n  let disposed = false\n\n  // Callback invoked by the pipeline runner with each batch of delta events\n  const onBatchProcessed = (events: Array<DeltaEvent<TRow, TKey>>) => {\n    if (disposed) return\n    if (events.length === 0) return\n\n    // Batch handler\n    if (config.onBatch) {\n      try {\n        const result = config.onBatch(events, ctx)\n        if (result instanceof Promise) {\n          const tracked = result.catch((error) => {\n            reportError(error, events[0]!, config.onError)\n          })\n          trackPromise(tracked, inFlightHandlers)\n        }\n      } catch (error) {\n        // For batch handler errors, report with first event as context\n        reportError(error, events[0]!, config.onError)\n      }\n    }\n\n    for (const event of events) {\n      if (abortController.signal.aborted) break\n\n      const handler = getHandlerForEvent(event, config)\n      if (!handler) continue\n\n      try {\n        const result = handler(event, ctx)\n        if (result instanceof Promise) {\n          const tracked = result.catch((error) => {\n            reportError(error, event, config.onError)\n          })\n          trackPromise(tracked, inFlightHandlers)\n        }\n      } catch (error) {\n        reportError(error, event, config.onError)\n      }\n    }\n  }\n\n  // The dispose function is referenced by both the returned Effect object\n  // and the onSourceError callback, so we define it first.\n  const dispose = async () => {\n    if (disposed) return\n    disposed = true\n\n    // Abort signal for in-flight handlers\n    abortController.abort()\n\n    // Tear down the pipeline (unsubscribe from sources, etc.)\n    runner.dispose()\n\n    // Wait for any in-flight async handlers to settle\n    if (inFlightHandlers.size > 0) {\n      await Promise.allSettled([...inFlightHandlers])\n    }\n  }\n\n  // Create and start the pipeline\n  const runner = new EffectPipelineRunner<TRow, TKey>({\n    query: config.query,\n    skipInitial: config.skipInitial ?? false,\n    onBatchProcessed,\n    onSourceError: (error: Error) => {\n      if (disposed) return\n\n      if (config.onSourceError) {\n        try {\n          config.onSourceError(error)\n        } catch (callbackError) {\n          console.error(\n            `[Effect '${id}'] onSourceError callback threw:`,\n            callbackError,\n          )\n        }\n      } else {\n        console.error(`[Effect '${id}'] ${error.message}. Disposing effect.`)\n      }\n\n      // Auto-dispose — the effect can no longer function\n      dispose()\n    },\n  })\n  runner.start()\n\n  return {\n    dispose,\n    get disposed() {\n      return disposed\n    },\n  }\n}\n\n// ---------------------------------------------------------------------------\n// EffectPipelineRunner\n// ---------------------------------------------------------------------------\n\ninterface EffectPipelineRunnerConfig<\n  TRow extends object,\n  TKey extends string | number,\n> {\n  query: EffectQueryInput<any>\n  skipInitial: boolean\n  onBatchProcessed: (events: Array<DeltaEvent<TRow, TKey>>) => void\n  /** Called when a source collection enters error or cleaned-up state */\n  onSourceError: (error: Error) => void\n}\n\n/**\n * Internal class that manages a D2 pipeline for effect delta processing.\n *\n * Sets up the IVM graph, subscribes to source collections, runs the graph\n * when changes arrive, and classifies output multiplicities into DeltaEvents.\n *\n * Unlike CollectionConfigBuilder, this does NOT:\n * - Create or write to a collection (no materialisation)\n * - Manage ordering, windowing, or lazy loading\n */\nclass EffectPipelineRunner<TRow extends object, TKey extends string | number> {\n  private readonly query: QueryIR\n  private readonly collections: Record<string, Collection<any, any, any>>\n  private readonly collectionByAlias: Record<string, Collection<any, any, any>>\n\n  private graph: D2 | undefined\n  private inputs: Record<string, RootStreamBuilder<unknown>> | undefined\n  private pipeline: ResultStream | undefined\n  private sourceWhereClauses: Map<string, BasicExpression<boolean>> | undefined\n  private compiledAliasToCollectionId: Record<string, string> = {}\n\n  // Mutable objects passed to compileQuery by reference.\n  // The join compiler captures these references and reads them later when\n  // the graph runs, so they must be populated before the first graph run.\n  private readonly subscriptions: Record<string, CollectionSubscription> = {}\n  private readonly lazySourcesCallbacks: Record<string, any> = {}\n  private readonly lazySources = new Set<string>()\n  // OrderBy optimization info populated by the compiler when limit is present\n  private readonly optimizableOrderByCollections: Record<\n    string,\n    OrderByOptimizationInfo\n  > = {}\n\n  // Ordered subscription state for cursor-based loading\n  private readonly biggestSentValue = new Map<string, any>()\n  private readonly lastLoadRequestKey = new Map<string, string>()\n  private pendingOrderedLoadPromise: Promise<void> | undefined\n\n  // Subscription management\n  private readonly unsubscribeCallbacks = new Set<() => void>()\n  // Duplicate insert prevention per alias\n  private readonly sentToD2KeysByAlias = new Map<string, Set<string | number>>()\n\n  // Output accumulator\n  private pendingChanges: Map<unknown, EffectChanges<TRow>> = new Map()\n\n  // skipInitial state\n  private readonly skipInitial: boolean\n  private initialLoadComplete = false\n\n  // Scheduler integration\n  private subscribedToAllCollections = false\n  private readonly builderDependencies = new Set<unknown>()\n  private readonly aliasDependencies: Record<string, Array<unknown>> = {}\n\n  // Reentrance guard\n  private isGraphRunning = false\n  private disposed = false\n  // When dispose() is called mid-graph-run, defer heavy cleanup until the run completes\n  private deferredCleanup = false\n\n  private readonly onBatchProcessed: (\n    events: Array<DeltaEvent<TRow, TKey>>,\n  ) => void\n  private readonly onSourceError: (error: Error) => void\n\n  constructor(config: EffectPipelineRunnerConfig<TRow, TKey>) {\n    this.skipInitial = config.skipInitial\n    this.onBatchProcessed = config.onBatchProcessed\n    this.onSourceError = config.onSourceError\n\n    // Parse query\n    this.query = buildQueryFromConfig({ query: config.query })\n\n    // Extract source collections\n    this.collections = extractCollectionsFromQuery(this.query)\n    const aliasesById = extractCollectionAliases(this.query)\n\n    // Build alias → collection map\n    this.collectionByAlias = {}\n    for (const [collectionId, aliases] of aliasesById.entries()) {\n      const collection = this.collections[collectionId]\n      if (!collection) continue\n      for (const alias of aliases) {\n        this.collectionByAlias[alias] = collection\n      }\n    }\n\n    // Compile the pipeline\n    this.compilePipeline()\n  }\n\n  /** Compile the D2 graph and query pipeline */\n  private compilePipeline(): void {\n    this.graph = new D2()\n    this.inputs = Object.fromEntries(\n      Object.keys(this.collectionByAlias).map((alias) => [\n        alias,\n        this.graph!.newInput<any>(),\n      ]),\n    )\n\n    const compilation = compileQuery(\n      this.query,\n      this.inputs as Record<string, KeyedStream>,\n      this.collections,\n      // These mutable objects are captured by reference. The join compiler\n      // reads them later when the graph runs, so they must be populated\n      // (in start()) before the first graph run.\n      this.subscriptions,\n      this.lazySourcesCallbacks,\n      this.lazySources,\n      this.optimizableOrderByCollections,\n      () => {}, // setWindowFn (no-op — effects don't paginate)\n    )\n\n    this.pipeline = compilation.pipeline\n    this.sourceWhereClauses = compilation.sourceWhereClauses\n    this.compiledAliasToCollectionId = compilation.aliasToCollectionId\n\n    // Attach the output operator that accumulates changes\n    this.pipeline.pipe(\n      output((data) => {\n        const messages = data.getInner()\n        messages.reduce(accumulateEffectChanges<TRow>, this.pendingChanges)\n      }),\n    )\n\n    this.graph.finalize()\n  }\n\n  /** Subscribe to source collections and start processing */\n  start(): void {\n    // Use compiled aliases as the source of truth\n    const compiledAliases = Object.entries(this.compiledAliasToCollectionId)\n    if (compiledAliases.length === 0) {\n      // Nothing to subscribe to\n      return\n    }\n\n    // When not skipping initial, we always process events immediately\n    if (!this.skipInitial) {\n      this.initialLoadComplete = true\n    }\n\n    // We need to defer initial data processing until ALL subscriptions are\n    // created, because join pipelines look up subscriptions by alias during\n    // the graph run. If we run the graph while some aliases are still missing,\n    // the join tap operator will throw.\n    //\n    // Strategy: subscribe to each collection but buffer incoming changes.\n    // After all subscriptions are in place, flush the buffers and switch to\n    // direct processing mode.\n\n    const pendingBuffers = new Map<\n      string,\n      Array<Array<ChangeMessage<any, string | number>>>\n    >()\n\n    for (const [alias, collectionId] of compiledAliases) {\n      const collection =\n        this.collectionByAlias[alias] ?? this.collections[collectionId]!\n\n      // Initialise per-alias duplicate tracking\n      this.sentToD2KeysByAlias.set(alias, new Set())\n\n      // Discover dependencies: if source collection is itself a live query\n      // collection, its builder must run first during transaction flushes.\n      const dependencyBuilder = getCollectionBuilder(collection)\n      if (dependencyBuilder) {\n        this.aliasDependencies[alias] = [dependencyBuilder]\n        this.builderDependencies.add(dependencyBuilder)\n      } else {\n        this.aliasDependencies[alias] = []\n      }\n\n      // Get where clause for this alias (for predicate push-down)\n      const whereClause = this.sourceWhereClauses?.get(alias)\n      const whereExpression = whereClause\n        ? normalizeExpressionPaths(whereClause, alias)\n        : undefined\n\n      // Initialise buffer for this alias\n      const buffer: Array<Array<ChangeMessage<any, string | number>>> = []\n      pendingBuffers.set(alias, buffer)\n\n      // Lazy aliases (marked by the join compiler) should NOT load initial state\n      // eagerly — the join tap operator will load exactly the rows it needs on demand.\n      // For on-demand collections, eager loading would trigger a full server fetch\n      // for data that should be lazily loaded based on join keys.\n      const isLazy = this.lazySources.has(alias)\n\n      // Check if this alias has orderBy optimization (cursor-based loading)\n      const orderByInfo = this.getOrderByInfoForAlias(alias)\n\n      // Build the change callback — for ordered aliases, split updates into\n      // delete+insert and track the biggest sent value for cursor positioning.\n      const changeCallback = orderByInfo\n        ? (changes: Array<ChangeMessage<any, string | number>>) => {\n            if (pendingBuffers.has(alias)) {\n              pendingBuffers.get(alias)!.push(changes)\n            } else {\n              this.trackSentValues(alias, changes, orderByInfo.comparator)\n              const split = [...splitUpdates(changes)]\n              this.handleSourceChanges(alias, split)\n            }\n          }\n        : (changes: Array<ChangeMessage<any, string | number>>) => {\n            if (pendingBuffers.has(alias)) {\n              pendingBuffers.get(alias)!.push(changes)\n            } else {\n              this.handleSourceChanges(alias, changes)\n            }\n          }\n\n      // Determine subscription options based on ordered vs unordered path\n      const subscriptionOptions = this.buildSubscriptionOptions(\n        alias,\n        isLazy,\n        orderByInfo,\n        whereExpression,\n      )\n\n      // Subscribe to source changes\n      const subscription = collection.subscribeChanges(\n        changeCallback,\n        subscriptionOptions,\n      )\n\n      // Store subscription immediately so the join compiler can find it\n      this.subscriptions[alias] = subscription\n\n      // For ordered aliases with an index, trigger the initial limited snapshot.\n      // This loads only the top N rows rather than the entire collection.\n      if (orderByInfo) {\n        this.requestInitialOrderedSnapshot(alias, orderByInfo, subscription)\n      }\n\n      this.unsubscribeCallbacks.add(() => {\n        subscription.unsubscribe()\n        delete this.subscriptions[alias]\n      })\n\n      // Listen for status changes on source collections\n      const statusUnsubscribe = collection.on(`status:change`, (event) => {\n        if (this.disposed) return\n\n        const { status } = event\n\n        // Source entered error state — effect can no longer function\n        if (status === `error`) {\n          this.onSourceError(\n            new Error(\n              `Source collection '${collectionId}' entered error state`,\n            ),\n          )\n          return\n        }\n\n        // Source was manually cleaned up — effect can no longer function\n        if (status === `cleaned-up`) {\n          this.onSourceError(\n            new Error(\n              `Source collection '${collectionId}' was cleaned up while effect depends on it`,\n            ),\n          )\n          return\n        }\n\n        // Track source readiness for skipInitial\n        if (\n          this.skipInitial &&\n          !this.initialLoadComplete &&\n          this.checkAllCollectionsReady()\n        ) {\n          this.initialLoadComplete = true\n        }\n      })\n      this.unsubscribeCallbacks.add(statusUnsubscribe)\n    }\n\n    // Mark as subscribed so the graph can start running\n    this.subscribedToAllCollections = true\n\n    // All subscriptions are now in place. Flush buffered changes by sending\n    // data to D2 inputs first (without running the graph), then run the graph\n    // once. This prevents intermediate join states from producing duplicates.\n    //\n    // We remove each alias from pendingBuffers *before* draining, which\n    // switches that alias to direct-processing mode. Any new callbacks that\n    // fire during the drain (e.g. from requestLimitedSnapshot) will go\n    // through handleSourceChanges directly instead of being lost.\n    for (const [alias] of pendingBuffers) {\n      const buffer = pendingBuffers.get(alias)!\n      pendingBuffers.delete(alias)\n\n      const orderByInfo = this.getOrderByInfoForAlias(alias)\n\n      // Drain all buffered batches. Since we deleted the alias from\n      // pendingBuffers above, any new changes arriving during drain go\n      // through handleSourceChanges directly (not back into this buffer).\n      for (const changes of buffer) {\n        if (orderByInfo) {\n          this.trackSentValues(alias, changes, orderByInfo.comparator)\n          const split = [...splitUpdates(changes)]\n          this.sendChangesToD2(alias, split)\n        } else {\n          this.sendChangesToD2(alias, changes)\n        }\n      }\n    }\n\n    // Initial graph run to process any synchronously-available data.\n    // For skipInitial, this run's output is discarded (initialLoadComplete is still false).\n    this.runGraph()\n\n    // After the initial graph run, if all sources are ready,\n    // mark initial load as complete so future events are processed.\n    if (this.skipInitial && !this.initialLoadComplete) {\n      if (this.checkAllCollectionsReady()) {\n        this.initialLoadComplete = true\n      }\n    }\n  }\n\n  /** Handle incoming changes from a source collection */\n  private handleSourceChanges(\n    alias: string,\n    changes: Array<ChangeMessage<any, string | number>>,\n  ): void {\n    this.sendChangesToD2(alias, changes)\n    this.scheduleGraphRun(alias)\n  }\n\n  /**\n   * Schedule a graph run via the transaction-scoped scheduler.\n   *\n   * When called within a transaction, the run is deferred until the\n   * transaction flushes, coalescing multiple changes into a single graph\n   * execution. Without a transaction, the graph runs immediately.\n   *\n   * Dependencies are discovered from source collections that are themselves\n   * live query collections, ensuring parent queries run before effects.\n   */\n  private scheduleGraphRun(alias?: string): void {\n    const contextId = getActiveTransaction()?.id\n\n    // Collect dependencies for this schedule call\n    const deps = new Set(this.builderDependencies)\n    if (alias) {\n      const aliasDeps = this.aliasDependencies[alias]\n      if (aliasDeps) {\n        for (const dep of aliasDeps) {\n          deps.add(dep)\n        }\n      }\n    }\n\n    // Ensure dependent builders are scheduled in this context so that\n    // dependency edges always point to a real job.\n    if (contextId) {\n      for (const dep of deps) {\n        if (\n          typeof dep === `object` &&\n          dep !== null &&\n          `scheduleGraphRun` in dep &&\n          typeof (dep as any).scheduleGraphRun === `function`\n        ) {\n          ;(dep as any).scheduleGraphRun(undefined, { contextId })\n        }\n      }\n    }\n\n    transactionScopedScheduler.schedule({\n      contextId,\n      jobId: this,\n      dependencies: deps,\n      run: () => this.executeScheduledGraphRun(),\n    })\n  }\n\n  /**\n   * Called by the scheduler when dependencies are satisfied.\n   * Checks that the effect is still active before running.\n   */\n  private executeScheduledGraphRun(): void {\n    if (this.disposed || !this.subscribedToAllCollections) return\n    this.runGraph()\n  }\n\n  /**\n   * Send changes to the D2 input for the given alias.\n   * Returns the number of multiset entries sent.\n   */\n  private sendChangesToD2(\n    alias: string,\n    changes: Array<ChangeMessage<any, string | number>>,\n  ): number {\n    if (this.disposed || !this.inputs || !this.graph) return 0\n\n    const input = this.inputs[alias]\n    if (!input) return 0\n\n    const collection = this.collectionByAlias[alias]\n    if (!collection) return 0\n\n    // Filter duplicates per alias\n    const sentKeys = this.sentToD2KeysByAlias.get(alias)!\n    const filtered = filterDuplicateInserts(changes, sentKeys)\n\n    return sendChangesToInput(input, filtered, collection.config.getKey)\n  }\n\n  /**\n   * Run the D2 graph until quiescence, then emit accumulated events once.\n   *\n   * All output across the entire while-loop is accumulated into a single\n   * batch so that users see one `onBatchProcessed` invocation per scheduler\n   * run, even when ordered loading causes multiple graph steps.\n   */\n  private runGraph(): void {\n    if (this.isGraphRunning || this.disposed || !this.graph) return\n\n    this.isGraphRunning = true\n    try {\n      while (this.graph.pendingWork()) {\n        this.graph.run()\n        // A handler (via onBatchProcessed) or source error callback may have\n        // called dispose() during graph.run(). Stop early to avoid operating\n        // on stale state. TS narrows disposed to false from the guard above\n        // but it can change during graph.run() via callbacks.\n        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n        if (this.disposed) break\n        // After each step, check if ordered queries need more data.\n        // loadMoreIfNeeded may send data to D2 inputs (via requestLimitedSnapshot),\n        // causing pendingWork() to return true for the next iteration.\n        this.loadMoreIfNeeded()\n      }\n      // Emit all accumulated events once the graph reaches quiescence\n      this.flushPendingChanges()\n    } finally {\n      this.isGraphRunning = false\n      // If dispose() was called during this graph run, it deferred the heavy\n      // cleanup (clearing graph/inputs/pipeline) to avoid nulling references\n      // mid-loop. Complete that cleanup now.\n      if (this.deferredCleanup) {\n        this.deferredCleanup = false\n        this.finalCleanup()\n      }\n    }\n  }\n\n  /** Classify accumulated changes into DeltaEvents and invoke the callback */\n  private flushPendingChanges(): void {\n    if (this.pendingChanges.size === 0) return\n\n    // If skipInitial and initial load isn't complete yet, discard\n    if (this.skipInitial && !this.initialLoadComplete) {\n      this.pendingChanges = new Map()\n      return\n    }\n\n    const events: Array<DeltaEvent<TRow, TKey>> = []\n\n    for (const [key, changes] of this.pendingChanges) {\n      const event = classifyDelta<TRow, TKey>(key as TKey, changes)\n      if (event) {\n        events.push(event)\n      }\n    }\n\n    this.pendingChanges = new Map()\n\n    if (events.length > 0) {\n      this.onBatchProcessed(events)\n    }\n  }\n\n  /** Check if all source collections are in the ready state */\n  private checkAllCollectionsReady(): boolean {\n    return Object.values(this.collections).every((collection) =>\n      collection.isReady(),\n    )\n  }\n\n  /**\n   * Build subscription options for an alias based on whether it uses ordered\n   * loading, is lazy, or should pass orderBy/limit hints.\n   */\n  private buildSubscriptionOptions(\n    alias: string,\n    isLazy: boolean,\n    orderByInfo: OrderByOptimizationInfo | undefined,\n    whereExpression: BasicExpression<boolean> | undefined,\n  ): {\n    includeInitialState?: boolean\n    whereExpression?: BasicExpression<boolean>\n    orderBy?: any\n    limit?: number\n  } {\n    // Ordered aliases explicitly disable initial state — data is loaded\n    // via requestLimitedSnapshot/requestSnapshot after subscription setup.\n    if (orderByInfo) {\n      return { includeInitialState: false, whereExpression }\n    }\n\n    const includeInitialState = !isLazy\n\n    // For unordered subscriptions, pass orderBy/limit hints so on-demand\n    // collections can optimise server-side fetching.\n    const hints = computeSubscriptionOrderByHints(this.query, alias)\n\n    return {\n      includeInitialState,\n      whereExpression,\n      ...(hints.orderBy ? { orderBy: hints.orderBy } : {}),\n      ...(hints.limit !== undefined ? { limit: hints.limit } : {}),\n    }\n  }\n\n  /**\n   * Request the initial ordered snapshot for an alias.\n   * Uses requestLimitedSnapshot (index-based cursor) or requestSnapshot\n   * (full load with limit) depending on whether an index is available.\n   */\n  private requestInitialOrderedSnapshot(\n    alias: string,\n    orderByInfo: OrderByOptimizationInfo,\n    subscription: CollectionSubscription,\n  ): void {\n    const { orderBy, offset, limit, index } = orderByInfo\n    const normalizedOrderBy = normalizeOrderByPaths(orderBy, alias)\n\n    if (index) {\n      subscription.setOrderByIndex(index)\n      subscription.requestLimitedSnapshot({\n        limit: offset + limit,\n        orderBy: normalizedOrderBy,\n        trackLoadSubsetPromise: false,\n      })\n    } else {\n      subscription.requestSnapshot({\n        orderBy: normalizedOrderBy,\n        limit: offset + limit,\n        trackLoadSubsetPromise: false,\n      })\n    }\n  }\n\n  /**\n   * Get orderBy optimization info for a given alias.\n   * Returns undefined if no optimization exists for this alias.\n   */\n  private getOrderByInfoForAlias(\n    alias: string,\n  ): OrderByOptimizationInfo | undefined {\n    // optimizableOrderByCollections is keyed by collection ID\n    const collectionId = this.compiledAliasToCollectionId[alias]\n    if (!collectionId) return undefined\n\n    const info = this.optimizableOrderByCollections[collectionId]\n    if (info && info.alias === alias) {\n      return info\n    }\n    return undefined\n  }\n\n  /**\n   * After each graph run step, check if any ordered query's topK operator\n   * needs more data. If so, load more rows via requestLimitedSnapshot.\n   */\n  private loadMoreIfNeeded(): void {\n    for (const [, orderByInfo] of Object.entries(\n      this.optimizableOrderByCollections,\n    )) {\n      if (!orderByInfo.dataNeeded || !orderByInfo.index) continue\n\n      if (this.pendingOrderedLoadPromise) {\n        // Wait for in-flight loads to complete before requesting more\n        continue\n      }\n\n      const n = orderByInfo.dataNeeded()\n      if (n > 0) {\n        this.loadNextItems(orderByInfo, n)\n      }\n    }\n  }\n\n  /**\n   * Load n more items from the source collection, starting from the cursor\n   * position (the biggest value sent so far).\n   */\n  private loadNextItems(orderByInfo: OrderByOptimizationInfo, n: number): void {\n    const { alias } = orderByInfo\n    const subscription = this.subscriptions[alias]\n    if (!subscription) return\n\n    const cursor = computeOrderedLoadCursor(\n      orderByInfo,\n      this.biggestSentValue.get(alias),\n      this.lastLoadRequestKey.get(alias),\n      alias,\n      n,\n    )\n    if (!cursor) return // Duplicate request — skip\n\n    this.lastLoadRequestKey.set(alias, cursor.loadRequestKey)\n\n    subscription.requestLimitedSnapshot({\n      orderBy: cursor.normalizedOrderBy,\n      limit: n,\n      minValues: cursor.minValues,\n      trackLoadSubsetPromise: false,\n      onLoadSubsetResult: (loadResult: Promise<void> | true) => {\n        // Track in-flight load to prevent redundant concurrent requests\n        if (loadResult instanceof Promise) {\n          this.pendingOrderedLoadPromise = loadResult\n          loadResult.finally(() => {\n            if (this.pendingOrderedLoadPromise === loadResult) {\n              this.pendingOrderedLoadPromise = undefined\n            }\n          })\n        }\n      },\n    })\n  }\n\n  /**\n   * Track the biggest value sent for a given ordered alias.\n   * Used for cursor-based pagination in loadNextItems.\n   */\n  private trackSentValues(\n    alias: string,\n    changes: Array<ChangeMessage<any, string | number>>,\n    comparator: (a: any, b: any) => number,\n  ): void {\n    const sentKeys = this.sentToD2KeysByAlias.get(alias) ?? new Set()\n    const result = trackBiggestSentValue(\n      changes,\n      this.biggestSentValue.get(alias),\n      sentKeys,\n      comparator,\n    )\n    this.biggestSentValue.set(alias, result.biggest)\n    if (result.shouldResetLoadKey) {\n      this.lastLoadRequestKey.delete(alias)\n    }\n  }\n\n  /** Tear down subscriptions and clear state */\n  dispose(): void {\n    if (this.disposed) return\n    this.disposed = true\n    this.subscribedToAllCollections = false\n\n    // Immediately unsubscribe from sources and clear cheap state\n    this.unsubscribeCallbacks.forEach((fn) => fn())\n    this.unsubscribeCallbacks.clear()\n    this.sentToD2KeysByAlias.clear()\n    this.pendingChanges.clear()\n    this.lazySources.clear()\n    this.builderDependencies.clear()\n    this.biggestSentValue.clear()\n    this.lastLoadRequestKey.clear()\n    this.pendingOrderedLoadPromise = undefined\n\n    // Clear mutable objects\n    for (const key of Object.keys(this.lazySourcesCallbacks)) {\n      delete this.lazySourcesCallbacks[key]\n    }\n    for (const key of Object.keys(this.aliasDependencies)) {\n      delete this.aliasDependencies[key]\n    }\n    for (const key of Object.keys(this.optimizableOrderByCollections)) {\n      delete this.optimizableOrderByCollections[key]\n    }\n\n    // If the graph is currently running, defer clearing graph/inputs/pipeline\n    // until runGraph() completes — otherwise we'd null references mid-loop.\n    if (this.isGraphRunning) {\n      this.deferredCleanup = true\n    } else {\n      this.finalCleanup()\n    }\n  }\n\n  /** Clear graph references — called after graph run completes or immediately from dispose */\n  private finalCleanup(): void {\n    this.graph = undefined\n    this.inputs = undefined\n    this.pipeline = undefined\n    this.sourceWhereClauses = undefined\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getHandlerForEvent<TRow extends object, TKey extends string | number>(\n  event: DeltaEvent<TRow, TKey>,\n  config: EffectConfig<TRow, TKey>,\n): EffectEventHandler<TRow, TKey> | undefined {\n  switch (event.type) {\n    case `enter`:\n      return config.onEnter\n    case `exit`:\n      return config.onExit\n    case `update`:\n      return config.onUpdate\n  }\n}\n\n/**\n * Accumulate D2 output multiplicities into per-key effect changes.\n * Tracks both insert values (new) and delete values (old) separately\n * so that update and exit events can include previousValue.\n */\nfunction accumulateEffectChanges<T>(\n  acc: Map<unknown, EffectChanges<T>>,\n  [[key, tupleData], multiplicity]: [\n    [unknown, [any, string | undefined]],\n    number,\n  ],\n): Map<unknown, EffectChanges<T>> {\n  const [value] = tupleData as [T, string | undefined]\n\n  const changes: EffectChanges<T> = acc.get(key) || {\n    deletes: 0,\n    inserts: 0,\n  }\n\n  if (multiplicity < 0) {\n    changes.deletes += Math.abs(multiplicity)\n    // Keep only the first delete value — this is the pre-batch state\n    changes.deleteValue ??= value\n  } else if (multiplicity > 0) {\n    changes.inserts += multiplicity\n    // Always overwrite with the latest insert — this is the post-batch state\n    changes.insertValue = value\n  }\n\n  acc.set(key, changes)\n  return acc\n}\n\n/** Classify accumulated per-key changes into a DeltaEvent */\nfunction classifyDelta<TRow extends object, TKey extends string | number>(\n  key: TKey,\n  changes: EffectChanges<TRow>,\n): DeltaEvent<TRow, TKey> | undefined {\n  const { inserts, deletes, insertValue, deleteValue } = changes\n\n  if (inserts > 0 && deletes === 0) {\n    // Row entered the query result\n    return { type: `enter`, key, value: insertValue! }\n  }\n\n  if (deletes > 0 && inserts === 0) {\n    // Row exited the query result — value is the exiting value,\n    // previousValue is omitted (it would be identical to value)\n    return { type: `exit`, key, value: deleteValue! }\n  }\n\n  if (inserts > 0 && deletes > 0) {\n    // Row updated within the query result\n    return {\n      type: `update`,\n      key,\n      value: insertValue!,\n      previousValue: deleteValue!,\n    }\n  }\n\n  // inserts === 0 && deletes === 0 — no net change (should not happen)\n  return undefined\n}\n\n/** Track a promise in the in-flight set, automatically removing on settlement */\nfunction trackPromise(\n  promise: Promise<void>,\n  inFlightHandlers: Set<Promise<void>>,\n): void {\n  inFlightHandlers.add(promise)\n  promise.finally(() => {\n    inFlightHandlers.delete(promise)\n  })\n}\n\n/** Report an error to the onError callback or console */\nfunction reportError<TRow extends object, TKey extends string | number>(\n  error: unknown,\n  event: DeltaEvent<TRow, TKey>,\n  onError?: (error: Error, event: DeltaEvent<TRow, TKey>) => void,\n): void {\n  const normalised = error instanceof Error ? error : new Error(String(error))\n  if (onError) {\n    try {\n      onError(normalised, event)\n    } catch (onErrorError) {\n      // Don't let onError errors propagate\n      console.error(`[Effect] Error in onError handler:`, onErrorError)\n      console.error(`[Effect] Original error:`, normalised)\n    }\n  } else {\n    console.error(`[Effect] Unhandled error in handler:`, normalised)\n  }\n}\n"],"names":["buildQueryFromConfig","extractCollectionsFromQuery","extractCollectionAliases","D2","compileQuery","output","getCollectionBuilder","normalizeExpressionPaths","splitUpdates","getActiveTransaction","transactionScopedScheduler","filterDuplicateInserts","sendChangesToInput","computeSubscriptionOrderByHints","index","normalizeOrderByPaths","computeOrderedLoadCursor","trackBiggestSentValue"],"mappings":"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"}