import type { FieldFilter, WALItem, AnyObject, ClientSyncHandles, SyncBatchParams, TableHandler, EqualityFilter, NormalizedRow } from "prostgles-types";
import { WAL } from "prostgles-types";
import type { DBHandlerClient } from "../prostgles";
export declare const debug: any;
type OmittedSyncProps = "onDebug" | "name" | "filter" | "db" | "onError";
export type SyncOptions = Partial<Omit<SyncedTableOptions, OmittedSyncProps>> & {
    select?: FieldFilter;
    handlesOnData?: boolean;
};
export type SyncOneOptions = Partial<Omit<SyncedTableOptions, OmittedSyncProps>> & {
    handlesOnData?: boolean;
};
type SyncDebugEvent = {
    type: "sync";
    tableName: string;
    command: keyof ClientSyncHandles | "notifySubscribers";
    data: AnyObject;
    info?: string;
};
type OnErrorHandler = (error: any) => void;
/**
 * Creates a local synchronized table
 */
type OnChange<T extends Record<string, unknown>> = (data: SyncDataItem<NormalizedRow<T>>[], delta?: Partial<T>[]) => any;
type SyncHandler<T> = {
    $unsync: () => void;
    $upsert: (newData: T[]) => void | Promise<void>;
    getItems: () => T[];
};
export type Sync<T extends AnyObject> = <TD extends T>(basicFilter: EqualityFilter<TD>, options: SyncOptions, onChange: OnChange<TD>, onError?: OnErrorHandler) => Promise<SyncHandler<TD>>;
type OnchangeOne<T extends Record<string, unknown>> = (data: SyncDataItem<NormalizedRow<T>>, delta?: Partial<NormalizedRow<T>>) => void | Promise<void>;
/**
 * Creates a local synchronized record
 */
export type SyncOne<T extends AnyObject = AnyObject> = <TD extends T>(basicFilter: Partial<TD>, options: SyncOneOptions, onChange: OnchangeOne<TD>, onError?: OnErrorHandler) => Promise<SingleSyncHandles<TD>>;
export type SyncBatchRequest = {
    from_synced?: string | number;
    to_synced?: string | number;
    offset: number;
    limit: number;
};
export type ItemUpdate = {
    idObj: AnyObject;
    delta: AnyObject;
    opts?: $UpdateOpts;
};
export type ItemUpdated = ItemUpdate & {
    oldItem: any;
    newItem: any;
    status: "inserted" | "updated" | "deleted" | "unchanged";
    from_server: boolean;
};
export type CloneSync<T extends AnyObject, Full extends boolean> = (onChange: SingleChangeListener<T, Full>, onError?: (error: any) => void) => SingleSyncHandles<T, Full>;
export type CloneMultiSync<T extends AnyObject> = (onChange: MultiChangeListener<T>, onError?: (error: any) => void) => MultiSyncHandles<T>;
export type $UpdateOpts = {
    deepMerge: boolean;
};
type DeepPartial<T> = T extends Array<any> ? T : T extends object ? {
    [P in keyof T]?: DeepPartial<T[P]>;
} : T;
/**
 * CRUD handles added if initialised with handlesOnData = true
 */
export type SingleSyncHandles<T extends AnyObject = AnyObject, Full extends boolean = false> = {
    $get: () => NormalizedRow<T> | undefined;
    $find: (idObj: Partial<NormalizedRow<T>>) => NormalizedRow<T> | undefined;
    $unsync: () => any;
    $delete: () => void;
    $update: <OPTS extends $UpdateOpts>(newData: OPTS extends {
        deepMerge: true;
    } ? DeepPartial<T> : Partial<T>, opts?: OPTS) => any;
    $cloneSync: CloneSync<T, Full>;
    $cloneMultiSync: CloneMultiSync<T>;
};
export type SyncDataItem<T extends AnyObject = AnyObject, Full extends boolean = false> = NormalizedRow<T> & (Full extends true ? SingleSyncHandles<NormalizedRow<T>, Full> : Partial<SingleSyncHandles<NormalizedRow<T>, Full>>);
export type MultiSyncHandles<T extends AnyObject> = {
    $unsync: () => void;
    $upsert: (newData: NormalizedRow<T>[]) => any;
    getItems: () => AnyObject[];
};
export type SubscriptionSingle<T extends AnyObject = AnyObject, Full extends boolean = false> = {
    _onChange: SingleChangeListener<T, Full>;
    notify: (data: T, delta?: DeepPartial<T>) => T;
    idObj: Partial<T>;
    handlesOnData?: boolean;
    handles?: SingleSyncHandles<T, Full>;
};
export type SubscriptionMulti<T extends AnyObject = AnyObject> = {
    _onChange: MultiChangeListener<T>;
    notify: (data: T[], delta: DeepPartial<T>[]) => T[];
    idObj?: Partial<T>;
    handlesOnData?: boolean;
    handles?: MultiSyncHandles<T>;
};
declare const STORAGE_TYPES: {
    readonly map: "map";
    readonly localStorage: "localStorage";
};
export type MultiChangeListener<T extends AnyObject = AnyObject> = (items: SyncDataItem<T>[], delta: DeepPartial<T>[]) => any;
export type SingleChangeListener<T extends AnyObject = AnyObject, Full extends boolean = false> = (item: SyncDataItem<T, Full>, delta?: DeepPartial<T>) => any;
type StorageType = keyof typeof STORAGE_TYPES;
export type SyncedTableOptions = {
    /**
     * Table name
     */
    name: string;
    /**
     * Basic filter
     */
    filter?: EqualityFilter<AnyObject>;
    /**
     * Data change listener.
     * Called on first sync and every time the data changes
     */
    onChange?: MultiChangeListener;
    onError?: OnErrorHandler;
    db: DBHandlerClient | Partial<DBHandlerClient>;
    /**
     * If true then the first onChange trigger is skipped
     */
    skipFirstTrigger?: boolean;
    select?: "*" | AnyObject;
    /**
     * Default is "object".
     * "localStorage" will persist the data
     */
    storageType?: StorageType;
    /**
     * If true then only the delta of the text field is sent to server.
     * Full text is sent if an error occurs
     */
    patchText?: boolean;
    patchJSON?: boolean;
    onReady: () => void;
    onDebug?: (event: SyncDebugEvent, tbl: SyncedTable) => Promise<void>;
};
export type DbTableSync = {
    unsync: () => void;
    syncData: (data?: AnyObject[], deleted?: AnyObject[], cb?: (err?: any) => void) => void;
};
export declare class SyncedTable {
    db: DBHandlerClient | Partial<DBHandlerClient>;
    name: string;
    select?: "*" | AnyObject;
    filter?: AnyObject;
    onChange?: MultiChangeListener;
    id_fields: string[];
    synced_field: string;
    throttle: number;
    batch_size: number;
    skipFirstTrigger: boolean;
    columns: {
        name: string;
        data_type: string;
    }[];
    wal?: WAL;
    notifyWal?: WAL;
    _multiSubscriptions: SubscriptionMulti[];
    _singleSubscriptions: SubscriptionSingle[];
    /**
     * add debug mode to fix sudden no data and sync listeners bug
     */
    set multiSubscriptions(mSubs: SubscriptionMulti[]);
    get multiSubscriptions(): SubscriptionMulti[];
    set singleSubscriptions(sSubs: SubscriptionSingle[]);
    get singleSubscriptions(): SubscriptionSingle[];
    dbSync?: DbTableSync;
    storageType?: StorageType;
    itemsMap: Map<string, AnyObject>;
    patchText: boolean;
    patchJSON: boolean;
    isSynced: boolean;
    onError: SyncedTableOptions["onError"];
    onDebug?: (evt: Omit<SyncDebugEvent, "type" | "tableName" | "channelName">) => Promise<void>;
    constructor({ name, filter, onChange, onReady, onDebug, db, skipFirstTrigger, select, storageType, patchText, patchJSON, onError, }: SyncedTableOptions);
    /**
     * Will update text/json fields through patching method
     * This will send less data to server
     * @param walData
     */
    updatePatches: (walData: WALItem[]) => Promise<any[]>;
    static create(opts: Omit<SyncedTableOptions, "onReady">): Promise<SyncedTable>;
    /**
     * Returns a sync handler to all records within the SyncedTable instance
     * @param onChange change listener <(items: object[], delta: object[]) => any >
     * @param handlesOnData If true then $upsert and $unsync handles will be added on each data item. True by default;
     */
    sync<T extends AnyObject = AnyObject>(onChange: MultiChangeListener<T>, handlesOnData?: boolean): MultiSyncHandles<T>;
    makeSingleSyncHandles<T extends AnyObject = AnyObject, Full extends boolean = false>(idObj: Partial<T>, onChange: SingleChangeListener<T, Full> | MultiChangeListener<T>): SingleSyncHandles<T, Full>;
    /**
     * Returns a sync handler to a specific record within the SyncedTable instance
     * @param idObj object containing the target id_fields properties
     * @param onChange change listener <(item: object, delta: object) => any >
     * @param handlesOnData If true then $update, $delete and $unsync handles will be added on the data item. True by default;
     */
    syncOne<T extends AnyObject = AnyObject, Full extends boolean = false>(idObj: Partial<T>, onChange: SingleChangeListener<T, Full>, handlesOnData?: boolean): SingleSyncHandles<T, Full>;
    /**
     * Notifies multi subs with ALL data + deltas. Attaches handles on data if required
     * @param newData -> updates. Must include id_fields + updates
     */
    _notifySubscribers: (changes?: Pick<ItemUpdated, "idObj" | "newItem" | "delta">[]) => void;
    unsubscribe: (onChange: SingleChangeListener | MultiChangeListener) => string;
    getIdStr(d: AnyObject): string;
    getIdObj(d: AnyObject): AnyObject;
    getRowSyncObj(d: AnyObject): AnyObject;
    unsync: () => void;
    destroy: () => void;
    matchesFilter(item: AnyObject | undefined): boolean;
    matchesIdObj(a: AnyObject | undefined, b: AnyObject | undefined): boolean;
    /**
     * Returns properties that are present in {n} and are different to {o}
     * @param o current full data item
     * @param n new data item
     */
    getDelta(o: AnyObject, n: AnyObject): AnyObject;
    deleteAll(): void;
    get tableHandler(): Pick<TableHandler, "update" | "updateBatch" | "delete"> | undefined;
    delete: (item: AnyObject, from_server?: boolean) => Promise<boolean>;
    /**
     * Ensures that all object keys match valid column names
     */
    checkItemCols: (item: AnyObject) => void;
    /**
     * Upserts data locally -> notify subs -> sends to server if required
     * synced_field is populated if data is not from server
     * @param items <{ idObj: object, delta: object }[]> Data items that changed
     * @param from_server : <boolean> If false then updates will be sent to server
     */
    upsert: (items: ItemUpdate[], from_server?: boolean) => Promise<any>;
    getItem<T = AnyObject>(idObj: Partial<T>): T | undefined;
    /**
     *
     * @param item data to be inserted/updated/deleted. Must include id_fields
     * @param index (optional) index within array
     * @param isFullData
     * @param deleteItem
     */
    setItem(_item: AnyObject, isFullData?: boolean, deleteItem?: boolean): void;
    /**
     * Sets the current data
     */
    setItems: (_items: AnyObject[]) => void;
    /**
     * Returns the current data ordered by synced_field ASC and matching the main filter;
     */
    getItems: <T extends AnyObject = AnyObject>() => T[];
    /**
     * Sync data request
     */
    getBatch: ({ from_synced, to_synced, offset, limit }?: SyncBatchParams) => {
        [x: string]: any;
    }[];
}
export declare const mergeDeep: (_target: Record<string, unknown> | undefined, _source: Record<string, unknown> | undefined) => {
    [x: string]: unknown;
};
export declare const quickClone: <T>(obj: T) => T;
export {};
//# sourceMappingURL=SyncedTable.d.ts.map