@loggerjs/browser 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"offline-first-transport.js","names":[],"sources":["../src/indexeddb-transport.ts","../src/offline-first-transport.ts"],"sourcesContent":["import {\n incrementLoggerMetaCounter,\n safeJsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n type TransportContext,\n} from \"@loggerjs/core\";\nimport type { BrowserHttpDropPolicy } from \"./http-transport\";\n\nexport interface IndexedDbLogEntry {\n id: string;\n seq: number;\n createdAt: number;\n level: number;\n levelName: string;\n logger: string;\n type?: string;\n byteLength: number;\n payload: string | Uint8Array;\n}\n\nexport interface IndexedDbTransportQueryOptions {\n from?: number;\n to?: number;\n minLevel?: LoggerLevel;\n logger?: string;\n type?: string;\n limit?: number;\n order?: \"asc\" | \"desc\";\n}\n\nexport type IndexedDbTransportDurability = \"default\" | \"strict\" | \"relaxed\";\n\nexport type IndexedDbStorageBucketDurability = \"strict\" | \"relaxed\";\n\nexport interface IndexedDbTransportStats {\n bufferDepth: number;\n maxBufferDepth: number;\n pendingFlush: boolean;\n enqueued: number;\n persisted: number;\n dropped: number;\n droppedByReason: Record<string, number>;\n persistedDropped: number;\n persistedDroppedByReason: Record<string, number>;\n flushes: number;\n flushErrors: number;\n lastFlushBatchSize: number;\n lastFlushDurationMs: number;\n prunes: number;\n pruneFallbacks: number;\n lastPruneDurationMs: number;\n queries: number;\n queryFallbacks: number;\n lastQueryDurationMs: number;\n databaseOpenCount: number;\n storageBucketFallbacks: number;\n transactionOptionFallbacks: number;\n errors: number;\n errorsByOperation: Record<string, number>;\n}\n\nexport interface IndexedDbTransportOptions {\n name?: string;\n dbName?: string;\n storeName?: string;\n maxEntries?: number;\n maxBytes?: number;\n ttlMs?: number;\n batchSize?: number;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n dropPolicy?: BrowserHttpDropPolicy;\n flushOnPageHide?: boolean;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n durability?: IndexedDbTransportDurability;\n storageBucketName?: string;\n storageBucketPersisted?: boolean;\n storageBucketDurability?: IndexedDbStorageBucketDurability;\n indexedDB?: IDBFactory;\n onDrop?: (event: LogEvent, reason: string) => void;\n onPersistedDrop?: (entry: IndexedDbLogEntry, reason: string) => void;\n}\n\nexport interface IndexedDbTransport extends Transport {\n count: () => Promise<number>;\n clear: () => Promise<void>;\n remove: (ids: string | readonly string[]) => Promise<void>;\n query: (options?: IndexedDbTransportQueryOptions) => AsyncIterable<LogEvent>;\n stats: () => IndexedDbTransportStats;\n}\n\nconst DEFAULT_DB_NAME = \"loggerjs\";\nconst DEFAULT_STORE_NAME = \"logs\";\nconst DB_VERSION = 2;\nconst ORDER_INDEX_NAME = \"createdAtSeq\";\n\ninterface StorageBucketLike {\n indexedDB?: IDBFactory;\n}\n\ninterface StorageBucketManagerLike {\n open: (\n name: string,\n options?: {\n durability?: IndexedDbStorageBucketDurability;\n persisted?: boolean;\n },\n ) => Promise<StorageBucketLike>;\n}\n\nfunction normalizePositiveInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value)) return fallback;\n return Math.max(0, Math.floor(value));\n}\n\nfunction nowMs(): number {\n return typeof performance !== \"undefined\" && typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n}\n\nfunction incrementRecord(record: Record<string, number>, key: string, amount = 1): void {\n record[key] = (record[key] ?? 0) + amount;\n}\n\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.addEventListener(\"success\", () => resolve(request.result), { once: true });\n request.addEventListener(\n \"error\",\n () => reject(request.error ?? new Error(\"IndexedDB request failed\")),\n { once: true },\n );\n });\n}\n\nfunction transactionToPromise(transaction: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n transaction.addEventListener(\"complete\", () => resolve(), { once: true });\n transaction.addEventListener(\n \"abort\",\n () => reject(transaction.error ?? new Error(\"IndexedDB transaction aborted\")),\n { once: true },\n );\n transaction.addEventListener(\n \"error\",\n () => reject(transaction.error ?? new Error(\"IndexedDB transaction failed\")),\n { once: true },\n );\n });\n}\n\nfunction payloadByteLength(payload: string | Uint8Array): number {\n if (typeof payload !== \"string\") return payload.byteLength;\n if (typeof TextEncoder !== \"undefined\") return new TextEncoder().encode(payload).byteLength;\n return new Blob([payload]).size;\n}\n\nfunction compareEntries(a: IndexedDbLogEntry, b: IndexedDbLogEntry): number {\n if (a.createdAt !== b.createdAt) return a.createdAt - b.createdAt;\n if (a.seq !== b.seq) return a.seq - b.seq;\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n}\n\nfunction orderEntries(\n entries: readonly IndexedDbLogEntry[],\n order: \"asc\" | \"desc\" = \"asc\",\n): IndexedDbLogEntry[] {\n // oxlint-disable-next-line no-array-sort -- Sort a copy to preserve ES2020 compatibility and caller immutability.\n const sorted = [...entries].sort(compareEntries);\n if (order === \"asc\") return sorted;\n const reversed: IndexedDbLogEntry[] = [];\n for (let index = sorted.length - 1; index >= 0; index -= 1) {\n reversed.push(sorted[index] as IndexedDbLogEntry);\n }\n return reversed;\n}\n\nfunction indexExists(store: IDBObjectStore, name: string): boolean {\n return store.indexNames.contains(name);\n}\n\nfunction ensureIndex(store: IDBObjectStore, name: string, keyPath: string | readonly string[]) {\n if (!indexExists(store, name)) store.createIndex(name, keyPath);\n}\n\nfunction getStoreIndex(store: IDBObjectStore, name: string): IDBIndex | undefined {\n const maybeStore = store as IDBObjectStore & {\n index?: (indexName: string) => IDBIndex;\n };\n if (typeof maybeStore.index !== \"function\" || !indexExists(store, name)) return undefined;\n return maybeStore.index(name);\n}\n\nfunction getStorageBucketManager(): StorageBucketManagerLike | undefined {\n return (globalThis.navigator as unknown as { storageBuckets?: StorageBucketManagerLike })\n ?.storageBuckets;\n}\n\nfunction storageBucketOpenOptions(options: IndexedDbTransportOptions):\n | {\n durability?: IndexedDbStorageBucketDurability;\n persisted?: boolean;\n }\n | undefined {\n if (\n options.storageBucketDurability === undefined &&\n options.storageBucketPersisted === undefined\n ) {\n return undefined;\n }\n return {\n durability: options.storageBucketDurability,\n persisted: options.storageBucketPersisted,\n };\n}\n\nfunction decodeEntry(\n entry: IndexedDbLogEntry,\n codec: Codec<string | Uint8Array>,\n): LogEvent | undefined {\n if (!codec.decode) throw new Error(\"indexedDbTransport query requires a codec with decode\");\n const decoded = codec.decode(entry.payload);\n return Array.isArray(decoded) ? decoded[0] : decoded;\n}\n\nfunction shouldKeepEntry(entry: IndexedDbLogEntry, options: IndexedDbTransportQueryOptions) {\n if (options.from !== undefined && entry.createdAt < options.from) return false;\n if (options.to !== undefined && entry.createdAt > options.to) return false;\n if (options.minLevel !== undefined && entry.level < toLevelValue(options.minLevel)) return false;\n if (options.logger !== undefined && entry.logger !== options.logger) return false;\n if (options.type !== undefined && entry.type !== options.type) return false;\n return true;\n}\n\nfunction orderRangeForQuery(options: IndexedDbTransportQueryOptions): IDBKeyRange | undefined {\n if (typeof IDBKeyRange === \"undefined\") return undefined;\n const lower =\n options.from === undefined ? undefined : [options.from, Number.MIN_SAFE_INTEGER, \"\"];\n const upper =\n options.to === undefined ? undefined : [options.to, Number.MAX_SAFE_INTEGER, \"\\uffff\"];\n if (lower && upper) return IDBKeyRange.bound(lower, upper);\n if (lower) return IDBKeyRange.lowerBound(lower);\n if (upper) return IDBKeyRange.upperBound(upper);\n return undefined;\n}\n\nfunction iterateCursor(\n request: IDBRequest<IDBCursorWithValue | null>,\n visit: (cursor: IDBCursorWithValue) => boolean | void,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n request.addEventListener(\n \"success\",\n () => {\n const cursor = request.result;\n if (!cursor) {\n resolve();\n return;\n }\n try {\n const shouldContinue = visit(cursor);\n if (shouldContinue === false) {\n resolve();\n return;\n }\n cursor.continue();\n } catch (error) {\n reject(error);\n }\n },\n false,\n );\n request.addEventListener(\n \"error\",\n () => reject(request.error ?? new Error(\"IndexedDB cursor failed\")),\n { once: true },\n );\n });\n}\n\nexport function indexedDbTransport(options: IndexedDbTransportOptions = {}): IndexedDbTransport {\n const dbName = options.dbName ?? DEFAULT_DB_NAME;\n const storeName = options.storeName ?? DEFAULT_STORE_NAME;\n const codec = options.codec ?? (safeJsonCodec() as Codec<string | Uint8Array>);\n const batchSize = normalizePositiveInteger(options.batchSize, 100);\n const flushIntervalMs = normalizePositiveInteger(options.flushIntervalMs, 1000);\n const maxBufferSize = normalizePositiveInteger(options.maxBufferSize, 1000);\n const maxEntries = normalizePositiveInteger(options.maxEntries, 50_000);\n const maxBytes = normalizePositiveInteger(options.maxBytes, 50 * 1024 * 1024);\n const dropPolicy = options.dropPolicy ?? \"drop-oldest\";\n const flushOnPageHide = options.flushOnPageHide ?? true;\n const durability = options.durability ?? \"default\";\n const rootIndexedDB = options.indexedDB ?? globalThis.indexedDB;\n const transactionOptions =\n durability === \"default\"\n ? undefined\n : ({\n durability,\n } satisfies IDBTransactionOptions);\n\n const buffer: LogEvent[] = [];\n let dbPromise: Promise<IDBDatabase> | undefined;\n let indexedDBFactoryPromise: Promise<IDBFactory | undefined> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let flushPromise: Promise<void> | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n const statsState: IndexedDbTransportStats = {\n bufferDepth: 0,\n maxBufferDepth: 0,\n pendingFlush: false,\n enqueued: 0,\n persisted: 0,\n dropped: 0,\n droppedByReason: {},\n persistedDropped: 0,\n persistedDroppedByReason: {},\n flushes: 0,\n flushErrors: 0,\n lastFlushBatchSize: 0,\n lastFlushDurationMs: 0,\n prunes: 0,\n pruneFallbacks: 0,\n lastPruneDurationMs: 0,\n queries: 0,\n queryFallbacks: 0,\n lastQueryDurationMs: 0,\n databaseOpenCount: 0,\n storageBucketFallbacks: 0,\n transactionOptionFallbacks: 0,\n errors: 0,\n errorsByOperation: {},\n };\n\n const snapshotStats = (): IndexedDbTransportStats => ({\n ...statsState,\n bufferDepth: buffer.length,\n droppedByReason: { ...statsState.droppedByReason },\n errorsByOperation: { ...statsState.errorsByOperation },\n pendingFlush: flushPromise !== undefined,\n persistedDroppedByReason: { ...statsState.persistedDroppedByReason },\n });\n\n const reportInternalError = (error: unknown, operation: string) => {\n statsState.errors += 1;\n incrementRecord(statsState.errorsByOperation, operation);\n lastContext?.reportInternalError(error, {\n operation,\n phase: \"transport\",\n transport: options.name ?? \"indexeddb\",\n });\n };\n\n const dropEvent = (event: LogEvent, reason: string) => {\n statsState.dropped += 1;\n incrementRecord(statsState.droppedByReason, reason);\n incrementLoggerMetaCounter(\"transport.indexeddb.dropped\");\n incrementLoggerMetaCounter(`transport.indexeddb.dropped.${reason}`);\n options.onDrop?.(event, reason);\n };\n\n const dropPersistedEntry = (entry: IndexedDbLogEntry, reason: string) => {\n statsState.persistedDropped += 1;\n incrementRecord(statsState.persistedDroppedByReason, reason);\n incrementLoggerMetaCounter(\"transport.indexeddb.persisted.dropped\");\n incrementLoggerMetaCounter(`transport.indexeddb.persisted.dropped.${reason}`);\n options.onPersistedDrop?.(entry, reason);\n };\n\n const resolveIndexedDBFactory = async () => {\n if (options.indexedDB || !options.storageBucketName) return rootIndexedDB;\n indexedDBFactoryPromise ??= (async () => {\n const manager = getStorageBucketManager();\n if (!manager) {\n statsState.storageBucketFallbacks += 1;\n return rootIndexedDB;\n }\n try {\n const bucket = await manager.open(\n options.storageBucketName as string,\n storageBucketOpenOptions(options),\n );\n if (bucket.indexedDB) return bucket.indexedDB;\n statsState.storageBucketFallbacks += 1;\n return rootIndexedDB;\n } catch (error) {\n statsState.storageBucketFallbacks += 1;\n reportInternalError(error, \"storage-bucket-open\");\n return rootIndexedDB;\n }\n })();\n return indexedDBFactoryPromise;\n };\n\n const openDb = () => {\n dbPromise ??= resolveIndexedDBFactory().then((idb) => {\n if (!idb) throw new Error(\"IndexedDB is not available for indexedDbTransport\");\n return new Promise<IDBDatabase>((resolve, reject) => {\n const request = idb.open(dbName, DB_VERSION);\n request.addEventListener(\"upgradeneeded\", () => {\n const db = request.result;\n const store = db.objectStoreNames.contains(storeName)\n ? request.transaction?.objectStore(storeName)\n : db.createObjectStore(storeName, { keyPath: \"id\" });\n if (!store) return;\n ensureIndex(store, \"createdAt\", \"createdAt\");\n ensureIndex(store, \"level\", \"level\");\n ensureIndex(store, \"logger\", \"logger\");\n ensureIndex(store, \"type\", \"type\");\n ensureIndex(store, ORDER_INDEX_NAME, [\"createdAt\", \"seq\", \"id\"]);\n });\n request.addEventListener(\n \"success\",\n () => {\n statsState.databaseOpenCount += 1;\n resolve(request.result);\n },\n { once: true },\n );\n request.addEventListener(\n \"error\",\n () => reject(request.error ?? new Error(\"IndexedDB open failed\")),\n { once: true },\n );\n });\n });\n return dbPromise;\n };\n\n const createTransaction = (db: IDBDatabase, mode: IDBTransactionMode) => {\n if (mode !== \"readwrite\" || !transactionOptions) return db.transaction(storeName, mode);\n try {\n return db.transaction(storeName, mode, transactionOptions);\n } catch {\n statsState.transactionOptionFallbacks += 1;\n return db.transaction(storeName, mode);\n }\n };\n\n const withStore = async <T>(\n mode: IDBTransactionMode,\n run: (store: IDBObjectStore, transaction: IDBTransaction) => T | Promise<T>,\n ): Promise<T> => {\n const db = await openDb();\n const tx = createTransaction(db, mode);\n const settled = transactionToPromise(tx);\n try {\n const result = await run(tx.objectStore(storeName), tx);\n await settled;\n return result;\n } catch (error) {\n if (tx.error === null) {\n try {\n tx.abort();\n } catch {\n // The transaction may already be committed or aborted.\n }\n }\n throw error;\n }\n };\n\n const getEntries = async () =>\n orderEntries(\n await withStore(\"readonly\", (store) =>\n requestToPromise(store.getAll() as IDBRequest<IndexedDbLogEntry[]>),\n ),\n );\n\n const countEntries = async (): Promise<number | undefined> =>\n withStore(\"readonly\", (store) => {\n const maybeStore = store as IDBObjectStore & {\n count?: () => IDBRequest<number>;\n };\n if (typeof maybeStore.count !== \"function\") return undefined;\n return requestToPromise(maybeStore.count.call(store));\n });\n\n const deleteEntries = async (entries: readonly IndexedDbLogEntry[], reason: string) => {\n if (entries.length === 0) return;\n await withStore(\"readwrite\", async (store) => {\n for (const entry of entries) {\n // oxlint-disable-next-line no-await-in-loop -- Deletes are sequenced for fake IDB compatibility.\n await requestToPromise(store.delete(entry.id));\n dropPersistedEntry(entry, reason);\n }\n });\n };\n\n const deleteByCursor = async (\n reason: string,\n openCursor: (store: IDBObjectStore) => IDBRequest<IDBCursorWithValue | null> | undefined,\n shouldDelete: (entry: IndexedDbLogEntry, deleted: number) => boolean,\n ): Promise<number | undefined> =>\n withStore(\"readwrite\", async (store) => {\n const request = openCursor(store);\n if (!request) return undefined;\n let deleted = 0;\n await iterateCursor(request, (cursor) => {\n const entry = cursor.value as IndexedDbLogEntry;\n if (!shouldDelete(entry, deleted)) return false;\n cursor.delete();\n deleted += 1;\n dropPersistedEntry(entry, reason);\n return true;\n });\n return deleted;\n });\n\n const deleteExpiredByCursor = async (cutoff: number): Promise<number | undefined> => {\n if (typeof IDBKeyRange === \"undefined\") return undefined;\n const range = IDBKeyRange.upperBound(cutoff, true);\n return deleteByCursor(\n \"ttl\",\n (store) => getStoreIndex(store, \"createdAt\")?.openCursor(range),\n () => true,\n );\n };\n\n const deleteOldestByCursor = async (\n reason: string,\n count: number,\n ): Promise<number | undefined> => {\n if (count <= 0) return 0;\n return deleteByCursor(\n reason,\n (store) => getStoreIndex(store, ORDER_INDEX_NAME)?.openCursor(),\n (_entry, deleted) => deleted < count,\n );\n };\n\n const deleteOldestBytesByCursor = async (bytesToFree: number): Promise<number | undefined> => {\n if (bytesToFree <= 0) return 0;\n let freed = 0;\n return deleteByCursor(\n \"max-bytes\",\n (store) => getStoreIndex(store, ORDER_INDEX_NAME)?.openCursor(),\n (entry) => {\n if (freed >= bytesToFree) return false;\n freed += entry.byteLength;\n return true;\n },\n );\n };\n\n const sumBytesByCursor = async (): Promise<number | undefined> =>\n withStore(\"readonly\", async (store) => {\n const index = getStoreIndex(store, ORDER_INDEX_NAME);\n if (!index) return undefined;\n let total = 0;\n await iterateCursor(index.openCursor(), (cursor) => {\n total += (cursor.value as IndexedDbLogEntry).byteLength;\n });\n return total;\n });\n\n const queryEntriesByCursor = async (\n queryOptions: IndexedDbTransportQueryOptions,\n ): Promise<IndexedDbLogEntry[] | undefined> =>\n withStore(\"readonly\", async (store) => {\n const index = getStoreIndex(store, ORDER_INDEX_NAME);\n if (!index) return undefined;\n const entries: IndexedDbLogEntry[] = [];\n const limit = queryOptions.limit;\n if (limit !== undefined && limit <= 0) return entries;\n await iterateCursor(\n index.openCursor(\n orderRangeForQuery(queryOptions),\n queryOptions.order === \"desc\" ? \"prev\" : \"next\",\n ),\n (cursor) => {\n const entry = cursor.value as IndexedDbLogEntry;\n if (!shouldKeepEntry(entry, queryOptions)) return true;\n entries.push(entry);\n return limit === undefined || entries.length < limit;\n },\n );\n return entries;\n });\n\n const pruneWithEntries = async () => {\n const now = Date.now();\n let entries = await getEntries();\n const expired =\n options.ttlMs === undefined\n ? []\n : entries.filter((entry) => now - entry.createdAt > (options.ttlMs as number));\n if (expired.length > 0) {\n await deleteEntries(expired, \"ttl\");\n const expiredIds = new Set(expired.map((entry) => entry.id));\n entries = entries.filter((entry) => !expiredIds.has(entry.id));\n }\n\n if (maxEntries >= 0 && entries.length > maxEntries) {\n const dropped = entries.slice(0, entries.length - maxEntries);\n await deleteEntries(dropped, \"max-entries\");\n entries = entries.slice(entries.length - maxEntries);\n }\n\n if (maxBytes >= 0) {\n let total = entries.reduce((sum, entry) => sum + entry.byteLength, 0);\n const dropped: IndexedDbLogEntry[] = [];\n for (const entry of entries) {\n if (total <= maxBytes) break;\n total -= entry.byteLength;\n dropped.push(entry);\n }\n await deleteEntries(dropped, \"max-bytes\");\n }\n };\n\n const pruneWithCursors = async (): Promise<boolean> => {\n if (options.ttlMs !== undefined) {\n const deleted = await deleteExpiredByCursor(Date.now() - options.ttlMs);\n if (deleted === undefined) return false;\n }\n\n if (maxEntries >= 0) {\n const persistedCount = await countEntries();\n if (persistedCount === undefined) return false;\n if (persistedCount > maxEntries) {\n const deleted = await deleteOldestByCursor(\"max-entries\", persistedCount - maxEntries);\n if (deleted === undefined) return false;\n }\n }\n\n if (maxBytes >= 0) {\n const totalBytes = await sumBytesByCursor();\n if (totalBytes === undefined) return false;\n if (totalBytes > maxBytes) {\n const deleted = await deleteOldestBytesByCursor(totalBytes - maxBytes);\n if (deleted === undefined) return false;\n }\n }\n\n return true;\n };\n\n const prune = async () => {\n const startedAt = nowMs();\n try {\n if (await pruneWithCursors()) return;\n statsState.pruneFallbacks += 1;\n await pruneWithEntries();\n } finally {\n statsState.prunes += 1;\n statsState.lastPruneDurationMs = nowMs() - startedAt;\n }\n };\n\n const eventToEntry = (event: LogEvent): IndexedDbLogEntry => {\n const payload = codec.encode(event);\n return {\n id: event.id,\n seq: event.seq,\n createdAt: event.time,\n level: event.level,\n levelName: event.levelName,\n logger: event.logger,\n type: event.type,\n byteLength: payloadByteLength(payload),\n payload,\n };\n };\n\n const writeBatch = async (events: readonly LogEvent[]) => {\n if (events.length === 0) return;\n const startedAt = nowMs();\n statsState.lastFlushBatchSize = events.length;\n const entries = events.map(eventToEntry);\n try {\n await withStore(\"readwrite\", (store) => {\n for (const entry of entries) {\n store.put(entry);\n }\n });\n statsState.persisted += entries.length;\n incrementLoggerMetaCounter(\"transport.indexeddb.persisted\", entries.length);\n await prune();\n statsState.flushes += 1;\n } catch (error) {\n statsState.flushErrors += 1;\n throw error;\n } finally {\n statsState.lastFlushDurationMs = nowMs() - startedAt;\n }\n };\n\n const clearTimer = () => {\n if (timer) clearTimeout(timer);\n timer = undefined;\n };\n\n const flushPending = async () => {\n if (flushPromise) return flushPromise;\n clearTimer();\n const batch = buffer.splice(0, buffer.length);\n flushPromise = writeBatch(batch).finally(() => {\n flushPromise = undefined;\n if (buffer.length > 0 && !closed) schedule();\n });\n return flushPromise;\n };\n\n const schedule = () => {\n if (timer || flushIntervalMs <= 0 || closed) return;\n timer = setTimeout(() => {\n void flushPending().catch((error: unknown) => reportInternalError(error, \"flush\"));\n }, flushIntervalMs);\n };\n\n const onPageHide = () => {\n void flushPending().catch((error: unknown) => reportInternalError(error, \"pagehide-flush\"));\n };\n\n const onVisibilityChange = () => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") {\n void flushPending().catch((error: unknown) => reportInternalError(error, \"visibility-flush\"));\n }\n };\n\n if (flushOnPageHide) {\n globalThis.addEventListener?.(\"pagehide\", onPageHide);\n globalThis.addEventListener?.(\"visibilitychange\", onVisibilityChange);\n }\n\n return {\n name: options.name ?? \"indexeddb\",\n minLevel: options.minLevel,\n log(event, context) {\n if (closed) return;\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n lastContext = context;\n if (maxBufferSize === 0 || buffer.length >= maxBufferSize) {\n if (dropPolicy === \"drop-newest\") {\n dropEvent(event, \"buffer-full\");\n return;\n }\n const dropped = buffer.shift();\n if (dropped) dropEvent(dropped, \"buffer-full\");\n }\n buffer.push(event);\n statsState.enqueued += 1;\n statsState.maxBufferDepth = Math.max(statsState.maxBufferDepth, buffer.length);\n if (buffer.length >= batchSize || flushIntervalMs === 0) {\n void flushPending().catch((error: unknown) => reportInternalError(error, \"flush\"));\n } else {\n schedule();\n }\n },\n flush() {\n return flushPending();\n },\n async count() {\n await flushPending();\n return (await countEntries()) ?? (await getEntries()).length;\n },\n async clear() {\n buffer.length = 0;\n clearTimer();\n await withStore(\"readwrite\", (store) => requestToPromise(store.clear()));\n },\n async remove(ids) {\n await flushPending();\n const items = typeof ids === \"string\" ? [ids] : ids;\n if (items.length === 0) return;\n await withStore(\"readwrite\", async (store) => {\n for (const id of items) {\n // oxlint-disable-next-line no-await-in-loop -- Deletes must preserve fake IDB transaction ordering.\n await requestToPromise(store.delete(id));\n }\n });\n },\n async *query(queryOptions: IndexedDbTransportQueryOptions = {}) {\n await flushPending();\n const startedAt = nowMs();\n const cursorEntries = await queryEntriesByCursor(queryOptions);\n if (cursorEntries === undefined) statsState.queryFallbacks += 1;\n const entries =\n cursorEntries ??\n orderEntries(await getEntries(), queryOptions.order ?? \"asc\")\n .filter((entry) => shouldKeepEntry(entry, queryOptions))\n .slice(0, queryOptions.limit);\n statsState.queries += 1;\n statsState.lastQueryDurationMs = nowMs() - startedAt;\n for (const entry of entries) {\n const event = decodeEntry(entry, codec);\n if (event) yield event;\n }\n },\n stats() {\n return snapshotStats();\n },\n close() {\n closed = true;\n clearTimer();\n globalThis.removeEventListener?.(\"pagehide\", onPageHide);\n globalThis.removeEventListener?.(\"visibilitychange\", onVisibilityChange);\n const flushed = flushPending();\n const promise = dbPromise;\n dbPromise = undefined;\n return flushed.finally(() => {\n void promise?.then((db) => db.close());\n });\n },\n };\n}\n","import {\n incrementLoggerMetaCounter,\n recordToEvent,\n retryTransport,\n toLevelValue,\n type LogEvent,\n type LoggerLevel,\n type RetryTransportOptions,\n type Transport,\n type TransportContext,\n} from \"@loggerjs/core\";\nimport {\n indexedDbTransport,\n type IndexedDbTransportOptions,\n type IndexedDbTransportQueryOptions,\n} from \"./indexeddb-transport\";\n\nexport interface OfflineFirstQueue {\n log: (event: LogEvent, context: TransportContext) => void | Promise<void>;\n flush?: () => void | Promise<void>;\n query: (options?: IndexedDbTransportQueryOptions) => AsyncIterable<LogEvent>;\n remove: (ids: string | readonly string[]) => Promise<void>;\n count?: () => Promise<number>;\n clear?: () => Promise<void>;\n close?: () => void | Promise<void>;\n}\n\nexport interface OfflineFirstTransportOptions {\n name?: string;\n minLevel?: LoggerLevel;\n queue?: OfflineFirstQueue;\n queueOptions?: IndexedDbTransportOptions;\n replayBatchSize?: number;\n replayOnOnline?: boolean;\n online?: () => boolean;\n retry?: RetryTransportOptions;\n onQueued?: (event: LogEvent, error?: unknown) => void;\n onReplayed?: (events: readonly LogEvent[]) => void;\n}\n\nexport interface OfflineFirstTransport extends Transport {\n replay: () => Promise<void>;\n queuedCount: () => Promise<number | undefined>;\n clearQueue: () => Promise<void>;\n queue: OfflineFirstQueue;\n}\n\nfunction normalizeBatchSize(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 100;\n return Math.floor(value);\n}\n\nasync function collectBatch(\n queue: OfflineFirstQueue,\n replayBatchSize: number,\n): Promise<LogEvent[]> {\n const events: LogEvent[] = [];\n for await (const event of queue.query({ order: \"asc\", limit: replayBatchSize })) {\n events.push(event);\n }\n return events;\n}\n\nfunction isBrowserOnline(options: OfflineFirstTransportOptions): boolean {\n if (options.online) return options.online();\n if (typeof navigator === \"undefined\") return true;\n return navigator.onLine !== false;\n}\n\nexport function offlineFirstTransport(\n remote: Transport,\n options: OfflineFirstTransportOptions = {},\n): OfflineFirstTransport {\n const name = options.name ?? `offline-first(${remote.name ?? \"transport\"})`;\n const replayBatchSize = normalizeBatchSize(options.replayBatchSize);\n const queue: OfflineFirstQueue =\n options.queue ??\n (indexedDbTransport({\n dbName: \"loggerjs-offline\",\n flushIntervalMs: 0,\n name: `${name}:queue`,\n storeName: \"offline-events\",\n ...options.queueOptions,\n }) as OfflineFirstQueue);\n const reliableRemote = retryTransport(remote, {\n maxRetries: 2,\n retryBaseDelayMs: 250,\n retryMaxDelayMs: 5000,\n ...options.retry,\n name: `${name}:remote`,\n }) as Transport & {\n log: NonNullable<Transport[\"log\"]>;\n logBatch: NonNullable<Transport[\"logBatch\"]>;\n };\n let lastContext: TransportContext | undefined;\n let replayPromise: Promise<void> | undefined;\n\n const replayContext: TransportContext = {\n loggerName: name,\n now: () => Date.now(),\n toEvent: recordToEvent,\n reportInternalError() {},\n };\n\n const contextForReplay = () => lastContext ?? replayContext;\n\n const reportInternalError = (error: unknown, operation: string) => {\n contextForReplay().reportInternalError(error, {\n phase: \"transport\",\n transport: name,\n operation,\n });\n };\n\n const queueEvent = async (event: LogEvent, context: TransportContext, error?: unknown) => {\n await queue.log(event, context);\n await queue.flush?.();\n incrementLoggerMetaCounter(\"transport.offline.queued\");\n options.onQueued?.(event, error);\n };\n\n const sendRemoteBatch = async (events: readonly LogEvent[], context: TransportContext) => {\n await reliableRemote.logBatch?.([...events], context);\n };\n\n const sendOrQueue = async (event: LogEvent, context: TransportContext) => {\n lastContext = context;\n if (!isBrowserOnline(options)) {\n await queueEvent(event, context);\n return;\n }\n\n try {\n await reliableRemote.log?.(event, context);\n } catch (error) {\n incrementLoggerMetaCounter(\"transport.offline.remote.failed\");\n await queueEvent(event, context, error);\n }\n };\n\n const replay = async () => {\n if (replayPromise) return replayPromise;\n replayPromise = (async () => {\n if (!isBrowserOnline(options)) return;\n const context = contextForReplay();\n await queue.flush?.();\n\n for (;;) {\n // oxlint-disable-next-line no-await-in-loop -- Replay reads the next durable batch after prior deletes settle.\n const batch = await collectBatch(queue, replayBatchSize);\n if (batch.length === 0) return;\n try {\n // oxlint-disable-next-line no-await-in-loop -- Replay removes durable entries only after delivery succeeds.\n await sendRemoteBatch(batch, context);\n // oxlint-disable-next-line no-await-in-loop -- Delete only the delivered ids before reading the next batch.\n await queue.remove(batch.map((event) => event.id));\n incrementLoggerMetaCounter(\"transport.offline.replayed\", batch.length);\n options.onReplayed?.(batch);\n } catch (error) {\n incrementLoggerMetaCounter(\"transport.offline.replay.failed\");\n reportInternalError(error, \"replay\");\n throw error;\n }\n }\n })().finally(() => {\n replayPromise = undefined;\n });\n return replayPromise;\n };\n\n const onOnline = () => {\n void replay().catch((error: unknown) => reportInternalError(error, \"online-replay\"));\n };\n\n if (options.replayOnOnline ?? true) {\n globalThis.addEventListener?.(\"online\", onOnline);\n }\n\n return {\n name,\n minLevel: options.minLevel ?? remote.minLevel,\n queue,\n write(record, context) {\n return sendOrQueue(context.toEvent(record), context);\n },\n writeBatch(records, context) {\n return Promise.all(\n records.map((record) => sendOrQueue(context.toEvent(record), context)),\n ).then(() => undefined);\n },\n log(event, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n return sendOrQueue(event, context);\n },\n logBatch(events, context) {\n return Promise.all(\n events.map((event) =>\n options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)\n ? undefined\n : sendOrQueue(event, context),\n ),\n ).then(() => undefined);\n },\n replay,\n async flush() {\n await replay();\n await reliableRemote.flush?.();\n await queue.flush?.();\n },\n flushSync() {\n reliableRemote.flushSync?.();\n },\n async close() {\n globalThis.removeEventListener?.(\"online\", onOnline);\n await replay();\n await reliableRemote.close?.();\n await queue.close?.();\n },\n async queuedCount() {\n await queue.flush?.();\n return queue.count?.();\n },\n async clearQueue() {\n await queue.clear?.();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"offline-first-transport.js","names":[],"sources":["../src/indexeddb-transport.ts","../src/offline-first-transport.ts"],"sourcesContent":["import {\n incrementLoggerMetaCounter,\n safeJsonCodec,\n safeJsonStringify,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n type TransportContext,\n} from \"@loggerjs/core\";\nimport type { BrowserHttpDropPolicy } from \"./http-transport\";\n\nexport interface IndexedDbLogEntry {\n id: string;\n sessionId?: string;\n seq: number;\n createdAt: number;\n level: number;\n levelName: string;\n logger: string;\n type?: string;\n byteLength: number;\n payload: string | Uint8Array;\n}\n\nexport interface IndexedDbTransportQueryOptions {\n from?: number;\n to?: number;\n sessionId?: string;\n minLevel?: LoggerLevel;\n logger?: string;\n type?: string;\n limit?: number;\n order?: \"asc\" | \"desc\";\n}\n\nexport type IndexedDbTransportDurability = \"default\" | \"strict\" | \"relaxed\";\n\nexport type IndexedDbStorageBucketDurability = \"strict\" | \"relaxed\";\n\nexport interface IndexedDbTransportSessionOptions {\n id?: string;\n getId?: (event: LogEvent) => string | undefined;\n contextKey?: string;\n}\n\nexport type IndexedDbTransportSession = string | false | IndexedDbTransportSessionOptions;\n\nexport interface IndexedDbLogSession {\n sessionId: string;\n firstSeen: number;\n lastSeen: number;\n count: number;\n byteLength: number;\n}\n\nexport interface IndexedDbTransportSessionQueryOptions {\n limit?: number;\n order?: \"asc\" | \"desc\";\n}\n\nexport interface IndexedDbLocalStorageSpillOptions {\n namespace?: string;\n maxEntries?: number;\n maxBytes?: number;\n maxOriginBytes?: number;\n minLevel?: LoggerLevel;\n drainOnCreate?: boolean;\n spillOnPageHide?: boolean;\n storage?: Storage;\n onDrop?: (event: LogEvent, reason: string) => void;\n}\n\nexport interface IndexedDbTransportStats {\n bufferDepth: number;\n maxBufferDepth: number;\n pendingFlush: boolean;\n enqueued: number;\n persisted: number;\n dropped: number;\n droppedByReason: Record<string, number>;\n persistedDropped: number;\n persistedDroppedByReason: Record<string, number>;\n flushes: number;\n flushErrors: number;\n lastFlushBatchSize: number;\n lastFlushDurationMs: number;\n prunes: number;\n pruneFallbacks: number;\n lastPruneDurationMs: number;\n queries: number;\n queryFallbacks: number;\n lastQueryDurationMs: number;\n databaseOpenCount: number;\n storageBucketFallbacks: number;\n transactionOptionFallbacks: number;\n errors: number;\n errorsByOperation: Record<string, number>;\n localStorageSpillWrites: number;\n localStorageSpillEntries: number;\n localStorageSpillDrains: number;\n localStorageSpillDrainedEntries: number;\n localStorageSpillDropped: number;\n localStorageSpillDroppedByReason: Record<string, number>;\n localStorageSpillErrors: number;\n localStorageSpillErrorsByOperation: Record<string, number>;\n}\n\nexport interface IndexedDbTransportOptions {\n name?: string;\n dbName?: string;\n storeName?: string;\n maxEntries?: number;\n maxBytes?: number;\n ttlMs?: number;\n batchSize?: number;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n dropPolicy?: BrowserHttpDropPolicy;\n flushOnPageHide?: boolean;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n durability?: IndexedDbTransportDurability;\n storageBucketName?: string;\n storageBucketPersisted?: boolean;\n storageBucketDurability?: IndexedDbStorageBucketDurability;\n session?: IndexedDbTransportSession;\n localStorageSpill?: false | IndexedDbLocalStorageSpillOptions;\n indexedDB?: IDBFactory;\n onDrop?: (event: LogEvent, reason: string) => void;\n onPersistedDrop?: (entry: IndexedDbLogEntry, reason: string) => void;\n}\n\nexport interface IndexedDbTransport extends Transport {\n count: () => Promise<number>;\n clear: () => Promise<void>;\n remove: (ids: string | readonly string[]) => Promise<void>;\n query: (options?: IndexedDbTransportQueryOptions) => AsyncIterable<LogEvent>;\n sessions: (options?: IndexedDbTransportSessionQueryOptions) => Promise<IndexedDbLogSession[]>;\n stats: () => IndexedDbTransportStats;\n}\n\nconst DEFAULT_DB_NAME = \"loggerjs\";\nconst DEFAULT_STORE_NAME = \"logs\";\nconst DEFAULT_SESSION_CONTEXT_KEY = \"sessionId\";\nconst DEFAULT_SPILL_MAX_ENTRIES = 200;\nconst DEFAULT_SPILL_MAX_BYTES = 512 * 1024;\nconst DEFAULT_SPILL_MAX_ORIGIN_BYTES = 5 * 1024 * 1024;\nconst DB_VERSION = 3;\nconst ORDER_INDEX_NAME = \"createdAtSeq\";\nconst SESSION_INDEX_NAME = \"sessionId\";\nconst SESSION_ORDER_INDEX_NAME = \"sessionIdCreatedAtSeq\";\nconst SPILL_SCHEMA = \"loggerjs.indexeddb-spill.v1\";\n\ninterface NormalizedSessionOptions {\n id?: string;\n getId?: (event: LogEvent) => string | undefined;\n contextKey: string;\n}\n\ninterface NormalizedLocalStorageSpillOptions {\n key: string;\n maxEntries: number;\n maxBytes: number;\n maxOriginBytes: number;\n minLevelValue?: number;\n drainOnCreate: boolean;\n spillOnPageHide: boolean;\n storage: Storage;\n onDrop?: (event: LogEvent, reason: string) => void;\n}\n\ninterface LocalStorageSpillPayload {\n schema: typeof SPILL_SCHEMA;\n createdAt: number;\n entries: readonly LogEvent[];\n}\n\ninterface LocalStorageSpillClearCandidate {\n value: string;\n pendingIds: Set<string>;\n}\n\ninterface StorageBucketLike {\n indexedDB?: IDBFactory;\n}\n\ninterface StorageBucketManagerLike {\n open: (\n name: string,\n options?: {\n durability?: IndexedDbStorageBucketDurability;\n persisted?: boolean;\n },\n ) => Promise<StorageBucketLike>;\n}\n\nfunction normalizePositiveInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value)) return fallback;\n return Math.max(0, Math.floor(value));\n}\n\nlet fallbackSessionSeq = 0;\n\nfunction defaultPageSessionId(): string {\n const randomUUID = globalThis.crypto?.randomUUID;\n if (randomUUID) return randomUUID.call(globalThis.crypto);\n return `${Date.now().toString(36)}-${(fallbackSessionSeq++).toString(36)}`;\n}\n\nfunction normalizeSessionId(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const normalized = value.trim();\n return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction normalizeSessionOptions(\n session: IndexedDbTransportSession | undefined,\n): NormalizedSessionOptions | undefined {\n if (session === false) return undefined;\n if (typeof session === \"string\") {\n return {\n contextKey: DEFAULT_SESSION_CONTEXT_KEY,\n id: normalizeSessionId(session) ?? defaultPageSessionId(),\n };\n }\n return {\n contextKey: session?.contextKey ?? DEFAULT_SESSION_CONTEXT_KEY,\n getId: session?.getId,\n id: normalizeSessionId(session?.id) ?? defaultPageSessionId(),\n };\n}\n\nfunction resolveLocalStorage(storage: Storage | undefined): Storage | undefined {\n if (storage) return storage;\n try {\n return globalThis.localStorage;\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeLocalStorageSpillOptions(\n spill: false | IndexedDbLocalStorageSpillOptions | undefined,\n dbName: string,\n storeName: string,\n): NormalizedLocalStorageSpillOptions | undefined {\n if (!spill) return undefined;\n const storage = resolveLocalStorage(spill.storage);\n if (!storage) return undefined;\n const namespace = spill.namespace ?? `${dbName}:${storeName}`;\n return {\n drainOnCreate: spill.drainOnCreate ?? true,\n key: `loggerjs:spill:v1:${namespace}`,\n maxBytes: normalizePositiveInteger(spill.maxBytes, DEFAULT_SPILL_MAX_BYTES),\n maxEntries: normalizePositiveInteger(spill.maxEntries, DEFAULT_SPILL_MAX_ENTRIES),\n maxOriginBytes: normalizePositiveInteger(spill.maxOriginBytes, DEFAULT_SPILL_MAX_ORIGIN_BYTES),\n minLevelValue: spill.minLevel === undefined ? undefined : toLevelValue(spill.minLevel),\n onDrop: spill.onDrop,\n spillOnPageHide: spill.spillOnPageHide ?? true,\n storage,\n };\n}\n\nfunction storageByteLength(key: string, value: string): number {\n return 2 * (key.length + value.length);\n}\n\nfunction estimateLocalStorageBytes(storage: Storage): number | undefined {\n try {\n let total = 0;\n for (let index = 0; index < storage.length; index += 1) {\n const key = storage.key(index);\n if (key === null) continue;\n total += storageByteLength(key, storage.getItem(key) ?? \"\");\n }\n return total;\n } catch {\n return undefined;\n }\n}\n\nfunction isQuotaExceededError(error: unknown): boolean {\n return (\n typeof DOMException !== \"undefined\" &&\n error instanceof DOMException &&\n (error.name === \"QuotaExceededError\" || error.name === \"NS_ERROR_DOM_QUOTA_REACHED\")\n );\n}\n\nfunction isSpillLogEvent(value: unknown): value is LogEvent {\n if (!value || typeof value !== \"object\") return false;\n const event = value as Partial<LogEvent>;\n return (\n typeof event.id === \"string\" &&\n typeof event.time === \"number\" &&\n typeof event.seq === \"number\" &&\n typeof event.level === \"number\" &&\n typeof event.levelName === \"string\" &&\n typeof event.logger === \"string\" &&\n typeof event.message === \"string\"\n );\n}\n\nfunction parseSpillPayload(raw: string): LogEvent[] | undefined {\n const parsed = JSON.parse(raw) as Partial<LocalStorageSpillPayload>;\n if (parsed.schema !== SPILL_SCHEMA || !Array.isArray(parsed.entries)) return undefined;\n return parsed.entries.filter(isSpillLogEvent);\n}\n\nfunction nowMs(): number {\n return typeof performance !== \"undefined\" && typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n}\n\nfunction incrementRecord(record: Record<string, number>, key: string, amount = 1): void {\n record[key] = (record[key] ?? 0) + amount;\n}\n\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.addEventListener(\"success\", () => resolve(request.result), { once: true });\n request.addEventListener(\n \"error\",\n () => reject(request.error ?? new Error(\"IndexedDB request failed\")),\n { once: true },\n );\n });\n}\n\nfunction transactionToPromise(transaction: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n transaction.addEventListener(\"complete\", () => resolve(), { once: true });\n transaction.addEventListener(\n \"abort\",\n () => reject(transaction.error ?? new Error(\"IndexedDB transaction aborted\")),\n { once: true },\n );\n transaction.addEventListener(\n \"error\",\n () => reject(transaction.error ?? new Error(\"IndexedDB transaction failed\")),\n { once: true },\n );\n });\n}\n\nfunction payloadByteLength(payload: string | Uint8Array): number {\n if (typeof payload !== \"string\") return payload.byteLength;\n if (typeof TextEncoder !== \"undefined\") return new TextEncoder().encode(payload).byteLength;\n return new Blob([payload]).size;\n}\n\nfunction compareEntries(a: IndexedDbLogEntry, b: IndexedDbLogEntry): number {\n if (a.createdAt !== b.createdAt) return a.createdAt - b.createdAt;\n if (a.seq !== b.seq) return a.seq - b.seq;\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n}\n\nfunction orderEntries(\n entries: readonly IndexedDbLogEntry[],\n order: \"asc\" | \"desc\" = \"asc\",\n): IndexedDbLogEntry[] {\n // oxlint-disable-next-line no-array-sort -- Sort a copy to preserve ES2020 compatibility and caller immutability.\n const sorted = [...entries].sort(compareEntries);\n if (order === \"asc\") return sorted;\n const reversed: IndexedDbLogEntry[] = [];\n for (let index = sorted.length - 1; index >= 0; index -= 1) {\n reversed.push(sorted[index] as IndexedDbLogEntry);\n }\n return reversed;\n}\n\nfunction indexExists(store: IDBObjectStore, name: string): boolean {\n return store.indexNames.contains(name);\n}\n\nfunction ensureIndex(store: IDBObjectStore, name: string, keyPath: string | readonly string[]) {\n if (!indexExists(store, name)) store.createIndex(name, keyPath);\n}\n\nfunction getStoreIndex(store: IDBObjectStore, name: string): IDBIndex | undefined {\n const maybeStore = store as IDBObjectStore & {\n index?: (indexName: string) => IDBIndex;\n };\n if (typeof maybeStore.index !== \"function\" || !indexExists(store, name)) return undefined;\n return maybeStore.index(name);\n}\n\nfunction getStorageBucketManager(): StorageBucketManagerLike | undefined {\n return (globalThis.navigator as unknown as { storageBuckets?: StorageBucketManagerLike })\n ?.storageBuckets;\n}\n\nfunction storageBucketOpenOptions(options: IndexedDbTransportOptions):\n | {\n durability?: IndexedDbStorageBucketDurability;\n persisted?: boolean;\n }\n | undefined {\n if (\n options.storageBucketDurability === undefined &&\n options.storageBucketPersisted === undefined\n ) {\n return undefined;\n }\n return {\n durability: options.storageBucketDurability,\n persisted: options.storageBucketPersisted,\n };\n}\n\nfunction decodeEntry(\n entry: IndexedDbLogEntry,\n codec: Codec<string | Uint8Array>,\n): LogEvent | undefined {\n if (!codec.decode) throw new Error(\"indexedDbTransport query requires a codec with decode\");\n const decoded = codec.decode(entry.payload);\n return Array.isArray(decoded) ? decoded[0] : decoded;\n}\n\nfunction shouldKeepEntry(entry: IndexedDbLogEntry, options: IndexedDbTransportQueryOptions) {\n if (options.from !== undefined && entry.createdAt < options.from) return false;\n if (options.to !== undefined && entry.createdAt > options.to) return false;\n if (options.sessionId !== undefined && entry.sessionId !== options.sessionId) return false;\n if (options.minLevel !== undefined && entry.level < toLevelValue(options.minLevel)) return false;\n if (options.logger !== undefined && entry.logger !== options.logger) return false;\n if (options.type !== undefined && entry.type !== options.type) return false;\n return true;\n}\n\nfunction orderRangeForQuery(options: IndexedDbTransportQueryOptions): IDBKeyRange | undefined {\n if (typeof IDBKeyRange === \"undefined\") return undefined;\n if (options.sessionId !== undefined) {\n const lower = [\n options.sessionId,\n options.from ?? Number.MIN_SAFE_INTEGER,\n Number.MIN_SAFE_INTEGER,\n \"\",\n ];\n const upper = [\n options.sessionId,\n options.to ?? Number.MAX_SAFE_INTEGER,\n Number.MAX_SAFE_INTEGER,\n \"\\uffff\",\n ];\n return IDBKeyRange.bound(lower, upper);\n }\n const lower =\n options.from === undefined ? undefined : [options.from, Number.MIN_SAFE_INTEGER, \"\"];\n const upper =\n options.to === undefined ? undefined : [options.to, Number.MAX_SAFE_INTEGER, \"\\uffff\"];\n if (lower && upper) return IDBKeyRange.bound(lower, upper);\n if (lower) return IDBKeyRange.lowerBound(lower);\n if (upper) return IDBKeyRange.upperBound(upper);\n return undefined;\n}\n\nfunction iterateCursor(\n request: IDBRequest<IDBCursorWithValue | null>,\n visit: (cursor: IDBCursorWithValue) => boolean | void,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n request.addEventListener(\n \"success\",\n () => {\n const cursor = request.result;\n if (!cursor) {\n resolve();\n return;\n }\n try {\n const shouldContinue = visit(cursor);\n if (shouldContinue === false) {\n resolve();\n return;\n }\n cursor.continue();\n } catch (error) {\n reject(error);\n }\n },\n false,\n );\n request.addEventListener(\n \"error\",\n () => reject(request.error ?? new Error(\"IndexedDB cursor failed\")),\n { once: true },\n );\n });\n}\n\nexport function indexedDbTransport(options: IndexedDbTransportOptions = {}): IndexedDbTransport {\n const dbName = options.dbName ?? DEFAULT_DB_NAME;\n const storeName = options.storeName ?? DEFAULT_STORE_NAME;\n const codec = options.codec ?? (safeJsonCodec() as Codec<string | Uint8Array>);\n const batchSize = normalizePositiveInteger(options.batchSize, 100);\n const flushIntervalMs = normalizePositiveInteger(options.flushIntervalMs, 1000);\n const maxBufferSize = normalizePositiveInteger(options.maxBufferSize, 1000);\n const maxEntries = normalizePositiveInteger(options.maxEntries, 50_000);\n const maxBytes = normalizePositiveInteger(options.maxBytes, 50 * 1024 * 1024);\n const dropPolicy = options.dropPolicy ?? \"drop-oldest\";\n const flushOnPageHide = options.flushOnPageHide ?? true;\n const durability = options.durability ?? \"default\";\n const rootIndexedDB = options.indexedDB ?? globalThis.indexedDB;\n const sessionOptions = normalizeSessionOptions(options.session);\n const spillOptions = normalizeLocalStorageSpillOptions(\n options.localStorageSpill,\n dbName,\n storeName,\n );\n const transactionOptions =\n durability === \"default\"\n ? undefined\n : ({\n durability,\n } satisfies IDBTransactionOptions);\n\n const buffer: LogEvent[] = [];\n let dbPromise: Promise<IDBDatabase> | undefined;\n let indexedDBFactoryPromise: Promise<IDBFactory | undefined> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let flushPromise: Promise<void> | undefined;\n let pendingFlushBatch: LogEvent[] | undefined;\n let spillDrainPromise: Promise<void> | undefined;\n let localStorageSpillClearCandidate: LocalStorageSpillClearCandidate | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n const statsState: IndexedDbTransportStats = {\n bufferDepth: 0,\n maxBufferDepth: 0,\n pendingFlush: false,\n enqueued: 0,\n persisted: 0,\n dropped: 0,\n droppedByReason: {},\n persistedDropped: 0,\n persistedDroppedByReason: {},\n flushes: 0,\n flushErrors: 0,\n lastFlushBatchSize: 0,\n lastFlushDurationMs: 0,\n prunes: 0,\n pruneFallbacks: 0,\n lastPruneDurationMs: 0,\n queries: 0,\n queryFallbacks: 0,\n lastQueryDurationMs: 0,\n databaseOpenCount: 0,\n storageBucketFallbacks: 0,\n transactionOptionFallbacks: 0,\n errors: 0,\n errorsByOperation: {},\n localStorageSpillWrites: 0,\n localStorageSpillEntries: 0,\n localStorageSpillDrains: 0,\n localStorageSpillDrainedEntries: 0,\n localStorageSpillDropped: 0,\n localStorageSpillDroppedByReason: {},\n localStorageSpillErrors: 0,\n localStorageSpillErrorsByOperation: {},\n };\n\n const snapshotStats = (): IndexedDbTransportStats => ({\n ...statsState,\n bufferDepth: buffer.length,\n droppedByReason: { ...statsState.droppedByReason },\n errorsByOperation: { ...statsState.errorsByOperation },\n localStorageSpillDroppedByReason: { ...statsState.localStorageSpillDroppedByReason },\n localStorageSpillErrorsByOperation: { ...statsState.localStorageSpillErrorsByOperation },\n pendingFlush: flushPromise !== undefined,\n persistedDroppedByReason: { ...statsState.persistedDroppedByReason },\n });\n\n const reportInternalError = (error: unknown, operation: string) => {\n statsState.errors += 1;\n incrementRecord(statsState.errorsByOperation, operation);\n lastContext?.reportInternalError(error, {\n operation,\n phase: \"transport\",\n transport: options.name ?? \"indexeddb\",\n });\n };\n\n const dropEvent = (event: LogEvent, reason: string) => {\n statsState.dropped += 1;\n incrementRecord(statsState.droppedByReason, reason);\n incrementLoggerMetaCounter(\"transport.indexeddb.dropped\");\n incrementLoggerMetaCounter(`transport.indexeddb.dropped.${reason}`);\n options.onDrop?.(event, reason);\n };\n\n const dropPersistedEntry = (entry: IndexedDbLogEntry, reason: string) => {\n statsState.persistedDropped += 1;\n incrementRecord(statsState.persistedDroppedByReason, reason);\n incrementLoggerMetaCounter(\"transport.indexeddb.persisted.dropped\");\n incrementLoggerMetaCounter(`transport.indexeddb.persisted.dropped.${reason}`);\n options.onPersistedDrop?.(entry, reason);\n };\n\n const reportSpillError = (error: unknown, operation: string) => {\n statsState.localStorageSpillErrors += 1;\n incrementRecord(statsState.localStorageSpillErrorsByOperation, operation);\n reportInternalError(error, `localstorage-spill-${operation}`);\n };\n\n const dropSpillEvent = (event: LogEvent, reason: string) => {\n statsState.localStorageSpillDropped += 1;\n incrementRecord(statsState.localStorageSpillDroppedByReason, reason);\n incrementLoggerMetaCounter(\"transport.indexeddb.localstorage_spill.dropped\");\n incrementLoggerMetaCounter(`transport.indexeddb.localstorage_spill.dropped.${reason}`);\n spillOptions?.onDrop?.(event, reason);\n };\n\n const resolveIndexedDBFactory = async () => {\n if (options.indexedDB || !options.storageBucketName) return rootIndexedDB;\n indexedDBFactoryPromise ??= (async () => {\n const manager = getStorageBucketManager();\n if (!manager) {\n statsState.storageBucketFallbacks += 1;\n return rootIndexedDB;\n }\n try {\n const bucket = await manager.open(\n options.storageBucketName as string,\n storageBucketOpenOptions(options),\n );\n if (bucket.indexedDB) return bucket.indexedDB;\n statsState.storageBucketFallbacks += 1;\n return rootIndexedDB;\n } catch (error) {\n statsState.storageBucketFallbacks += 1;\n reportInternalError(error, \"storage-bucket-open\");\n return rootIndexedDB;\n }\n })();\n return indexedDBFactoryPromise;\n };\n\n const openDb = () => {\n dbPromise ??= resolveIndexedDBFactory().then((idb) => {\n if (!idb) throw new Error(\"IndexedDB is not available for indexedDbTransport\");\n return new Promise<IDBDatabase>((resolve, reject) => {\n const request = idb.open(dbName, DB_VERSION);\n request.addEventListener(\"upgradeneeded\", () => {\n const db = request.result;\n const store = db.objectStoreNames.contains(storeName)\n ? request.transaction?.objectStore(storeName)\n : db.createObjectStore(storeName, { keyPath: \"id\" });\n if (!store) return;\n ensureIndex(store, \"createdAt\", \"createdAt\");\n ensureIndex(store, \"level\", \"level\");\n ensureIndex(store, \"logger\", \"logger\");\n ensureIndex(store, \"type\", \"type\");\n ensureIndex(store, ORDER_INDEX_NAME, [\"createdAt\", \"seq\", \"id\"]);\n ensureIndex(store, SESSION_INDEX_NAME, \"sessionId\");\n ensureIndex(store, SESSION_ORDER_INDEX_NAME, [\"sessionId\", \"createdAt\", \"seq\", \"id\"]);\n });\n request.addEventListener(\n \"success\",\n () => {\n statsState.databaseOpenCount += 1;\n resolve(request.result);\n },\n { once: true },\n );\n request.addEventListener(\n \"error\",\n () => reject(request.error ?? new Error(\"IndexedDB open failed\")),\n { once: true },\n );\n });\n });\n return dbPromise;\n };\n\n const createTransaction = (db: IDBDatabase, mode: IDBTransactionMode) => {\n if (mode !== \"readwrite\" || !transactionOptions) return db.transaction(storeName, mode);\n try {\n return db.transaction(storeName, mode, transactionOptions);\n } catch {\n statsState.transactionOptionFallbacks += 1;\n return db.transaction(storeName, mode);\n }\n };\n\n const withStore = async <T>(\n mode: IDBTransactionMode,\n run: (store: IDBObjectStore, transaction: IDBTransaction) => T | Promise<T>,\n ): Promise<T> => {\n const db = await openDb();\n const tx = createTransaction(db, mode);\n const settled = transactionToPromise(tx);\n try {\n const result = await run(tx.objectStore(storeName), tx);\n await settled;\n return result;\n } catch (error) {\n if (tx.error === null) {\n try {\n tx.abort();\n } catch {\n // The transaction may already be committed or aborted.\n }\n }\n throw error;\n }\n };\n\n const getEntries = async () =>\n orderEntries(\n await withStore(\"readonly\", (store) =>\n requestToPromise(store.getAll() as IDBRequest<IndexedDbLogEntry[]>),\n ),\n );\n\n const countEntries = async (): Promise<number | undefined> =>\n withStore(\"readonly\", (store) => {\n const maybeStore = store as IDBObjectStore & {\n count?: () => IDBRequest<number>;\n };\n if (typeof maybeStore.count !== \"function\") return undefined;\n return requestToPromise(maybeStore.count.call(store));\n });\n\n const deleteEntries = async (entries: readonly IndexedDbLogEntry[], reason: string) => {\n if (entries.length === 0) return;\n await withStore(\"readwrite\", async (store) => {\n for (const entry of entries) {\n // oxlint-disable-next-line no-await-in-loop -- Deletes are sequenced for fake IDB compatibility.\n await requestToPromise(store.delete(entry.id));\n dropPersistedEntry(entry, reason);\n }\n });\n };\n\n const deleteByCursor = async (\n reason: string,\n openCursor: (store: IDBObjectStore) => IDBRequest<IDBCursorWithValue | null> | undefined,\n shouldDelete: (entry: IndexedDbLogEntry, deleted: number) => boolean,\n ): Promise<number | undefined> =>\n withStore(\"readwrite\", async (store) => {\n const request = openCursor(store);\n if (!request) return undefined;\n let deleted = 0;\n await iterateCursor(request, (cursor) => {\n const entry = cursor.value as IndexedDbLogEntry;\n if (!shouldDelete(entry, deleted)) return false;\n cursor.delete();\n deleted += 1;\n dropPersistedEntry(entry, reason);\n return true;\n });\n return deleted;\n });\n\n const deleteExpiredByCursor = async (cutoff: number): Promise<number | undefined> => {\n if (typeof IDBKeyRange === \"undefined\") return undefined;\n const range = IDBKeyRange.upperBound(cutoff, true);\n return deleteByCursor(\n \"ttl\",\n (store) => getStoreIndex(store, \"createdAt\")?.openCursor(range),\n () => true,\n );\n };\n\n const deleteOldestByCursor = async (\n reason: string,\n count: number,\n ): Promise<number | undefined> => {\n if (count <= 0) return 0;\n return deleteByCursor(\n reason,\n (store) => getStoreIndex(store, ORDER_INDEX_NAME)?.openCursor(),\n (_entry, deleted) => deleted < count,\n );\n };\n\n const deleteOldestBytesByCursor = async (bytesToFree: number): Promise<number | undefined> => {\n if (bytesToFree <= 0) return 0;\n let freed = 0;\n return deleteByCursor(\n \"max-bytes\",\n (store) => getStoreIndex(store, ORDER_INDEX_NAME)?.openCursor(),\n (entry) => {\n if (freed >= bytesToFree) return false;\n freed += entry.byteLength;\n return true;\n },\n );\n };\n\n const sumBytesByCursor = async (): Promise<number | undefined> =>\n withStore(\"readonly\", async (store) => {\n const index = getStoreIndex(store, ORDER_INDEX_NAME);\n if (!index) return undefined;\n let total = 0;\n await iterateCursor(index.openCursor(), (cursor) => {\n total += (cursor.value as IndexedDbLogEntry).byteLength;\n });\n return total;\n });\n\n const queryEntriesByCursor = async (\n queryOptions: IndexedDbTransportQueryOptions,\n ): Promise<IndexedDbLogEntry[] | undefined> =>\n withStore(\"readonly\", async (store) => {\n const index = getStoreIndex(\n store,\n queryOptions.sessionId === undefined ? ORDER_INDEX_NAME : SESSION_ORDER_INDEX_NAME,\n );\n if (!index) return undefined;\n const entries: IndexedDbLogEntry[] = [];\n const limit = queryOptions.limit;\n if (limit !== undefined && limit <= 0) return entries;\n await iterateCursor(\n index.openCursor(\n orderRangeForQuery(queryOptions),\n queryOptions.order === \"desc\" ? \"prev\" : \"next\",\n ),\n (cursor) => {\n const entry = cursor.value as IndexedDbLogEntry;\n if (!shouldKeepEntry(entry, queryOptions)) return true;\n entries.push(entry);\n return limit === undefined || entries.length < limit;\n },\n );\n return entries;\n });\n\n const listSessions = async (\n sessionQueryOptions: IndexedDbTransportSessionQueryOptions = {},\n ): Promise<IndexedDbLogSession[]> => {\n const sessions = new Map<string, IndexedDbLogSession>();\n for (const entry of await getEntries()) {\n if (!entry.sessionId) continue;\n const current = sessions.get(entry.sessionId);\n if (current) {\n current.firstSeen = Math.min(current.firstSeen, entry.createdAt);\n current.lastSeen = Math.max(current.lastSeen, entry.createdAt);\n current.count += 1;\n current.byteLength += entry.byteLength;\n } else {\n sessions.set(entry.sessionId, {\n byteLength: entry.byteLength,\n count: 1,\n firstSeen: entry.createdAt,\n lastSeen: entry.createdAt,\n sessionId: entry.sessionId,\n });\n }\n }\n\n // oxlint-disable-next-line no-array-sort -- Sort a copy for deterministic session lists.\n const ordered = [...sessions.values()].sort((left, right) => {\n if (left.lastSeen !== right.lastSeen) return left.lastSeen - right.lastSeen;\n return left.sessionId < right.sessionId ? -1 : left.sessionId > right.sessionId ? 1 : 0;\n });\n if (sessionQueryOptions.order !== \"asc\") ordered.reverse();\n return ordered.slice(0, sessionQueryOptions.limit);\n };\n\n const pruneWithEntries = async () => {\n const now = Date.now();\n let entries = await getEntries();\n const expired =\n options.ttlMs === undefined\n ? []\n : entries.filter((entry) => now - entry.createdAt > (options.ttlMs as number));\n if (expired.length > 0) {\n await deleteEntries(expired, \"ttl\");\n const expiredIds = new Set(expired.map((entry) => entry.id));\n entries = entries.filter((entry) => !expiredIds.has(entry.id));\n }\n\n if (maxEntries >= 0 && entries.length > maxEntries) {\n const dropped = entries.slice(0, entries.length - maxEntries);\n await deleteEntries(dropped, \"max-entries\");\n entries = entries.slice(entries.length - maxEntries);\n }\n\n if (maxBytes >= 0) {\n let total = entries.reduce((sum, entry) => sum + entry.byteLength, 0);\n const dropped: IndexedDbLogEntry[] = [];\n for (const entry of entries) {\n if (total <= maxBytes) break;\n total -= entry.byteLength;\n dropped.push(entry);\n }\n await deleteEntries(dropped, \"max-bytes\");\n }\n };\n\n const pruneWithCursors = async (): Promise<boolean> => {\n if (options.ttlMs !== undefined) {\n const deleted = await deleteExpiredByCursor(Date.now() - options.ttlMs);\n if (deleted === undefined) return false;\n }\n\n if (maxEntries >= 0) {\n const persistedCount = await countEntries();\n if (persistedCount === undefined) return false;\n if (persistedCount > maxEntries) {\n const deleted = await deleteOldestByCursor(\"max-entries\", persistedCount - maxEntries);\n if (deleted === undefined) return false;\n }\n }\n\n if (maxBytes >= 0) {\n const totalBytes = await sumBytesByCursor();\n if (totalBytes === undefined) return false;\n if (totalBytes > maxBytes) {\n const deleted = await deleteOldestBytesByCursor(totalBytes - maxBytes);\n if (deleted === undefined) return false;\n }\n }\n\n return true;\n };\n\n const prune = async () => {\n const startedAt = nowMs();\n try {\n if (await pruneWithCursors()) return;\n statsState.pruneFallbacks += 1;\n await pruneWithEntries();\n } finally {\n statsState.prunes += 1;\n statsState.lastPruneDurationMs = nowMs() - startedAt;\n }\n };\n\n const sessionIdForEvent = (event: LogEvent): string | undefined => {\n if (!sessionOptions) return undefined;\n return (\n normalizeSessionId(sessionOptions.getId?.(event)) ??\n normalizeSessionId(event.context?.[sessionOptions.contextKey]) ??\n sessionOptions.id\n );\n };\n\n const eventWithSessionContext = (event: LogEvent, sessionId: string | undefined): LogEvent => {\n if (!sessionId || !sessionOptions?.contextKey) return event;\n if (event.context?.[sessionOptions.contextKey] === sessionId) return event;\n return {\n ...event,\n context: {\n ...event.context,\n [sessionOptions.contextKey]: sessionId,\n },\n };\n };\n\n const eventWithResolvedSession = (\n event: LogEvent,\n ): {\n event: LogEvent;\n sessionId?: string;\n } => {\n const sessionId = sessionIdForEvent(event);\n return {\n event: eventWithSessionContext(event, sessionId),\n sessionId,\n };\n };\n\n const eventToEntry = (event: LogEvent): IndexedDbLogEntry => {\n const resolved = eventWithResolvedSession(event);\n const payload = codec.encode(resolved.event);\n return {\n id: event.id,\n sessionId: resolved.sessionId,\n seq: event.seq,\n createdAt: event.time,\n level: event.level,\n levelName: event.levelName,\n logger: event.logger,\n type: event.type,\n byteLength: payloadByteLength(payload),\n payload,\n };\n };\n\n const clearLocalStorageSpill = (expectedValue?: string): boolean => {\n if (!spillOptions) return false;\n try {\n if (\n expectedValue !== undefined &&\n spillOptions.storage.getItem(spillOptions.key) !== expectedValue\n ) {\n return false;\n }\n spillOptions.storage.removeItem(spillOptions.key);\n if (expectedValue === undefined || localStorageSpillClearCandidate?.value === expectedValue) {\n localStorageSpillClearCandidate = undefined;\n }\n return true;\n } catch (error) {\n reportSpillError(error, \"clear\");\n return false;\n }\n };\n\n const rememberLocalStorageSpill = (value: string, events: readonly LogEvent[]) => {\n localStorageSpillClearCandidate = {\n pendingIds: new Set(events.map((event) => event.id)),\n value,\n };\n };\n\n const markLocalStorageSpillPersisted = (events: readonly LogEvent[]) => {\n const candidate = localStorageSpillClearCandidate;\n if (!candidate || events.length === 0) return;\n for (const event of events) candidate.pendingIds.delete(event.id);\n if (candidate.pendingIds.size === 0) clearLocalStorageSpill(candidate.value);\n };\n\n const writeBatch = async (events: readonly LogEvent[]) => {\n if (events.length === 0) return;\n const startedAt = nowMs();\n statsState.lastFlushBatchSize = events.length;\n const entries = events.map(eventToEntry);\n try {\n await withStore(\"readwrite\", (store) => {\n for (const entry of entries) {\n store.put(entry);\n }\n });\n statsState.persisted += entries.length;\n incrementLoggerMetaCounter(\"transport.indexeddb.persisted\", entries.length);\n markLocalStorageSpillPersisted(events);\n await prune();\n statsState.flushes += 1;\n } catch (error) {\n statsState.flushErrors += 1;\n throw error;\n } finally {\n statsState.lastFlushDurationMs = nowMs() - startedAt;\n }\n };\n\n const stringifySpillPayload = (events: readonly LogEvent[]): string =>\n safeJsonStringify(\n {\n createdAt: Date.now(),\n entries: events,\n schema: SPILL_SCHEMA,\n } satisfies LocalStorageSpillPayload,\n { maxArrayLength: spillOptions?.maxEntries ?? DEFAULT_SPILL_MAX_ENTRIES },\n );\n\n const spillStorageBudget = (currentValue: string | null): number => {\n if (!spillOptions) return 0;\n const ownBytes = currentValue ? storageByteLength(spillOptions.key, currentValue) : 0;\n const originBytes = estimateLocalStorageBytes(spillOptions.storage);\n if (originBytes === undefined) return spillOptions.maxBytes;\n return Math.max(\n 0,\n Math.min(spillOptions.maxBytes, spillOptions.maxOriginBytes - originBytes + ownBytes),\n );\n };\n\n const pendingEventsForSpill = (): LogEvent[] => {\n const events = [...(pendingFlushBatch ?? []), ...buffer];\n const seen = new Set<string>();\n const deduped: LogEvent[] = [];\n for (const event of events) {\n if (seen.has(event.id)) continue;\n seen.add(event.id);\n if (spillOptions?.minLevelValue !== undefined && event.level < spillOptions.minLevelValue) {\n continue;\n }\n deduped.push(eventWithResolvedSession(event).event);\n }\n return deduped;\n };\n\n const trimSpillEventsToBudget = (events: LogEvent[], budget: number): string | undefined => {\n if (!spillOptions) return undefined;\n while (events.length > spillOptions.maxEntries) {\n const dropped = events.shift();\n if (dropped) dropSpillEvent(dropped, \"max-entries\");\n }\n while (events.length > 0) {\n const value = stringifySpillPayload(events);\n if (storageByteLength(spillOptions.key, value) <= budget) return value;\n const dropped = events.shift();\n if (dropped) dropSpillEvent(dropped, \"max-bytes\");\n }\n return undefined;\n };\n\n const writeLocalStorageSpill = () => {\n if (!spillOptions) return;\n const pendingEvents = pendingEventsForSpill();\n if (pendingEvents.length === 0) return;\n\n try {\n const currentValue = spillOptions.storage.getItem(spillOptions.key);\n let events = pendingEvents;\n if (currentValue) {\n const merged = new Map<string, LogEvent>();\n try {\n for (const event of parseSpillPayload(currentValue) ?? []) merged.set(event.id, event);\n } catch {\n // Ignore malformed previous spill data; the current page tail is still worth saving.\n }\n for (const event of pendingEvents) merged.set(event.id, event);\n events = [...merged.values()];\n }\n const budget = spillStorageBudget(currentValue);\n const value = trimSpillEventsToBudget(events, budget);\n if (!value) return;\n try {\n spillOptions.storage.setItem(spillOptions.key, value);\n } catch (error) {\n if (!isQuotaExceededError(error)) throw error;\n while (events.length > 0) {\n const dropped = events.shift();\n if (dropped) dropSpillEvent(dropped, \"quota\");\n const retryValue = trimSpillEventsToBudget(events, budget);\n if (!retryValue) return;\n try {\n spillOptions.storage.setItem(spillOptions.key, retryValue);\n rememberLocalStorageSpill(retryValue, events);\n statsState.localStorageSpillWrites += 1;\n statsState.localStorageSpillEntries += events.length;\n return;\n } catch (retryError) {\n if (!isQuotaExceededError(retryError)) throw retryError;\n }\n }\n return;\n }\n rememberLocalStorageSpill(value, events);\n statsState.localStorageSpillWrites += 1;\n statsState.localStorageSpillEntries += events.length;\n } catch (error) {\n reportSpillError(error, \"write\");\n }\n };\n\n const drainLocalStorageSpill = async () => {\n if (!spillOptions) return;\n let raw: string | null;\n try {\n raw = spillOptions.storage.getItem(spillOptions.key);\n } catch (error) {\n reportSpillError(error, \"read\");\n return;\n }\n if (!raw) return;\n\n let events: LogEvent[] | undefined;\n try {\n events = parseSpillPayload(raw);\n } catch (error) {\n reportSpillError(error, \"parse\");\n clearLocalStorageSpill();\n return;\n }\n if (!events || events.length === 0) {\n clearLocalStorageSpill();\n return;\n }\n\n await writeBatch(events);\n clearLocalStorageSpill(raw);\n statsState.localStorageSpillDrains += 1;\n statsState.localStorageSpillDrainedEntries += events.length;\n };\n\n const ensureSpillDrained = async () => {\n const promise = spillDrainPromise;\n if (!promise) return;\n await promise;\n };\n\n if (spillOptions?.drainOnCreate) {\n spillDrainPromise = drainLocalStorageSpill()\n .catch((error: unknown) => reportSpillError(error, \"drain\"))\n .finally(() => {\n spillDrainPromise = undefined;\n });\n }\n\n const clearTimer = () => {\n if (timer) clearTimeout(timer);\n timer = undefined;\n };\n\n const flushPending = async () => {\n if (flushPromise) return flushPromise;\n clearTimer();\n const batch = buffer.splice(0, buffer.length);\n pendingFlushBatch = batch;\n flushPromise = (async () => {\n await ensureSpillDrained();\n await writeBatch(batch);\n })().finally(() => {\n if (pendingFlushBatch === batch) pendingFlushBatch = undefined;\n flushPromise = undefined;\n if (buffer.length > 0 && !closed) schedule();\n });\n return flushPromise;\n };\n\n const schedule = () => {\n if (timer || flushIntervalMs <= 0 || closed) return;\n timer = setTimeout(() => {\n void flushPending().catch((error: unknown) => reportInternalError(error, \"flush\"));\n }, flushIntervalMs);\n };\n\n const onPageHide = () => {\n if (spillOptions?.spillOnPageHide) writeLocalStorageSpill();\n if (flushOnPageHide) {\n void flushPending().catch((error: unknown) => reportInternalError(error, \"pagehide-flush\"));\n }\n };\n\n const onVisibilityChange = () => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") {\n if (spillOptions?.spillOnPageHide) writeLocalStorageSpill();\n if (flushOnPageHide) {\n void flushPending().catch((error: unknown) =>\n reportInternalError(error, \"visibility-flush\"),\n );\n }\n }\n };\n\n const addVisibilityChangeListener = (): (() => void) | undefined => {\n if (typeof document !== \"undefined\" && typeof document.addEventListener === \"function\") {\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n return () => document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n }\n if (typeof globalThis.addEventListener !== \"function\") return undefined;\n globalThis.addEventListener(\"visibilitychange\", onVisibilityChange);\n return () => globalThis.removeEventListener?.(\"visibilitychange\", onVisibilityChange);\n };\n\n let removeVisibilityChangeListener: (() => void) | undefined;\n if (flushOnPageHide || spillOptions?.spillOnPageHide) {\n globalThis.addEventListener?.(\"pagehide\", onPageHide);\n removeVisibilityChangeListener = addVisibilityChangeListener();\n }\n\n return {\n name: options.name ?? \"indexeddb\",\n minLevel: options.minLevel,\n log(event, context) {\n if (closed) return;\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n lastContext = context;\n if (maxBufferSize === 0 || buffer.length >= maxBufferSize) {\n if (dropPolicy === \"drop-newest\") {\n dropEvent(event, \"buffer-full\");\n return;\n }\n const dropped = buffer.shift();\n if (dropped) dropEvent(dropped, \"buffer-full\");\n }\n buffer.push(event);\n statsState.enqueued += 1;\n statsState.maxBufferDepth = Math.max(statsState.maxBufferDepth, buffer.length);\n if (buffer.length >= batchSize || flushIntervalMs === 0) {\n void flushPending().catch((error: unknown) => reportInternalError(error, \"flush\"));\n } else {\n schedule();\n }\n },\n flush() {\n return flushPending();\n },\n async count() {\n await flushPending();\n return (await countEntries()) ?? (await getEntries()).length;\n },\n async clear() {\n buffer.length = 0;\n clearTimer();\n clearLocalStorageSpill();\n await withStore(\"readwrite\", (store) => requestToPromise(store.clear()));\n },\n async remove(ids) {\n await flushPending();\n const items = typeof ids === \"string\" ? [ids] : ids;\n if (items.length === 0) return;\n await withStore(\"readwrite\", async (store) => {\n for (const id of items) {\n // oxlint-disable-next-line no-await-in-loop -- Deletes must preserve fake IDB transaction ordering.\n await requestToPromise(store.delete(id));\n }\n });\n },\n async *query(queryOptions: IndexedDbTransportQueryOptions = {}) {\n await flushPending();\n const startedAt = nowMs();\n const cursorEntries = await queryEntriesByCursor(queryOptions);\n if (cursorEntries === undefined) statsState.queryFallbacks += 1;\n const entries =\n cursorEntries ??\n orderEntries(await getEntries(), queryOptions.order ?? \"asc\")\n .filter((entry) => shouldKeepEntry(entry, queryOptions))\n .slice(0, queryOptions.limit);\n statsState.queries += 1;\n statsState.lastQueryDurationMs = nowMs() - startedAt;\n for (const entry of entries) {\n const event = decodeEntry(entry, codec);\n if (event) yield event;\n }\n },\n async sessions(sessionQueryOptions: IndexedDbTransportSessionQueryOptions = {}) {\n await flushPending();\n return listSessions(sessionQueryOptions);\n },\n stats() {\n return snapshotStats();\n },\n close() {\n closed = true;\n clearTimer();\n globalThis.removeEventListener?.(\"pagehide\", onPageHide);\n removeVisibilityChangeListener?.();\n removeVisibilityChangeListener = undefined;\n const flushed = flushPending();\n return flushed.finally(() => {\n const promise = dbPromise;\n dbPromise = undefined;\n return promise?.then((db) => db.close());\n });\n },\n };\n}\n","import {\n incrementLoggerMetaCounter,\n recordToEvent,\n retryTransport,\n toLevelValue,\n type LogEvent,\n type LoggerLevel,\n type RetryTransportOptions,\n type Transport,\n type TransportContext,\n} from \"@loggerjs/core\";\nimport {\n indexedDbTransport,\n type IndexedDbTransportOptions,\n type IndexedDbTransportQueryOptions,\n} from \"./indexeddb-transport\";\n\nexport interface OfflineFirstQueue {\n log: (event: LogEvent, context: TransportContext) => void | Promise<void>;\n flush?: () => void | Promise<void>;\n query: (options?: IndexedDbTransportQueryOptions) => AsyncIterable<LogEvent>;\n remove: (ids: string | readonly string[]) => Promise<void>;\n count?: () => Promise<number>;\n clear?: () => Promise<void>;\n close?: () => void | Promise<void>;\n}\n\nexport interface OfflineFirstTransportOptions {\n name?: string;\n minLevel?: LoggerLevel;\n queue?: OfflineFirstQueue;\n queueOptions?: IndexedDbTransportOptions;\n replayBatchSize?: number;\n replayOnOnline?: boolean;\n online?: () => boolean;\n retry?: RetryTransportOptions;\n onQueued?: (event: LogEvent, error?: unknown) => void;\n onReplayed?: (events: readonly LogEvent[]) => void;\n}\n\nexport interface OfflineFirstTransport extends Transport {\n replay: () => Promise<void>;\n queuedCount: () => Promise<number | undefined>;\n clearQueue: () => Promise<void>;\n queue: OfflineFirstQueue;\n}\n\nfunction normalizeBatchSize(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 100;\n return Math.floor(value);\n}\n\nasync function collectBatch(\n queue: OfflineFirstQueue,\n replayBatchSize: number,\n): Promise<LogEvent[]> {\n const events: LogEvent[] = [];\n for await (const event of queue.query({ order: \"asc\", limit: replayBatchSize })) {\n events.push(event);\n }\n return events;\n}\n\nfunction isBrowserOnline(options: OfflineFirstTransportOptions): boolean {\n if (options.online) return options.online();\n if (typeof navigator === \"undefined\") return true;\n return navigator.onLine !== false;\n}\n\nexport function offlineFirstTransport(\n remote: Transport,\n options: OfflineFirstTransportOptions = {},\n): OfflineFirstTransport {\n const name = options.name ?? `offline-first(${remote.name ?? \"transport\"})`;\n const replayBatchSize = normalizeBatchSize(options.replayBatchSize);\n const queueOptions = options.queueOptions;\n const queue: OfflineFirstQueue =\n options.queue ??\n (indexedDbTransport({\n dbName: \"loggerjs-offline\",\n flushIntervalMs: 0,\n name: `${name}:queue`,\n storeName: \"offline-events\",\n ...queueOptions,\n session: queueOptions?.session ?? false,\n }) as OfflineFirstQueue);\n const reliableRemote = retryTransport(remote, {\n maxRetries: 2,\n retryBaseDelayMs: 250,\n retryMaxDelayMs: 5000,\n ...options.retry,\n name: `${name}:remote`,\n }) as Transport & {\n log: NonNullable<Transport[\"log\"]>;\n logBatch: NonNullable<Transport[\"logBatch\"]>;\n };\n let lastContext: TransportContext | undefined;\n let replayPromise: Promise<void> | undefined;\n\n const replayContext: TransportContext = {\n loggerName: name,\n now: () => Date.now(),\n toEvent: recordToEvent,\n reportInternalError() {},\n };\n\n const contextForReplay = () => lastContext ?? replayContext;\n\n const reportInternalError = (error: unknown, operation: string) => {\n contextForReplay().reportInternalError(error, {\n phase: \"transport\",\n transport: name,\n operation,\n });\n };\n\n const queueEvent = async (event: LogEvent, context: TransportContext, error?: unknown) => {\n await queue.log(event, context);\n await queue.flush?.();\n incrementLoggerMetaCounter(\"transport.offline.queued\");\n options.onQueued?.(event, error);\n };\n\n const sendRemoteBatch = async (events: readonly LogEvent[], context: TransportContext) => {\n await reliableRemote.logBatch?.([...events], context);\n };\n\n const sendOrQueue = async (event: LogEvent, context: TransportContext) => {\n lastContext = context;\n if (!isBrowserOnline(options)) {\n await queueEvent(event, context);\n return;\n }\n\n try {\n await reliableRemote.log?.(event, context);\n } catch (error) {\n incrementLoggerMetaCounter(\"transport.offline.remote.failed\");\n await queueEvent(event, context, error);\n }\n };\n\n const replay = async () => {\n if (replayPromise) return replayPromise;\n replayPromise = (async () => {\n if (!isBrowserOnline(options)) return;\n const context = contextForReplay();\n await queue.flush?.();\n\n for (;;) {\n // oxlint-disable-next-line no-await-in-loop -- Replay reads the next durable batch after prior deletes settle.\n const batch = await collectBatch(queue, replayBatchSize);\n if (batch.length === 0) return;\n try {\n // oxlint-disable-next-line no-await-in-loop -- Replay removes durable entries only after delivery succeeds.\n await sendRemoteBatch(batch, context);\n // oxlint-disable-next-line no-await-in-loop -- Delete only the delivered ids before reading the next batch.\n await queue.remove(batch.map((event) => event.id));\n incrementLoggerMetaCounter(\"transport.offline.replayed\", batch.length);\n options.onReplayed?.(batch);\n } catch (error) {\n incrementLoggerMetaCounter(\"transport.offline.replay.failed\");\n reportInternalError(error, \"replay\");\n throw error;\n }\n }\n })().finally(() => {\n replayPromise = undefined;\n });\n return replayPromise;\n };\n\n const onOnline = () => {\n void replay().catch((error: unknown) => reportInternalError(error, \"online-replay\"));\n };\n\n if (options.replayOnOnline ?? true) {\n globalThis.addEventListener?.(\"online\", onOnline);\n }\n\n return {\n name,\n minLevel: options.minLevel ?? remote.minLevel,\n queue,\n write(record, context) {\n return sendOrQueue(context.toEvent(record), context);\n },\n writeBatch(records, context) {\n return Promise.all(\n records.map((record) => sendOrQueue(context.toEvent(record), context)),\n ).then(() => undefined);\n },\n log(event, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n return sendOrQueue(event, context);\n },\n logBatch(events, context) {\n return Promise.all(\n events.map((event) =>\n options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)\n ? undefined\n : sendOrQueue(event, context),\n ),\n ).then(() => undefined);\n },\n replay,\n async flush() {\n await replay();\n await reliableRemote.flush?.();\n await queue.flush?.();\n },\n flushSync() {\n reliableRemote.flushSync?.();\n },\n async close() {\n globalThis.removeEventListener?.(\"online\", onOnline);\n await replay();\n await reliableRemote.close?.();\n await queue.close?.();\n },\n async queuedCount() {\n await queue.flush?.();\n return queue.count?.();\n },\n async clearQueue() {\n await queue.clear?.();\n },\n };\n}\n"],"mappings":"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"}
@@ -9,6 +9,7 @@ const ZIP_MAX_UINT32 = 4294967295;
9
9
  const LOCAL_FILE_HEADER_SIGNATURE = 67324752;
10
10
  const CENTRAL_DIRECTORY_SIGNATURE = 33639248;
11
11
  const END_OF_CENTRAL_DIRECTORY_SIGNATURE = 101010256;
12
+ const DEFAULT_SESSION_CONTEXT_KEY = "sessionId";
12
13
  let crc32Table;
13
14
  function getCrc32Table() {
14
15
  if (crc32Table) return crc32Table;
@@ -148,6 +149,56 @@ function normalizeMaxEvents(value) {
148
149
  if (!Number.isFinite(value)) return void 0;
149
150
  return Math.max(0, Math.floor(value));
150
151
  }
152
+ function normalizeSessionExportOptions(value, logFileName) {
153
+ if (!value) return void 0;
154
+ const options = value === true ? {} : value;
155
+ return {
156
+ contextKey: options.contextKey ?? DEFAULT_SESSION_CONTEXT_KEY,
157
+ directory: options.directory ?? "sessions",
158
+ fallbackSessionId: options.fallbackSessionId ?? "unknown",
159
+ logFileName: options.logFileName ?? logFileName
160
+ };
161
+ }
162
+ function normalizeRecentExportOptions(value, format) {
163
+ if (!value) return void 0;
164
+ const options = value === true ? {} : value;
165
+ return {
166
+ logFileName: options.logFileName ?? (format === "json" ? "recent.json" : "recent.ndjson"),
167
+ maxEvents: normalizeMaxEvents(options.maxEvents) ?? 100
168
+ };
169
+ }
170
+ function logContentForLines(lines, format) {
171
+ if (format === "json") return `[${lines.join(",")}]`;
172
+ return lines.length > 0 ? `${lines.join("\n")}\n` : "";
173
+ }
174
+ function eventTimeRange(items) {
175
+ let from;
176
+ let to;
177
+ for (const item of items) {
178
+ from = from === void 0 ? item.event.time : Math.min(from, item.event.time);
179
+ to = to === void 0 ? item.event.time : Math.max(to, item.event.time);
180
+ }
181
+ return from === void 0 || to === void 0 ? void 0 : {
182
+ from,
183
+ to
184
+ };
185
+ }
186
+ function sessionIdForEvent(event, options) {
187
+ const value = event.context?.[options.contextKey];
188
+ return typeof value === "string" && value.length > 0 ? value : options.fallbackSessionId;
189
+ }
190
+ function safeZipPathSegment(value) {
191
+ const encoded = encodeURIComponent(value).replace(/%/g, "_");
192
+ if (!encoded || encoded === "." || encoded === "..") return "_";
193
+ return encoded;
194
+ }
195
+ function sortByEventTime(items) {
196
+ return [...items].sort((left, right) => {
197
+ if (left.event.time !== right.event.time) return left.event.time - right.event.time;
198
+ if (left.event.seq !== right.event.seq) return left.event.seq - right.event.seq;
199
+ return left.event.id < right.event.id ? -1 : left.event.id > right.event.id ? 1 : 0;
200
+ });
201
+ }
151
202
  function createLogZipBlob(files, options = {}) {
152
203
  const normalizedFiles = createNormalizedZipFiles(files, options);
153
204
  const parts = [];
@@ -170,27 +221,72 @@ async function exportLogsToZip(source, options = {}) {
170
221
  const createdAt = options.createdAt ?? Date.now();
171
222
  const logFileName = options.logFileName ?? (format === "json" ? "logs.json" : "logs.ndjson");
172
223
  const maxEvents = normalizeMaxEvents(options.maxEvents);
224
+ const sessionExportOptions = normalizeSessionExportOptions(options.groupBySession, logFileName);
225
+ const recentExportOptions = normalizeRecentExportOptions(options.includeRecent, format);
173
226
  const serialize = options.serializeEvent ?? ((event) => (0, _loggerjs_core.safeJsonStringify)(event, options.stringify ?? {}));
174
- const lines = [];
227
+ const exported = [];
175
228
  let skippedCount = 0;
176
- let from;
177
- let to;
178
229
  for await (const event of resolveSource(source, options.query)) {
179
- if (maxEvents !== void 0 && lines.length >= maxEvents) break;
230
+ if (maxEvents !== void 0 && exported.length >= maxEvents) break;
180
231
  const mapped = options.mapEvent ? options.mapEvent(event) : event;
181
232
  if (!mapped) {
182
233
  skippedCount += 1;
183
234
  continue;
184
235
  }
185
- lines.push(serialize(mapped));
186
- from = from === void 0 ? mapped.time : Math.min(from, mapped.time);
187
- to = to === void 0 ? mapped.time : Math.max(to, mapped.time);
236
+ exported.push({
237
+ event: mapped,
238
+ line: serialize(mapped)
239
+ });
188
240
  }
189
241
  const files = [{
190
242
  name: logFileName,
191
- content: format === "json" ? `[${lines.join(",")}]` : lines.length > 0 ? `${lines.join("\n")}\n` : "",
243
+ content: logContentForLines(exported.map((item) => item.line), format),
192
244
  lastModified: createdAt
193
245
  }];
246
+ const sessionManifests = [];
247
+ if (sessionExportOptions) {
248
+ const groups = /* @__PURE__ */ new Map();
249
+ for (const item of exported) {
250
+ const sessionId = sessionIdForEvent(item.event, sessionExportOptions);
251
+ const group = groups.get(sessionId) ?? [];
252
+ group.push(item);
253
+ groups.set(sessionId, group);
254
+ }
255
+ const usedFileNames = new Set(files.map((file) => normalizeZipFileName(file.name)));
256
+ const sortedGroups = [...groups.entries()];
257
+ sortedGroups.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
258
+ for (const [sessionId, group] of sortedGroups) {
259
+ let fileName = normalizeZipFileName(`${sessionExportOptions.directory}/${safeZipPathSegment(sessionId)}/${sessionExportOptions.logFileName}`);
260
+ let suffix = 1;
261
+ while (usedFileNames.has(fileName)) {
262
+ fileName = normalizeZipFileName(`${sessionExportOptions.directory}/${safeZipPathSegment(sessionId)}-${suffix}/${sessionExportOptions.logFileName}`);
263
+ suffix += 1;
264
+ }
265
+ usedFileNames.add(fileName);
266
+ const orderedGroup = sortByEventTime(group);
267
+ files.push({
268
+ name: fileName,
269
+ content: logContentForLines(orderedGroup.map((item) => item.line), format),
270
+ lastModified: createdAt
271
+ });
272
+ sessionManifests.push({
273
+ logCount: orderedGroup.length,
274
+ logFileName: fileName,
275
+ sessionId,
276
+ timeRange: eventTimeRange(orderedGroup)
277
+ });
278
+ }
279
+ }
280
+ let recentLogFileName;
281
+ if (recentExportOptions) {
282
+ const recentItems = recentExportOptions.maxEvents <= 0 ? [] : sortByEventTime(exported).slice(-recentExportOptions.maxEvents);
283
+ recentLogFileName = normalizeZipFileName(recentExportOptions.logFileName);
284
+ files.push({
285
+ name: recentLogFileName,
286
+ content: logContentForLines(recentItems.map((item) => item.line), format),
287
+ lastModified: createdAt
288
+ });
289
+ }
194
290
  if (options.includeManifest ?? true) {
195
291
  const manifest = {
196
292
  schema: "loggerjs.log-export.v1",
@@ -198,13 +294,13 @@ async function exportLogsToZip(source, options = {}) {
198
294
  source: options.source,
199
295
  format,
200
296
  logFileName: normalizeZipFileName(logFileName),
201
- logCount: lines.length,
297
+ logCount: exported.length,
202
298
  skippedCount,
203
299
  query: options.query,
204
- timeRange: from === void 0 || to === void 0 ? void 0 : {
205
- from,
206
- to
207
- }
300
+ recentLogFileName,
301
+ sessionCount: sessionExportOptions ? sessionManifests.length : void 0,
302
+ sessions: sessionExportOptions ? sessionManifests : void 0,
303
+ timeRange: eventTimeRange(exported)
208
304
  };
209
305
  files.push({
210
306
  name: options.manifestFileName ?? "manifest.json",
@@ -1 +1 @@
1
- {"version":3,"file":"zip-export.cjs","names":[],"sources":["../src/zip-export.ts"],"sourcesContent":["import { safeJsonStringify, type LogEvent, type SafeStringifyOptions } from \"@loggerjs/core\";\nimport type { IndexedDbTransportQueryOptions } from \"./indexeddb-transport\";\n\nexport type LogZipExportFormat = \"ndjson\" | \"json\";\n\nexport interface ZipExportFile {\n name: string;\n content: string | Uint8Array | ArrayBuffer;\n lastModified?: number;\n}\n\nexport interface LogZipBlobOptions {\n lastModified?: number;\n type?: string;\n}\n\nexport interface LogZipExportQuerySource {\n query: (options?: IndexedDbTransportQueryOptions) => AsyncIterable<LogEvent>;\n}\n\nexport type LogZipExportSource = AsyncIterable<LogEvent> | LogZipExportQuerySource;\n\nexport interface LogZipExportManifest {\n schema: \"loggerjs.log-export.v1\";\n createdAt: string;\n source?: string;\n format: LogZipExportFormat;\n logFileName: string;\n logCount: number;\n skippedCount: number;\n query?: IndexedDbTransportQueryOptions;\n timeRange?: {\n from: number;\n to: number;\n };\n}\n\nexport interface LogZipExportOptions {\n query?: IndexedDbTransportQueryOptions;\n format?: LogZipExportFormat;\n logFileName?: string;\n includeManifest?: boolean;\n manifestFileName?: string;\n source?: string;\n createdAt?: number;\n maxEvents?: number;\n stringify?: SafeStringifyOptions;\n serializeEvent?: (event: LogEvent) => string;\n mapEvent?: (event: LogEvent) => LogEvent | false | null | undefined;\n}\n\nexport interface DownloadBlobOptions {\n filename?: string;\n document?: Document;\n url?: Pick<typeof URL, \"createObjectURL\" | \"revokeObjectURL\">;\n revokeDelayMs?: number;\n}\n\ninterface NormalizedZipFile {\n name: string;\n nameBytes: Uint8Array;\n contentBytes: Uint8Array;\n crc32: number;\n modifiedTime: number;\n modifiedDate: number;\n localHeaderOffset: number;\n}\n\nconst ZIP_UTF8_FLAG = 0x0800;\nconst ZIP_STORE_METHOD = 0;\nconst ZIP_VERSION = 20;\nconst ZIP_MAX_UINT16 = 0xffff;\nconst ZIP_MAX_UINT32 = 0xffffffff;\nconst LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;\nconst CENTRAL_DIRECTORY_SIGNATURE = 0x02014b50;\nconst END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;\n\nlet crc32Table: Uint32Array | undefined;\n\nfunction getCrc32Table(): Uint32Array {\n if (crc32Table) return crc32Table;\n const table = new Uint32Array(256);\n for (let index = 0; index < table.length; index += 1) {\n let crc = index;\n for (let bit = 0; bit < 8; bit += 1) {\n crc = crc & 1 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1;\n }\n table[index] = crc >>> 0;\n }\n crc32Table = table;\n return table;\n}\n\nfunction crc32(bytes: Uint8Array): number {\n const table = getCrc32Table();\n let crc = 0xffffffff;\n for (const byte of bytes) {\n crc = (crc >>> 8) ^ (table[(crc ^ byte) & 0xff] ?? 0);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nfunction encodeText(value: string): Uint8Array {\n return new TextEncoder().encode(value);\n}\n\nfunction normalizeFileContent(content: ZipExportFile[\"content\"]): Uint8Array {\n if (typeof content === \"string\") return encodeText(content);\n if (content instanceof Uint8Array) return content;\n return new Uint8Array(content);\n}\n\nfunction normalizeZipFileName(name: string): string {\n const normalized = name\n .replace(/\\\\/g, \"/\")\n .split(\"/\")\n .filter((part) => part.length > 0 && part !== \".\" && part !== \"..\")\n .join(\"/\");\n if (!normalized) throw new Error(\"ZIP file name cannot be empty\");\n return normalized;\n}\n\nfunction toDosDateTime(timestamp: number): { time: number; date: number } {\n const date = new Date(Number.isFinite(timestamp) ? timestamp : Date.now());\n const year = Math.min(2107, Math.max(1980, date.getFullYear()));\n return {\n time: (date.getHours() << 11) | (date.getMinutes() << 5) | Math.floor(date.getSeconds() / 2),\n date: ((year - 1980) << 9) | ((date.getMonth() + 1) << 5) | date.getDate(),\n };\n}\n\nfunction assertZipUint16(value: number, label: string) {\n if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT16) {\n throw new Error(`${label} exceeds ZIP32 limit`);\n }\n}\n\nfunction assertZipUint32(value: number, label: string) {\n if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT32) {\n throw new Error(`${label} exceeds ZIP32 limit`);\n }\n}\n\nfunction createLocalFileHeader(file: NormalizedZipFile): Uint8Array {\n const header = new Uint8Array(30);\n const view = new DataView(header.buffer);\n view.setUint32(0, LOCAL_FILE_HEADER_SIGNATURE, true);\n view.setUint16(4, ZIP_VERSION, true);\n view.setUint16(6, ZIP_UTF8_FLAG, true);\n view.setUint16(8, ZIP_STORE_METHOD, true);\n view.setUint16(10, file.modifiedTime, true);\n view.setUint16(12, file.modifiedDate, true);\n view.setUint32(14, file.crc32, true);\n view.setUint32(18, file.contentBytes.byteLength, true);\n view.setUint32(22, file.contentBytes.byteLength, true);\n view.setUint16(26, file.nameBytes.byteLength, true);\n view.setUint16(28, 0, true);\n return header;\n}\n\nfunction createCentralDirectoryHeader(file: NormalizedZipFile): Uint8Array {\n const header = new Uint8Array(46);\n const view = new DataView(header.buffer);\n view.setUint32(0, CENTRAL_DIRECTORY_SIGNATURE, true);\n view.setUint16(4, ZIP_VERSION, true);\n view.setUint16(6, ZIP_VERSION, true);\n view.setUint16(8, ZIP_UTF8_FLAG, true);\n view.setUint16(10, ZIP_STORE_METHOD, true);\n view.setUint16(12, file.modifiedTime, true);\n view.setUint16(14, file.modifiedDate, true);\n view.setUint32(16, file.crc32, true);\n view.setUint32(20, file.contentBytes.byteLength, true);\n view.setUint32(24, file.contentBytes.byteLength, true);\n view.setUint16(28, file.nameBytes.byteLength, true);\n view.setUint16(30, 0, true);\n view.setUint16(32, 0, true);\n view.setUint16(34, 0, true);\n view.setUint16(36, 0, true);\n view.setUint32(38, 0, true);\n view.setUint32(42, file.localHeaderOffset, true);\n return header;\n}\n\nfunction createEndOfCentralDirectory(\n fileCount: number,\n centralDirectorySize: number,\n centralDirectoryOffset: number,\n): Uint8Array {\n assertZipUint16(fileCount, \"ZIP file count\");\n assertZipUint32(centralDirectorySize, \"ZIP central directory size\");\n assertZipUint32(centralDirectoryOffset, \"ZIP central directory offset\");\n\n const footer = new Uint8Array(22);\n const view = new DataView(footer.buffer);\n view.setUint32(0, END_OF_CENTRAL_DIRECTORY_SIGNATURE, true);\n view.setUint16(4, 0, true);\n view.setUint16(6, 0, true);\n view.setUint16(8, fileCount, true);\n view.setUint16(10, fileCount, true);\n view.setUint32(12, centralDirectorySize, true);\n view.setUint32(16, centralDirectoryOffset, true);\n view.setUint16(20, 0, true);\n return footer;\n}\n\nfunction createNormalizedZipFiles(\n files: readonly ZipExportFile[],\n options: LogZipBlobOptions,\n): NormalizedZipFile[] {\n const names = new Set<string>();\n let offset = 0;\n return files.map((file) => {\n const name = normalizeZipFileName(file.name);\n if (names.has(name)) throw new Error(`Duplicate ZIP file name: ${name}`);\n names.add(name);\n\n const nameBytes = encodeText(name);\n const contentBytes = normalizeFileContent(file.content);\n assertZipUint16(nameBytes.byteLength, `ZIP file name ${name}`);\n assertZipUint32(contentBytes.byteLength, `ZIP file ${name}`);\n assertZipUint32(offset, \"ZIP local header offset\");\n\n const modified = toDosDateTime(file.lastModified ?? options.lastModified ?? Date.now());\n const normalized: NormalizedZipFile = {\n name,\n nameBytes,\n contentBytes,\n crc32: crc32(contentBytes),\n modifiedTime: modified.time,\n modifiedDate: modified.date,\n localHeaderOffset: offset,\n };\n offset += 30 + nameBytes.byteLength + contentBytes.byteLength;\n assertZipUint32(offset, \"ZIP local data size\");\n return normalized;\n });\n}\n\nfunction isQuerySource(source: LogZipExportSource): source is LogZipExportQuerySource {\n return \"query\" in source && typeof source.query === \"function\";\n}\n\nfunction resolveSource(\n source: LogZipExportSource,\n query: IndexedDbTransportQueryOptions | undefined,\n): AsyncIterable<LogEvent> {\n if (isQuerySource(source)) return source.query(query);\n if (query) throw new Error(\"Log ZIP export query options require a source with query()\");\n return source;\n}\n\nfunction normalizeMaxEvents(value: number | undefined): number | undefined {\n if (value === undefined) return undefined;\n if (!Number.isFinite(value)) return undefined;\n return Math.max(0, Math.floor(value));\n}\n\nexport function createLogZipBlob(\n files: readonly ZipExportFile[],\n options: LogZipBlobOptions = {},\n): Blob {\n const normalizedFiles = createNormalizedZipFiles(files, options);\n const parts: Uint8Array[] = [];\n let centralDirectoryOffset = 0;\n for (const file of normalizedFiles) {\n parts.push(createLocalFileHeader(file), file.nameBytes, file.contentBytes);\n centralDirectoryOffset += 30 + file.nameBytes.byteLength + file.contentBytes.byteLength;\n }\n\n let centralDirectorySize = 0;\n for (const file of normalizedFiles) {\n const header = createCentralDirectoryHeader(file);\n parts.push(header, file.nameBytes);\n centralDirectorySize += header.byteLength + file.nameBytes.byteLength;\n }\n parts.push(\n createEndOfCentralDirectory(\n normalizedFiles.length,\n centralDirectorySize,\n centralDirectoryOffset,\n ),\n );\n\n return new Blob(parts as unknown as BlobPart[], { type: options.type ?? \"application/zip\" });\n}\n\nexport async function exportLogsToZip(\n source: LogZipExportSource,\n options: LogZipExportOptions = {},\n): Promise<Blob> {\n const format = options.format ?? \"ndjson\";\n const createdAt = options.createdAt ?? Date.now();\n const logFileName = options.logFileName ?? (format === \"json\" ? \"logs.json\" : \"logs.ndjson\");\n const maxEvents = normalizeMaxEvents(options.maxEvents);\n const serialize =\n options.serializeEvent ??\n ((event: LogEvent) => safeJsonStringify(event, options.stringify ?? {}));\n const lines: string[] = [];\n let skippedCount = 0;\n let from: number | undefined;\n let to: number | undefined;\n\n for await (const event of resolveSource(source, options.query)) {\n if (maxEvents !== undefined && lines.length >= maxEvents) break;\n const mapped = options.mapEvent ? options.mapEvent(event) : event;\n if (!mapped) {\n skippedCount += 1;\n continue;\n }\n lines.push(serialize(mapped));\n from = from === undefined ? mapped.time : Math.min(from, mapped.time);\n to = to === undefined ? mapped.time : Math.max(to, mapped.time);\n }\n\n const logContent =\n format === \"json\" ? `[${lines.join(\",\")}]` : lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n\n const files: ZipExportFile[] = [\n {\n name: logFileName,\n content: logContent,\n lastModified: createdAt,\n },\n ];\n\n if (options.includeManifest ?? true) {\n const manifest: LogZipExportManifest = {\n schema: \"loggerjs.log-export.v1\",\n createdAt: new Date(createdAt).toISOString(),\n source: options.source,\n format,\n logFileName: normalizeZipFileName(logFileName),\n logCount: lines.length,\n skippedCount,\n query: options.query,\n timeRange: from === undefined || to === undefined ? undefined : { from, to },\n };\n files.push({\n name: options.manifestFileName ?? \"manifest.json\",\n content: `${safeJsonStringify(manifest, { space: 2 })}\\n`,\n lastModified: createdAt,\n });\n }\n\n return createLogZipBlob(files, { lastModified: createdAt });\n}\n\nexport function downloadBlob(\n blob: Blob,\n filenameOrOptions: string | DownloadBlobOptions = {},\n): string {\n const options =\n typeof filenameOrOptions === \"string\" ? { filename: filenameOrOptions } : filenameOrOptions;\n const documentRef = options.document ?? globalThis.document;\n const urlRef = options.url ?? globalThis.URL;\n if (!documentRef?.body || !urlRef?.createObjectURL || !urlRef.revokeObjectURL) {\n throw new Error(\"downloadBlob requires document and URL.createObjectURL\");\n }\n\n const href = urlRef.createObjectURL(blob);\n const anchor = documentRef.createElement(\"a\");\n anchor.href = href;\n anchor.download = options.filename ?? \"loggerjs-logs.zip\";\n anchor.style.display = \"none\";\n documentRef.body.appendChild(anchor);\n anchor.click();\n anchor.remove();\n\n const revoke = () => urlRef.revokeObjectURL(href);\n const revokeDelayMs = options.revokeDelayMs ?? 1000;\n if (revokeDelayMs <= 0) {\n revoke();\n } else {\n setTimeout(revoke, revokeDelayMs);\n }\n\n return href;\n}\n"],"mappings":";;;AAoEA,MAAM,gBAAgB;AACtB,MAAM,mBAAmB;AACzB,MAAM,cAAc;AACpB,MAAM,iBAAiB;AACvB,MAAM,iBAAiB;AACvB,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;AACpC,MAAM,qCAAqC;AAE3C,IAAI;AAEJ,SAAS,gBAA6B;CACpC,IAAI,YAAY,OAAO;CACvB,MAAM,QAAQ,IAAI,YAAY,GAAG;CACjC,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACpD,IAAI,MAAM;EACV,KAAK,IAAI,MAAM,GAAG,MAAM,GAAG,OAAO,GAChC,MAAM,MAAM,IAAI,aAAc,QAAQ,IAAK,QAAQ;EAErD,MAAM,SAAS,QAAQ;CACzB;CACA,aAAa;CACb,OAAO;AACT;AAEA,SAAS,MAAM,OAA2B;CACxC,MAAM,QAAQ,cAAc;CAC5B,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,OACjB,MAAO,QAAQ,KAAM,OAAO,MAAM,QAAQ,QAAS;CAErD,QAAQ,MAAM,gBAAgB;AAChC;AAEA,SAAS,WAAW,OAA2B;CAC7C,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;AACvC;AAEA,SAAS,qBAAqB,SAA+C;CAC3E,IAAI,OAAO,YAAY,UAAU,OAAO,WAAW,OAAO;CAC1D,IAAI,mBAAmB,YAAY,OAAO;CAC1C,OAAO,IAAI,WAAW,OAAO;AAC/B;AAEA,SAAS,qBAAqB,MAAsB;CAClD,MAAM,aAAa,KAChB,QAAQ,OAAO,GAAG,CAAC,CACnB,MAAM,GAAG,CAAC,CACV,QAAQ,SAAS,KAAK,SAAS,KAAK,SAAS,OAAO,SAAS,IAAI,CAAC,CAClE,KAAK,GAAG;CACX,IAAI,CAAC,YAAY,MAAM,IAAI,MAAM,+BAA+B;CAChE,OAAO;AACT;AAEA,SAAS,cAAc,WAAmD;CACxE,MAAM,OAAO,IAAI,KAAK,OAAO,SAAS,SAAS,IAAI,YAAY,KAAK,IAAI,CAAC;CACzE,MAAM,OAAO,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,YAAY,CAAC,CAAC;CAC9D,OAAO;EACL,MAAO,KAAK,SAAS,KAAK,KAAO,KAAK,WAAW,KAAK,IAAK,KAAK,MAAM,KAAK,WAAW,IAAI,CAAC;EAC3F,MAAQ,OAAO,QAAS,IAAO,KAAK,SAAS,IAAI,KAAM,IAAK,KAAK,QAAQ;CAC3E;AACF;AAEA,SAAS,gBAAgB,OAAe,OAAe;CACrD,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,KAAK,QAAQ,gBACvD,MAAM,IAAI,MAAM,GAAG,MAAM,qBAAqB;AAElD;AAEA,SAAS,gBAAgB,OAAe,OAAe;CACrD,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,KAAK,QAAQ,gBACvD,MAAM,IAAI,MAAM,GAAG,MAAM,qBAAqB;AAElD;AAEA,SAAS,sBAAsB,MAAqC;CAClE,MAAM,SAAS,IAAI,WAAW,EAAE;CAChC,MAAM,OAAO,IAAI,SAAS,OAAO,MAAM;CACvC,KAAK,UAAU,GAAG,6BAA6B,IAAI;CACnD,KAAK,UAAU,GAAG,aAAa,IAAI;CACnC,KAAK,UAAU,GAAG,eAAe,IAAI;CACrC,KAAK,UAAU,GAAG,kBAAkB,IAAI;CACxC,KAAK,UAAU,IAAI,KAAK,cAAc,IAAI;CAC1C,KAAK,UAAU,IAAI,KAAK,cAAc,IAAI;CAC1C,KAAK,UAAU,IAAI,KAAK,OAAO,IAAI;CACnC,KAAK,UAAU,IAAI,KAAK,aAAa,YAAY,IAAI;CACrD,KAAK,UAAU,IAAI,KAAK,aAAa,YAAY,IAAI;CACrD,KAAK,UAAU,IAAI,KAAK,UAAU,YAAY,IAAI;CAClD,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,OAAO;AACT;AAEA,SAAS,6BAA6B,MAAqC;CACzE,MAAM,SAAS,IAAI,WAAW,EAAE;CAChC,MAAM,OAAO,IAAI,SAAS,OAAO,MAAM;CACvC,KAAK,UAAU,GAAG,6BAA6B,IAAI;CACnD,KAAK,UAAU,GAAG,aAAa,IAAI;CACnC,KAAK,UAAU,GAAG,aAAa,IAAI;CACnC,KAAK,UAAU,GAAG,eAAe,IAAI;CACrC,KAAK,UAAU,IAAI,kBAAkB,IAAI;CACzC,KAAK,UAAU,IAAI,KAAK,cAAc,IAAI;CAC1C,KAAK,UAAU,IAAI,KAAK,cAAc,IAAI;CAC1C,KAAK,UAAU,IAAI,KAAK,OAAO,IAAI;CACnC,KAAK,UAAU,IAAI,KAAK,aAAa,YAAY,IAAI;CACrD,KAAK,UAAU,IAAI,KAAK,aAAa,YAAY,IAAI;CACrD,KAAK,UAAU,IAAI,KAAK,UAAU,YAAY,IAAI;CAClD,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,KAAK,UAAU,IAAI,KAAK,mBAAmB,IAAI;CAC/C,OAAO;AACT;AAEA,SAAS,4BACP,WACA,sBACA,wBACY;CACZ,gBAAgB,WAAW,gBAAgB;CAC3C,gBAAgB,sBAAsB,4BAA4B;CAClE,gBAAgB,wBAAwB,8BAA8B;CAEtE,MAAM,SAAS,IAAI,WAAW,EAAE;CAChC,MAAM,OAAO,IAAI,SAAS,OAAO,MAAM;CACvC,KAAK,UAAU,GAAG,oCAAoC,IAAI;CAC1D,KAAK,UAAU,GAAG,GAAG,IAAI;CACzB,KAAK,UAAU,GAAG,GAAG,IAAI;CACzB,KAAK,UAAU,GAAG,WAAW,IAAI;CACjC,KAAK,UAAU,IAAI,WAAW,IAAI;CAClC,KAAK,UAAU,IAAI,sBAAsB,IAAI;CAC7C,KAAK,UAAU,IAAI,wBAAwB,IAAI;CAC/C,KAAK,UAAU,IAAI,GAAG,IAAI;CAC1B,OAAO;AACT;AAEA,SAAS,yBACP,OACA,SACqB;CACrB,MAAM,wBAAQ,IAAI,IAAY;CAC9B,IAAI,SAAS;CACb,OAAO,MAAM,KAAK,SAAS;EACzB,MAAM,OAAO,qBAAqB,KAAK,IAAI;EAC3C,IAAI,MAAM,IAAI,IAAI,GAAG,MAAM,IAAI,MAAM,4BAA4B,MAAM;EACvE,MAAM,IAAI,IAAI;EAEd,MAAM,YAAY,WAAW,IAAI;EACjC,MAAM,eAAe,qBAAqB,KAAK,OAAO;EACtD,gBAAgB,UAAU,YAAY,iBAAiB,MAAM;EAC7D,gBAAgB,aAAa,YAAY,YAAY,MAAM;EAC3D,gBAAgB,QAAQ,yBAAyB;EAEjD,MAAM,WAAW,cAAc,KAAK,gBAAgB,QAAQ,gBAAgB,KAAK,IAAI,CAAC;EACtF,MAAM,aAAgC;GACpC;GACA;GACA;GACA,OAAO,MAAM,YAAY;GACzB,cAAc,SAAS;GACvB,cAAc,SAAS;GACvB,mBAAmB;EACrB;EACA,UAAU,KAAK,UAAU,aAAa,aAAa;EACnD,gBAAgB,QAAQ,qBAAqB;EAC7C,OAAO;CACT,CAAC;AACH;AAEA,SAAS,cAAc,QAA+D;CACpF,OAAO,WAAW,UAAU,OAAO,OAAO,UAAU;AACtD;AAEA,SAAS,cACP,QACA,OACyB;CACzB,IAAI,cAAc,MAAM,GAAG,OAAO,OAAO,MAAM,KAAK;CACpD,IAAI,OAAO,MAAM,IAAI,MAAM,4DAA4D;CACvF,OAAO;AACT;AAEA,SAAS,mBAAmB,OAA+C;CACzE,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,IAAI,CAAC,OAAO,SAAS,KAAK,GAAG,OAAO,KAAA;CACpC,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;AACtC;AAEA,SAAgB,iBACd,OACA,UAA6B,CAAC,GACxB;CACN,MAAM,kBAAkB,yBAAyB,OAAO,OAAO;CAC/D,MAAM,QAAsB,CAAC;CAC7B,IAAI,yBAAyB;CAC7B,KAAK,MAAM,QAAQ,iBAAiB;EAClC,MAAM,KAAK,sBAAsB,IAAI,GAAG,KAAK,WAAW,KAAK,YAAY;EACzE,0BAA0B,KAAK,KAAK,UAAU,aAAa,KAAK,aAAa;CAC/E;CAEA,IAAI,uBAAuB;CAC3B,KAAK,MAAM,QAAQ,iBAAiB;EAClC,MAAM,SAAS,6BAA6B,IAAI;EAChD,MAAM,KAAK,QAAQ,KAAK,SAAS;EACjC,wBAAwB,OAAO,aAAa,KAAK,UAAU;CAC7D;CACA,MAAM,KACJ,4BACE,gBAAgB,QAChB,sBACA,sBACF,CACF;CAEA,OAAO,IAAI,KAAK,OAAgC,EAAE,MAAM,QAAQ,QAAQ,kBAAkB,CAAC;AAC7F;AAEA,eAAsB,gBACpB,QACA,UAA+B,CAAC,GACjB;CACf,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,YAAY,QAAQ,aAAa,KAAK,IAAI;CAChD,MAAM,cAAc,QAAQ,gBAAgB,WAAW,SAAS,cAAc;CAC9E,MAAM,YAAY,mBAAmB,QAAQ,SAAS;CACtD,MAAM,YACJ,QAAQ,oBACN,WAAA,GAAA,eAAA,kBAAA,CAAsC,OAAO,QAAQ,aAAa,CAAC,CAAC;CACxE,MAAM,QAAkB,CAAC;CACzB,IAAI,eAAe;CACnB,IAAI;CACJ,IAAI;CAEJ,WAAW,MAAM,SAAS,cAAc,QAAQ,QAAQ,KAAK,GAAG;EAC9D,IAAI,cAAc,KAAA,KAAa,MAAM,UAAU,WAAW;EAC1D,MAAM,SAAS,QAAQ,WAAW,QAAQ,SAAS,KAAK,IAAI;EAC5D,IAAI,CAAC,QAAQ;GACX,gBAAgB;GAChB;EACF;EACA,MAAM,KAAK,UAAU,MAAM,CAAC;EAC5B,OAAO,SAAS,KAAA,IAAY,OAAO,OAAO,KAAK,IAAI,MAAM,OAAO,IAAI;EACpE,KAAK,OAAO,KAAA,IAAY,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,IAAI;CAChE;CAKA,MAAM,QAAyB,CAC7B;EACE,MAAM;EACN,SALF,WAAW,SAAS,IAAI,MAAM,KAAK,GAAG,EAAE,KAAK,MAAM,SAAS,IAAI,GAAG,MAAM,KAAK,IAAI,EAAE,MAAM;EAMxF,cAAc;CAChB,CACF;CAEA,IAAI,QAAQ,mBAAmB,MAAM;EACnC,MAAM,WAAiC;GACrC,QAAQ;GACR,WAAW,IAAI,KAAK,SAAS,CAAC,CAAC,YAAY;GAC3C,QAAQ,QAAQ;GAChB;GACA,aAAa,qBAAqB,WAAW;GAC7C,UAAU,MAAM;GAChB;GACA,OAAO,QAAQ;GACf,WAAW,SAAS,KAAA,KAAa,OAAO,KAAA,IAAY,KAAA,IAAY;IAAE;IAAM;GAAG;EAC7E;EACA,MAAM,KAAK;GACT,MAAM,QAAQ,oBAAoB;GAClC,SAAS,IAAA,GAAA,eAAA,kBAAA,CAAqB,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE;GACtD,cAAc;EAChB,CAAC;CACH;CAEA,OAAO,iBAAiB,OAAO,EAAE,cAAc,UAAU,CAAC;AAC5D;AAEA,SAAgB,aACd,MACA,oBAAkD,CAAC,GAC3C;CACR,MAAM,UACJ,OAAO,sBAAsB,WAAW,EAAE,UAAU,kBAAkB,IAAI;CAC5E,MAAM,cAAc,QAAQ,YAAY,WAAW;CACnD,MAAM,SAAS,QAAQ,OAAO,WAAW;CACzC,IAAI,CAAC,aAAa,QAAQ,CAAC,QAAQ,mBAAmB,CAAC,OAAO,iBAC5D,MAAM,IAAI,MAAM,wDAAwD;CAG1E,MAAM,OAAO,OAAO,gBAAgB,IAAI;CACxC,MAAM,SAAS,YAAY,cAAc,GAAG;CAC5C,OAAO,OAAO;CACd,OAAO,WAAW,QAAQ,YAAY;CACtC,OAAO,MAAM,UAAU;CACvB,YAAY,KAAK,YAAY,MAAM;CACnC,OAAO,MAAM;CACb,OAAO,OAAO;CAEd,MAAM,eAAe,OAAO,gBAAgB,IAAI;CAChD,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,IAAI,iBAAiB,GACnB,OAAO;MAEP,WAAW,QAAQ,aAAa;CAGlC,OAAO;AACT"}
1
+ {"version":3,"file":"zip-export.cjs","names":[],"sources":["../src/zip-export.ts"],"sourcesContent":["import { safeJsonStringify, type LogEvent, type SafeStringifyOptions } from \"@loggerjs/core\";\nimport type { IndexedDbTransportQueryOptions } from \"./indexeddb-transport\";\n\nexport type LogZipExportFormat = \"ndjson\" | \"json\";\n\nexport interface ZipExportFile {\n name: string;\n content: string | Uint8Array | ArrayBuffer;\n lastModified?: number;\n}\n\nexport interface LogZipBlobOptions {\n lastModified?: number;\n type?: string;\n}\n\nexport interface LogZipExportQuerySource {\n query: (options?: IndexedDbTransportQueryOptions) => AsyncIterable<LogEvent>;\n}\n\nexport type LogZipExportSource = AsyncIterable<LogEvent> | LogZipExportQuerySource;\n\nexport interface LogZipExportManifest {\n schema: \"loggerjs.log-export.v1\";\n createdAt: string;\n source?: string;\n format: LogZipExportFormat;\n logFileName: string;\n logCount: number;\n skippedCount: number;\n query?: IndexedDbTransportQueryOptions;\n timeRange?: {\n from: number;\n to: number;\n };\n recentLogFileName?: string;\n sessionCount?: number;\n sessions?: LogZipExportSessionManifest[];\n}\n\nexport interface LogZipExportSessionManifest {\n sessionId: string;\n logFileName: string;\n logCount: number;\n timeRange?: {\n from: number;\n to: number;\n };\n}\n\nexport interface LogZipExportSessionOptions {\n contextKey?: string;\n directory?: string;\n fallbackSessionId?: string;\n logFileName?: string;\n}\n\nexport interface LogZipExportRecentOptions {\n logFileName?: string;\n maxEvents?: number;\n}\n\nexport interface LogZipExportOptions {\n query?: IndexedDbTransportQueryOptions;\n format?: LogZipExportFormat;\n logFileName?: string;\n includeManifest?: boolean;\n manifestFileName?: string;\n source?: string;\n createdAt?: number;\n maxEvents?: number;\n stringify?: SafeStringifyOptions;\n serializeEvent?: (event: LogEvent) => string;\n mapEvent?: (event: LogEvent) => LogEvent | false | null | undefined;\n groupBySession?: boolean | LogZipExportSessionOptions;\n includeRecent?: boolean | LogZipExportRecentOptions;\n}\n\nexport interface DownloadBlobOptions {\n filename?: string;\n document?: Document;\n url?: Pick<typeof URL, \"createObjectURL\" | \"revokeObjectURL\">;\n revokeDelayMs?: number;\n}\n\ninterface NormalizedZipFile {\n name: string;\n nameBytes: Uint8Array;\n contentBytes: Uint8Array;\n crc32: number;\n modifiedTime: number;\n modifiedDate: number;\n localHeaderOffset: number;\n}\n\ninterface ExportedLogLine {\n event: LogEvent;\n line: string;\n}\n\ninterface NormalizedSessionExportOptions {\n contextKey: string;\n directory: string;\n fallbackSessionId: string;\n logFileName: string;\n}\n\ninterface NormalizedRecentExportOptions {\n logFileName: string;\n maxEvents: number;\n}\n\nconst ZIP_UTF8_FLAG = 0x0800;\nconst ZIP_STORE_METHOD = 0;\nconst ZIP_VERSION = 20;\nconst ZIP_MAX_UINT16 = 0xffff;\nconst ZIP_MAX_UINT32 = 0xffffffff;\nconst LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;\nconst CENTRAL_DIRECTORY_SIGNATURE = 0x02014b50;\nconst END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;\nconst DEFAULT_SESSION_CONTEXT_KEY = \"sessionId\";\n\nlet crc32Table: Uint32Array | undefined;\n\nfunction getCrc32Table(): Uint32Array {\n if (crc32Table) return crc32Table;\n const table = new Uint32Array(256);\n for (let index = 0; index < table.length; index += 1) {\n let crc = index;\n for (let bit = 0; bit < 8; bit += 1) {\n crc = crc & 1 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1;\n }\n table[index] = crc >>> 0;\n }\n crc32Table = table;\n return table;\n}\n\nfunction crc32(bytes: Uint8Array): number {\n const table = getCrc32Table();\n let crc = 0xffffffff;\n for (const byte of bytes) {\n crc = (crc >>> 8) ^ (table[(crc ^ byte) & 0xff] ?? 0);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nfunction encodeText(value: string): Uint8Array {\n return new TextEncoder().encode(value);\n}\n\nfunction normalizeFileContent(content: ZipExportFile[\"content\"]): Uint8Array {\n if (typeof content === \"string\") return encodeText(content);\n if (content instanceof Uint8Array) return content;\n return new Uint8Array(content);\n}\n\nfunction normalizeZipFileName(name: string): string {\n const normalized = name\n .replace(/\\\\/g, \"/\")\n .split(\"/\")\n .filter((part) => part.length > 0 && part !== \".\" && part !== \"..\")\n .join(\"/\");\n if (!normalized) throw new Error(\"ZIP file name cannot be empty\");\n return normalized;\n}\n\nfunction toDosDateTime(timestamp: number): { time: number; date: number } {\n const date = new Date(Number.isFinite(timestamp) ? timestamp : Date.now());\n const year = Math.min(2107, Math.max(1980, date.getFullYear()));\n return {\n time: (date.getHours() << 11) | (date.getMinutes() << 5) | Math.floor(date.getSeconds() / 2),\n date: ((year - 1980) << 9) | ((date.getMonth() + 1) << 5) | date.getDate(),\n };\n}\n\nfunction assertZipUint16(value: number, label: string) {\n if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT16) {\n throw new Error(`${label} exceeds ZIP32 limit`);\n }\n}\n\nfunction assertZipUint32(value: number, label: string) {\n if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT32) {\n throw new Error(`${label} exceeds ZIP32 limit`);\n }\n}\n\nfunction createLocalFileHeader(file: NormalizedZipFile): Uint8Array {\n const header = new Uint8Array(30);\n const view = new DataView(header.buffer);\n view.setUint32(0, LOCAL_FILE_HEADER_SIGNATURE, true);\n view.setUint16(4, ZIP_VERSION, true);\n view.setUint16(6, ZIP_UTF8_FLAG, true);\n view.setUint16(8, ZIP_STORE_METHOD, true);\n view.setUint16(10, file.modifiedTime, true);\n view.setUint16(12, file.modifiedDate, true);\n view.setUint32(14, file.crc32, true);\n view.setUint32(18, file.contentBytes.byteLength, true);\n view.setUint32(22, file.contentBytes.byteLength, true);\n view.setUint16(26, file.nameBytes.byteLength, true);\n view.setUint16(28, 0, true);\n return header;\n}\n\nfunction createCentralDirectoryHeader(file: NormalizedZipFile): Uint8Array {\n const header = new Uint8Array(46);\n const view = new DataView(header.buffer);\n view.setUint32(0, CENTRAL_DIRECTORY_SIGNATURE, true);\n view.setUint16(4, ZIP_VERSION, true);\n view.setUint16(6, ZIP_VERSION, true);\n view.setUint16(8, ZIP_UTF8_FLAG, true);\n view.setUint16(10, ZIP_STORE_METHOD, true);\n view.setUint16(12, file.modifiedTime, true);\n view.setUint16(14, file.modifiedDate, true);\n view.setUint32(16, file.crc32, true);\n view.setUint32(20, file.contentBytes.byteLength, true);\n view.setUint32(24, file.contentBytes.byteLength, true);\n view.setUint16(28, file.nameBytes.byteLength, true);\n view.setUint16(30, 0, true);\n view.setUint16(32, 0, true);\n view.setUint16(34, 0, true);\n view.setUint16(36, 0, true);\n view.setUint32(38, 0, true);\n view.setUint32(42, file.localHeaderOffset, true);\n return header;\n}\n\nfunction createEndOfCentralDirectory(\n fileCount: number,\n centralDirectorySize: number,\n centralDirectoryOffset: number,\n): Uint8Array {\n assertZipUint16(fileCount, \"ZIP file count\");\n assertZipUint32(centralDirectorySize, \"ZIP central directory size\");\n assertZipUint32(centralDirectoryOffset, \"ZIP central directory offset\");\n\n const footer = new Uint8Array(22);\n const view = new DataView(footer.buffer);\n view.setUint32(0, END_OF_CENTRAL_DIRECTORY_SIGNATURE, true);\n view.setUint16(4, 0, true);\n view.setUint16(6, 0, true);\n view.setUint16(8, fileCount, true);\n view.setUint16(10, fileCount, true);\n view.setUint32(12, centralDirectorySize, true);\n view.setUint32(16, centralDirectoryOffset, true);\n view.setUint16(20, 0, true);\n return footer;\n}\n\nfunction createNormalizedZipFiles(\n files: readonly ZipExportFile[],\n options: LogZipBlobOptions,\n): NormalizedZipFile[] {\n const names = new Set<string>();\n let offset = 0;\n return files.map((file) => {\n const name = normalizeZipFileName(file.name);\n if (names.has(name)) throw new Error(`Duplicate ZIP file name: ${name}`);\n names.add(name);\n\n const nameBytes = encodeText(name);\n const contentBytes = normalizeFileContent(file.content);\n assertZipUint16(nameBytes.byteLength, `ZIP file name ${name}`);\n assertZipUint32(contentBytes.byteLength, `ZIP file ${name}`);\n assertZipUint32(offset, \"ZIP local header offset\");\n\n const modified = toDosDateTime(file.lastModified ?? options.lastModified ?? Date.now());\n const normalized: NormalizedZipFile = {\n name,\n nameBytes,\n contentBytes,\n crc32: crc32(contentBytes),\n modifiedTime: modified.time,\n modifiedDate: modified.date,\n localHeaderOffset: offset,\n };\n offset += 30 + nameBytes.byteLength + contentBytes.byteLength;\n assertZipUint32(offset, \"ZIP local data size\");\n return normalized;\n });\n}\n\nfunction isQuerySource(source: LogZipExportSource): source is LogZipExportQuerySource {\n return \"query\" in source && typeof source.query === \"function\";\n}\n\nfunction resolveSource(\n source: LogZipExportSource,\n query: IndexedDbTransportQueryOptions | undefined,\n): AsyncIterable<LogEvent> {\n if (isQuerySource(source)) return source.query(query);\n if (query) throw new Error(\"Log ZIP export query options require a source with query()\");\n return source;\n}\n\nfunction normalizeMaxEvents(value: number | undefined): number | undefined {\n if (value === undefined) return undefined;\n if (!Number.isFinite(value)) return undefined;\n return Math.max(0, Math.floor(value));\n}\n\nfunction normalizeSessionExportOptions(\n value: boolean | LogZipExportSessionOptions | undefined,\n logFileName: string,\n): NormalizedSessionExportOptions | undefined {\n if (!value) return undefined;\n const options = value === true ? {} : value;\n return {\n contextKey: options.contextKey ?? DEFAULT_SESSION_CONTEXT_KEY,\n directory: options.directory ?? \"sessions\",\n fallbackSessionId: options.fallbackSessionId ?? \"unknown\",\n logFileName: options.logFileName ?? logFileName,\n };\n}\n\nfunction normalizeRecentExportOptions(\n value: boolean | LogZipExportRecentOptions | undefined,\n format: LogZipExportFormat,\n): NormalizedRecentExportOptions | undefined {\n if (!value) return undefined;\n const options = value === true ? {} : value;\n return {\n logFileName: options.logFileName ?? (format === \"json\" ? \"recent.json\" : \"recent.ndjson\"),\n maxEvents: normalizeMaxEvents(options.maxEvents) ?? 100,\n };\n}\n\nfunction logContentForLines(lines: readonly string[], format: LogZipExportFormat): string {\n if (format === \"json\") return `[${lines.join(\",\")}]`;\n return lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\";\n}\n\nfunction eventTimeRange(items: readonly ExportedLogLine[]):\n | {\n from: number;\n to: number;\n }\n | undefined {\n let from: number | undefined;\n let to: number | undefined;\n for (const item of items) {\n from = from === undefined ? item.event.time : Math.min(from, item.event.time);\n to = to === undefined ? item.event.time : Math.max(to, item.event.time);\n }\n return from === undefined || to === undefined ? undefined : { from, to };\n}\n\nfunction sessionIdForEvent(event: LogEvent, options: NormalizedSessionExportOptions): string {\n const value = event.context?.[options.contextKey];\n return typeof value === \"string\" && value.length > 0 ? value : options.fallbackSessionId;\n}\n\nfunction safeZipPathSegment(value: string): string {\n const encoded = encodeURIComponent(value).replace(/%/g, \"_\");\n if (!encoded || encoded === \".\" || encoded === \"..\") return \"_\";\n return encoded;\n}\n\nfunction sortByEventTime(items: readonly ExportedLogLine[]): ExportedLogLine[] {\n // oxlint-disable-next-line no-array-sort -- Sort a copy for stable export ordering.\n return [...items].sort((left, right) => {\n if (left.event.time !== right.event.time) return left.event.time - right.event.time;\n if (left.event.seq !== right.event.seq) return left.event.seq - right.event.seq;\n return left.event.id < right.event.id ? -1 : left.event.id > right.event.id ? 1 : 0;\n });\n}\n\nexport function createLogZipBlob(\n files: readonly ZipExportFile[],\n options: LogZipBlobOptions = {},\n): Blob {\n const normalizedFiles = createNormalizedZipFiles(files, options);\n const parts: Uint8Array[] = [];\n let centralDirectoryOffset = 0;\n for (const file of normalizedFiles) {\n parts.push(createLocalFileHeader(file), file.nameBytes, file.contentBytes);\n centralDirectoryOffset += 30 + file.nameBytes.byteLength + file.contentBytes.byteLength;\n }\n\n let centralDirectorySize = 0;\n for (const file of normalizedFiles) {\n const header = createCentralDirectoryHeader(file);\n parts.push(header, file.nameBytes);\n centralDirectorySize += header.byteLength + file.nameBytes.byteLength;\n }\n parts.push(\n createEndOfCentralDirectory(\n normalizedFiles.length,\n centralDirectorySize,\n centralDirectoryOffset,\n ),\n );\n\n return new Blob(parts as unknown as BlobPart[], { type: options.type ?? \"application/zip\" });\n}\n\nexport async function exportLogsToZip(\n source: LogZipExportSource,\n options: LogZipExportOptions = {},\n): Promise<Blob> {\n const format = options.format ?? \"ndjson\";\n const createdAt = options.createdAt ?? Date.now();\n const logFileName = options.logFileName ?? (format === \"json\" ? \"logs.json\" : \"logs.ndjson\");\n const maxEvents = normalizeMaxEvents(options.maxEvents);\n const sessionExportOptions = normalizeSessionExportOptions(options.groupBySession, logFileName);\n const recentExportOptions = normalizeRecentExportOptions(options.includeRecent, format);\n const serialize =\n options.serializeEvent ??\n ((event: LogEvent) => safeJsonStringify(event, options.stringify ?? {}));\n const exported: ExportedLogLine[] = [];\n let skippedCount = 0;\n\n for await (const event of resolveSource(source, options.query)) {\n if (maxEvents !== undefined && exported.length >= maxEvents) break;\n const mapped = options.mapEvent ? options.mapEvent(event) : event;\n if (!mapped) {\n skippedCount += 1;\n continue;\n }\n exported.push({ event: mapped, line: serialize(mapped) });\n }\n\n const files: ZipExportFile[] = [\n {\n name: logFileName,\n content: logContentForLines(\n exported.map((item) => item.line),\n format,\n ),\n lastModified: createdAt,\n },\n ];\n const sessionManifests: LogZipExportSessionManifest[] = [];\n\n if (sessionExportOptions) {\n const groups = new Map<string, ExportedLogLine[]>();\n for (const item of exported) {\n const sessionId = sessionIdForEvent(item.event, sessionExportOptions);\n const group = groups.get(sessionId) ?? [];\n group.push(item);\n groups.set(sessionId, group);\n }\n const usedFileNames = new Set(files.map((file) => normalizeZipFileName(file.name)));\n const sortedGroups = [...groups.entries()];\n sortedGroups.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0));\n for (const [sessionId, group] of sortedGroups) {\n const sessionPath = `${sessionExportOptions.directory}/${safeZipPathSegment(sessionId)}/${\n sessionExportOptions.logFileName\n }`;\n let fileName = normalizeZipFileName(sessionPath);\n let suffix = 1;\n while (usedFileNames.has(fileName)) {\n fileName = normalizeZipFileName(\n `${sessionExportOptions.directory}/${safeZipPathSegment(sessionId)}-${suffix}/${\n sessionExportOptions.logFileName\n }`,\n );\n suffix += 1;\n }\n usedFileNames.add(fileName);\n const orderedGroup = sortByEventTime(group);\n files.push({\n name: fileName,\n content: logContentForLines(\n orderedGroup.map((item) => item.line),\n format,\n ),\n lastModified: createdAt,\n });\n sessionManifests.push({\n logCount: orderedGroup.length,\n logFileName: fileName,\n sessionId,\n timeRange: eventTimeRange(orderedGroup),\n });\n }\n }\n\n let recentLogFileName: string | undefined;\n if (recentExportOptions) {\n const recentItems =\n recentExportOptions.maxEvents <= 0\n ? []\n : sortByEventTime(exported).slice(-recentExportOptions.maxEvents);\n recentLogFileName = normalizeZipFileName(recentExportOptions.logFileName);\n files.push({\n name: recentLogFileName,\n content: logContentForLines(\n recentItems.map((item) => item.line),\n format,\n ),\n lastModified: createdAt,\n });\n }\n\n if (options.includeManifest ?? true) {\n const manifest: LogZipExportManifest = {\n schema: \"loggerjs.log-export.v1\",\n createdAt: new Date(createdAt).toISOString(),\n source: options.source,\n format,\n logFileName: normalizeZipFileName(logFileName),\n logCount: exported.length,\n skippedCount,\n query: options.query,\n recentLogFileName,\n sessionCount: sessionExportOptions ? sessionManifests.length : undefined,\n sessions: sessionExportOptions ? sessionManifests : undefined,\n timeRange: eventTimeRange(exported),\n };\n files.push({\n name: options.manifestFileName ?? \"manifest.json\",\n content: `${safeJsonStringify(manifest, { space: 2 })}\\n`,\n lastModified: createdAt,\n });\n }\n\n return createLogZipBlob(files, { lastModified: createdAt });\n}\n\nexport function downloadBlob(\n blob: Blob,\n filenameOrOptions: string | DownloadBlobOptions = {},\n): string {\n const options =\n typeof filenameOrOptions === \"string\" ? { filename: filenameOrOptions } : filenameOrOptions;\n const documentRef = options.document ?? globalThis.document;\n const urlRef = options.url ?? globalThis.URL;\n if (!documentRef?.body || !urlRef?.createObjectURL || !urlRef.revokeObjectURL) {\n throw new Error(\"downloadBlob requires document and URL.createObjectURL\");\n }\n\n const href = urlRef.createObjectURL(blob);\n const anchor = documentRef.createElement(\"a\");\n anchor.href = href;\n anchor.download = options.filename ?? \"loggerjs-logs.zip\";\n anchor.style.display = \"none\";\n documentRef.body.appendChild(anchor);\n anchor.click();\n anchor.remove();\n\n const revoke = () => urlRef.revokeObjectURL(href);\n const revokeDelayMs = options.revokeDelayMs ?? 1000;\n if (revokeDelayMs <= 0) {\n revoke();\n } else {\n setTimeout(revoke, revokeDelayMs);\n }\n\n return href;\n}\n"],"mappings":"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"}