import type { Filter, KeyValues, PaginationCursor, QueryOptions } from '../types/query-types.js';
import type { LevelWrapperBatchOperation } from './level-wrapper.js';
import { createLevelDatabase, LevelWrapper } from './level-wrapper.js';
export type IndexLevelConfig = {
    location: string;
    createLevelDatabase?: typeof createLevelDatabase;
    compoundIndexes?: CompoundIndexDefinition[];
};
export type IndexedItem = {
    messageCid: string;
    indexes: KeyValues;
};
/**
 * Defines a compound index that covers multiple filter properties and a sort property.
 *
 * When a query's equality filter properties match all `properties` AND its sort property
 * matches `sortProperty`, this compound index can serve the entire query from a single
 * LevelDB range scan — no in-memory filtering or sorting required.
 *
 * Key encoding: `<prop1>\x01<prop2>\x01...\x01<sortValue>\x00<messageCid>`
 */
export type CompoundIndexDefinition = {
    /** Unique name for this compound index (used as the sublevel partition name). */
    name: string;
    /** Filter properties that must all be present as equality filters in the query. */
    properties: string[];
    /** The sort property embedded at the end of the compound key. */
    sortProperty: string;
};
export interface IndexLevelOptions {
    signal?: AbortSignal;
}
/**
 * A LevelDB implementation for indexing the messages and events stored in the DWN.
 */
export declare class IndexLevel {
    db: LevelWrapper<string>;
    config: IndexLevelConfig;
    private readonly _compoundIndexes;
    constructor(config: IndexLevelConfig, db?: LevelWrapper<string>);
    open(): Promise<void>;
    close(): Promise<void>;
    /**
     * deletes everything in the underlying index db.
     */
    clear(): Promise<void>;
    /**
     * Put an item into the index using information that will allow it to be queried for.
     *
     * @param tenant
     * @param messageCid a unique ID that represents the item being indexed, this is also used as the cursor value in a query.
     * @param indexes - (key-value pairs) to be included as part of indexing this item. Must include at least one indexing property.
     * @param options IndexLevelOptions that include an AbortSignal.
     */
    put(tenant: string, messageCid: string, indexes: KeyValues, options?: IndexLevelOptions): Promise<void>;
    /**
     * Creates the batch operations for indexing an item without executing them.
     */
    createPutOperations(tenant: string, messageCid: string, indexes: KeyValues): Promise<LevelWrapperBatchOperation<string>[]>;
    /**
     *  Deletes all of the index data associated with the item.
     */
    delete(tenant: string, messageCid: string, options?: IndexLevelOptions): Promise<void>;
    /**
     * Creates the batch operations for removing an item's index data without executing them.
     */
    createDeleteOperations(tenant: string, messageCid: string): Promise<LevelWrapperBatchOperation<string>[]>;
    /**
     * Creates an IndexLevel `put` operation for indexing an item, creating a partition by `tenant` and by `indexName`
     */
    private createPutIndexedItemOperation;
    /**
     * Creates an IndexLevel `del` operation for deleting an item, creating a partition by `tenant` and by `indexName`
     */
    private createDeleteIndexedItemOperation;
    /**
     * Wraps the given operation as an operation for the specified index partition.
     */
    private createOperationForIndexPartition;
    /**
     * Wraps the given operation as an operation for the messageCid to indexes lookup partition.
     */
    private createOperationForIndexesLookupPartition;
    private static getIndexPartitionName;
    /**
     * Gets the index partition of the given indexName.
     */
    private getIndexPartition;
    /**
     * Gets the messageCid to indexes lookup partition.
     */
    private getIndexesLookupPartition;
    /**
     * Queries the index for items that match the filters. If no filters are provided, all items are returned.
     *
     * Query strategy selection (in priority order):
     * 1. **Compound index**: If a single filter matches a registered compound index (all equality properties + sort property),
     *    the entire query is served from a single LevelDB range scan. Supports cursors natively.
     * 2. **In-memory paging**: For "concise" filters without cursors. Scans the most selective property index, verifies in memory.
     * 3. **Iterator paging**: Default fallback. Scans the sort property index, testing each item against the filter.
     *
     * @param filters Array of filters that are treated as an OR query.
     * @param queryOptions query options for sort and pagination, requires at least `sortProperty`. The default sort direction is ascending.
     * @param options IndexLevelOptions that include an AbortSignal.
     * @returns {IndexedItem[]} an array of `IndexedItem` that match the given filters.
     */
    query(tenant: string, filters: Filter[], queryOptions: QueryOptions, options?: IndexLevelOptions): Promise<IndexedItem[]>;
    /**
     * Counts the number of items that match the given filters without loading full records.
     *
     * When a compound index covers the query, counting is a simple key iteration without value deserialization.
     * Otherwise, falls back to counting via the existing query strategies.
     */
    count(tenant: string, filters: Filter[], queryOptions: Omit<QueryOptions, 'limit' | 'cursor'>, options?: IndexLevelOptions): Promise<number>;
    /**
     * Queries the sort property index for items that match the filters. If no filters are provided, all items are returned.
     * This query is a linear iterator over the sorted index, checking each item for a match.
     * If a cursor is provided it starts the iteration from the cursor point.
     */
    queryWithIteratorPaging(tenant: string, filters: Filter[], queryOptions: QueryOptions, options?: IndexLevelOptions): Promise<IndexedItem[]>;
    /**
     * Creates an AsyncGenerator that returns each sorted index item given a specific sortProperty.
     * If a cursor is passed, the starting value (gt or lt) is derived from that.
     */
    private getIndexIterator;
    /**
     * Creates the starting point for a LevelDB query given an messageCid as a cursor and the indexed property.
     * Used as (gt) for ascending queries, or (lt) for descending queries.
     */
    private createStartingKeyFromCursor;
    /**
     * Returns a PaginationCursor using the last item of a given array of IndexedItems.
     * If the given array is empty, undefined is returned.
     *
     * @throws {DwnError} if the sort property or cursor value is invalid.
     */
    static createCursorFromLastArrayItem(items: IndexedItem[], sortProperty: string): PaginationCursor | undefined;
    /**
     * Creates a PaginationCursor from a given IndexedItem and sortProperty.
     *
     * @throws {DwnError} if the sort property or cursor value is invalid.
     */
    static createCursorFromItem(item: IndexedItem, sortProperty: string): PaginationCursor;
    /**
     * Queries the provided searchFilters asynchronously, returning results that match the matchFilters.
     *
     * @param filters the filters passed to the parent query.
     * @param searchFilters the modified filters used for the LevelDB query to search for a subset of items to match against.
     *
     * @throws {DwnErrorCode.IndexLevelInMemoryInvalidSortProperty} if an invalid sort property is provided.
     */
    queryWithInMemoryPaging(tenant: string, filters: Filter[], queryOptions: QueryOptions, options?: IndexLevelOptions): Promise<IndexedItem[]>;
    /**
     * Execute a filtered query against a single filter and return all results.
     *
     * Sub-queries (exact match, range, OneOf) are executed sequentially to avoid opening
     * multiple concurrent IndexedDB cursors. Firefox's IDB implementation intermittently
     * drops results when cursors overlap — see https://github.com/enboxorg/enbox/issues/264
     */
    private executeSingleFilterQuery;
    private getAllItems;
    /**
     * Returns items that match the exact property and value.
     */
    private filterExactMatches;
    /**
     * Returns items that match the range filter.
     *
     * For `lte` bounds, the encoded value is extended with a trailing `\xff` so that composite keys
     * of the form `<encodedValue>\x00<messageCid>` are naturally included in the range scan,
     * eliminating the need for a separate exact-match query.
     */
    private filterRangeMatches;
    /**
     * Sorts Items lexicographically in ascending or descending order given a specific indexName, using the messageCid as a tie breaker.
     * We know the indexes include the indexName and they are only of string or number type and not Arrays or booleans.
     * because they have already been checked within executeSingleFilterQuery.
     */
    private sortItems;
    /**
     * Find the starting position for pagination within the IndexedItem array using binary search.
     * Returns the index of the first item after the cursor, or -1 if no such item exists.
     *
     * Since the array is already sorted, binary search provides O(log n) performance instead of O(n).
     */
    private findCursorStartingIndex;
    /**
     * Gets the indexes given an messageCid. This is a reverse lookup to construct starting keys, as well as deleting indexed items.
     */
    private getIndexes;
    /**
     * Given a key from an indexed partitioned property key.
     *  ex:
     *    key: '"2023-05-25T11:22:33.000000Z"\u0000bayfreigu....'
     *    returns "2023-05-25T11:22:33.000000Z"
     */
    private extractIndexValueFromKey;
    /**
     * Joins the given values using the `\x00` (\u0000) character.
     */
    private static readonly delimiter;
    private static keySegmentJoin;
    /**
     *  Encodes a numerical value as a string for lexicographical comparison.
     *  If the number is positive it simply pads it with leading zeros.
     *  ex.: input:  1024 => "0000000000001024"
     *       input: -1024 => "!9007199254739967"
     *
     * @param value the number to encode.
     * @returns a string representation of the number.
     */
    static encodeNumberValue(value: number): string;
    /**
     * Encodes an indexed value to a string
     *
     * NOTE: we currently only use this for strings, numbers and booleans.
     */
    static encodeValue(value: string | number | boolean): string;
    private static shouldQueryWithInMemoryPaging;
    static isFilterConcise(filter: Filter, queryOptions: QueryOptions): boolean;
}
//# sourceMappingURL=index-level.d.ts.map