code:
export interface OpenDBCallbacks<DBTypes extends DBSchema | unknown> {
    /**
     * Called if this version of the database has never been opened before. Use it to specify the
     * schema for the database.
     *
     * @param database A database instance that you can use to add/remove stores and indexes.
     * @param oldVersion Last version of the database opened by the user.
     * @param newVersion Whatever new version you provided.
     * @param transaction The transaction for this upgrade.
     * This is useful if you need to get data from other stores as part of a migration.
     * @param event The event object for the associated 'upgradeneeded' event.
     */
    upgrade?(database: IDBPDatabase<DBTypes>, oldVersion: number, newVersion: number | null, transaction: IDBPTransaction<DBTypes, StoreNames<DBTypes>[], 'versionchange'>, event: IDBVersionChangeEvent): void;
    /**
     * Called if there are older versions of the database open on the origin, so this version cannot
     * open.
     *
     * @param currentVersion Version of the database that's blocking this one.
     * @param blockedVersion The version of the database being blocked (whatever version you provided to `openDB`).
     * @param event The event object for the associated `blocked` event.
     */
    blocked?(currentVersion: number, blockedVersion: number | null, event: IDBVersionChangeEvent): void;
    /**
     * Called if this connection is blocking a future version of the database from opening.
     *
     * @param currentVersion Version of the open database (whatever version you provided to `openDB`).
     * @param blockedVersion The version of the database that's being blocked.
     * @param event The event object for the associated `versionchange` event.
     */
    blocking?(currentVersion: number, blockedVersion: number | null, event: IDBVersionChangeEvent): void;
    /**
     * Called if the browser abnormally terminates the connection.
     * This is not called when `db.close()` is called.
     */
    terminated?(): void;
}
/**
 * Open a database.
 *
 * @param name Name of the database.
 * @param version Schema version.
 * @param callbacks Additional callbacks.
 */
export declare function openDB<DBTypes extends DBSchema | unknown = unknown>(name: string, version?: number, { blocked, upgrade, blocking, terminated }?: OpenDBCallbacks<DBTypes>): Promise<IDBPDatabase<DBTypes>>;
export interface DeleteDBCallbacks {
    /**
     * Called if there are connections to this database open, so it cannot be deleted.
     *
     * @param currentVersion Version of the database that's blocking the delete operation.
     * @param event The event object for the associated `blocked` event.
     */
    blocked?(currentVersion: number, event: IDBVersionChangeEvent): void;
}
/**
 * Delete a database.
 *
 * @param name Name of the database.
 */
export declare function deleteDB(name: string, { blocked }?: DeleteDBCallbacks): Promise<void>;
// export { unwrap, wrap } from './wrap-idb-value.js';
declare type KeyToKeyNoIndex<T> = {
    [K in keyof T]: string extends K ? never : number extends K ? never : K;
};
declare type ValuesOf<T> = T extends {
    [K in keyof T]: infer U;
} ? U : never;
declare type KnownKeys<T> = ValuesOf<KeyToKeyNoIndex<T>>;
declare type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
export interface DBSchema {
    [s: string]: DBSchemaValue;
}
interface IndexKeys {
    [s: string]: IDBValidKey;
}
interface DBSchemaValue {
    key: IDBValidKey;
    value: any;
    indexes?: IndexKeys;
}
/**
 * Extract known object store names from the DB schema type.
 *
 * @template DBTypes DB schema type, or unknown if the DB isn't typed.
 */
export declare type StoreNames<DBTypes extends DBSchema | unknown> = DBTypes extends DBSchema ? KnownKeys<DBTypes> : string;
/**
 * Extract database value types from the DB schema type.
 *
 * @template DBTypes DB schema type, or unknown if the DB isn't typed.
 * @template StoreName Names of the object stores to get the types of.
 */
export declare type StoreValue<DBTypes extends DBSchema | unknown, StoreName extends StoreNames<DBTypes>> = DBTypes extends DBSchema ? DBTypes[StoreName]['value'] : any;
/**
 * Extract database key types from the DB schema type.
 *
 * @template DBTypes DB schema type, or unknown if the DB isn't typed.
 * @template StoreName Names of the object stores to get the types of.
 */
export declare type StoreKey<DBTypes extends DBSchema | unknown, StoreName extends StoreNames<DBTypes>> = DBTypes extends DBSchema ? DBTypes[StoreName]['key'] : IDBValidKey;
/**
 * Extract the names of indexes in certain object stores from the DB schema type.
 *
 * @template DBTypes DB schema type, or unknown if the DB isn't typed.
 * @template StoreName Names of the object stores to get the types of.
 */
export declare type IndexNames<DBTypes extends DBSchema | unknown, StoreName extends StoreNames<DBTypes>> = DBTypes extends DBSchema ? keyof DBTypes[StoreName]['indexes'] : string;
/**
 * Extract the types of indexes in certain object stores from the DB schema type.
 *
 * @template DBTypes DB schema type, or unknown if the DB isn't typed.
 * @template StoreName Names of the object stores to get the types of.
 * @template IndexName Names of the indexes to get the types of.
 */
export declare type IndexKey<DBTypes extends DBSchema | unknown, StoreName extends StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName>> = DBTypes extends DBSchema ? IndexName extends keyof DBTypes[StoreName]['indexes'] ? DBTypes[StoreName]['indexes'][IndexName] : IDBValidKey : IDBValidKey;
declare type CursorSource<DBTypes extends DBSchema | unknown, TxStores extends ArrayLike<StoreNames<DBTypes>>, StoreName extends StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName> | unknown, Mode extends IDBTransactionMode = 'readonly'> = IndexName extends IndexNames<DBTypes, StoreName> ? IDBPIndex<DBTypes, TxStores, StoreName, IndexName, Mode> : IDBPObjectStore<DBTypes, TxStores, StoreName, Mode>;
declare type CursorKey<DBTypes extends DBSchema | unknown, StoreName extends StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName> | unknown> = IndexName extends IndexNames<DBTypes, StoreName> ? IndexKey<DBTypes, StoreName, IndexName> : StoreKey<DBTypes, StoreName>;
declare type IDBPDatabaseExtends = Omit<IDBDatabase, 'createObjectStore' | 'deleteObjectStore' | 'transaction' | 'objectStoreNames'>;
/**
 * A variation of DOMStringList with precise string types
 */
export interface TypedDOMStringList<T extends string> extends DOMStringList {
    contains(string: T): boolean;
    item(index: number): T | null;
    [index: number]: T;
    [Symbol.iterator](): IterableIterator<T>;
}
interface IDBTransactionOptions {
    /**
     * The durability of the transaction.
     *
     * The default is "default". Using "relaxed" provides better performance, but with fewer
     * guarantees. Web applications are encouraged to use "relaxed" for ephemeral data such as caches
     * or quickly changing records, and "strict" in cases where reducing the risk of data loss
     * outweighs the impact to performance and power.
     */
    durability?: 'default' | 'strict' | 'relaxed';
}
export interface IDBPDatabase<DBTypes extends DBSchema | unknown = unknown> extends IDBPDatabaseExtends {
    /**
     * The names of stores in the database.
     */
    readonly objectStoreNames: TypedDOMStringList<StoreNames<DBTypes>>;
    /**
     * Creates a new object store.
     *
     * Throws a "InvalidStateError" DOMException if not called within an upgrade transaction.
     */
    createObjectStore<Name extends StoreNames<DBTypes>>(name: Name, optionalParameters?: IDBObjectStoreParameters): IDBPObjectStore<DBTypes, ArrayLike<StoreNames<DBTypes>>, Name, 'versionchange'>;
    /**
     * Deletes the object store with the given name.
     *
     * Throws a "InvalidStateError" DOMException if not called within an upgrade transaction.
     */
    deleteObjectStore(name: StoreNames<DBTypes>): void;
    /**
     * Start a new transaction.
     *
     * @param storeNames The object store(s) this transaction needs.
     * @param mode
     * @param options
     */
    transaction<Name extends StoreNames<DBTypes>, Mode extends IDBTransactionMode = 'readonly'>(storeNames: Name, mode?: Mode, options?: IDBTransactionOptions): IDBPTransaction<DBTypes, [Name], Mode>;
    transaction<Names extends ArrayLike<StoreNames<DBTypes>>, Mode extends IDBTransactionMode = 'readonly'>(storeNames: Names, mode?: Mode, options?: IDBTransactionOptions): IDBPTransaction<DBTypes, Names, Mode>;
    /**
     * Add a value to a store.
     *
     * Rejects if an item of a given key already exists in the store.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param value
     * @param key
     */
    add<Name extends StoreNames<DBTypes>>(storeName: Name, value: StoreValue<DBTypes, Name>, key?: StoreKey<DBTypes, Name> | IDBKeyRange): Promise<StoreKey<DBTypes, Name>>;
    /**
     * Deletes all records in a store.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     */
    clear(name: StoreNames<DBTypes>): Promise<void>;
    /**
     * Retrieves the number of records matching the given query in a store.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param key
     */
    count<Name extends StoreNames<DBTypes>>(storeName: Name, key?: StoreKey<DBTypes, Name> | IDBKeyRange | null): Promise<number>;
    /**
     * Retrieves the number of records matching the given query in an index.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param indexName Name of the index within the store.
     * @param key
     */
    countFromIndex<Name extends StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, Name>>(storeName: Name, indexName: IndexName, key?: IndexKey<DBTypes, Name, IndexName> | IDBKeyRange | null): Promise<number>;
    /**
     * Deletes records in a store matching the given query.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param key
     */
    delete<Name extends StoreNames<DBTypes>>(storeName: Name, key: StoreKey<DBTypes, Name> | IDBKeyRange): Promise<void>;
    /**
     * Retrieves the value of the first record in a store matching the query.
     *
     * Resolves with undefined if no match is found.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param query
     */
    get<Name extends StoreNames<DBTypes>>(storeName: Name, query: StoreKey<DBTypes, Name> | IDBKeyRange): Promise<StoreValue<DBTypes, Name> | undefined>;
    /**
     * Retrieves the value of the first record in an index matching the query.
     *
     * Resolves with undefined if no match is found.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param indexName Name of the index within the store.
     * @param query
     */
    getFromIndex<Name extends StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, Name>>(storeName: Name, indexName: IndexName, query: IndexKey<DBTypes, Name, IndexName> | IDBKeyRange): Promise<StoreValue<DBTypes, Name> | undefined>;
    /**
     * Retrieves all values in a store that match the query.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param query
     * @param count Maximum number of values to return.
     */
    getAll<Name extends StoreNames<DBTypes>>(storeName: Name, query?: StoreKey<DBTypes, Name> | IDBKeyRange | null, count?: number): Promise<StoreValue<DBTypes, Name>[]>;
    /**
     * Retrieves all values in an index that match the query.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param indexName Name of the index within the store.
     * @param query
     * @param count Maximum number of values to return.
     */
    getAllFromIndex<Name extends StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, Name>>(storeName: Name, indexName: IndexName, query?: IndexKey<DBTypes, Name, IndexName> | IDBKeyRange | null, count?: number): Promise<StoreValue<DBTypes, Name>[]>;
    /**
     * Retrieves the keys of records in a store matching the query.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param query
     * @param count Maximum number of keys to return.
     */
    getAllKeys<Name extends StoreNames<DBTypes>>(storeName: Name, query?: StoreKey<DBTypes, Name> | IDBKeyRange | null, count?: number): Promise<StoreKey<DBTypes, Name>[]>;
    /**
     * Retrieves the keys of records in an index matching the query.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param indexName Name of the index within the store.
     * @param query
     * @param count Maximum number of keys to return.
     */
    getAllKeysFromIndex<Name extends StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, Name>>(storeName: Name, indexName: IndexName, query?: IndexKey<DBTypes, Name, IndexName> | IDBKeyRange | null, count?: number): Promise<StoreKey<DBTypes, Name>[]>;
    /**
     * Retrieves the key of the first record in a store that matches the query.
     *
     * Resolves with undefined if no match is found.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param query
     */
    getKey<Name extends StoreNames<DBTypes>>(storeName: Name, query: StoreKey<DBTypes, Name> | IDBKeyRange): Promise<StoreKey<DBTypes, Name> | undefined>;
    /**
     * Retrieves the key of the first record in an index that matches the query.
     *
     * Resolves with undefined if no match is found.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param indexName Name of the index within the store.
     * @param query
     */
    getKeyFromIndex<Name extends StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, Name>>(storeName: Name, indexName: IndexName, query: IndexKey<DBTypes, Name, IndexName> | IDBKeyRange): Promise<StoreKey<DBTypes, Name> | undefined>;
    /**
     * Put an item in the database.
     *
     * Replaces any item with the same key.
     *
     * This is a shortcut that creates a transaction for this single action. If you need to do more
     * than one action, create a transaction instead.
     *
     * @param storeName Name of the store.
     * @param value
     * @param key
     */
    put<Name extends StoreNames<DBTypes>>(storeName: Name, value: StoreValue<DBTypes, Name>, key?: StoreKey<DBTypes, Name> | IDBKeyRange): Promise<StoreKey<DBTypes, Name>>;
}
declare type IDBPTransactionExtends = Omit<IDBTransaction, 'db' | 'objectStore' | 'objectStoreNames'>;
export interface IDBPTransaction<DBTypes extends DBSchema | unknown = unknown, TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>, Mode extends IDBTransactionMode = 'readonly'> extends IDBPTransactionExtends {
    /**
     * The transaction's mode.
     */
    readonly mode: Mode;
    /**
     * The names of stores in scope for this transaction.
     */
    readonly objectStoreNames: TypedDOMStringList<TxStores[number]>;
    /**
     * The transaction's connection.
     */
    readonly db: IDBPDatabase<DBTypes>;
    /**
     * Promise for the completion of this transaction.
     */
    readonly done: Promise<void>;
    /**
     * The associated object store, if the transaction covers a single store, otherwise undefined.
     */
    readonly store: TxStores[1] extends undefined ? IDBPObjectStore<DBTypes, TxStores, TxStores[0], Mode> : undefined;
    /**
     * Returns an IDBObjectStore in the transaction's scope.
     */
    objectStore<StoreName extends TxStores[number]>(name: StoreName): IDBPObjectStore<DBTypes, TxStores, StoreName, Mode>;
}
declare type IDBPObjectStoreExtends = Omit<IDBObjectStore, 'transaction' | 'add' | 'clear' | 'count' | 'createIndex' | 'delete' | 'get' | 'getAll' | 'getAllKeys' | 'getKey' | 'index' | 'openCursor' | 'openKeyCursor' | 'put' | 'indexNames'>;
export interface IDBPObjectStore<DBTypes extends DBSchema | unknown = unknown, TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>, StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>, Mode extends IDBTransactionMode = 'readonly'> extends IDBPObjectStoreExtends {
    /**
     * The names of indexes in the store.
     */
    readonly indexNames: TypedDOMStringList<IndexNames<DBTypes, StoreName>>;
    /**
     * The associated transaction.
     */
    readonly transaction: IDBPTransaction<DBTypes, TxStores, Mode>;
    /**
     * Add a value to the store.
     *
     * Rejects if an item of a given key already exists in the store.
     */
    add: Mode extends 'readonly' ? undefined : (value: StoreValue<DBTypes, StoreName>, key?: StoreKey<DBTypes, StoreName> | IDBKeyRange) => Promise<StoreKey<DBTypes, StoreName>>;
    /**
     * Deletes all records in store.
     */
    clear: Mode extends 'readonly' ? undefined : () => Promise<void>;
    /**
     * Retrieves the number of records matching the given query.
     */
    count(key?: StoreKey<DBTypes, StoreName> | IDBKeyRange | null): Promise<number>;
    /**
     * Creates a new index in store.
     *
     * Throws an "InvalidStateError" DOMException if not called within an upgrade transaction.
     */
    createIndex: Mode extends 'versionchange' ? <IndexName extends IndexNames<DBTypes, StoreName>>(name: IndexName, keyPath: string | string[], options?: IDBIndexParameters) => IDBPIndex<DBTypes, TxStores, StoreName, IndexName, Mode> : undefined;
    /**
     * Deletes records in store matching the given query.
     */
    delete: Mode extends 'readonly' ? undefined : (key: StoreKey<DBTypes, StoreName> | IDBKeyRange) => Promise<void>;
    /**
     * Retrieves the value of the first record matching the query.
     *
     * Resolves with undefined if no match is found.
     */
    get(query: StoreKey<DBTypes, StoreName> | IDBKeyRange): Promise<StoreValue<DBTypes, StoreName> | undefined>;
    /**
     * Retrieves all values that match the query.
     *
     * @param query
     * @param count Maximum number of values to return.
     */
    getAll(query?: StoreKey<DBTypes, StoreName> | IDBKeyRange | null, count?: number): Promise<StoreValue<DBTypes, StoreName>[]>;
    /**
     * Retrieves the keys of records matching the query.
     *
     * @param query
     * @param count Maximum number of keys to return.
     */
    getAllKeys(query?: StoreKey<DBTypes, StoreName> | IDBKeyRange | null, count?: number): Promise<StoreKey<DBTypes, StoreName>[]>;
    /**
     * Retrieves the key of the first record that matches the query.
     *
     * Resolves with undefined if no match is found.
     */
    getKey(query: StoreKey<DBTypes, StoreName> | IDBKeyRange): Promise<StoreKey<DBTypes, StoreName> | undefined>;
    /**
     * Get a query of a given name.
     */
    index<IndexName extends IndexNames<DBTypes, StoreName>>(name: IndexName): IDBPIndex<DBTypes, TxStores, StoreName, IndexName, Mode>;
    /**
     * Opens a cursor over the records matching the query.
     *
     * Resolves with null if no matches are found.
     *
     * @param query If null, all records match.
     * @param direction
     */
    openCursor(query?: StoreKey<DBTypes, StoreName> | IDBKeyRange | null, direction?: IDBCursorDirection): Promise<IDBPCursorWithValue<DBTypes, TxStores, StoreName, unknown, Mode> | null>;
    /**
     * Opens a cursor over the keys matching the query.
     *
     * Resolves with null if no matches are found.
     *
     * @param query If null, all records match.
     * @param direction
     */
    openKeyCursor(query?: StoreKey<DBTypes, StoreName> | IDBKeyRange | null, direction?: IDBCursorDirection): Promise<IDBPCursor<DBTypes, TxStores, StoreName, unknown, Mode> | null>;
    /**
     * Put an item in the store.
     *
     * Replaces any item with the same key.
     */
    put: Mode extends 'readonly' ? undefined : (value: StoreValue<DBTypes, StoreName>, key?: StoreKey<DBTypes, StoreName> | IDBKeyRange) => Promise<StoreKey<DBTypes, StoreName>>;
    /**
     * Iterate over the store.
     */
    [Symbol.asyncIterator](): AsyncIterableIterator<IDBPCursorWithValueIteratorValue<DBTypes, TxStores, StoreName, unknown, Mode>>;
    /**
     * Iterate over the records matching the query.
     *
     * @param query If null, all records match.
     * @param direction
     */
    iterate(query?: StoreKey<DBTypes, StoreName> | IDBKeyRange | null, direction?: IDBCursorDirection): AsyncIterableIterator<IDBPCursorWithValueIteratorValue<DBTypes, TxStores, StoreName, unknown, Mode>>;
}
declare type IDBPIndexExtends = Omit<IDBIndex, 'objectStore' | 'count' | 'get' | 'getAll' | 'getAllKeys' | 'getKey' | 'openCursor' | 'openKeyCursor'>;
export interface IDBPIndex<DBTypes extends DBSchema | unknown = unknown, TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>, StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName> = IndexNames<DBTypes, StoreName>, Mode extends IDBTransactionMode = 'readonly'> extends IDBPIndexExtends {
    /**
     * The IDBObjectStore the index belongs to.
     */
    readonly objectStore: IDBPObjectStore<DBTypes, TxStores, StoreName, Mode>;
    /**
     * Retrieves the number of records matching the given query.
     */
    count(key?: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange | null): Promise<number>;
    /**
     * Retrieves the value of the first record matching the query.
     *
     * Resolves with undefined if no match is found.
     */
    get(query: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange): Promise<StoreValue<DBTypes, StoreName> | undefined>;
    /**
     * Retrieves all values that match the query.
     *
     * @param query
     * @param count Maximum number of values to return.
     */
    getAll(query?: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange | null, count?: number): Promise<StoreValue<DBTypes, StoreName>[]>;
    /**
     * Retrieves the keys of records matching the query.
     *
     * @param query
     * @param count Maximum number of keys to return.
     */
    getAllKeys(query?: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange | null, count?: number): Promise<StoreKey<DBTypes, StoreName>[]>;
    /**
     * Retrieves the key of the first record that matches the query.
     *
     * Resolves with undefined if no match is found.
     */
    getKey(query: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange): Promise<StoreKey<DBTypes, StoreName> | undefined>;
    /**
     * Opens a cursor over the records matching the query.
     *
     * Resolves with null if no matches are found.
     *
     * @param query If null, all records match.
     * @param direction
     */
    openCursor(query?: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange | null, direction?: IDBCursorDirection): Promise<IDBPCursorWithValue<DBTypes, TxStores, StoreName, IndexName, Mode> | null>;
    /**
     * Opens a cursor over the keys matching the query.
     *
     * Resolves with null if no matches are found.
     *
     * @param query If null, all records match.
     * @param direction
     */
    openKeyCursor(query?: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange | null, direction?: IDBCursorDirection): Promise<IDBPCursor<DBTypes, TxStores, StoreName, IndexName, Mode> | null>;
    /**
     * Iterate over the index.
     */
    [Symbol.asyncIterator](): AsyncIterableIterator<IDBPCursorWithValueIteratorValue<DBTypes, TxStores, StoreName, IndexName, Mode>>;
    /**
     * Iterate over the records matching the query.
     *
     * Resolves with null if no matches are found.
     *
     * @param query If null, all records match.
     * @param direction
     */
    iterate(query?: IndexKey<DBTypes, StoreName, IndexName> | IDBKeyRange | null, direction?: IDBCursorDirection): AsyncIterableIterator<IDBPCursorWithValueIteratorValue<DBTypes, TxStores, StoreName, IndexName, Mode>>;
}
declare type IDBPCursorExtends = Omit<IDBCursor, 'key' | 'primaryKey' | 'source' | 'advance' | 'continue' | 'continuePrimaryKey' | 'delete' | 'update'>;
export interface IDBPCursor<DBTypes extends DBSchema | unknown = unknown, TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>, StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName> | unknown = unknown, Mode extends IDBTransactionMode = 'readonly'> extends IDBPCursorExtends {
    /**
     * The key of the current index or object store item.
     */
    readonly key: CursorKey<DBTypes, StoreName, IndexName>;
    /**
     * The key of the current object store item.
     */
    readonly primaryKey: StoreKey<DBTypes, StoreName>;
    /**
     * Returns the IDBObjectStore or IDBIndex the cursor was opened from.
     */
    readonly source: CursorSource<DBTypes, TxStores, StoreName, IndexName, Mode>;
    /**
     * Advances the cursor a given number of records.
     *
     * Resolves to null if no matching records remain.
     */
    advance<T>(this: T, count: number): Promise<T | null>;
    /**
     * Advance the cursor by one record (unless 'key' is provided).
     *
     * Resolves to null if no matching records remain.
     *
     * @param key Advance to the index or object store with a key equal to or greater than this value.
     */
    continue<T>(this: T, key?: CursorKey<DBTypes, StoreName, IndexName>): Promise<T | null>;
    /**
     * Advance the cursor by given keys.
     *
     * The operation is 'and' – both keys must be satisfied.
     *
     * Resolves to null if no matching records remain.
     *
     * @param key Advance to the index or object store with a key equal to or greater than this value.
     * @param primaryKey and where the object store has a key equal to or greater than this value.
     */
    continuePrimaryKey<T>(this: T, key: CursorKey<DBTypes, StoreName, IndexName>, primaryKey: StoreKey<DBTypes, StoreName>): Promise<T | null>;
    /**
     * Delete the current record.
     */
    delete: Mode extends 'readonly' ? undefined : () => Promise<void>;
    /**
     * Updated the current record.
     */
    update: Mode extends 'readonly' ? undefined : (value: StoreValue<DBTypes, StoreName>) => Promise<StoreKey<DBTypes, StoreName>>;
    /**
     * Iterate over the cursor.
     */
    [Symbol.asyncIterator](): AsyncIterableIterator<IDBPCursorIteratorValue<DBTypes, TxStores, StoreName, IndexName, Mode>>;
}
declare type IDBPCursorIteratorValueExtends<DBTypes extends DBSchema | unknown = unknown, TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>, StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName> | unknown = unknown, Mode extends IDBTransactionMode = 'readonly'> = Omit<IDBPCursor<DBTypes, TxStores, StoreName, IndexName, Mode>, 'advance' | 'continue' | 'continuePrimaryKey'>;
export interface IDBPCursorIteratorValue<DBTypes extends DBSchema | unknown = unknown, TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>, StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName> | unknown = unknown, Mode extends IDBTransactionMode = 'readonly'> extends IDBPCursorIteratorValueExtends<DBTypes, TxStores, StoreName, IndexName, Mode> {
    /**
     * Advances the cursor a given number of records.
     */
    advance<T>(this: T, count: number): void;
    /**
     * Advance the cursor by one record (unless 'key' is provided).
     *
     * @param key Advance to the index or object store with a key equal to or greater than this value.
     */
    continue<T>(this: T, key?: CursorKey<DBTypes, StoreName, IndexName>): void;
    /**
     * Advance the cursor by given keys.
     *
     * The operation is 'and' – both keys must be satisfied.
     *
     * @param key Advance to the index or object store with a key equal to or greater than this value.
     * @param primaryKey and where the object store has a key equal to or greater than this value.
     */
    continuePrimaryKey<T>(this: T, key: CursorKey<DBTypes, StoreName, IndexName>, primaryKey: StoreKey<DBTypes, StoreName>): void;
}
export interface IDBPCursorWithValue<DBTypes extends DBSchema | unknown = unknown, TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>, StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName> | unknown = unknown, Mode extends IDBTransactionMode = 'readonly'> extends IDBPCursor<DBTypes, TxStores, StoreName, IndexName, Mode> {
    /**
     * The value of the current item.
     */
    readonly value: StoreValue<DBTypes, StoreName>;
    /**
     * Iterate over the cursor.
     */
    [Symbol.asyncIterator](): AsyncIterableIterator<IDBPCursorWithValueIteratorValue<DBTypes, TxStores, StoreName, IndexName, Mode>>;
}
declare type IDBPCursorWithValueIteratorValueExtends<DBTypes extends DBSchema | unknown = unknown, TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>, StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName> | unknown = unknown, Mode extends IDBTransactionMode = 'readonly'> = Omit<IDBPCursorWithValue<DBTypes, TxStores, StoreName, IndexName, Mode>, 'advance' | 'continue' | 'continuePrimaryKey'>;
export interface IDBPCursorWithValueIteratorValue<DBTypes extends DBSchema | unknown = unknown, TxStores extends ArrayLike<StoreNames<DBTypes>> = ArrayLike<StoreNames<DBTypes>>, StoreName extends StoreNames<DBTypes> = StoreNames<DBTypes>, IndexName extends IndexNames<DBTypes, StoreName> | unknown = unknown, Mode extends IDBTransactionMode = 'readonly'> extends IDBPCursorWithValueIteratorValueExtends<DBTypes, TxStores, StoreName, IndexName, Mode> {
    /**
     * Advances the cursor a given number of records.
     */
    advance<T>(this: T, count: number): void;
    /**
     * Advance the cursor by one record (unless 'key' is provided).
     *
     * @param key Advance to the index or object store with a key equal to or greater than this value.
     */
    continue<T>(this: T, key?: CursorKey<DBTypes, StoreName, IndexName>): void;
    /**
     * Advance the cursor by given keys.
     *
     * The operation is 'and' – both keys must be satisfied.
     *
     * @param key Advance to the index or object store with a key equal to or greater than this value.
     * @param primaryKey and where the object store has a key equal to or greater than this value.
     */
    continuePrimaryKey<T>(this: T, key: CursorKey<DBTypes, StoreName, IndexName>, primaryKey: StoreKey<DBTypes, StoreName>): void;
}


code:
import * as allIDB from './entry.js'

// IDB v7.1.1 https://github.com/jakearchibald/idb

declare global {
    const idb: typeof allIDB;
}

repo_name: Ducati/fc

license: mit


code:
import { IDBPCursor, IDBPCursorWithValue, IDBPDatabase, IDBPIndex, IDBPObjectStore, IDBPTransaction } from './entry.js';
export declare const reverseTransformCache: WeakMap<object, any>;
export declare function replaceTraps(callback: (currentTraps: ProxyHandler<any>) => ProxyHandler<any>): void;
/**
 * Enhance an IDB object with helpers.
 *
 * @param value The thing to enhance.
 */
export declare function wrap(value: IDBDatabase): IDBPDatabase;
export declare function wrap(value: IDBIndex): IDBPIndex;
export declare function wrap(value: IDBObjectStore): IDBPObjectStore;
export declare function wrap(value: IDBTransaction): IDBPTransaction;
export declare function wrap(value: IDBOpenDBRequest): Promise<IDBPDatabase | undefined>;
export declare function wrap<T>(value: IDBRequest<T>): Promise<T>;
/**
 * Revert an enhanced IDB object to a plain old miserable IDB one.
 *
 * Will also revert a promise back to an IDBRequest.
 *
 * @param value The enhanced object to revert.
 */
interface Unwrap {
    (value: IDBPCursorWithValue<any, any, any, any, any>): IDBCursorWithValue;
    (value: IDBPCursor<any, any, any, any, any>): IDBCursor;
    (value: IDBPDatabase): IDBDatabase;
    (value: IDBPIndex<any, any, any, any, any>): IDBIndex;
    (value: IDBPObjectStore<any, any, any, any>): IDBObjectStore;
    (value: IDBPTransaction<any, any, any>): IDBTransaction;
    <T extends any>(value: Promise<IDBPDatabase<T>>): IDBOpenDBRequest;
    (value: Promise<IDBPDatabase>): IDBOpenDBRequest;
    <T>(value: Promise<T>): IDBRequest<T>;
}
export declare const unwrap: Unwrap;
export {};


code:
import {Tippy} from "./tippy";

declare global {
    const tippy: Tippy;
}


code:
import * as Popper from '@popperjs/core';

export type BasePlacement = Popper.BasePlacement;

export type Placement = Popper.Placement;

export type Content =
  | string
  | Element
  | DocumentFragment
  | ((ref: Element) => string | Element | DocumentFragment);

export type SingleTarget = Element;

export type MultipleTargets = string | Element[] | NodeList;

export type Targets = SingleTarget | MultipleTargets;

export interface ReferenceElement<TProps = Props> extends Element {
  _tippy?: Instance<TProps>;
}

export interface PopperElement<TProps = Props> extends HTMLDivElement {
  _tippy?: Instance<TProps>;
}

export interface LifecycleHooks<TProps = Props> {
  onAfterUpdate(
    instance: Instance<TProps>,
    partialProps: Partial<TProps>
  ): void;
  onBeforeUpdate(
    instance: Instance<TProps>,
    partialProps: Partial<TProps>
  ): void;
  onCreate(instance: Instance<TProps>): void;
  onDestroy(instance: Instance<TProps>): void;
  onHidden(instance: Instance<TProps>): void;
  onHide(instance: Instance<TProps>): void | false;
  onMount(instance: Instance<TProps>): void;
  onShow(instance: Instance<TProps>): void | false;
  onShown(instance: Instance<TProps>): void;
  onTrigger(instance: Instance<TProps>, event: Event): void;
  onUntrigger(instance: Instance<TProps>, event: Event): void;
  onClickOutside(instance: Instance<TProps>, event: Event): void;
}

export interface RenderProps {
  allowHTML: boolean;
  animation: string | boolean;
  arrow: boolean | string | SVGElement | DocumentFragment;
  content: Content;
  inertia: boolean;
  maxWidth: number | string;
  role: string;
  theme: string;
  zIndex: number;
}

export interface GetReferenceClientRect {
  (): ClientRect | DOMRect;
  contextElement?: Element;
}

export interface Props extends LifecycleHooks, RenderProps {
  animateFill: boolean;
  appendTo: 'parent' | Element | ((ref: Element) => Element);
  aria: {
    content?: 'auto' | 'describedby' | 'labelledby' | null;
    expanded?: 'auto' | boolean;
  };
  delay: number | [number | null, number | null];
  duration: number | [number | null, number | null];
  followCursor: boolean | 'horizontal' | 'vertical' | 'initial';
  getReferenceClientRect: null | GetReferenceClientRect;
  hideOnClick: boolean | 'toggle';
  ignoreAttributes: boolean;
  inlinePositioning: boolean;
  interactive: boolean;
  interactiveBorder: number;
  interactiveDebounce: number;
  moveTransition: string;
  offset:
    | [number, number]
    | (({
        placement,
        popper,
        reference,
      }: {
        placement: Placement;
        popper: Popper.Rect;
        reference: Popper.Rect;
      }) => [number, number]);
  placement: Placement;
  plugins: Plugin<unknown>[];
  popperOptions: Partial<Popper.Options>;
  render:
    | ((
        instance: Instance
      ) => {
        popper: PopperElement;
        onUpdate?: (prevProps: Props, nextProps: Props) => void;
      })
    | null;
  showOnCreate: boolean;
  sticky: boolean | 'reference' | 'popper';
  touch: boolean | 'hold' | ['hold', number];
  trigger: string;
  triggerTarget: Element | Element[] | null;
}

export interface DefaultProps extends Omit<Props, 'delay' | 'duration'> {
  delay: number | [number, number];
  duration: number | [number, number];
}

export interface Instance<TProps = Props> {
  clearDelayTimeouts(): void;
  destroy(): void;
  disable(): void;
  enable(): void;
  hide(): void;
  hideWithInteractivity(event: MouseEvent): void;
  id: number;
  plugins: Plugin<TProps>[];
  popper: PopperElement<TProps>;
  popperInstance: Popper.Instance | null;
  props: TProps;
  reference: ReferenceElement<TProps>;
  setContent(content: Content): void;
  setProps(partialProps: Partial<TProps>): void;
  show(): void;
  state: {
    isEnabled: boolean;
    isVisible: boolean;
    isDestroyed: boolean;
    isMounted: boolean;
    isShown: boolean;
  };
  unmount(): void;
}

export interface TippyStatics {
  readonly currentInput: {isTouch: boolean};
  readonly defaultProps: DefaultProps;
  setDefaultProps(partialProps: Partial<DefaultProps>): void;
}

export interface Tippy<TProps = Props> extends TippyStatics {
  (targets: SingleTarget, optionalProps?: Partial<TProps>): Instance<TProps>;
}

export interface Tippy<TProps = Props> extends TippyStatics {
  (targets: MultipleTargets, optionalProps?: Partial<TProps>): Instance<
    TProps
  >[];
}

declare const tippy: Tippy;

// =============================================================================
// Addon types
// =============================================================================
export interface DelegateInstance<TProps = Props> extends Instance<TProps> {
  destroy(shouldDestroyTargetInstances?: boolean): void;
}

export interface Delegate<TProps = Props> {
  (
    targets: SingleTarget,
    props: Partial<TProps> & {target: string}
  ): DelegateInstance<TProps>;
}

export interface Delegate<TProps = Props> {
  (
    targets: MultipleTargets,
    props: Partial<TProps> & {target: string}
  ): DelegateInstance<TProps>[];
}

export type CreateSingletonProps<TProps = Props> = TProps & {
  overrides: Array<keyof TProps>;
};

export type CreateSingletonInstance<TProps = CreateSingletonProps> = Instance<
  TProps
> & {
  setInstances(instances: Instance<any>[]): void;
  show(target?: ReferenceElement | Instance | number): void;
  showNext(): void;
  showPrevious(): void;
};

export type CreateSingleton<TProps = Props> = (
  tippyInstances: Instance<any>[],
  optionalProps?: Partial<CreateSingletonProps<TProps>>
) => CreateSingletonInstance<CreateSingletonProps<TProps>>;

declare const delegate: Delegate;
declare const createSingleton: CreateSingleton;

// =============================================================================
// Plugin types
// =============================================================================
export interface Plugin<TProps = Props> {
  name?: string;
  defaultValue?: any;
  fn(instance: Instance<TProps>): Partial<LifecycleHooks<TProps>>;
}

export interface AnimateFill extends Plugin {
  name: 'animateFill';
  defaultValue: false;
}

export interface FollowCursor extends Plugin {
  name: 'followCursor';
  defaultValue: false;
}

export interface InlinePositioning extends Plugin {
  name: 'inlinePositioning';
  defaultValue: false;
}

export interface Sticky extends Plugin {
  name: 'sticky';
  defaultValue: false;
}

declare const animateFill: AnimateFill;
declare const followCursor: FollowCursor;
declare const inlinePositioning: InlinePositioning;
declare const sticky: Sticky;

// =============================================================================
// Misc types
// =============================================================================
export interface HideAllOptions {
  duration?: number;
  exclude?: Instance | ReferenceElement;
}

export type HideAll = (options?: HideAllOptions) => void;

declare const hideAll: HideAll;
declare const roundArrow: string;

export default tippy;
export {
  hideAll,
  delegate,
  createSingleton,
  animateFill,
  followCursor,
  inlinePositioning,
  sticky,
  roundArrow,
};


code:
#!/bin/bash
repo=upstream
branch=pregmod-master
function displayHelp() {
cat <<HelpText
Package required: git

Usage: $0 [OPTION]...
Options:
 -r, --repo  X    : Repo to check. Default: $repo
 -b, --branch X   : Branch to check. Default: $branch
 -v, --verbose X  : Be verbose. Default: $verbose
 -h, --help       : Show this help text
HelpText
}
while [[ "$1" ]]; do
	case $1 in
	-r | --repo)
		repo=$2
		shift
		;;
	-b | --branch)
		branch=$2
		shift
		;;
	-v | --verbose)
		verbose=true
		;;
	-h | --help)
		displayHelp
		exit 0
		;;
	*)
		echo "Unknown argument $1."
		displayHelp
		exit -1
		;;
	esac
	shift
done

git fetch $repo -q
if [[ $(git rev-list HEAD...$repo/$branch) ]]; then # https://adamj.eu/tech/2020/01/18/a-git-check-for-missing-commits-from-a-remote/
	if [[ $(git rev-parse --abbrev-ref HEAD) = "$branch" ]]; then
		git reset --hard $repo/$branch -q
	elif [[ $(git rev-parse --abbrev-ref HEAD) != "$branch" ]]; then
		git merge $repo/$branch -q
	fi
	exit=1
fi
if [ $verbose ]; then
	echo "Updated: $(if [ $exit ]; then echo "Yes"; else echo "No"; fi)" # stackoverflow.com/a/73539272
fi


code:
#!/bin/bash
email=xyz1256@corp.com
password=12345678
local=fc/
remote=FC/
gen=0;
runstep=0

function displayHelp() {
cat <<HelpText
Packages required: git, megacmd

Usage: $0 [OPTION]...
Options:
 -s, --step  X    : Step # to manually execute e.g. 1 to run "Applying WebGL decensor patch."
 -e, --email X    : Set MEGA email to X. Default: $email
 -p, --password X : Set MEGA password to X. Default: $password
 -l, --local X    : Set local (File System) folder/directory to X. Default: $local
 -r, --remote X   : Set remote (MEGA) folder/directory to X. Default: $remote
 -h, --help       : Show this help text
HelpText
}

if [[ "$#" -eq 0 ]]; then echo -e "For more options see $0 -h.\n"; fi
while [[ "$1" ]]; do
	case $1 in
	-s | --step)
		runstep=$2
		shift
		;;
	-e | --email)
		email=$2
		shift
		;;
	-p | --password)
		password=$2
		shift
		;;
	-l | --local)
		local=$2
		shift
		;;
	-r | --remote)
		remote=$2
		shift
		;;
	-h | --help)
		displayHelp
		exit 0
		;;
	*)
		echo "Unknown argument $1."
		displayHelp
		exit 1
		;;
	esac
	shift
done

find $local > /dev/null
if [[ `echo $?` > 0 ]]; then git clone -q https://gitgud.io/pregmodfan/fc-pregmod.git --depth 1 $local && gen=1; fi
cd $local

find js/000-browserSupport.js > /dev/null
if [[ `echo $?` > 0 ]]; then
	echo "creating file to work with browsers that do not support globalThis."
	echo "const globalThis = eval.call(undefined, 'this');" > js/000-browserSupport.js
fi

if [[ $runstep == 0 && $(./devTools/updateTool.sh -r origin) && `echo $?` > 0 ]]; then
	git reset --hard HEAD -q && git pull -q && gen=1;
fi # stackoverflow.com/a/17192101

if [[ $gen > 0 || $runstep == 1 ]]; then
	echo "Applying WebGL decensor patch."
	sed -i '1,/p.applyPanty = true/ s/p.applyPanty = true/p.applyPanty = false/' src/art/webgl/art.js
	sed -i 's/p.underage = slave.age < 18 || slave.visualAge < 18/p.underage = false/' src/art/webgl/art.js
	sed -i 's#p.height = Math.max(slave.height, 140); // clamp underage#p.height = slave.height;#' src/art/webgl/art.js
	sed -i 's#morphs.push(["physicalAgeYoung", -(Math.max(slave.visualAge, 18)-20)/10]) // clamp underage#morphs.push(["physicalAgeYoung", -(slave.visualAge-20)/10])#' src/art/webgl/art.js
fi

if [[ $gen > 0 || $runstep == 2 ]]; then
	rm bin/*.html > /dev/null 2>&1 ; ./compile.sh -q -m -f preCompiled-FC-`git log -1 --format=%cd --date=format:%Y-%m-%d-%H-%M`-$(git rev-parse --short HEAD)
fi

mega-login $email $password > /dev/null 2>&1
if [[ $gen > 0 || $runstep == 3 ]]; then mega-put -c bin/*.html $remote; fi
if [[ $gen > 0 || $runstep == 4 ]]; then
		if [[ `mega-ls $remote|grep .html|grep -v revert-RA|wc -l` > 2 ]]; then
			mega-rm $remote`mega-ls $remote|grep .html|grep -v revert-RA|head -n 1`
		fi
fi


code:
# Build and run an FC Docker container
# $ docker build -t fc .
# $ docker run -d --rm -p 8080:80 fc
# Access with brownser at http://localhost:8080

# Compile
FROM ubuntu
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=America/New_York
RUN apt update
RUN apt install -y git
WORKDIR /opt
RUN git clone https://gitgud.io/pregmodfan/fc-pregmod.git
WORKDIR /opt/fc-pregmod
RUN chmod +x devTools/tweeGo/tweego_nix*
RUN bash ./compile.sh

# Install the 3d renders hosted on mega.nz
RUN apt install -y megatools unzip
WORKDIR /tmp
RUN megadl --path=renders.zip https://mega.nz/#!upoAlBaZ!EbZ5wCixxZxBhMN_ireJTXt0SIPOywO2JW9XzTIPhe0
RUN unzip renders.zip
RUN cp -r 'Free Cities v 0.10.4 renders'/resources /opt/fc-pregmod/bin/

# Serve with nginx
FROM nginx
COPY --from=0 /opt/fc-pregmod/bin /usr/share/nginx/html
WORKDIR /usr/share/nginx/html
RUN mv FC_pregmod.html index.html

path: docker/fc-docker/Dockerfile


code:
# Create docker and upload to gitgud.io
# docker build -t gitgud.io:5050/user/project:megacmd .
# docker login gitgud.io
# docker push gitgud.io:5050/user/project:megacmd
#
# Debug build process
# docker build --no-cache --progress=plain -t megacmd .

# Create debian as base image
FROM debian

# Update
RUN apt-get -y update
RUN apt-get -y upgrade

# Install zip
RUN apt-get -y install zip

# mega cmd
RUN apt-get -y install wget
RUN wget https://mega.nz/linux/repo/Debian_11/amd64/megacmd-Debian_11_amd64.deb
RUN apt-get -y install ./megacmd-Debian_11_amd64.deb

path: docker/megacmd/Dockerfile


code:
# fc-pregmod:build
# Compiling needs node and Go installed

# Start with the base golang image, cause installing go is way to complicated
FROM golang

RUN curl -sL https://deb.nodesource.com/setup_19.x | bash
RUN apt-get install -y nodejs

path: docker/stage-build/Dockerfile


code:
/**
 * @file defines the gulp tasks that we use to build FC
 */

/* globals process */
// cSpell: words embedsourcemaps, sourcemapsincludecontent, pmodversion, fnames, nothrow, pathcmp, autoprefix

import yargs from "yargs";
import {hideBin} from "yargs/helpers";
import terser from "gulp-terser";
import cleanCSS from "gulp-clean-css";
import autoprefixer from "autoprefixer";
import gulp from "gulp";
import concat from "gulp-concat";
import gulpIgnore from "gulp-ignore";
import log from "fancy-log-levels";
import noop from "gulp-noop";
import postcss from "gulp-postcss";
import shell from "gulp-shell";
import sort from "gulp-sort";
import sourcemaps from "gulp-sourcemaps";
import stripCssJSComments from "gulp-strip-comments";
import {execSync} from "child_process";
import which from "which";
import jetpack from "fs-jetpack";
import path from "path";
import os from "os";
import {fileURLToPath} from "url";

// load json without using "...assert {type: "json"}" which is still in to proposal stage
// https://github.com/tc39/proposal-json-modules
const cfg = jetpack.read(fileURLToPath(new URL('./build.config.json', import.meta.url)), "json");

// run `npx gulp` to display help
const args = yargs(hideBin(process.argv))
	.showHelpOnFail(true)
	.option('verbosity', {
		alias: 'v',
		type: 'number',
		description: 'Logging verbosity level, 1-6',
		default: 6,
	})
	.option('release', {
		alias: 'r',
		type: 'boolean',
		description: 'Build source maps if not supplied',
		default: false,
	})
	.options('ci', {
		type: 'boolean',
		description: 'Assumes gitlab CI environment',
		default: false,
	})
	.option('minify', {
		alias: 'm',
		type: 'boolean',
		description: 'Makes builds smaller at the cost of readability and compiling time',
		default: false,
	})
	.options('embedsourcemaps', {
		type: 'boolean',
		description: 'Embed source maps into final html file',
		default: true,
	})
	.options('sourcemapsincludecontent', {
		type: 'boolean',
		description: 'Add original js and css code to the source maps. Loses file names and paths!',
		default: false,
	})
	.options('debug', {
		type: 'boolean',
		description: 'Add files that match the *.debug.* pattern, otherwise ignore them',
		default: false,
	})
	.options('hash', {
		type: 'boolean',
		description: 'Adds the git commit hash to the output filename',
		default: false,
	})
	.options('epoch', {
		type: 'boolean',
		description: 'Adds the current epoch time to the output filename',
		default: false,
	})
	.options('pmodversion', {
		type: 'boolean',
		description: 'Adds the current pregmod version number to the output filename',
		default: false,
	})
	.option('filename', {
		type: 'string',
		description: 'The filename to save the compiled HTML file as',
		default: cfg.output,
	})
	.option('inject-live-reload', {
		type: 'boolean',
		description: 'Injects code used by the watcher to live reload FC',
		default: false,
	})
	// commands should exist as exported gulp tasks
	.command('html', "Build FC")
	.command('themes', "Build themes")
	.command('mods', "Build mods")
	.command('all', "Build FC, themes, and mods")
	.demandCommand()
	.parse();

/**
 * Options used to minify js code using terser
 * @type {import("terser").MinifyOptions}
 */
const terserMinifyConfig = {
	// https://www.npmjs.com/package/terser#minify-options
	mangle: true,
	// eslint-disable-next-line camelcase
	keep_classnames: true,
	// eslint-disable-next-line camelcase
	keep_fnames: true,
};
/**
 * Options used to minify css code using CleanCSS
 */
const cleanCssMinifyConfig = {
	// https://www.npmjs.com/package/clean-css#constructor-options
};
/** Filename for the temporary output. Tweego will write here and then it will be moved into the output dir */
const htmlOut = "tmp.html";

// set log verbosity basing on the command line argument
log(args.verbosity);

// -------------- Helper functions -----------------------

/**
 * Locates a tweego executable.
 * Looks in the host $PATH, otherwise uses one of the bundled executables in devTools/tweeGo
 * @returns {string} Returns the path to the tweego executable
 */
function tweeCompilerExecutable() {
	const systemTweego = which.sync("tweego", {nothrow: true});

	if (systemTweego) {
		log.info("Found system tweego at ", systemTweego);
		return systemTweego;
	}

	const archSuffix = os.arch() === "x64" ? "64" : "86";
	const platformSuffix = {
		"Darwin": "osx",
		"Linux": "nix",
		"Windows_NT": "win"
	}[os.type()];
	const extension = os.type() === "Windows_NT" ? ".exe" : "";
	const res = path.join(".", "devTools", "tweeGo", `tweego_${platformSuffix}${archSuffix}${extension}`);

	log.info("Using bundled tweego at ", res);
	return res;
}

/**
 * Composes tweego invocation command
 *
 * Combines paths to tweego and options defined in the build.config.json file to
 * return a full tweego launch command, which will combine all story elements, pick up modules,
 * and generate an HTML file in the intermediate directory
 * @returns {string} Full tweego command string
 */
function tweeCompileCommand() {
	const sources = [path.join(cfg.dirs.intermediate, "story")];

	sources.push(...cfg.sources.story.media);

	const modules = [path.join(cfg.dirs.intermediate, "module")];
	const moduleArgs = modules.map(fn => `--module=${fn}`);

	return `"${tweeCompilerExecutable()}" --head=${cfg.sources.head} -o ${path.join(cfg.dirs.intermediate, htmlOut)} ${moduleArgs.join(" ")} ${sources.join(" ")}`;
}

/**
 * gulp-sort uses String.localeCompare() by default, which may be case-insensitive,
 * while we require case-sensitive sorting for sources
 * @param {Vinyl} a
 * @param {Vinyl} b
 * @returns {number}
 */
function pathcmp(a, b) {
	return (a.path < b.path ? -1 : (a.path > b.path ? 1 : 0));
}

/**
 * Creates a pipeline that sorts and combines files
 * @param {string|string[]} srcGlob Glob(s) to combine
 * @param {string} destDir destination directory to save to
 * @param {string} destFileName the filename to save as
 * @returns {NodeJS.ReadWriteStream}
 */
function concatFiles(srcGlob, destDir, destFileName) {
	return gulp.src(srcGlob)
		.pipe(args.debug
			? noop()
			: gulpIgnore.exclude("*.debug.*")
		)
		.pipe(sort(pathcmp))
		.pipe(concat(destFileName))
		.pipe(gulp.dest(destDir));
}

/**
 * Creates a pipeline for processing JS scripts
 *
 * The pipeline collects sources, sorts them, concatenates and
 * saves in the destination dir. If no "release" command line switch was
 * supplied, sourcemaps will be written for the files
 * @param {string} srcGlob Glob to process
 * @param {string} destDir destination directory to save to
 * @param {string} destFileName the filename to save as
 * @returns {NodeJS.ReadWriteStream}
 */
function processScripts(srcGlob, destDir, destFileName) {
	const addSourcemaps = !args.release;
	const prefix = path.relative(destDir, srcGlob.substr(0, srcGlob.indexOf("*")));

	if (args.injectLiveReload === true && destFileName === "module-script.js") {
		const liveReloadScriptPath = "devTools/scripts/watcherLiveReload.js";
		if (jetpack.exists(liveReloadScriptPath) === "file") {
			log.info("Injecting live reload code");
			if (typeof srcGlob === "string") {
				srcGlob = [srcGlob];
			}
			srcGlob.push(liveReloadScriptPath);
		} else {
			log.error(`Live reload script is missing from "${liveReloadScriptPath}"!`);
		}
	}

	return gulp.src(srcGlob)
		.pipe(args.debug
			? noop()
			: gulpIgnore.exclude("*.debug.*")
		)
		.pipe(sort(pathcmp))
		.pipe(addSourcemaps ? sourcemaps.init() : noop())
		.pipe(concat(destFileName))
		.pipe(args.minify
			// @ts-expect-error This is correct
			? terser(terserMinifyConfig)
			: noop())
		.pipe(addSourcemaps
			? sourcemaps.write(args.embedsourcemaps ? undefined : ".", {
				includeContent: args.sourcemapsincludecontent,
				sourceRoot: prefix,
				sourceMappingURLPrefix: path.relative(cfg.dirs.output, destDir)
			})
			: noop())
		.pipe(gulp.dest(destDir));
}

/**
 * Creates a pipeline for processing CSS stylesheets
 *
 * The pipeline collects sources, sorts them, concatenates, pass through
 * an autoprefixer and saves in the destination dir. If no "release" command
 * line switch was supplied, sourcemaps will be written for the files
 * @param {string} srcGlob Glob to process
 * @param {string} destDir destination directory to save to
 * @param {string} destFileName the filename to save as
 * @returns {NodeJS.ReadWriteStream}
 */
function processStylesheets(srcGlob, destDir, destFileName) {
	const addSourcemaps = !args.release;
	const prefix = path.relative(destDir, srcGlob.substr(0, srcGlob.indexOf("*")));

	return gulp.src(srcGlob)
		.pipe(args.debug
			? noop()
			: gulpIgnore.exclude("*.debug.*")
		)
		.pipe(sort(pathcmp))
		.pipe(addSourcemaps ? sourcemaps.init() : noop())
		.pipe(concat(destFileName))
		.pipe(cfg.options.css.autoprefix
			? postcss([autoprefixer({overrideBrowserslist: ["last 2 versions"]})])
			: noop())
		// minify css using CleanCSS
		.pipe(args.minify
			? cleanCSS(cleanCssMinifyConfig)
			: noop())
		.pipe(addSourcemaps
			? sourcemaps.write(args.embedsourcemaps ? undefined : ".", {
				includeContent: args.sourcemapsincludecontent,
				sourceRoot: prefix,
				sourceMappingURLPrefix: path.relative(cfg.dirs.output, destDir)
			})
			: noop())
		.pipe(gulp.dest(destDir));
}

/**
 * Creates tasks for processing sources with provided function
 *
 * This function is a workaround. Gulp can handle multiple globs at time, but
 * for writing sourcemaps we have to process them one by one, because we detect
 * where to write source maps from each glob pattern in order to make them accessible
 * from the compiled HTML
 * @param {("module-js"|"module-css"|"story-js"|"story-css"|"story-twee"|"story-media")} name
 * @param {Function} processorFunc
 * @param {string[]} globs
 * @param {string} destDir destination directory to save to
 * @param {string} destFileName filename to save as
 * @param  {...any} args extra args to pass to processorFunc
 * @returns {import("gulp").TaskFunction}
 */
function processSrc(name, processorFunc, globs, destDir, destFileName, ...args) {
	/** @type {import("gulp").TaskFunction[]} */
	const tasks = [];

	if (!Array.isArray(globs) || globs.length === 1) {
		const src = Array.isArray(globs) ? globs[0] : globs;

		tasks.push(() => processorFunc(src, destDir, destFileName, args));
		tasks[tasks.length - 1].displayName = "process-" + name;
	} else { // many globs
		const ext = path.extname(destFileName);
		const bn = path.basename(destFileName, ext);

		for (let i = 0; i < globs.length; ++i) {
			tasks.push(() => processorFunc(globs[i], destDir, `${bn}-${i}${ext}`, args));
			tasks[tasks.length - 1].displayName = `process-${name}-${i}`;
		}
	}

	const res = gulp.parallel(...tasks);

	res.displayName = name;
	return res;
}

function gitExecutableExists() {
	return which.sync('git', {nothrow: true}) !== null;
}

/**
 * Returns true if the working directory is a Git repository
 * @returns {boolean}
 */
function isGitCheckout() {
	return jetpack.exists(".git") === "dir";
}

let gitHash = "UNKNOWN";

/**
 * Invokes git and writes the hash of the head commit to the file, specified in the 'gitVersionFile' config property
 * @param {Function} cb callback function
 */
function injectGitCommit(cb) {
	// check if we are in CI mode, if yes, just read out the hash from environment variables
	gitHash = args.ci
		? execSync("echo $CI_COMMIT_SHORT_SHA").toString().trim()
		: execSync("git rev-parse --short HEAD").toString().trim();

	if (gitHash === "$CI_COMMIT_SHORT_SHA" || (gitHash === "" && args.ci === true)) {
		// This should only fire if the CI flag is used out of GitLab CI
		// If it fires in GitLab CI then $CI_COMMIT_SHORT_SHA is no longer valid or something has gone terribly wrong.
		log.warn(`git hash === "${gitHash}"!`);
		log.warn("Are you in a GitLab CI environment?");
		log.warn("If not please remove the --ci flag from the commands arguments to remove this message.");
		log.warn("Sleeping for 10 seconds and then setting ci to false");
		// sleep for 10 second
		Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10000);
		args.ci = false;
		return injectGitCommit(cb);
	} else if (gitHash === "") {
		throw new Error("Failed to get git hash!");
	}

	log.info("current git hash:", gitHash);
	jetpack.write(cfg.gitVersionFile, `App.Version.commitHash = '${gitHash}';\n`, {atomic: true});
	cb();
}

/**
 * Ensures the file with the git commit hash does not exists
 * @param {Function} cb callback function
 */
function cleanupGit(cb) {
	jetpack.remove(cfg.gitVersionFile);
	cb();
}

/**
 * Copies a file/directory to a destination
 * @param {string} source
 * @param {string} destination
 * @param {boolean} removeExisting
 * @param {Function} callback
 */
function copy(source, destination, removeExisting, callback) {
	// if not debug mode filter out *.debug.* files
	let matching = "!*.debug.*";

	if (args.debug === true) {
		matching = "*";
	}

	if (removeExisting) {
		jetpack.remove(destination);
	}

	jetpack.copy(source, destination, {overwrite: true, matching: matching});
	callback();
}

// --------------- build tasks definitions -----------------
/*
The main build process, which produces the assembled HTML file, consists of several stages.
The game sources, which are .twee, .js, and .css files, belong to one of the two categories:
they are either part of the story in Tweego terms, or modules. These categories are called
"components" below. For each component files of the same type are processed in the same way
and results for each component/file type pair are concatenated file in the intermediate builds
directory and a source map for that file.
When all intermediate files are ready, tweego picks them up and assembles the HTML file.
*/

// Create task to execute tweego
gulp.task("compileStory", shell.task(tweeCompileCommand(), {
	env: {...process.env, ...cfg.options.twee.environment},
	verbose: args.verbosity >= 3
}));

/**
 * Creates tasks for preparing intermediate files for a component
 * @param {"story"|"module"} name "story" or "module"
 * @returns {import("gulp").TaskFunction}
 */
function prepareComponent(name) {
	const processors = {
		"css": {
			func: processStylesheets,
			output: "-styles.css"
		},
		"js": {
			func: processScripts,
			output: "-script.js"
		},
		"twee": {
			func: concatFiles,
			output: "-story.twee"
		},
		"media": {
			func: null
		}
	};
	const c = cfg.sources[name];
	const outDir = path.join(cfg.dirs.intermediate, name);
	const subTasks = [];

	/** @type {"css"|"js"|"twee"|"media"} */
	let srcType;

	for (srcType in c) {
		const proc = processors[srcType];

		if (proc.func) {
			// @ts-expect-error ${name}-${srcType} is valid
			subTasks.push(processSrc(`${name}-${srcType}`, proc.func, c[srcType], outDir, `${name}${proc.output}`, cfg.options[srcType]));
		}
	}

	const r = gulp.parallel(subTasks);

	r.displayName = "prepare-" + name;
	return r;
}

/**
 *  Creates a task for compiling a theme
 * @param {string} themeName theme directory name
 * @returns {string} task name
 */
function makeThemeCompilationTask(themeName) {
	// make sure it's a name, not a path
	if (themeName.replace(/\/+$/, "").replace(/\\+$/, "").includes(path.sep)) {
		// only keep the last path component
		themeName = themeName.split(path.sep).pop();
	}

	const taskName = `make-theme-${themeName}`;

	gulp.task(taskName, function() {
		return concatFiles(`${cfg.sources.themes}/${themeName}/**/*.css`, cfg.dirs.output, `${themeName}.css`);
	});
	return taskName;
}

/**
 *  Creates a task for compiling a mod
 * @param {string} modName mod directory name
 * @returns {string} task name
 */
function makeModCompilationTask(modName) {
	// make sure it's a name, not a path
	if (modName.replace(/\/+$/, "").replace(/\\+$/, "").includes(path.sep)) {
		// only keep the last path component
		modName = modName.split(path.sep).pop();
	}

	const taskName = `make-mod-${modName}`;

	gulp.task(taskName, function(cb) {
		return copy(`${cfg.sources.mods}/${modName}`, `${cfg.dirs.modOutput}/${modName}`, true, cb);
	});
	return taskName;
}

/**
 * Moves compiled HTML file from the intermediate location to the final output
 * @param {Function} cb callback function
 */
function moveHTML(cb) {
	if (jetpack.exists(path.join(cfg.dirs.intermediate, htmlOut)) === "file") {
		let finalPath = path.join(cfg.dirs.output, args.filename);
		let extraString = "";

		if (args.epoch) {
			extraString += "." + String(Math.floor(Date.now() / 1000));
		}

		if (args.pmodversion) {
			// open ./src/002-config/fc-version.js
			jetpack.read("./src/002-config/fc-version.js").split("\n").forEach(line => {
				if (line.trim().includes("pmod: ")) {
					// add everything between first and last " to extraString
					extraString += "." + line.split("\"")[1].split("\"")[0];
				}
			});
		}

		if (args.hash) {
			extraString += "." + gitHash;
		}

		finalPath = finalPath.replace("[extras]", extraString);

		jetpack.move(path.join(cfg.dirs.intermediate, htmlOut), path.resolve(finalPath), {overwrite: true});

		log.info("FC saved to \"" + finalPath + "\"");
		cb();
	}
}

/**
 *  Removes intermediate compilation files if any
 * @param {Function} cb callback function
 */
function removeIntermediateFiles(cb) {
	jetpack.remove(cfg.dirs.intermediate);
	cb();
}

// Creates task to assemble components in the intermediate dir where they are ready for tweego
gulp.task("prepare", gulp.parallel(prepareComponent("module"), prepareComponent("story")));

// Creates theme build task for each subdirectory in the 'themes' dir
const themeTasks = jetpack.find(cfg.sources.themes, {files: false, directories: true, recursive: false})
	.map(entry => makeThemeCompilationTask(entry));

// create cfg.sources.mods directory if it doesn't exist
jetpack.dir(cfg.sources.mods);

// Creates mod build task for each subdirectory in the 'mods' dir
const modTasks = jetpack.find(cfg.sources.mods, {files: false, directories: true, recursive: false})
	.map(entry => makeModCompilationTask(entry));

// If modTasks is empty, create a task that does nothing
if (modTasks.length === 0) {
	gulp.task("make-mod-empty-folder", function(cb) {
		return cb();
	});

	modTasks.push("make-mod-empty-folder");
}

// Create the main build and clean tasks, which include writing Git commit hash if we are working in a Git repo
let cleanOp = noop();

if (isGitCheckout() && gitExecutableExists()) {
	cleanOp = gulp.parallel(cleanupGit, removeIntermediateFiles);

	gulp.task("buildHTML", gulp.series(cleanOp, injectGitCommit, "prepare", "compileStory", cleanupGit));
} else if (args.ci) {
	// CI environment is already clean

	gulp.task("buildHTML", gulp.series(injectGitCommit, "prepare", "compileStory"));
} else {
	if (isGitCheckout()) {
		log.info("git executable not found.");
	}
	cleanOp = gulp.parallel(removeIntermediateFiles);
	gulp.task("buildHTML", gulp.series(cleanOp, "prepare", "compileStory"));
}

export const clean = cleanOp;

// Create user-invocable targets for building HTML and themes
export const html = gulp.series("buildHTML", moveHTML);
export const themes = gulp.parallel(themeTasks);
export const mods = gulp.parallel(modTasks);

// Convenient shortcut to build everything (HTML, themes, and mods)
export const all = gulp.parallel(html, themes, mods);

// legacy tasks

gulp.task("twineCSS", function() {
	return concatFiles([...cfg.sources.module.css, ...cfg.sources.story.css], "devNotes", "twine CSS.txt");
});

gulp.task("twineJS", function() {
	return gulp.src([...cfg.sources.module.js, ...cfg.sources.story.js, "!src/art/assistantArt.js"])
		.pipe(args.debug
			? noop()
			: gulpIgnore.exclude("*.debug.*")
		)
		.pipe(stripCssJSComments({trim: true}))
		.pipe(sort(pathcmp))
		.pipe(concat("twine JS.txt"))
		.pipe(gulp.dest("devNotes"));
});

export const twine = gulp.parallel("twineCSS", "twineJS");


code:
// IDB v7.1.1 https://github.com/jakearchibald/idb

// @ts-nocheck
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.idb = {}));
})(this, (function (exports) { 'use strict';

    const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c);

    let idbProxyableTypes;
    let cursorAdvanceMethods;
    // This is a function to prevent it throwing up in node environments.
    function getIdbProxyableTypes() {
        return (idbProxyableTypes ||
            (idbProxyableTypes = [
                IDBDatabase,
                IDBObjectStore,
                IDBIndex,
                IDBCursor,
                IDBTransaction,
            ]));
    }
    // This is a function to prevent it throwing up in node environments.
    function getCursorAdvanceMethods() {
        return (cursorAdvanceMethods ||
            (cursorAdvanceMethods = [
                IDBCursor.prototype.advance,
                IDBCursor.prototype.continue,
                IDBCursor.prototype.continuePrimaryKey,
            ]));
    }
    const cursorRequestMap = new WeakMap();
    const transactionDoneMap = new WeakMap();
    const transactionStoreNamesMap = new WeakMap();
    const transformCache = new WeakMap();
    const reverseTransformCache = new WeakMap();
    function promisifyRequest(request) {
        const promise = new Promise((resolve, reject) => {
            const unlisten = () => {
                request.removeEventListener('success', success);
                request.removeEventListener('error', error);
            };
            const success = () => {
                resolve(wrap(request.result));
                unlisten();
            };
            const error = () => {
                reject(request.error);
                unlisten();
            };
            request.addEventListener('success', success);
            request.addEventListener('error', error);
        });
        promise
            .then((value) => {
            // Since cursoring reuses the IDBRequest (*sigh*), we cache it for later retrieval
            // (see wrapFunction).
            if (value instanceof IDBCursor) {
                cursorRequestMap.set(value, request);
            }
            // Catching to avoid "Uncaught Promise exceptions"
        })
            .catch(() => { });
        // This mapping exists in reverseTransformCache but doesn't doesn't exist in transformCache. This
        // is because we create many promises from a single IDBRequest.
        reverseTransformCache.set(promise, request);
        return promise;
    }
    function cacheDonePromiseForTransaction(tx) {
        // Early bail if we've already created a done promise for this transaction.
        if (transactionDoneMap.has(tx))
            return;
        const done = new Promise((resolve, reject) => {
            const unlisten = () => {
                tx.removeEventListener('complete', complete);
                tx.removeEventListener('error', error);
                tx.removeEventListener('abort', error);
            };
            const complete = () => {
                resolve();
                unlisten();
            };
            const error = () => {
                reject(tx.error || new DOMException('AbortError', 'AbortError'));
                unlisten();
            };
            tx.addEventListener('complete', complete);
            tx.addEventListener('error', error);
            tx.addEventListener('abort', error);
        });
        // Cache it for later retrieval.
        transactionDoneMap.set(tx, done);
    }
    let idbProxyTraps = {
        get(target, prop, receiver) {
            if (target instanceof IDBTransaction) {
                // Special handling for transaction.done.
                if (prop === 'done')
                    return transactionDoneMap.get(target);
                // Polyfill for objectStoreNames because of Edge.
                if (prop === 'objectStoreNames') {
                    return target.objectStoreNames || transactionStoreNamesMap.get(target);
                }
                // Make tx.store return the only store in the transaction, or undefined if there are many.
                if (prop === 'store') {
                    return receiver.objectStoreNames[1]
                        ? undefined
                        : receiver.objectStore(receiver.objectStoreNames[0]);
                }
            }
            // Else transform whatever we get back.
            return wrap(target[prop]);
        },
        set(target, prop, value) {
            target[prop] = value;
            return true;
        },
        has(target, prop) {
            if (target instanceof IDBTransaction &&
                (prop === 'done' || prop === 'store')) {
                return true;
            }
            return prop in target;
        },
    };
    function replaceTraps(callback) {
        idbProxyTraps = callback(idbProxyTraps);
    }
    function wrapFunction(func) {
        // Due to expected object equality (which is enforced by the caching in `wrap`), we
        // only create one new func per func.
        // Edge doesn't support objectStoreNames (booo), so we polyfill it here.
        if (func === IDBDatabase.prototype.transaction &&
            !('objectStoreNames' in IDBTransaction.prototype)) {
            return function (storeNames, ...args) {
                const tx = func.call(unwrap(this), storeNames, ...args);
                transactionStoreNamesMap.set(tx, storeNames.sort ? storeNames.sort() : [storeNames]);
                return wrap(tx);
            };
        }
        // Cursor methods are special, as the behaviour is a little more different to standard IDB. In
        // IDB, you advance the cursor and wait for a new 'success' on the IDBRequest that gave you the
        // cursor. It's kinda like a promise that can resolve with many values. That doesn't make sense
        // with real promises, so each advance methods returns a new promise for the cursor object, or
        // undefined if the end of the cursor has been reached.
        if (getCursorAdvanceMethods().includes(func)) {
            return function (...args) {
                // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use
                // the original object.
                func.apply(unwrap(this), args);
                return wrap(cursorRequestMap.get(this));
            };
        }
        return function (...args) {
            // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use
            // the original object.
            return wrap(func.apply(unwrap(this), args));
        };
    }
    function transformCachableValue(value) {
        if (typeof value === 'function')
            return wrapFunction(value);
        // This doesn't return, it just creates a 'done' promise for the transaction,
        // which is later returned for transaction.done (see idbObjectHandler).
        if (value instanceof IDBTransaction)
            cacheDonePromiseForTransaction(value);
        if (instanceOfAny(value, getIdbProxyableTypes()))
            return new Proxy(value, idbProxyTraps);
        // Return the same value back if we're not going to transform it.
        return value;
    }
    function wrap(value) {
        // We sometimes generate multiple promises from a single IDBRequest (eg when cursoring), because
        // IDB is weird and a single IDBRequest can yield many responses, so these can't be cached.
        if (value instanceof IDBRequest)
            return promisifyRequest(value);
        // If we've already transformed this value before, reuse the transformed value.
        // This is faster, but it also provides object equality.
        if (transformCache.has(value))
            return transformCache.get(value);
        const newValue = transformCachableValue(value);
        // Not all types are transformed.
        // These may be primitive types, so they can't be WeakMap keys.
        if (newValue !== value) {
            transformCache.set(value, newValue);
            reverseTransformCache.set(newValue, value);
        }
        return newValue;
    }
    const unwrap = (value) => reverseTransformCache.get(value);

    /**
     * Open a database.
     *
     * @param name Name of the database.
     * @param version Schema version.
     * @param callbacks Additional callbacks.
     */
    // @ts-ignore
    function openDB(name, version, { blocked, upgrade, blocking, terminated } = {}) {
        const request = indexedDB.open(name, version);
        const openPromise = wrap(request);
        if (upgrade) {
            request.addEventListener('upgradeneeded', (event) => {
                upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction), event);
            });
        }
        if (blocked) {
            request.addEventListener('blocked', (event) => blocked(
            // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405
            event.oldVersion, event.newVersion, event));
        }
        openPromise
            .then((db) => {
            if (terminated)
                db.addEventListener('close', () => terminated());
            if (blocking) {
                db.addEventListener('versionchange', (event) => blocking(event.oldVersion, event.newVersion, event));
            }
        })
            .catch(() => { });
        return openPromise;
    }
    /**
     * Delete a database.
     *
     * @param name Name of the database.
     */
    // @ts-ignore
    function deleteDB(name, { blocked } = {}) {
        const request = indexedDB.deleteDatabase(name);
        if (blocked) {
            request.addEventListener('blocked', (event) => blocked(
            // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405
            event.oldVersion, event));
        }
        return wrap(request).then(() => undefined);
    }

    const readMethods = ['get', 'getKey', 'getAll', 'getAllKeys', 'count'];
    const writeMethods = ['put', 'add', 'delete', 'clear'];
    const cachedMethods = new Map();
    function getMethod(target, prop) {
        if (!(target instanceof IDBDatabase &&
            !(prop in target) &&
            typeof prop === 'string')) {
            return;
        }
        if (cachedMethods.get(prop))
            return cachedMethods.get(prop);
        const targetFuncName = prop.replace(/FromIndex$/, '');
        const useIndex = prop !== targetFuncName;
        const isWrite = writeMethods.includes(targetFuncName);
        if (
        // Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge.
        !(targetFuncName in (useIndex ? IDBIndex : IDBObjectStore).prototype) ||
            !(isWrite || readMethods.includes(targetFuncName))) {
            return;
        }
        const method = async function (storeName, ...args) {
            // isWrite ? 'readwrite' : undefined gzipps better, but fails in Edge :(
            const tx = this.transaction(storeName, isWrite ? 'readwrite' : 'readonly');
            let target = tx.store;
            if (useIndex)
                target = target.index(args.shift());
            // Must reject if op rejects.
            // If it's a write operation, must reject if tx.done rejects.
            // Must reject with op rejection first.
            // Must resolve with op value.
            // Must handle both promises (no unhandled rejections)
            return (await Promise.all([
                target[targetFuncName](...args),
                isWrite && tx.done,
            ]))[0];
        };
        cachedMethods.set(prop, method);
        return method;
    }
    replaceTraps((oldTraps) => ({
        ...oldTraps,
        get: (target, prop, receiver) => getMethod(target, prop) || oldTraps.get(target, prop, receiver),
        has: (target, prop) => !!getMethod(target, prop) || oldTraps.has(target, prop),
    }));

    const advanceMethodProps = ['continue', 'continuePrimaryKey', 'advance'];
    const methodMap = {};
    const advanceResults = new WeakMap();
    const ittrProxiedCursorToOriginalProxy = new WeakMap();
    const cursorIteratorTraps = {
        get(target, prop) {
            if (!advanceMethodProps.includes(prop))
                return target[prop];
            let cachedFunc = methodMap[prop];
            if (!cachedFunc) {
                cachedFunc = methodMap[prop] = function (...args) {
                    advanceResults.set(this, ittrProxiedCursorToOriginalProxy.get(this)[prop](...args));
                };
            }
            return cachedFunc;
        },
    };
    async function* iterate(...args) {
        // tslint:disable-next-line:no-this-assignment
        let cursor = this;
        if (!(cursor instanceof IDBCursor)) {
            cursor = await cursor.openCursor(...args);
        }
        if (!cursor)
            return;
        cursor = cursor;
        const proxiedCursor = new Proxy(cursor, cursorIteratorTraps);
        ittrProxiedCursorToOriginalProxy.set(proxiedCursor, cursor);
        // Map this double-proxy back to the original, so other cursor methods work.
        reverseTransformCache.set(proxiedCursor, unwrap(cursor));
        while (cursor) {
            yield proxiedCursor;
            // If one of the advancing methods was not called, call continue().
            cursor = await (advanceResults.get(proxiedCursor) || cursor.continue());
            advanceResults.delete(proxiedCursor);
        }
    }
    function isIteratorProp(target, prop) {
        return ((prop === Symbol.asyncIterator &&
            instanceOfAny(target, [IDBIndex, IDBObjectStore, IDBCursor])) ||
            (prop === 'iterate' && instanceOfAny(target, [IDBIndex, IDBObjectStore])));
    }
    replaceTraps((oldTraps) => ({
        ...oldTraps,
        get(target, prop, receiver) {
            if (isIteratorProp(target, prop))
                return iterate;
            return oldTraps.get(target, prop, receiver);
        },
        has(target, prop) {
            return isIteratorProp(target, prop) || oldTraps.has(target, prop);
        },
    }));

    exports.deleteDB = deleteDB;
    exports.openDB = openDB;
    exports.unwrap = unwrap;
    exports.wrap = wrap;

}));


code:
/* eslint-disable no-var */
// @ts-ignore
"use strict";

var App = {}; // eslint-disable-line no-redeclare

// When adding namespace declarations, please consider needs of those using VSCode:
// when you declare App.A{ A1:{}, A2:{} }, VSCode considers A, A1, and A2 to be
// sealed namespaces and no new members are added to them with nested declaration later on.
// This breaks code completion completely. Please instead declare them as:
// App.A = {}; App.A.A1 = {}; App.A.A2 = {}. Thank you.

// Also, such declaration basically required only for namespaces that span more than a single file.

App.Arcology = {};
App.Arcology.Cell = {};
App.Art = {};
App.Art.GenAI = {};
App.Budget = {};
App.Corporate = {};
App.Corporate.Division = {};
App.Corporate.Shared = {};
App.Data = {};
App.Data.Arcology = {};
App.Data.Art = {};
App.Data.FCTV = {};
App.Data.HeroSlaves = {};
App.Data.Medicine = {};
App.Data.Policies = {};
App.Data.Policies.Selection = {};
App.Data.SecExp = {};
App.Data.Slave = {};
App.Data.Weather = {};
App.Debug = {};
App.Desc = {};
App.Desc.Player = {};
App.Encyclopedia = {};
App.EndWeek = {};
App.EndWeek.Player = {};
App.EndWeek.Shared = {};
App.Entity = {};
App.Entity.Utils = {};
App.Events = {};
App.Facilities = {};
App.Facilities.Arcade = {};
App.Facilities.Brothel = {};
App.Facilities.Cellblock = {};
App.Facilities.Clinic = {};
App.Facilities.Club = {};
App.Facilities.Dairy = {};
App.Facilities.Farmyard = {};
App.Facilities.HGSuite = {};
App.Facilities.Incubator = {};
App.Facilities.MasterSuite = {};
App.Facilities.Nursery = {};
App.Facilities.Pit = {};
App.Facilities.Pit.Fights = {};
App.Facilities.Schoolroom = {};
App.Facilities.ServantsQuarters = {};
App.Facilities.Spa = {};
App.Facilities.TransportHub = {};
App.Interact = {};
App.Interact.Sale = {};
App.Intro = {};
App.MainView = {};
App.Markets = {};
App.Medicine = {};
App.Medicine.Modification = {};
App.Medicine.Modification.Brands = {};
App.Medicine.Modification.Select = {};
App.Medicine.OrganFarm = {};
App.Medicine.Salon = {};
App.Medicine.Surgery = {};
App.Medicine.Surgery.Procedures = {};
App.Medicine.Surgery.Reactions = {};
App.Mods = {};
App.Mods.DinnerParty = {};
App.Mods.Drugs = {};
App.Mods.events = {
	nonRandom: [],
	random: {
		individual: [],
		nonindividual: [],
		recruitment: [],
	},
	recruit: [],
};
App.Mods.SecExp = {};
App.Mods.SF = {};
App.Neighbor = {};
App.PersonalAttention = {};
App.Porn = {};
App.RA = {};
App.RA.Activation = {};
App.Ratings = {};
App.SlaveAssignment = {};
App.StartingGirls = {};
App.Status = {};
App.Status.storyReady = false;
App.UI = {};
App.UI.Cheat = {};
App.UI.DOM = {};
App.UI.DOM.Widgets = {};
App.UI.DressingRoom = {};
App.UI.SlaveInteract = {};
App.UI.View = {};
App.Update = {};
App.Utils = {};
App.Utils.Math = {};


code:
App.Data.Arcology.Terrain = ["marine", "marine", "oceanic", "ravine", "rural", "rural", "rural", "urban", "urban"];
App.Data.Arcology.Continents = ["Africa", "Asia", "Asia", "Australia", "Western Europe", "Southern Europe", "Central Europe", "Eastern Europe", "Scandinavia", "Japan", "North America", "North America", "South America", "the Middle East"];


code:
App.Data.Assistant = {};
/**
 * Assistant appearances mapped to the FS that love seeing her look like she do.
 * @type {Map<FC.FutureSociety, Array<assistantAppearance>>}
 */
App.Data.Assistant.appearanceForFS = new Map([
	["FSSupremacist", ["amazon", "monstergirl", "succubus"]],
	["FSSubjugationist", ["amazon", "businesswoman", "imp"]],
	["FSGenderRadicalist", ["incubus", "monstergirl", "shemale", "succubus", "witch"]],
	["FSGenderFundamentalist", ["angel", "cherub", "fairy", "goddess", "hypergoddess", "loli", "preggololi", "pregnant fairy", "schoolgirl", "succubus", "witch"]],
	["FSDegradationist", ["businesswoman", "imp", "incubus", "monstergirl", "preggololi", "succubus"]],
	["FSPaternalist", ["angel", "cherub", "fairy", "goddess", "hypergoddess", "loli", "preggololi", "pregnant fairy", "schoolgirl"]],
	["FSBodyPurist", ["amazon", "angel", "fairy", "goddess", "incubus", "loli", "pregnant fairy", "succubus", "witch"]],
	["FSTransformationFetishist", ["businesswoman", "ERROR_1606_APPEARANCE_FILE_CORRUPT", "incubus", "shemale", "succubus", "witch"]],
	["FSYouthPreferentialist", ["angel", "cherub", "imp", "loli", "preggololi", "schoolgirl", "shemale", "succubus", "witch"]],
	["FSMaturityPreferentialist", ["angel", "businesswoman", "goddess", "incubus", "succubus", "witch"]],
	["FSSlimnessEnthusiast", ["cherub", "imp", "loli", "schoolgirl", "shemale", "succubus", "witch"]],
	["FSAssetExpansionist", ["businesswoman", "hypergoddess", "incubus", "shemale", "succubus", "witch"]],
	["FSPastoralist", ["cowgirl", "goddess", "hypergoddess", "incubus", "shemale", "succubus", "witch"]],
	["FSPhysicalIdealist", ["amazon", "incubus", "shemale", "succubus", "witch"]],
	["FSHedonisticDecadence", ["goddess", "hypergoddess", "imp", "incubus", "preggololi", "succubus", "witch"]],
	["FSChattelReligionist", ["angel", "cherub", "goddess", "imp", "incubus", "monstergirl", "succubus", "witch"]],
	["FSNull", []],
	["FSRomanRevivalist", ["amazon", "businesswoman", "incubus", "succubus"]],
	["FSNeoImperialist", ["amazon", "businesswoman", "incubus", "shemale", "angel"]],
	["FSEgyptianRevivalist", ["goddess", "incubus", "monstergirl", "succubus"]],
	["FSEdoRevivalist", ["amazon", "incubus", "kitsunegirl", "loli", "monstergirl", "succubus"]],
	["FSArabianRevivalist", ["businesswoman", "incubus", "schoolgirl", "succubus"]],
	["FSChineseRevivalist", ["incubus", "monstergirl", "schoolgirl", "succubus"]],
	["FSAztecRevivalist", ["amazon", "businesswoman", "incubus", "succubus"]],
	["FSAntebellumRevivalist", ["angel", "businesswoman", "goddess", "monstergirl", "succubus", "witch"]],
	["FSRepopulationFocus", ["goddess", "hypergoddess", "preggololi", "pregnant fairy", "succubus", "witch"]],
	["FSRestart", ["angel", "businesswoman", "goddess", "incubus", "loli", "schoolgirl", "succubus", "witch"]],
	["FSIntellectualDependency", ["shemale", "succubus", "witch"]],
	["FSSlaveProfessionalism", ["angel", "businesswoman", "incubus", "goddess", "schoolgirl", "succubus"]],
	["FSPetiteAdmiration", ["cherub", "fairy", "imp", "incubus", "loli", "preggololi", "pregnant fairy", "schoolgirl", "succubus", "witch"]],
	["FSStatuesqueGlorification", ["amazon", "goddess", "incubus", "succubus", "witch"]]
]);

/**
 * @type {Map<assistantAppearance, {readonly requirements: boolean}>}
 */
App.Data.Assistant.appearances = new Map([
	["normal", {get requirements() { return true; }}],
	["monstergirl", {get requirements() { return (V.seeDicks > 0); }}],
	["shemale", {get requirements() { return V.seeDicks > 0; }}],
	["amazon", {get requirements() { return true; }}],
	["businesswoman", {get requirements() { return true; }}],
	["goddess", {get requirements() { return (V.seePreg !== 0); }}],
	["hypergoddess", {get requirements() { return (V.seePreg !== 0 && V.seeHyperPreg !== 0); }}],
	["schoolgirl", {get requirements() { return true; }}],
	["loli", {get requirements() { return (V.minimumSlaveAge < 13); }}],
	["preggololi", {get requirements() { return (V.seePreg !== 0 && V.minimumSlaveAge < 13); }}],
	["fairy", {get requirements() { return true; }}],
	["pregnant fairy", {get requirements() { return (V.seePreg !== 0); }}],

	// Extra
	["slimegirl", {get requirements() { return (V.assistant.Extra1 === 1); }}],
	["cowgirl", {get requirements() { return (V.assistant.Extra1 === 1); }}],
	["harpygirl", {get requirements() { return (V.assistant.Extra1 === 1); }}],
	["kitsunegirl", {get requirements() { return (V.assistant.Extra1 === 1); }}],
	["lamiagirl", {get requirements() { return (V.assistant.Extra1 === 1); }}],
	["spidergirl", {get requirements() { return (V.assistant.Extra1 === 1); }}],

	// Extra 2
	["angel", {get requirements() { return (V.assistant.Extra2 === 1); }}],
	["cherub", {get requirements() { return (V.assistant.Extra2 === 1); }}],
	["incubus", {get requirements() { return (V.assistant.Extra2 === 1 && V.seeDicks > 0); }}],
	["succubus", {get requirements() { return (V.assistant.Extra2 === 1); }}],
	["imp", {get requirements() { return (V.assistant.Extra2 === 1); }}],
	["witch", {get requirements() { return (V.assistant.Extra2 === 1); }}],
	["ERROR_1606_APPEARANCE_FILE_CORRUPT", {get requirements() { return (V.assistant.Extra2 === 1); }}],
]);