/**
 * @module DataStore  
 * This module contains the DataStore class, which is a general purpose, sync and async persistent database for JSON-serializable data - [see the documentation for more info](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#class-datastore)  
 */
import type { DataStoreEngine } from "./DataStoreEngine.js";
import type { LooseUnion, Prettify, SerializableVal } from "./types.js";
/** Function that takes the data in the old format and returns the data in the new format. Also supports an asynchronous migration. */
type MigrationFunc = (oldData: any) => any | Promise<any>;
/** Dictionary of format version numbers and the function that migrates to them from the previous whole integer */
export type DataMigrationsDict = Record<number, MigrationFunc>;
/** Options for the DataStore instance */
export type DataStoreOptions<TData extends DataStoreData> = Prettify<{
    /**
     * A unique internal ID for this data store.  
     * To avoid conflicts with other scripts, it is recommended to use a prefix that is unique to your script.  
     * If you want to change the ID, you should make use of the {@linkcode DataStore.migrateId()} method.  
     */
    id: string;
    /**
     * The default data object to use if no data is saved in persistent storage yet.  
     * Until the data is loaded from persistent storage with {@linkcode DataStore.loadData()}, this will be the data returned by {@linkcode DataStore.getData()}.  
     *  
     * - ⚠️ This has to be an object that can be serialized to JSON using `JSON.stringify()`, so no functions or circular references are allowed, they will cause unexpected behavior.  
     */
    defaultData: TData;
    /**
     * An incremental, whole integer version number of the current format of data.  
     * If the format of the data is changed in any way, this number should be incremented, in which case all necessary functions of the migrations dictionary will be run consecutively.  
     *  
     * - ⚠️ Never decrement this number and optimally don't skip any numbers either!  
     */
    formatVersion: number;
    /**
     * The engine middleware to use for persistent storage.  
     * Create an instance of {@linkcode FileStorageEngine} (Node.js), {@linkcode BrowserStorageEngine} (DOM) or your own engine class that extends {@linkcode DataStoreEngine} and pass it here.  
     *  
     * ⚠️ Don't reuse the same engine instance for multiple DataStores, unless it explicitly supports it!  
     */
    engine: (() => DataStoreEngine<TData>) | DataStoreEngine<TData>;
    /**
     * A dictionary of functions that can be used to migrate data from older versions to newer ones.  
     * The keys of the dictionary should be the format version that the functions can migrate to, from the previous whole integer value.  
     * The values should be functions that take the data in the old format and return the data in the new format.  
     * The functions will be run in order from the oldest to the newest version.  
     * If the current format version is not in the dictionary, no migrations will be run.  
     */
    migrations?: DataMigrationsDict;
    /**
     * If an ID or multiple IDs are passed here, the data will be migrated from the old ID(s) to the current ID.  
     * This will happen once per page load, when {@linkcode DataStore.loadData()} is called.  
     * All future calls to {@linkcode DataStore.loadData()} in the session will not check for the old ID(s) anymore.  
     * To migrate IDs manually, use the method {@linkcode DataStore.migrateId()} instead.  
     */
    migrateIds?: string | string[];
} & ({
    encodeData?: never;
    decodeData?: never;
    /**
     * The format to use for compressing the data. Defaults to `deflate-raw`. Explicitly set to `null` to store data uncompressed.  
     * ⚠️ Use either this, or `encodeData` and `decodeData`, but not all three at a time!  
     */
    compressionFormat?: CompressionFormat | null;
} | {
    /**
     * Tuple of a compression format identifier and a function to use to encode the data prior to saving it in persistent storage.  
     * If this is specified, `compressionFormat` can't be used. Also make sure to declare {@linkcode decodeData()} as well.  
     *  
     * You can make use of the [`compress()` function](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#function-compress) here to make the data use up less space at the cost of a little bit of performance.  
     * @param data The input data as a serialized object (JSON string)  
     */
    encodeData: [format: LooseUnion<CompressionFormat>, encode: (data: string) => string | Promise<string>];
    /**
     * Tuple of a compression format identifier and a function to use to decode the data after reading it from persistent storage.  
     * If this is specified, `compressionFormat` can't be used. Also make sure to declare {@linkcode encodeData()} as well.  
     *  
     * You can make use of the [`decompress()` function](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#function-decompress) here to make the data use up less space at the cost of a little bit of performance.  
     * @returns The resulting data as a valid serialized object (JSON string)  
     */
    decodeData: [format: LooseUnion<CompressionFormat>, decode: (data: string) => string | Promise<string>];
    compressionFormat?: never;
})>;
/** Generic type that represents the serializable data structure saved in a {@linkcode DataStore} instance. */
export type DataStoreData<TData extends SerializableVal = SerializableVal> = Record<string, SerializableVal | TData>;
/**
 * Manages a hybrid synchronous & asynchronous persistent JSON database that is cached in memory and persistently saved across sessions using one of the preset DataStoreEngines or your own one.  
 * Supports migrating data from older format versions to newer ones and populating the cache with default data if no persistent data is found.  
 * Can be overridden to implement any other storage method.  
 *  
 * All methods are `protected` or `public`, so you can easily extend this class and overwrite them to use a different storage method or to add other functionality.  
 * Remember that you can use `super.methodName()` in the subclass to call the original method if needed.  
 *  
 * - ⚠️ The data is stored as a JSON string, so only data compatible with [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) can be used. Circular structures and complex objects (containing functions, symbols, etc.) will either throw an error on load and save or cause otherwise unexpected behavior. Properties with a value of `undefined` will be removed from the data prior to saving it, so use `null` instead.  
 * - ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultData`  
 *  
 * @template TData The type of the data that is saved in persistent storage for the currently set format version (FIXME:will be automatically inferred from `defaultData` if not provided)  
 */
export declare class DataStore<TData extends DataStoreData> {
    readonly id: string;
    readonly formatVersion: number;
    readonly defaultData: TData;
    readonly encodeData: DataStoreOptions<TData>["encodeData"];
    readonly decodeData: DataStoreOptions<TData>["decodeData"];
    readonly compressionFormat = "deflate-raw";
    readonly engine: DataStoreEngine<TData>;
    protected firstInit: boolean;
    private cachedData;
    private migrations?;
    private migrateIds;
    /**
     * Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.  
     * Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found.  
     *  
     * - ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"`  
     * - ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultData`  
     *  
     * @template TData The type of the data that is saved in persistent storage for the currently set format version (will be automatically inferred from `defaultData` if not provided) - **This has to be a JSON-compatible object!** (no undefined, circular references, etc.)  
     * @param opts The options for this DataStore instance  
     */
    constructor(opts: DataStoreOptions<TData>);
    /**
     * Loads the data saved in persistent storage into the in-memory cache and also returns a copy of it.  
     * Automatically populates persistent storage with default data if it doesn't contain any data yet.  
     * Also runs all necessary migration functions if the data format has changed since the last time the data was saved.  
     */
    loadData(): Promise<TData>;
    /**
     * Returns a copy of the data from the in-memory cache.  
     * Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).  
     */
    getData(): TData;
    /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
    setData(data: TData): Promise<void>;
    /** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
    saveDefaultData(): Promise<void>;
    /**
     * Call this method to clear all persistently stored data associated with this DataStore instance.  
     * The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}  
     * Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.  
     *  
     * - ⚠️ This requires the additional directive `@grant GM.deleteValue` if the storageMethod is left as the default of `"GM"`  
     */
    deleteData(): Promise<void>;
    /** Returns whether encoding and decoding are enabled for this DataStore instance */
    encodingEnabled(): this is Required<Pick<DataStoreOptions<TData>, "encodeData" | "decodeData">>;
    /**
     * Runs all necessary migration functions consecutively and saves the result to the in-memory cache and persistent storage and also returns it.  
     * This method is automatically called by {@linkcode loadData()} if the data format has changed since the last time the data was saved.  
     * Though calling this method manually is not necessary, it can be useful if you want to run migrations for special occasions like a user importing potentially outdated data that has been previously exported.  
     *  
     * If one of the migrations fails, the data will be reset to the default value if `resetOnError` is set to `true` (default). Otherwise, an error will be thrown and no data will be saved.  
     */
    runMigrations(oldData: unknown, oldFmtVer: number, resetOnError?: boolean): Promise<TData>;
    /**
     * Tries to migrate the currently saved persistent data from one or more old IDs to the ID set in the constructor.  
     * If no data exist for the old ID(s), nothing will be done, but some time may still pass trying to fetch the non-existent data.  
     */
    migrateId(oldIds: string | string[]): Promise<void>;
}
export {};
