@loggerjs/browser 0.5.0 → 0.5.2
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.
- package/dist/broadcast-channel-transport.cjs +115 -0
- package/dist/broadcast-channel-transport.cjs.map +1 -0
- package/dist/broadcast-channel-transport.js +114 -0
- package/dist/broadcast-channel-transport.js.map +1 -0
- package/dist/console-integration.cjs +98 -0
- package/dist/console-integration.cjs.map +1 -0
- package/dist/console-integration.js +97 -0
- package/dist/console-integration.js.map +1 -0
- package/dist/context-propagation-integration.cjs +78 -0
- package/dist/context-propagation-integration.cjs.map +1 -0
- package/dist/context-propagation-integration.js +77 -0
- package/dist/context-propagation-integration.js.map +1 -0
- package/dist/error-integration.cjs +128 -0
- package/dist/error-integration.cjs.map +1 -0
- package/dist/error-integration.js +127 -0
- package/dist/error-integration.js.map +1 -0
- package/dist/fetch-integration.cjs +120 -0
- package/dist/fetch-integration.cjs.map +1 -0
- package/dist/fetch-integration.js +119 -0
- package/dist/fetch-integration.js.map +1 -0
- package/dist/framework-error-integration.cjs +102 -0
- package/dist/framework-error-integration.cjs.map +1 -0
- package/dist/framework-error-integration.js +101 -0
- package/dist/framework-error-integration.js.map +1 -0
- package/dist/framework-router-integrations.cjs +88 -0
- package/dist/framework-router-integrations.cjs.map +1 -0
- package/dist/framework-router-integrations.js +84 -0
- package/dist/framework-router-integrations.js.map +1 -0
- package/dist/http-transport.cjs +301 -0
- package/dist/http-transport.cjs.map +1 -0
- package/dist/http-transport.js +299 -0
- package/dist/http-transport.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-offline-queue.cjs +105 -0
- package/dist/indexeddb-offline-queue.cjs.map +1 -0
- package/dist/indexeddb-offline-queue.js +104 -0
- package/dist/indexeddb-offline-queue.js.map +1 -0
- package/dist/indexeddb-transport.cjs +531 -0
- package/dist/indexeddb-transport.cjs.map +1 -0
- package/dist/indexeddb-transport.js +530 -0
- package/dist/indexeddb-transport.js.map +1 -0
- package/dist/offline-first-transport.cjs +675 -0
- package/dist/offline-first-transport.cjs.map +1 -0
- package/dist/offline-first-transport.js +674 -0
- package/dist/offline-first-transport.js.map +1 -0
- package/dist/page-lifecycle.cjs +39 -0
- package/dist/page-lifecycle.cjs.map +1 -0
- package/dist/page-lifecycle.js +38 -0
- package/dist/page-lifecycle.js.map +1 -0
- package/dist/payload-transforms.cjs +30 -0
- package/dist/payload-transforms.cjs.map +1 -0
- package/dist/payload-transforms.js +29 -0
- package/dist/payload-transforms.js.map +1 -0
- package/dist/performance-integration.cjs +135 -0
- package/dist/performance-integration.cjs.map +1 -0
- package/dist/performance-integration.js +133 -0
- package/dist/performance-integration.js.map +1 -0
- package/dist/reporting-integration.cjs +110 -0
- package/dist/reporting-integration.cjs.map +1 -0
- package/dist/reporting-integration.js +109 -0
- package/dist/reporting-integration.js.map +1 -0
- package/dist/router-integration.cjs +97 -0
- package/dist/router-integration.cjs.map +1 -0
- package/dist/router-integration.js +96 -0
- package/dist/router-integration.js.map +1 -0
- package/dist/runtime-host-integration.cjs +181 -0
- package/dist/runtime-host-integration.cjs.map +1 -0
- package/dist/runtime-host-integration.js +180 -0
- package/dist/runtime-host-integration.js.map +1 -0
- package/dist/service-worker-integration.cjs +109 -0
- package/dist/service-worker-integration.cjs.map +1 -0
- package/dist/service-worker-integration.js +108 -0
- package/dist/service-worker-integration.js.map +1 -0
- package/dist/service-worker-transport.cjs +184 -0
- package/dist/service-worker-transport.cjs.map +1 -0
- package/dist/service-worker-transport.js +183 -0
- package/dist/service-worker-transport.js.map +1 -0
- package/dist/user-action-integration.cjs +100 -0
- package/dist/user-action-integration.cjs.map +1 -0
- package/dist/user-action-integration.js +99 -0
- package/dist/user-action-integration.js.map +1 -0
- package/dist/web-vitals-integration.cjs +157 -0
- package/dist/web-vitals-integration.cjs.map +1 -0
- package/dist/web-vitals-integration.js +156 -0
- package/dist/web-vitals-integration.js.map +1 -0
- package/dist/websocket-integration.cjs +158 -0
- package/dist/websocket-integration.cjs.map +1 -0
- package/dist/websocket-integration.js +157 -0
- package/dist/websocket-integration.js.map +1 -0
- package/dist/websocket-transport.cjs +187 -0
- package/dist/websocket-transport.cjs.map +1 -0
- package/dist/websocket-transport.js +186 -0
- package/dist/websocket-transport.js.map +1 -0
- package/dist/xhr-integration.cjs +107 -0
- package/dist/xhr-integration.cjs.map +1 -0
- package/dist/xhr-integration.js +106 -0
- package/dist/xhr-integration.js.map +1 -0
- package/dist/zip-export.cjs +241 -0
- package/dist/zip-export.cjs.map +1 -0
- package/dist/zip-export.js +238 -0
- package/dist/zip-export.js.map +1 -0
- package/package.json +104 -77
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _loggerjs_core = require("@loggerjs/core");
|
|
3
|
+
//#region src/indexeddb-transport.ts
|
|
4
|
+
const DEFAULT_DB_NAME = "loggerjs";
|
|
5
|
+
const DEFAULT_STORE_NAME = "logs";
|
|
6
|
+
const DB_VERSION = 2;
|
|
7
|
+
const ORDER_INDEX_NAME = "createdAtSeq";
|
|
8
|
+
function normalizePositiveInteger(value, fallback) {
|
|
9
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
10
|
+
return Math.max(0, Math.floor(value));
|
|
11
|
+
}
|
|
12
|
+
function nowMs() {
|
|
13
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
14
|
+
}
|
|
15
|
+
function incrementRecord(record, key, amount = 1) {
|
|
16
|
+
record[key] = (record[key] ?? 0) + amount;
|
|
17
|
+
}
|
|
18
|
+
function requestToPromise(request) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
request.addEventListener("success", () => resolve(request.result), { once: true });
|
|
21
|
+
request.addEventListener("error", () => reject(request.error ?? /* @__PURE__ */ new Error("IndexedDB request failed")), { once: true });
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function transactionToPromise(transaction) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
transaction.addEventListener("complete", () => resolve(), { once: true });
|
|
27
|
+
transaction.addEventListener("abort", () => reject(transaction.error ?? /* @__PURE__ */ new Error("IndexedDB transaction aborted")), { once: true });
|
|
28
|
+
transaction.addEventListener("error", () => reject(transaction.error ?? /* @__PURE__ */ new Error("IndexedDB transaction failed")), { once: true });
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function payloadByteLength(payload) {
|
|
32
|
+
if (typeof payload !== "string") return payload.byteLength;
|
|
33
|
+
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(payload).byteLength;
|
|
34
|
+
return new Blob([payload]).size;
|
|
35
|
+
}
|
|
36
|
+
function compareEntries(a, b) {
|
|
37
|
+
if (a.createdAt !== b.createdAt) return a.createdAt - b.createdAt;
|
|
38
|
+
if (a.seq !== b.seq) return a.seq - b.seq;
|
|
39
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
40
|
+
}
|
|
41
|
+
function orderEntries(entries, order = "asc") {
|
|
42
|
+
const sorted = [...entries].sort(compareEntries);
|
|
43
|
+
if (order === "asc") return sorted;
|
|
44
|
+
const reversed = [];
|
|
45
|
+
for (let index = sorted.length - 1; index >= 0; index -= 1) reversed.push(sorted[index]);
|
|
46
|
+
return reversed;
|
|
47
|
+
}
|
|
48
|
+
function indexExists(store, name) {
|
|
49
|
+
return store.indexNames.contains(name);
|
|
50
|
+
}
|
|
51
|
+
function ensureIndex(store, name, keyPath) {
|
|
52
|
+
if (!indexExists(store, name)) store.createIndex(name, keyPath);
|
|
53
|
+
}
|
|
54
|
+
function getStoreIndex(store, name) {
|
|
55
|
+
const maybeStore = store;
|
|
56
|
+
if (typeof maybeStore.index !== "function" || !indexExists(store, name)) return void 0;
|
|
57
|
+
return maybeStore.index(name);
|
|
58
|
+
}
|
|
59
|
+
function getStorageBucketManager() {
|
|
60
|
+
return globalThis.navigator?.storageBuckets;
|
|
61
|
+
}
|
|
62
|
+
function storageBucketOpenOptions(options) {
|
|
63
|
+
if (options.storageBucketDurability === void 0 && options.storageBucketPersisted === void 0) return;
|
|
64
|
+
return {
|
|
65
|
+
durability: options.storageBucketDurability,
|
|
66
|
+
persisted: options.storageBucketPersisted
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function decodeEntry(entry, codec) {
|
|
70
|
+
if (!codec.decode) throw new Error("indexedDbTransport query requires a codec with decode");
|
|
71
|
+
const decoded = codec.decode(entry.payload);
|
|
72
|
+
return Array.isArray(decoded) ? decoded[0] : decoded;
|
|
73
|
+
}
|
|
74
|
+
function shouldKeepEntry(entry, options) {
|
|
75
|
+
if (options.from !== void 0 && entry.createdAt < options.from) return false;
|
|
76
|
+
if (options.to !== void 0 && entry.createdAt > options.to) return false;
|
|
77
|
+
if (options.minLevel !== void 0 && entry.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return false;
|
|
78
|
+
if (options.logger !== void 0 && entry.logger !== options.logger) return false;
|
|
79
|
+
if (options.type !== void 0 && entry.type !== options.type) return false;
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
function orderRangeForQuery(options) {
|
|
83
|
+
if (typeof IDBKeyRange === "undefined") return void 0;
|
|
84
|
+
const lower = options.from === void 0 ? void 0 : [
|
|
85
|
+
options.from,
|
|
86
|
+
Number.MIN_SAFE_INTEGER,
|
|
87
|
+
""
|
|
88
|
+
];
|
|
89
|
+
const upper = options.to === void 0 ? void 0 : [
|
|
90
|
+
options.to,
|
|
91
|
+
Number.MAX_SAFE_INTEGER,
|
|
92
|
+
""
|
|
93
|
+
];
|
|
94
|
+
if (lower && upper) return IDBKeyRange.bound(lower, upper);
|
|
95
|
+
if (lower) return IDBKeyRange.lowerBound(lower);
|
|
96
|
+
if (upper) return IDBKeyRange.upperBound(upper);
|
|
97
|
+
}
|
|
98
|
+
function iterateCursor(request, visit) {
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
request.addEventListener("success", () => {
|
|
101
|
+
const cursor = request.result;
|
|
102
|
+
if (!cursor) {
|
|
103
|
+
resolve();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
if (visit(cursor) === false) {
|
|
108
|
+
resolve();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
cursor.continue();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
reject(error);
|
|
114
|
+
}
|
|
115
|
+
}, false);
|
|
116
|
+
request.addEventListener("error", () => reject(request.error ?? /* @__PURE__ */ new Error("IndexedDB cursor failed")), { once: true });
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function indexedDbTransport(options = {}) {
|
|
120
|
+
const dbName = options.dbName ?? DEFAULT_DB_NAME;
|
|
121
|
+
const storeName = options.storeName ?? DEFAULT_STORE_NAME;
|
|
122
|
+
const codec = options.codec ?? (0, _loggerjs_core.safeJsonCodec)();
|
|
123
|
+
const batchSize = normalizePositiveInteger(options.batchSize, 100);
|
|
124
|
+
const flushIntervalMs = normalizePositiveInteger(options.flushIntervalMs, 1e3);
|
|
125
|
+
const maxBufferSize = normalizePositiveInteger(options.maxBufferSize, 1e3);
|
|
126
|
+
const maxEntries = normalizePositiveInteger(options.maxEntries, 5e4);
|
|
127
|
+
const maxBytes = normalizePositiveInteger(options.maxBytes, 50 * 1024 * 1024);
|
|
128
|
+
const dropPolicy = options.dropPolicy ?? "drop-oldest";
|
|
129
|
+
const flushOnPageHide = options.flushOnPageHide ?? true;
|
|
130
|
+
const durability = options.durability ?? "default";
|
|
131
|
+
const rootIndexedDB = options.indexedDB ?? globalThis.indexedDB;
|
|
132
|
+
const transactionOptions = durability === "default" ? void 0 : { durability };
|
|
133
|
+
const buffer = [];
|
|
134
|
+
let dbPromise;
|
|
135
|
+
let indexedDBFactoryPromise;
|
|
136
|
+
let timer;
|
|
137
|
+
let flushPromise;
|
|
138
|
+
let lastContext;
|
|
139
|
+
let closed = false;
|
|
140
|
+
const statsState = {
|
|
141
|
+
bufferDepth: 0,
|
|
142
|
+
maxBufferDepth: 0,
|
|
143
|
+
pendingFlush: false,
|
|
144
|
+
enqueued: 0,
|
|
145
|
+
persisted: 0,
|
|
146
|
+
dropped: 0,
|
|
147
|
+
droppedByReason: {},
|
|
148
|
+
persistedDropped: 0,
|
|
149
|
+
persistedDroppedByReason: {},
|
|
150
|
+
flushes: 0,
|
|
151
|
+
flushErrors: 0,
|
|
152
|
+
lastFlushBatchSize: 0,
|
|
153
|
+
lastFlushDurationMs: 0,
|
|
154
|
+
prunes: 0,
|
|
155
|
+
pruneFallbacks: 0,
|
|
156
|
+
lastPruneDurationMs: 0,
|
|
157
|
+
queries: 0,
|
|
158
|
+
queryFallbacks: 0,
|
|
159
|
+
lastQueryDurationMs: 0,
|
|
160
|
+
databaseOpenCount: 0,
|
|
161
|
+
storageBucketFallbacks: 0,
|
|
162
|
+
transactionOptionFallbacks: 0,
|
|
163
|
+
errors: 0,
|
|
164
|
+
errorsByOperation: {}
|
|
165
|
+
};
|
|
166
|
+
const snapshotStats = () => ({
|
|
167
|
+
...statsState,
|
|
168
|
+
bufferDepth: buffer.length,
|
|
169
|
+
droppedByReason: { ...statsState.droppedByReason },
|
|
170
|
+
errorsByOperation: { ...statsState.errorsByOperation },
|
|
171
|
+
pendingFlush: flushPromise !== void 0,
|
|
172
|
+
persistedDroppedByReason: { ...statsState.persistedDroppedByReason }
|
|
173
|
+
});
|
|
174
|
+
const reportInternalError = (error, operation) => {
|
|
175
|
+
statsState.errors += 1;
|
|
176
|
+
incrementRecord(statsState.errorsByOperation, operation);
|
|
177
|
+
lastContext?.reportInternalError(error, {
|
|
178
|
+
operation,
|
|
179
|
+
phase: "transport",
|
|
180
|
+
transport: options.name ?? "indexeddb"
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
const dropEvent = (event, reason) => {
|
|
184
|
+
statsState.dropped += 1;
|
|
185
|
+
incrementRecord(statsState.droppedByReason, reason);
|
|
186
|
+
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.indexeddb.dropped");
|
|
187
|
+
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.indexeddb.dropped.${reason}`);
|
|
188
|
+
options.onDrop?.(event, reason);
|
|
189
|
+
};
|
|
190
|
+
const dropPersistedEntry = (entry, reason) => {
|
|
191
|
+
statsState.persistedDropped += 1;
|
|
192
|
+
incrementRecord(statsState.persistedDroppedByReason, reason);
|
|
193
|
+
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.indexeddb.persisted.dropped");
|
|
194
|
+
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.indexeddb.persisted.dropped.${reason}`);
|
|
195
|
+
options.onPersistedDrop?.(entry, reason);
|
|
196
|
+
};
|
|
197
|
+
const resolveIndexedDBFactory = async () => {
|
|
198
|
+
if (options.indexedDB || !options.storageBucketName) return rootIndexedDB;
|
|
199
|
+
indexedDBFactoryPromise ??= (async () => {
|
|
200
|
+
const manager = getStorageBucketManager();
|
|
201
|
+
if (!manager) {
|
|
202
|
+
statsState.storageBucketFallbacks += 1;
|
|
203
|
+
return rootIndexedDB;
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
const bucket = await manager.open(options.storageBucketName, storageBucketOpenOptions(options));
|
|
207
|
+
if (bucket.indexedDB) return bucket.indexedDB;
|
|
208
|
+
statsState.storageBucketFallbacks += 1;
|
|
209
|
+
return rootIndexedDB;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
statsState.storageBucketFallbacks += 1;
|
|
212
|
+
reportInternalError(error, "storage-bucket-open");
|
|
213
|
+
return rootIndexedDB;
|
|
214
|
+
}
|
|
215
|
+
})();
|
|
216
|
+
return indexedDBFactoryPromise;
|
|
217
|
+
};
|
|
218
|
+
const openDb = () => {
|
|
219
|
+
dbPromise ??= resolveIndexedDBFactory().then((idb) => {
|
|
220
|
+
if (!idb) throw new Error("IndexedDB is not available for indexedDbTransport");
|
|
221
|
+
return new Promise((resolve, reject) => {
|
|
222
|
+
const request = idb.open(dbName, DB_VERSION);
|
|
223
|
+
request.addEventListener("upgradeneeded", () => {
|
|
224
|
+
const db = request.result;
|
|
225
|
+
const store = db.objectStoreNames.contains(storeName) ? request.transaction?.objectStore(storeName) : db.createObjectStore(storeName, { keyPath: "id" });
|
|
226
|
+
if (!store) return;
|
|
227
|
+
ensureIndex(store, "createdAt", "createdAt");
|
|
228
|
+
ensureIndex(store, "level", "level");
|
|
229
|
+
ensureIndex(store, "logger", "logger");
|
|
230
|
+
ensureIndex(store, "type", "type");
|
|
231
|
+
ensureIndex(store, ORDER_INDEX_NAME, [
|
|
232
|
+
"createdAt",
|
|
233
|
+
"seq",
|
|
234
|
+
"id"
|
|
235
|
+
]);
|
|
236
|
+
});
|
|
237
|
+
request.addEventListener("success", () => {
|
|
238
|
+
statsState.databaseOpenCount += 1;
|
|
239
|
+
resolve(request.result);
|
|
240
|
+
}, { once: true });
|
|
241
|
+
request.addEventListener("error", () => reject(request.error ?? /* @__PURE__ */ new Error("IndexedDB open failed")), { once: true });
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
return dbPromise;
|
|
245
|
+
};
|
|
246
|
+
const createTransaction = (db, mode) => {
|
|
247
|
+
if (mode !== "readwrite" || !transactionOptions) return db.transaction(storeName, mode);
|
|
248
|
+
try {
|
|
249
|
+
return db.transaction(storeName, mode, transactionOptions);
|
|
250
|
+
} catch {
|
|
251
|
+
statsState.transactionOptionFallbacks += 1;
|
|
252
|
+
return db.transaction(storeName, mode);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
const withStore = async (mode, run) => {
|
|
256
|
+
const tx = createTransaction(await openDb(), mode);
|
|
257
|
+
const settled = transactionToPromise(tx);
|
|
258
|
+
try {
|
|
259
|
+
const result = await run(tx.objectStore(storeName), tx);
|
|
260
|
+
await settled;
|
|
261
|
+
return result;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (tx.error === null) try {
|
|
264
|
+
tx.abort();
|
|
265
|
+
} catch {}
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
const getEntries = async () => orderEntries(await withStore("readonly", (store) => requestToPromise(store.getAll())));
|
|
270
|
+
const countEntries = async () => withStore("readonly", (store) => {
|
|
271
|
+
const maybeStore = store;
|
|
272
|
+
if (typeof maybeStore.count !== "function") return void 0;
|
|
273
|
+
return requestToPromise(maybeStore.count.call(store));
|
|
274
|
+
});
|
|
275
|
+
const deleteEntries = async (entries, reason) => {
|
|
276
|
+
if (entries.length === 0) return;
|
|
277
|
+
await withStore("readwrite", async (store) => {
|
|
278
|
+
for (const entry of entries) {
|
|
279
|
+
await requestToPromise(store.delete(entry.id));
|
|
280
|
+
dropPersistedEntry(entry, reason);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
const deleteByCursor = async (reason, openCursor, shouldDelete) => withStore("readwrite", async (store) => {
|
|
285
|
+
const request = openCursor(store);
|
|
286
|
+
if (!request) return void 0;
|
|
287
|
+
let deleted = 0;
|
|
288
|
+
await iterateCursor(request, (cursor) => {
|
|
289
|
+
const entry = cursor.value;
|
|
290
|
+
if (!shouldDelete(entry, deleted)) return false;
|
|
291
|
+
cursor.delete();
|
|
292
|
+
deleted += 1;
|
|
293
|
+
dropPersistedEntry(entry, reason);
|
|
294
|
+
return true;
|
|
295
|
+
});
|
|
296
|
+
return deleted;
|
|
297
|
+
});
|
|
298
|
+
const deleteExpiredByCursor = async (cutoff) => {
|
|
299
|
+
if (typeof IDBKeyRange === "undefined") return void 0;
|
|
300
|
+
const range = IDBKeyRange.upperBound(cutoff, true);
|
|
301
|
+
return deleteByCursor("ttl", (store) => getStoreIndex(store, "createdAt")?.openCursor(range), () => true);
|
|
302
|
+
};
|
|
303
|
+
const deleteOldestByCursor = async (reason, count) => {
|
|
304
|
+
if (count <= 0) return 0;
|
|
305
|
+
return deleteByCursor(reason, (store) => getStoreIndex(store, ORDER_INDEX_NAME)?.openCursor(), (_entry, deleted) => deleted < count);
|
|
306
|
+
};
|
|
307
|
+
const deleteOldestBytesByCursor = async (bytesToFree) => {
|
|
308
|
+
if (bytesToFree <= 0) return 0;
|
|
309
|
+
let freed = 0;
|
|
310
|
+
return deleteByCursor("max-bytes", (store) => getStoreIndex(store, ORDER_INDEX_NAME)?.openCursor(), (entry) => {
|
|
311
|
+
if (freed >= bytesToFree) return false;
|
|
312
|
+
freed += entry.byteLength;
|
|
313
|
+
return true;
|
|
314
|
+
});
|
|
315
|
+
};
|
|
316
|
+
const sumBytesByCursor = async () => withStore("readonly", async (store) => {
|
|
317
|
+
const index = getStoreIndex(store, ORDER_INDEX_NAME);
|
|
318
|
+
if (!index) return void 0;
|
|
319
|
+
let total = 0;
|
|
320
|
+
await iterateCursor(index.openCursor(), (cursor) => {
|
|
321
|
+
total += cursor.value.byteLength;
|
|
322
|
+
});
|
|
323
|
+
return total;
|
|
324
|
+
});
|
|
325
|
+
const queryEntriesByCursor = async (queryOptions) => withStore("readonly", async (store) => {
|
|
326
|
+
const index = getStoreIndex(store, ORDER_INDEX_NAME);
|
|
327
|
+
if (!index) return void 0;
|
|
328
|
+
const entries = [];
|
|
329
|
+
const limit = queryOptions.limit;
|
|
330
|
+
if (limit !== void 0 && limit <= 0) return entries;
|
|
331
|
+
await iterateCursor(index.openCursor(orderRangeForQuery(queryOptions), queryOptions.order === "desc" ? "prev" : "next"), (cursor) => {
|
|
332
|
+
const entry = cursor.value;
|
|
333
|
+
if (!shouldKeepEntry(entry, queryOptions)) return true;
|
|
334
|
+
entries.push(entry);
|
|
335
|
+
return limit === void 0 || entries.length < limit;
|
|
336
|
+
});
|
|
337
|
+
return entries;
|
|
338
|
+
});
|
|
339
|
+
const pruneWithEntries = async () => {
|
|
340
|
+
const now = Date.now();
|
|
341
|
+
let entries = await getEntries();
|
|
342
|
+
const expired = options.ttlMs === void 0 ? [] : entries.filter((entry) => now - entry.createdAt > options.ttlMs);
|
|
343
|
+
if (expired.length > 0) {
|
|
344
|
+
await deleteEntries(expired, "ttl");
|
|
345
|
+
const expiredIds = new Set(expired.map((entry) => entry.id));
|
|
346
|
+
entries = entries.filter((entry) => !expiredIds.has(entry.id));
|
|
347
|
+
}
|
|
348
|
+
if (maxEntries >= 0 && entries.length > maxEntries) {
|
|
349
|
+
await deleteEntries(entries.slice(0, entries.length - maxEntries), "max-entries");
|
|
350
|
+
entries = entries.slice(entries.length - maxEntries);
|
|
351
|
+
}
|
|
352
|
+
if (maxBytes >= 0) {
|
|
353
|
+
let total = entries.reduce((sum, entry) => sum + entry.byteLength, 0);
|
|
354
|
+
const dropped = [];
|
|
355
|
+
for (const entry of entries) {
|
|
356
|
+
if (total <= maxBytes) break;
|
|
357
|
+
total -= entry.byteLength;
|
|
358
|
+
dropped.push(entry);
|
|
359
|
+
}
|
|
360
|
+
await deleteEntries(dropped, "max-bytes");
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
const pruneWithCursors = async () => {
|
|
364
|
+
if (options.ttlMs !== void 0) {
|
|
365
|
+
if (await deleteExpiredByCursor(Date.now() - options.ttlMs) === void 0) return false;
|
|
366
|
+
}
|
|
367
|
+
if (maxEntries >= 0) {
|
|
368
|
+
const persistedCount = await countEntries();
|
|
369
|
+
if (persistedCount === void 0) return false;
|
|
370
|
+
if (persistedCount > maxEntries) {
|
|
371
|
+
if (await deleteOldestByCursor("max-entries", persistedCount - maxEntries) === void 0) return false;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (maxBytes >= 0) {
|
|
375
|
+
const totalBytes = await sumBytesByCursor();
|
|
376
|
+
if (totalBytes === void 0) return false;
|
|
377
|
+
if (totalBytes > maxBytes) {
|
|
378
|
+
if (await deleteOldestBytesByCursor(totalBytes - maxBytes) === void 0) return false;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return true;
|
|
382
|
+
};
|
|
383
|
+
const prune = async () => {
|
|
384
|
+
const startedAt = nowMs();
|
|
385
|
+
try {
|
|
386
|
+
if (await pruneWithCursors()) return;
|
|
387
|
+
statsState.pruneFallbacks += 1;
|
|
388
|
+
await pruneWithEntries();
|
|
389
|
+
} finally {
|
|
390
|
+
statsState.prunes += 1;
|
|
391
|
+
statsState.lastPruneDurationMs = nowMs() - startedAt;
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
const eventToEntry = (event) => {
|
|
395
|
+
const payload = codec.encode(event);
|
|
396
|
+
return {
|
|
397
|
+
id: event.id,
|
|
398
|
+
seq: event.seq,
|
|
399
|
+
createdAt: event.time,
|
|
400
|
+
level: event.level,
|
|
401
|
+
levelName: event.levelName,
|
|
402
|
+
logger: event.logger,
|
|
403
|
+
type: event.type,
|
|
404
|
+
byteLength: payloadByteLength(payload),
|
|
405
|
+
payload
|
|
406
|
+
};
|
|
407
|
+
};
|
|
408
|
+
const writeBatch = async (events) => {
|
|
409
|
+
if (events.length === 0) return;
|
|
410
|
+
const startedAt = nowMs();
|
|
411
|
+
statsState.lastFlushBatchSize = events.length;
|
|
412
|
+
const entries = events.map(eventToEntry);
|
|
413
|
+
try {
|
|
414
|
+
await withStore("readwrite", (store) => {
|
|
415
|
+
for (const entry of entries) store.put(entry);
|
|
416
|
+
});
|
|
417
|
+
statsState.persisted += entries.length;
|
|
418
|
+
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.indexeddb.persisted", entries.length);
|
|
419
|
+
await prune();
|
|
420
|
+
statsState.flushes += 1;
|
|
421
|
+
} catch (error) {
|
|
422
|
+
statsState.flushErrors += 1;
|
|
423
|
+
throw error;
|
|
424
|
+
} finally {
|
|
425
|
+
statsState.lastFlushDurationMs = nowMs() - startedAt;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
const clearTimer = () => {
|
|
429
|
+
if (timer) clearTimeout(timer);
|
|
430
|
+
timer = void 0;
|
|
431
|
+
};
|
|
432
|
+
const flushPending = async () => {
|
|
433
|
+
if (flushPromise) return flushPromise;
|
|
434
|
+
clearTimer();
|
|
435
|
+
flushPromise = writeBatch(buffer.splice(0, buffer.length)).finally(() => {
|
|
436
|
+
flushPromise = void 0;
|
|
437
|
+
if (buffer.length > 0 && !closed) schedule();
|
|
438
|
+
});
|
|
439
|
+
return flushPromise;
|
|
440
|
+
};
|
|
441
|
+
const schedule = () => {
|
|
442
|
+
if (timer || flushIntervalMs <= 0 || closed) return;
|
|
443
|
+
timer = setTimeout(() => {
|
|
444
|
+
flushPending().catch((error) => reportInternalError(error, "flush"));
|
|
445
|
+
}, flushIntervalMs);
|
|
446
|
+
};
|
|
447
|
+
const onPageHide = () => {
|
|
448
|
+
flushPending().catch((error) => reportInternalError(error, "pagehide-flush"));
|
|
449
|
+
};
|
|
450
|
+
const onVisibilityChange = () => {
|
|
451
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") flushPending().catch((error) => reportInternalError(error, "visibility-flush"));
|
|
452
|
+
};
|
|
453
|
+
if (flushOnPageHide) {
|
|
454
|
+
globalThis.addEventListener?.("pagehide", onPageHide);
|
|
455
|
+
globalThis.addEventListener?.("visibilitychange", onVisibilityChange);
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
name: options.name ?? "indexeddb",
|
|
459
|
+
minLevel: options.minLevel,
|
|
460
|
+
log(event, context) {
|
|
461
|
+
if (closed) return;
|
|
462
|
+
if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
|
|
463
|
+
lastContext = context;
|
|
464
|
+
if (maxBufferSize === 0 || buffer.length >= maxBufferSize) {
|
|
465
|
+
if (dropPolicy === "drop-newest") {
|
|
466
|
+
dropEvent(event, "buffer-full");
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const dropped = buffer.shift();
|
|
470
|
+
if (dropped) dropEvent(dropped, "buffer-full");
|
|
471
|
+
}
|
|
472
|
+
buffer.push(event);
|
|
473
|
+
statsState.enqueued += 1;
|
|
474
|
+
statsState.maxBufferDepth = Math.max(statsState.maxBufferDepth, buffer.length);
|
|
475
|
+
if (buffer.length >= batchSize || flushIntervalMs === 0) flushPending().catch((error) => reportInternalError(error, "flush"));
|
|
476
|
+
else schedule();
|
|
477
|
+
},
|
|
478
|
+
flush() {
|
|
479
|
+
return flushPending();
|
|
480
|
+
},
|
|
481
|
+
async count() {
|
|
482
|
+
await flushPending();
|
|
483
|
+
return await countEntries() ?? (await getEntries()).length;
|
|
484
|
+
},
|
|
485
|
+
async clear() {
|
|
486
|
+
buffer.length = 0;
|
|
487
|
+
clearTimer();
|
|
488
|
+
await withStore("readwrite", (store) => requestToPromise(store.clear()));
|
|
489
|
+
},
|
|
490
|
+
async remove(ids) {
|
|
491
|
+
await flushPending();
|
|
492
|
+
const items = typeof ids === "string" ? [ids] : ids;
|
|
493
|
+
if (items.length === 0) return;
|
|
494
|
+
await withStore("readwrite", async (store) => {
|
|
495
|
+
for (const id of items) await requestToPromise(store.delete(id));
|
|
496
|
+
});
|
|
497
|
+
},
|
|
498
|
+
async *query(queryOptions = {}) {
|
|
499
|
+
await flushPending();
|
|
500
|
+
const startedAt = nowMs();
|
|
501
|
+
const cursorEntries = await queryEntriesByCursor(queryOptions);
|
|
502
|
+
if (cursorEntries === void 0) statsState.queryFallbacks += 1;
|
|
503
|
+
const entries = cursorEntries ?? orderEntries(await getEntries(), queryOptions.order ?? "asc").filter((entry) => shouldKeepEntry(entry, queryOptions)).slice(0, queryOptions.limit);
|
|
504
|
+
statsState.queries += 1;
|
|
505
|
+
statsState.lastQueryDurationMs = nowMs() - startedAt;
|
|
506
|
+
for (const entry of entries) {
|
|
507
|
+
const event = decodeEntry(entry, codec);
|
|
508
|
+
if (event) yield event;
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
stats() {
|
|
512
|
+
return snapshotStats();
|
|
513
|
+
},
|
|
514
|
+
close() {
|
|
515
|
+
closed = true;
|
|
516
|
+
clearTimer();
|
|
517
|
+
globalThis.removeEventListener?.("pagehide", onPageHide);
|
|
518
|
+
globalThis.removeEventListener?.("visibilitychange", onVisibilityChange);
|
|
519
|
+
const flushed = flushPending();
|
|
520
|
+
const promise = dbPromise;
|
|
521
|
+
dbPromise = void 0;
|
|
522
|
+
return flushed.finally(() => {
|
|
523
|
+
promise?.then((db) => db.close());
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
529
|
+
exports.indexedDbTransport = indexedDbTransport;
|
|
530
|
+
|
|
531
|
+
//# sourceMappingURL=indexeddb-transport.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexeddb-transport.cjs","names":[],"sources":["../src/indexeddb-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"],"mappings":"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"}
|