import { EventEmitter } from "../event-emitter.cjs";
import { BasicExpression, OrderBy } from "../query/ir.cjs";
import { IndexReader } from "../indexes/base-index.cjs";
import { ChangeMessage, CollectionDependent, LoadSubsetOptions, LoadSubsetRequestResult, SubscribeChangesOptions, Subscription, SubscriptionEvents, SubscriptionLoadSubsetErrorEvent, SubscriptionStatus, SubscriptionUnsubscribedEvent } from "../types.cjs";
import { CollectionImpl } from "./index.cjs";
type RequestSnapshotOptions = {
    refetch?: boolean;
    where?: BasicExpression<boolean>;
    signal?: AbortSignal;
    optimizedOnly?: boolean;
    trackLoadSubsetPromise?: boolean;
    /** Optional orderBy to pass to loadSubset for backend optimization */
    orderBy?: OrderBy;
    /** Optional limit to pass to loadSubset for backend optimization */
    limit?: number;
    /** Read only the first `limit` local rows: the source holds every row and an index serves the order. */
    boundedPrefix?: boolean;
    /** Callback that receives the normalized loadSubset result for internal tracking */
    onLoadSubsetResult?: SubsetResultObserver;
    /** Called when the local snapshot must fall back from an index to a scan. */
    onUnoptimized?: () => void;
};
type RequestLimitedSnapshotOptions = {
    refetch?: boolean;
    orderBy: OrderBy;
    limit: number;
    /** A single cursor value; composite cursor inputs are rejected. */
    minValues?: Array<unknown>;
    /** Row offset for offset-based pagination (passed to sync layer) */
    offset?: number;
    /** Whether to track the loadSubset promise on this subscription (default: true) */
    trackLoadSubsetPromise?: boolean;
    /** Callback that receives the normalized loadSubset result for internal tracking */
    onLoadSubsetResult?: SubsetResultObserver;
};
export type ReleaseLoadSubset = (primaryFailure?: {
    error: unknown;
}) => void;
type SubsetResultObserver = (result: LoadSubsetRequestResult, options: LoadSubsetOptions, release: ReleaseLoadSubset) => void;
type CollectionSubscriptionOptions = {
    includeInitialState?: boolean;
    /** Pre-compiled expression for filtering changes */
    whereExpression?: BasicExpression<boolean>;
    /** Callback to call when the subscription is unsubscribed */
    onUnsubscribe?: (event: SubscriptionUnsubscribedEvent) => void;
    /** Callback for subset-load failures scoped to this subscription. */
    onLoadSubsetError?: (event: SubscriptionLoadSubsetErrorEvent) => void;
    truncateReplayPublication?: TruncateReplayPublicationControl;
    /**
     * Whether this subscription defers provider work: it starts no idle source
     * sync run and no acquisition attempt until its acquisition resumes. A
     * live-query Collection defers its source subscriptions until it has a
     * subscriber or a preload in its current sync run.
     */
    deferAcquisition?: boolean;
    /** Start sync, then return the ownership notification for a live resumption. */
    onResumeAcquisition?: () => () => void;
    /** The live query or Effect this subscription reads for. */
    owner?: CollectionDependent;
};
type TruncateReplayPublicationControl = NonNullable<SubscribeChangesOptions[`truncateReplayPublication`]>;
export declare class CollectionSubscription extends EventEmitter<SubscriptionEvents> implements Subscription {
    private collection;
    private callback;
    private options;
    private loadedInitialState;
    private skipFiltering;
    private snapshotSent;
    /**
     * Track all loadSubset calls made by this subscription so we can unload them on cleanup.
     * We store the exact LoadSubsetOptions we passed to loadSubset to ensure symmetric unload.
     */
    private subsetDemands;
    private readonly requestedSubsetWhere;
    private sentKeys;
    private publishedRows;
    private stalePublishedRows;
    private filteredCallback;
    private orderByIndex;
    private _status;
    private statusRevision;
    private _lastError;
    private pendingLoadSubsetParticipants;
    private truncateCleanup;
    private collectionCleanup;
    private collectionRestartCleanup;
    private truncateReplayState;
    private readonly loadSubsetPromiseErrors;
    private truncateReplacementPending;
    private unsubscribed;
    private defersAcquisition;
    /** @internal The live query or Effect this subscription reads for. */
    get owner(): CollectionSubscriptionOptions[`owner`];
    get status(): SubscriptionStatus;
    get lastError(): unknown | undefined;
    constructor(collection: CollectionImpl<any, any, any, any, any>, callback: (changes: Array<ChangeMessage<any, any>>) => void, options: CollectionSubscriptionOptions);
    /** Whether this subscription still defers provider work. */
    isDeferringAcquisition(): boolean;
    /**
     * Resume deferred acquisition: provider work may now start. This may start
     * an idle source Collection's sync run, then starts acquisition attempts for
     * the on-demand demand that stayed detached while acquisition was deferred.
     */
    resumeDeferredAcquisition(): void;
    /**
     * @internal A reload retired the sync run. Its demand detaches from the
     * retired run, and on-demand demand replays through the new run at once,
     * as after a truncate, so a consumer of an aborted load follows that
     * replay. The rows stay: the new run's writes apply on them.
     */
    retireSyncRun(): void;
    /** Whether a truncate acquires this subscription's demand again. */
    private replaysDemand;
    /**
     * Detach logical demand from work owned by a discarded sync run. A reload
     * keeps waiting callers for its replay, and the rows.
     */
    private handleCollectionCleanup;
    /** Acquire detached demand after startup or initial-error recovery. */
    private restartDetachedDemands;
    /**
     * Handle collection truncate event by resetting state and re-requesting subsets.
     * This is called when the sync layer receives a must-refetch and clears all data.
     *
     * To prevent a flash of missing content, we buffer all changes (deletes from truncate
     * and inserts from refetch) until all loadSubset calls succeed, then emit them together.
     * A failed replay keeps the last published snapshot private until a later
     * authoritative replay succeeds.
     */
    private handleTruncate;
    /** Make tentative replay ownership visible before adapter code can reenter. */
    private startTruncateReplayDemand;
    private settleTruncateReplay;
    /**
     * Keep every acquisition begun during recovery inside its publication
     * barrier until it settles or aborts. An abort ends the load: the replay
     * no longer waits for it, and its later settlement decides nothing.
     */
    private trackTruncateReplayParticipant;
    /** Stop obsolete logical demand from pinning a replay barrier. */
    private removeTruncateReplayParticipant;
    /** Publish only after every overlapping replay attempt has settled. */
    private checkTruncateReplayComplete;
    /**
     * Keep an incomplete replay private. The source no longer proves a complete
     * state, so only a later successful truncate replay may reopen publication.
     */
    private abandonTruncateReplay;
    /** Publish the buffered replacement as one batch, or release the delegate. */
    private flushTruncateReplay;
    /** Replace the public snapshot with a replay's private rows in one batch. */
    private publishReplayRows;
    private restorePublishedSnapshotTracking;
    /** Fold changes into the private replacement; false when they publish now. */
    private bufferPrivately;
    private createStateDiff;
    private get isBufferingForTruncate();
    private setReadyIfIdle;
    private isSyncRunGenerationCurrent;
    private retireStaleTruncateReplay;
    /**
     * A lazy demand that failed outside this subscription's replay, while a
     * failed replay of another of the graph's subscriptions to this collection
     * holds publication, fails this subscription's replacement too. It keeps
     * the last publication until every demand load that joins this replay
     * loads, a later replay succeeds, or this subscription's last demand
     * retires. The failed demand's retry joins it, so it cannot publish
     * without that demand's rows.
     */
    failReplacement(error: Error): void;
    /**
     * A graph's subscriptions to one collection share a barrier. While another
     * one's replay still loads, this replay stays open, so demand this one
     * starts meanwhile joins it; it finishes once those replays settle.
     */
    private awaitsSiblingReplays;
    /** Whether this subscription's replay still waits on its own work. */
    get hasTruncateReplayWork(): boolean;
    /** Drop the replay without publishing; an unfinished wait rejects as aborted. */
    private discardTruncateReplay;
    private resetSnapshotTracking;
    /** One replay state; only direct subscribers buffer a private replacement. */
    private createTruncateReplayState;
    /** Start one attempt's demands, then release the setup hold on publication. */
    private startTruncateReplayAttempt;
    get hasPendingTruncateReplacement(): boolean;
    /** Whether the source accepted sync rows that are not yet visible. */
    get hasHeldSyncTransaction(): boolean;
    get pendingTruncateReplacement(): Promise<void> | undefined;
    get hasFailedTruncateReplacement(): boolean;
    setOrderByIndex(index: IndexReader<any>): void;
    /**
     * Set subscription status and emit events if changed
     */
    private setStatus;
    /** Observe an asynchronous subset load and restore status on settlement. */
    private observeLoadSubsetResult;
    /** Give every logical observer of one transport rejection the same Error. */
    private normalizeLoadSubsetPromiseError;
    private stopDemandStatusParticipants;
    /** Create the record for a fresh, abortable acquisition attempt. */
    private createSubsetAcquisitionRecord;
    /**
     * Retire an acquisition before user code; failed cleanup is not retryable.
     * An `unloadSubset` that throws is an adapter bug: it is reported uncaught
     * and fails no load, replay, or caller.
     */
    private releaseAcquisition;
    /** Start and retain the first acquisition for one logical subset demand. */
    private startSubsetDemand;
    /** Re-check ownership after adapter and event callbacks that may reenter. */
    private isDemandActive;
    private recordLoadSubsetError;
    emitEvents(changes: Array<ChangeMessage<any, any>>): boolean;
    /** Keep direct snapshot reads private while an authoritative replay is open. */
    private publishSnapshot;
    /**
     * Sends the snapshot to the callback.
     * Returns a boolean indicating if it succeeded.
     * It can only fail if there is no index to fulfill the request
     * and the optimizedOnly option is set to true,
     * or, the entire state was already loaded or the request was cancelled.
     */
    requestSnapshot(opts?: RequestSnapshotOptions): boolean;
    /**
     * Release exact subset requests while keeping the subscription alive. The
     * releases are one demand change (law 9): an open replay is judged once,
     * after all of them.
     */
    releaseSnapshot(...wheres: Array<BasicExpression<boolean>>): void;
    /**
     * @internal The snapshot for `where` replaces those for `replaced` for
     * their owner, which releases them once it applies. Until then they keep
     * their older work in the replay.
     */
    replaceSnapshots(where: BasicExpression<boolean>, replaced: Array<BasicExpression<boolean>>): void;
    private requestedWhere;
    private releaseDemand;
    /** Retire held demands as one step, then judge an open replay once. */
    private releaseDemands;
    /**
     * A replay with no remaining logical demand cannot establish more rows. One
     * that has not failed publishes the rows it holds, so the subscriber does
     * not keep values the source has replaced or deleted. A failed replay keeps
     * the last public snapshot until an authoritative replay succeeds.
     */
    private retireEmptyReplay;
    /** Read source-derived ordered rows without starting demand. */
    readOrderedSnapshot(options: LoadSubsetOptions): Array<ChangeMessage<Record<string, unknown>, string | number>>;
    /**
     * Sends a snapshot that fulfills the `where` clause and all rows are bigger or equal to the cursor.
     * Requires a range index to be set with `setOrderByIndex` prior to calling this method.
     * It uses that range index to load the items in the order of the index.
     *
     * Cursor requests use one minValue for the leading order term. For a
     * multi-column order, a separate equality request loads the complete
     * leading-value tie before local ordering applies the trailing terms.
     *
     * Note 1: it may load more rows than the provided LIMIT because it loads all values equal to the first cursor value + limit values greater.
     *         This is needed to ensure that it does not accidentally skip duplicate values when the limit falls in the middle of some duplicated values.
     * Note 2: it does not send keys that have already been sent before.
     */
    requestLimitedSnapshot({ refetch, orderBy, limit, minValues, offset, trackLoadSubsetPromise: shouldTrackLoadSubsetPromise, onLoadSubsetResult, }: RequestLimitedSnapshotOptions): void;
    /**
     * Filters and flips changes for keys that have not been sent yet.
     * Deletes are filtered out for keys that have not been sent yet.
     * Updates are flipped into inserts for keys that have not been sent yet.
     * Duplicate inserts are filtered out to prevent D2 multiplicity > 1.
     */
    private filterAndFlipChanges;
    /**
     * After a failed replay, the source collection is empty but subscribers still
     * hold the last good publication. Reconcile the first later source delta for
     * each retained key against that publication instead of treating it as a
     * duplicate insert.
     */
    private reconcileStalePublishedChanges;
    private trackPublishedRows;
    private trackSentKeys;
    /**
     * Mark that the subscription should not filter any changes.
     * This is used when includeInitialState is explicitly set to false,
     * meaning the caller doesn't want initial state but does want ALL future changes.
     */
    markAllStateAsSeen(): void;
    unsubscribe(): void;
}
export {};
