{"version":3,"file":"create-offline-store.cjs","names":[],"sources":["../../src/offline/create-offline-store.ts"],"sourcesContent":["import Dexie, { type Table, type UpdateSpec } from \"dexie\";\n\nexport interface OfflineStoreConfig<TItem> {\n    /** IndexedDB database name. */\n    databaseName: string;\n    /** Schema version. Bump when changing indexes; pair with a migration if needed. */\n    version: number;\n    /** Object-store name. */\n    tableName: string;\n    /**\n     * Dexie index definition for the table. Use `&` for the primary key\n     * (unique), e.g. `\"&id, owner_id, created_at\"`. See Dexie docs for the\n     * full syntax.\n     */\n    indexes: string;\n    /** Property used as the primary key (default: `\"id\"`). */\n    keyPath?: keyof TItem & string;\n    /**\n     * Optional owner scoping. When set, every read/write method honors the\n     * `owner` argument and persists it on each record (e.g. multi-tenant\n     * notifications keyed by `user_id`).\n     */\n    ownerField?: keyof TItem & string;\n}\n\nexport interface ListOptions<TItem> {\n    /** Property to order by. Default: `keyPath`. */\n    orderBy?: keyof TItem & string;\n    /** Reverse the ordering. Default: false. */\n    reverse?: boolean;\n    /** Maximum number of items to return. */\n    limit?: number;\n    /** Skip this many items from the start of the result set. */\n    offset?: number;\n    /** Custom predicate applied after the index query. */\n    filter?: (item: TItem) => boolean;\n}\n\nexport interface OfflineStore<TItem, TKey extends string | number> {\n    /** Insert or replace a record. */\n    put: (item: TItem, owner?: string) => Promise<TKey>;\n    /** Insert or replace multiple records in a single transaction. */\n    bulkPut: (items: TItem[], owner?: string) => Promise<TKey>;\n    /** Fetch one record by its primary key. */\n    get: (key: TKey) => Promise<TItem | undefined>;\n    /** List records, optionally scoped to `owner` when `ownerField` is configured. */\n    list: (owner?: string, options?: ListOptions<TItem>) => Promise<TItem[]>;\n    /** Partial update by primary key. Returns the number of records changed. */\n    update: (key: TKey, changes: Partial<TItem>) => Promise<number>;\n    /** Apply a modification to every record matching `owner`. */\n    updateMany: (owner: string | undefined, changes: Partial<TItem>) => Promise<number>;\n    /** Delete one record by primary key. */\n    delete: (key: TKey) => Promise<void>;\n    /** Delete every record matching `owner` (or the entire table when no scope is set). */\n    clear: (owner?: string) => Promise<void>;\n    /** Count records, optionally scoped to `owner`. */\n    count: (owner?: string) => Promise<number>;\n    /** Raw Dexie table for advanced queries. */\n    raw: Table<TItem, TKey>;\n    /** Underlying Dexie instance. */\n    db: Dexie;\n}\n\nclass GenericDb<TItem, TKey extends string | number> extends Dexie {\n    store!: Table<TItem, TKey>;\n\n    constructor(name: string, version: number, tableName: string, indexes: string) {\n        super(name);\n        this.version(version).stores({ [tableName]: indexes });\n        this.store = this.table<TItem, TKey>(tableName);\n    }\n}\n\n/**\n * Build a typed IndexedDB-backed store using Dexie. Optionally scope every\n * operation by an `ownerField` (useful for multi-user SSE history, drafts,\n * cache per workspace, etc.).\n *\n * Dexie is an **optional peer dependency** — install it (`npm i dexie`) only\n * when your app needs offline storage.\n *\n * @example\n * type Note = { id: string; owner_id: string; text: string; created_at: string };\n * const notes = createOfflineStore<Note, string>({\n *     databaseName: \"TempestNotes\",\n *     version: 1,\n *     tableName: \"notes\",\n *     indexes: \"&id, owner_id, created_at\",\n *     ownerField: \"owner_id\",\n * });\n * await notes.put({ id: \"n1\", owner_id: \"u1\", text: \"hi\", created_at: ... }, \"u1\");\n * const mine = await notes.list(\"u1\", { orderBy: \"created_at\", reverse: true });\n */\nexport function createOfflineStore<TItem, TKey extends string | number = string>(\n    config: OfflineStoreConfig<TItem>,\n): OfflineStore<TItem, TKey> {\n    const { databaseName, version, tableName, indexes } = config;\n    const db = new GenericDb<TItem, TKey>(databaseName, version, tableName, indexes);\n    return buildStore<TItem, TKey>(db, db.store, config);\n}\n\n/**\n * Wrap an existing Dexie table in the {@link OfflineStore} surface.\n *\n * Split out so a store can be built over a table this function did not create,\n * which is what lets {@link createOfflineDatabase} place several stores on one\n * database rather than one database per table.\n *\n * @param db - The Dexie instance owning the table.\n * @param table - The table to wrap.\n * @param config - Key path and owner scoping for this table.\n * @returns The store surface bound to that table.\n */\nexport function buildStore<TItem, TKey extends string | number = string>(\n    db: Dexie,\n    table: Table<TItem, TKey>,\n    config: Pick<OfflineStoreConfig<TItem>, \"keyPath\" | \"ownerField\">,\n): OfflineStore<TItem, TKey> {\n    const { keyPath = \"id\", ownerField } = config;\n\n    function withOwner(item: TItem, owner?: string): TItem {\n        if (!ownerField || !owner) return item;\n        return { ...item, [ownerField]: owner } as TItem;\n    }\n\n    async function list(owner?: string, options: ListOptions<TItem> = {}): Promise<TItem[]> {\n        const { orderBy = keyPath, reverse = false, limit, offset, filter } = options;\n\n        let collection =\n            ownerField && owner ? table.where(ownerField).equals(owner) : table.toCollection();\n\n        if (filter) collection = collection.filter(filter);\n\n        let items =\n            orderBy === keyPath ? await collection.toArray() : await collection.sortBy(orderBy);\n\n        if (reverse) items = items.reverse();\n        if (offset) items = items.slice(offset);\n        if (typeof limit === \"number\") items = items.slice(0, limit);\n        return items;\n    }\n\n    return {\n        put: (item, owner) => table.put(withOwner(item, owner)) as Promise<TKey>,\n        bulkPut: (items, owner) =>\n            table.bulkPut(items.map((item) => withOwner(item, owner))) as Promise<TKey>,\n        get: (key) => table.get(key),\n        list,\n        update: (key, changes) => table.update(key, changes as UpdateSpec<TItem>),\n        updateMany: async (owner, changes) => {\n            const spec = changes as UpdateSpec<TItem>;\n            if (ownerField && owner) {\n                return table.where(ownerField).equals(owner).modify(spec);\n            }\n            return table.toCollection().modify(spec);\n        },\n        delete: (key) => table.delete(key),\n        clear: async (owner) => {\n            if (ownerField && owner) {\n                await table.where(ownerField).equals(owner).delete();\n                return;\n            }\n            await table.clear();\n        },\n        count: (owner) => {\n            if (ownerField && owner) {\n                return table.where(ownerField).equals(owner).count();\n            }\n            return table.count();\n        },\n        raw: table,\n        db,\n    };\n}\n"],"mappings":"6FA+DA,IAAM,EAAN,cAA6D,EAAA,OAAM,CAC/D,MAEA,YAAY,EAAc,EAAiB,EAAmB,EAAiB,CAC3E,MAAM,CAAI,EACV,KAAK,QAAQ,CAAO,CAAC,CAAC,OAAO,EAAG,GAAY,CAAQ,CAAC,EACrD,KAAK,MAAQ,KAAK,MAAmB,CAAS,CAClD,CACJ,EAsBA,SAAgB,EACZ,EACyB,CACzB,GAAM,CAAE,eAAc,UAAS,YAAW,WAAY,EAChD,EAAK,IAAI,EAAuB,EAAc,EAAS,EAAW,CAAO,EAC/E,OAAO,EAAwB,EAAI,EAAG,MAAO,CAAM,CACvD,CAcA,SAAgB,EACZ,EACA,EACA,EACyB,CACzB,GAAM,CAAE,UAAU,KAAM,cAAe,EAEvC,SAAS,EAAU,EAAa,EAAuB,CAEnD,MADI,CAAC,GAAc,CAAC,EAAc,EAC3B,CAAE,GAAG,GAAO,GAAa,CAAM,CAC1C,CAEA,eAAe,EAAK,EAAgB,EAA8B,CAAC,EAAqB,CACpF,GAAM,CAAE,UAAU,EAAS,UAAU,GAAO,QAAO,SAAQ,UAAW,EAElE,EACA,GAAc,EAAQ,EAAM,MAAM,CAAU,CAAC,CAAC,OAAO,CAAK,EAAI,EAAM,aAAa,EAEjF,IAAQ,EAAa,EAAW,OAAO,CAAM,GAEjD,IAAI,EACA,IAAY,EAAU,MAAM,EAAW,QAAQ,EAAI,MAAM,EAAW,OAAO,CAAO,EAKtF,OAHI,IAAS,EAAQ,EAAM,QAAQ,GAC/B,IAAQ,EAAQ,EAAM,MAAM,CAAM,GAClC,OAAO,GAAU,WAAU,EAAQ,EAAM,MAAM,EAAG,CAAK,GACpD,CACX,CAEA,MAAO,CACH,KAAM,EAAM,IAAU,EAAM,IAAI,EAAU,EAAM,CAAK,CAAC,EACtD,SAAU,EAAO,IACb,EAAM,QAAQ,EAAM,IAAK,GAAS,EAAU,EAAM,CAAK,CAAC,CAAC,EAC7D,IAAM,GAAQ,EAAM,IAAI,CAAG,EAC3B,OACA,QAAS,EAAK,IAAY,EAAM,OAAO,EAAK,CAA4B,EACxE,WAAY,MAAO,EAAO,IAAY,CAClC,IAAM,EAAO,EAIb,OAHI,GAAc,EACP,EAAM,MAAM,CAAU,CAAC,CAAC,OAAO,CAAK,CAAC,CAAC,OAAO,CAAI,EAErD,EAAM,aAAa,CAAC,CAAC,OAAO,CAAI,CAC3C,EACA,OAAS,GAAQ,EAAM,OAAO,CAAG,EACjC,MAAO,KAAO,IAAU,CACpB,GAAI,GAAc,EAAO,CACrB,MAAM,EAAM,MAAM,CAAU,CAAC,CAAC,OAAO,CAAK,CAAC,CAAC,OAAO,EACnD,MACJ,CACA,MAAM,EAAM,MAAM,CACtB,EACA,MAAQ,GACA,GAAc,EACP,EAAM,MAAM,CAAU,CAAC,CAAC,OAAO,CAAK,CAAC,CAAC,MAAM,EAEhD,EAAM,MAAM,EAEvB,IAAK,EACL,IACJ,CACJ"}