@loggerjs/browser 0.5.2 → 0.5.4
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/README.md +40 -2
- package/dist/index.cjs +641 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +641 -41
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-transport.cjs +529 -27
- package/dist/indexeddb-transport.cjs.map +1 -1
- package/dist/indexeddb-transport.d.ts +41 -0
- package/dist/indexeddb-transport.d.ts.map +1 -1
- package/dist/indexeddb-transport.js +530 -28
- package/dist/indexeddb-transport.js.map +1 -1
- package/dist/offline-first-transport.cjs +532 -28
- package/dist/offline-first-transport.cjs.map +1 -1
- package/dist/offline-first-transport.d.ts.map +1 -1
- package/dist/offline-first-transport.js +533 -29
- package/dist/offline-first-transport.js.map +1 -1
- package/dist/zip-export.cjs +109 -13
- package/dist/zip-export.cjs.map +1 -1
- package/dist/zip-export.d.ts +24 -0
- package/dist/zip-export.d.ts.map +1 -1
- package/dist/zip-export.js +109 -13
- package/dist/zip-export.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,13 +1,99 @@
|
|
|
1
|
-
import { incrementLoggerMetaCounter, recordToEvent, retryTransport, safeJsonCodec, toLevelValue } from "@loggerjs/core";
|
|
1
|
+
import { incrementLoggerMetaCounter, recordToEvent, retryTransport, safeJsonCodec, safeJsonStringify, toLevelValue } from "@loggerjs/core";
|
|
2
2
|
//#region src/indexeddb-transport.ts
|
|
3
3
|
const DEFAULT_DB_NAME = "loggerjs";
|
|
4
4
|
const DEFAULT_STORE_NAME = "logs";
|
|
5
|
-
const
|
|
5
|
+
const DEFAULT_SESSION_CONTEXT_KEY = "sessionId";
|
|
6
|
+
const DEFAULT_SPILL_MAX_ENTRIES = 200;
|
|
7
|
+
const DEFAULT_SPILL_MAX_BYTES = 512 * 1024;
|
|
8
|
+
const DEFAULT_SPILL_MAX_ORIGIN_BYTES = 5 * 1024 * 1024;
|
|
9
|
+
const DB_VERSION = 4;
|
|
6
10
|
const ORDER_INDEX_NAME = "createdAtSeq";
|
|
11
|
+
const SESSION_INDEX_NAME = "sessionId";
|
|
12
|
+
const SESSION_ORDER_INDEX_NAME = "sessionIdCreatedAtSeq";
|
|
13
|
+
const SESSION_STORE_SUFFIX = "__sessions";
|
|
14
|
+
const SESSION_LAST_SEEN_INDEX_NAME = "lastSeenSessionId";
|
|
15
|
+
const SPILL_SCHEMA = "loggerjs.indexeddb-spill.v1";
|
|
7
16
|
function normalizePositiveInteger(value, fallback) {
|
|
8
17
|
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
9
18
|
return Math.max(0, Math.floor(value));
|
|
10
19
|
}
|
|
20
|
+
let fallbackSessionSeq = 0;
|
|
21
|
+
function defaultPageSessionId() {
|
|
22
|
+
const randomUUID = globalThis.crypto?.randomUUID;
|
|
23
|
+
if (randomUUID) return randomUUID.call(globalThis.crypto);
|
|
24
|
+
return `${Date.now().toString(36)}-${(fallbackSessionSeq++).toString(36)}`;
|
|
25
|
+
}
|
|
26
|
+
function normalizeSessionId(value) {
|
|
27
|
+
if (typeof value !== "string") return void 0;
|
|
28
|
+
const normalized = value.trim();
|
|
29
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
30
|
+
}
|
|
31
|
+
function normalizeSessionOptions(session) {
|
|
32
|
+
if (session === false) return void 0;
|
|
33
|
+
if (typeof session === "string") return {
|
|
34
|
+
contextKey: DEFAULT_SESSION_CONTEXT_KEY,
|
|
35
|
+
id: normalizeSessionId(session) ?? defaultPageSessionId()
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
contextKey: session?.contextKey ?? DEFAULT_SESSION_CONTEXT_KEY,
|
|
39
|
+
getId: session?.getId,
|
|
40
|
+
id: normalizeSessionId(session?.id) ?? defaultPageSessionId()
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function resolveLocalStorage(storage) {
|
|
44
|
+
if (storage) return storage;
|
|
45
|
+
try {
|
|
46
|
+
return globalThis.localStorage;
|
|
47
|
+
} catch {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function normalizeLocalStorageSpillOptions(spill, dbName, storeName) {
|
|
52
|
+
if (!spill) return void 0;
|
|
53
|
+
const storage = resolveLocalStorage(spill.storage);
|
|
54
|
+
if (!storage) return void 0;
|
|
55
|
+
const namespace = spill.namespace ?? `${dbName}:${storeName}`;
|
|
56
|
+
return {
|
|
57
|
+
drainOnCreate: spill.drainOnCreate ?? true,
|
|
58
|
+
key: `loggerjs:spill:v1:${namespace}`,
|
|
59
|
+
maxBytes: normalizePositiveInteger(spill.maxBytes, DEFAULT_SPILL_MAX_BYTES),
|
|
60
|
+
maxEntries: normalizePositiveInteger(spill.maxEntries, DEFAULT_SPILL_MAX_ENTRIES),
|
|
61
|
+
maxOriginBytes: normalizePositiveInteger(spill.maxOriginBytes, DEFAULT_SPILL_MAX_ORIGIN_BYTES),
|
|
62
|
+
minLevelValue: spill.minLevel === void 0 ? void 0 : toLevelValue(spill.minLevel),
|
|
63
|
+
onDrop: spill.onDrop,
|
|
64
|
+
spillOnPageHide: spill.spillOnPageHide ?? true,
|
|
65
|
+
storage
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function storageByteLength(key, value) {
|
|
69
|
+
return 2 * (key.length + value.length);
|
|
70
|
+
}
|
|
71
|
+
function estimateLocalStorageBytes(storage) {
|
|
72
|
+
try {
|
|
73
|
+
let total = 0;
|
|
74
|
+
for (let index = 0; index < storage.length; index += 1) {
|
|
75
|
+
const key = storage.key(index);
|
|
76
|
+
if (key === null) continue;
|
|
77
|
+
total += storageByteLength(key, storage.getItem(key) ?? "");
|
|
78
|
+
}
|
|
79
|
+
return total;
|
|
80
|
+
} catch {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function isQuotaExceededError(error) {
|
|
85
|
+
return typeof DOMException !== "undefined" && error instanceof DOMException && (error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED");
|
|
86
|
+
}
|
|
87
|
+
function isSpillLogEvent(value) {
|
|
88
|
+
if (!value || typeof value !== "object") return false;
|
|
89
|
+
const event = value;
|
|
90
|
+
return typeof event.id === "string" && typeof event.time === "number" && typeof event.seq === "number" && typeof event.level === "number" && typeof event.levelName === "string" && typeof event.logger === "string" && typeof event.message === "string";
|
|
91
|
+
}
|
|
92
|
+
function parseSpillPayload(raw) {
|
|
93
|
+
const parsed = JSON.parse(raw);
|
|
94
|
+
if (parsed.schema !== SPILL_SCHEMA || !Array.isArray(parsed.entries)) return void 0;
|
|
95
|
+
return parsed.entries.filter(isSpillLogEvent);
|
|
96
|
+
}
|
|
11
97
|
function nowMs() {
|
|
12
98
|
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
13
99
|
}
|
|
@@ -50,6 +136,9 @@ function indexExists(store, name) {
|
|
|
50
136
|
function ensureIndex(store, name, keyPath) {
|
|
51
137
|
if (!indexExists(store, name)) store.createIndex(name, keyPath);
|
|
52
138
|
}
|
|
139
|
+
function sessionStoreNameFor(storeName) {
|
|
140
|
+
return `${storeName}${SESSION_STORE_SUFFIX}`;
|
|
141
|
+
}
|
|
53
142
|
function getStoreIndex(store, name) {
|
|
54
143
|
const maybeStore = store;
|
|
55
144
|
if (typeof maybeStore.index !== "function" || !indexExists(store, name)) return void 0;
|
|
@@ -73,13 +162,67 @@ function decodeEntry(entry, codec) {
|
|
|
73
162
|
function shouldKeepEntry(entry, options) {
|
|
74
163
|
if (options.from !== void 0 && entry.createdAt < options.from) return false;
|
|
75
164
|
if (options.to !== void 0 && entry.createdAt > options.to) return false;
|
|
165
|
+
if (options.sessionId !== void 0 && entry.sessionId !== options.sessionId) return false;
|
|
76
166
|
if (options.minLevel !== void 0 && entry.level < toLevelValue(options.minLevel)) return false;
|
|
77
167
|
if (options.logger !== void 0 && entry.logger !== options.logger) return false;
|
|
78
168
|
if (options.type !== void 0 && entry.type !== options.type) return false;
|
|
79
169
|
return true;
|
|
80
170
|
}
|
|
171
|
+
function addSessionSummary(sessions, entry) {
|
|
172
|
+
if (!entry.sessionId) return;
|
|
173
|
+
const current = sessions.get(entry.sessionId);
|
|
174
|
+
if (current) {
|
|
175
|
+
current.firstSeen = Math.min(current.firstSeen, entry.createdAt);
|
|
176
|
+
current.lastSeen = Math.max(current.lastSeen, entry.createdAt);
|
|
177
|
+
current.count += 1;
|
|
178
|
+
current.byteLength += entry.byteLength;
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
sessions.set(entry.sessionId, {
|
|
182
|
+
byteLength: entry.byteLength,
|
|
183
|
+
count: 1,
|
|
184
|
+
firstSeen: entry.createdAt,
|
|
185
|
+
lastSeen: entry.createdAt,
|
|
186
|
+
sessionId: entry.sessionId
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function mergeSessionSummary(current, incoming) {
|
|
190
|
+
if (!current) return incoming;
|
|
191
|
+
return {
|
|
192
|
+
byteLength: current.byteLength + incoming.byteLength,
|
|
193
|
+
count: current.count + incoming.count,
|
|
194
|
+
firstSeen: Math.min(current.firstSeen, incoming.firstSeen),
|
|
195
|
+
lastSeen: Math.max(current.lastSeen, incoming.lastSeen),
|
|
196
|
+
sessionId: incoming.sessionId
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function orderSessions(sessions, order = "desc") {
|
|
200
|
+
const sorted = [...sessions].sort((left, right) => {
|
|
201
|
+
if (left.lastSeen !== right.lastSeen) return left.lastSeen - right.lastSeen;
|
|
202
|
+
return left.sessionId < right.sessionId ? -1 : left.sessionId > right.sessionId ? 1 : 0;
|
|
203
|
+
});
|
|
204
|
+
if (order === "asc") return sorted;
|
|
205
|
+
const reversed = [];
|
|
206
|
+
for (let index = sorted.length - 1; index >= 0; index -= 1) reversed.push(sorted[index]);
|
|
207
|
+
return reversed;
|
|
208
|
+
}
|
|
81
209
|
function orderRangeForQuery(options) {
|
|
82
210
|
if (typeof IDBKeyRange === "undefined") return void 0;
|
|
211
|
+
if (options.sessionId !== void 0) {
|
|
212
|
+
const lower = [
|
|
213
|
+
options.sessionId,
|
|
214
|
+
options.from ?? Number.MIN_SAFE_INTEGER,
|
|
215
|
+
Number.MIN_SAFE_INTEGER,
|
|
216
|
+
""
|
|
217
|
+
];
|
|
218
|
+
const upper = [
|
|
219
|
+
options.sessionId,
|
|
220
|
+
options.to ?? Number.MAX_SAFE_INTEGER,
|
|
221
|
+
Number.MAX_SAFE_INTEGER,
|
|
222
|
+
""
|
|
223
|
+
];
|
|
224
|
+
return IDBKeyRange.bound(lower, upper);
|
|
225
|
+
}
|
|
83
226
|
const lower = options.from === void 0 ? void 0 : [
|
|
84
227
|
options.from,
|
|
85
228
|
Number.MIN_SAFE_INTEGER,
|
|
@@ -94,6 +237,33 @@ function orderRangeForQuery(options) {
|
|
|
94
237
|
if (lower) return IDBKeyRange.lowerBound(lower);
|
|
95
238
|
if (upper) return IDBKeyRange.upperBound(upper);
|
|
96
239
|
}
|
|
240
|
+
function orderRangeForSessionId(sessionId) {
|
|
241
|
+
if (typeof IDBKeyRange === "undefined") return void 0;
|
|
242
|
+
return IDBKeyRange.bound([
|
|
243
|
+
sessionId,
|
|
244
|
+
Number.MIN_SAFE_INTEGER,
|
|
245
|
+
Number.MIN_SAFE_INTEGER,
|
|
246
|
+
""
|
|
247
|
+
], [
|
|
248
|
+
sessionId,
|
|
249
|
+
Number.MAX_SAFE_INTEGER,
|
|
250
|
+
Number.MAX_SAFE_INTEGER,
|
|
251
|
+
""
|
|
252
|
+
]);
|
|
253
|
+
}
|
|
254
|
+
function migrateSessionSummaries(logStore, sessionStore) {
|
|
255
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
256
|
+
const request = logStore.openCursor();
|
|
257
|
+
request.addEventListener("success", () => {
|
|
258
|
+
const cursor = request.result;
|
|
259
|
+
if (!cursor) {
|
|
260
|
+
for (const summary of summaries.values()) sessionStore.put(summary);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
addSessionSummary(summaries, cursor.value);
|
|
264
|
+
cursor.continue();
|
|
265
|
+
});
|
|
266
|
+
}
|
|
97
267
|
function iterateCursor(request, visit) {
|
|
98
268
|
return new Promise((resolve, reject) => {
|
|
99
269
|
request.addEventListener("success", () => {
|
|
@@ -118,6 +288,7 @@ function iterateCursor(request, visit) {
|
|
|
118
288
|
function indexedDbTransport(options = {}) {
|
|
119
289
|
const dbName = options.dbName ?? DEFAULT_DB_NAME;
|
|
120
290
|
const storeName = options.storeName ?? DEFAULT_STORE_NAME;
|
|
291
|
+
const sessionStoreName = sessionStoreNameFor(storeName);
|
|
121
292
|
const codec = options.codec ?? safeJsonCodec();
|
|
122
293
|
const batchSize = normalizePositiveInteger(options.batchSize, 100);
|
|
123
294
|
const flushIntervalMs = normalizePositiveInteger(options.flushIntervalMs, 1e3);
|
|
@@ -128,12 +299,18 @@ function indexedDbTransport(options = {}) {
|
|
|
128
299
|
const flushOnPageHide = options.flushOnPageHide ?? true;
|
|
129
300
|
const durability = options.durability ?? "default";
|
|
130
301
|
const rootIndexedDB = options.indexedDB ?? globalThis.indexedDB;
|
|
302
|
+
const sessionOptions = normalizeSessionOptions(options.session);
|
|
303
|
+
const spillOptions = normalizeLocalStorageSpillOptions(options.localStorageSpill, dbName, storeName);
|
|
131
304
|
const transactionOptions = durability === "default" ? void 0 : { durability };
|
|
132
305
|
const buffer = [];
|
|
133
306
|
let dbPromise;
|
|
134
307
|
let indexedDBFactoryPromise;
|
|
135
308
|
let timer;
|
|
136
309
|
let flushPromise;
|
|
310
|
+
let clearPromise;
|
|
311
|
+
let pendingFlushBatch;
|
|
312
|
+
let spillDrainPromise;
|
|
313
|
+
let localStorageSpillClearCandidate;
|
|
137
314
|
let lastContext;
|
|
138
315
|
let closed = false;
|
|
139
316
|
const statsState = {
|
|
@@ -160,13 +337,23 @@ function indexedDbTransport(options = {}) {
|
|
|
160
337
|
storageBucketFallbacks: 0,
|
|
161
338
|
transactionOptionFallbacks: 0,
|
|
162
339
|
errors: 0,
|
|
163
|
-
errorsByOperation: {}
|
|
340
|
+
errorsByOperation: {},
|
|
341
|
+
localStorageSpillWrites: 0,
|
|
342
|
+
localStorageSpillEntries: 0,
|
|
343
|
+
localStorageSpillDrains: 0,
|
|
344
|
+
localStorageSpillDrainedEntries: 0,
|
|
345
|
+
localStorageSpillDropped: 0,
|
|
346
|
+
localStorageSpillDroppedByReason: {},
|
|
347
|
+
localStorageSpillErrors: 0,
|
|
348
|
+
localStorageSpillErrorsByOperation: {}
|
|
164
349
|
};
|
|
165
350
|
const snapshotStats = () => ({
|
|
166
351
|
...statsState,
|
|
167
352
|
bufferDepth: buffer.length,
|
|
168
353
|
droppedByReason: { ...statsState.droppedByReason },
|
|
169
354
|
errorsByOperation: { ...statsState.errorsByOperation },
|
|
355
|
+
localStorageSpillDroppedByReason: { ...statsState.localStorageSpillDroppedByReason },
|
|
356
|
+
localStorageSpillErrorsByOperation: { ...statsState.localStorageSpillErrorsByOperation },
|
|
170
357
|
pendingFlush: flushPromise !== void 0,
|
|
171
358
|
persistedDroppedByReason: { ...statsState.persistedDroppedByReason }
|
|
172
359
|
});
|
|
@@ -193,6 +380,18 @@ function indexedDbTransport(options = {}) {
|
|
|
193
380
|
incrementLoggerMetaCounter(`transport.indexeddb.persisted.dropped.${reason}`);
|
|
194
381
|
options.onPersistedDrop?.(entry, reason);
|
|
195
382
|
};
|
|
383
|
+
const reportSpillError = (error, operation) => {
|
|
384
|
+
statsState.localStorageSpillErrors += 1;
|
|
385
|
+
incrementRecord(statsState.localStorageSpillErrorsByOperation, operation);
|
|
386
|
+
reportInternalError(error, `localstorage-spill-${operation}`);
|
|
387
|
+
};
|
|
388
|
+
const dropSpillEvent = (event, reason) => {
|
|
389
|
+
statsState.localStorageSpillDropped += 1;
|
|
390
|
+
incrementRecord(statsState.localStorageSpillDroppedByReason, reason);
|
|
391
|
+
incrementLoggerMetaCounter("transport.indexeddb.localstorage_spill.dropped");
|
|
392
|
+
incrementLoggerMetaCounter(`transport.indexeddb.localstorage_spill.dropped.${reason}`);
|
|
393
|
+
spillOptions?.onDrop?.(event, reason);
|
|
394
|
+
};
|
|
196
395
|
const resolveIndexedDBFactory = async () => {
|
|
197
396
|
if (options.indexedDB || !options.storageBucketName) return rootIndexedDB;
|
|
198
397
|
indexedDBFactoryPromise ??= (async () => {
|
|
@@ -219,7 +418,7 @@ function indexedDbTransport(options = {}) {
|
|
|
219
418
|
if (!idb) throw new Error("IndexedDB is not available for indexedDbTransport");
|
|
220
419
|
return new Promise((resolve, reject) => {
|
|
221
420
|
const request = idb.open(dbName, DB_VERSION);
|
|
222
|
-
request.addEventListener("upgradeneeded", () => {
|
|
421
|
+
request.addEventListener("upgradeneeded", (event) => {
|
|
223
422
|
const db = request.result;
|
|
224
423
|
const store = db.objectStoreNames.contains(storeName) ? request.transaction?.objectStore(storeName) : db.createObjectStore(storeName, { keyPath: "id" });
|
|
225
424
|
if (!store) return;
|
|
@@ -232,6 +431,17 @@ function indexedDbTransport(options = {}) {
|
|
|
232
431
|
"seq",
|
|
233
432
|
"id"
|
|
234
433
|
]);
|
|
434
|
+
ensureIndex(store, SESSION_INDEX_NAME, "sessionId");
|
|
435
|
+
ensureIndex(store, SESSION_ORDER_INDEX_NAME, [
|
|
436
|
+
"sessionId",
|
|
437
|
+
"createdAt",
|
|
438
|
+
"seq",
|
|
439
|
+
"id"
|
|
440
|
+
]);
|
|
441
|
+
const sessionStore = db.objectStoreNames.contains(sessionStoreName) ? request.transaction?.objectStore(sessionStoreName) : db.createObjectStore(sessionStoreName, { keyPath: "sessionId" });
|
|
442
|
+
if (!sessionStore) return;
|
|
443
|
+
ensureIndex(sessionStore, SESSION_LAST_SEEN_INDEX_NAME, ["lastSeen", "sessionId"]);
|
|
444
|
+
if (event.oldVersion < 4) migrateSessionSummaries(store, sessionStore);
|
|
235
445
|
});
|
|
236
446
|
request.addEventListener("success", () => {
|
|
237
447
|
statsState.databaseOpenCount += 1;
|
|
@@ -242,13 +452,13 @@ function indexedDbTransport(options = {}) {
|
|
|
242
452
|
});
|
|
243
453
|
return dbPromise;
|
|
244
454
|
};
|
|
245
|
-
const createTransaction = (db, mode) => {
|
|
246
|
-
if (mode !== "readwrite" || !transactionOptions) return db.transaction(
|
|
455
|
+
const createTransaction = (db, mode, stores = storeName) => {
|
|
456
|
+
if (mode !== "readwrite" || !transactionOptions) return db.transaction(stores, mode);
|
|
247
457
|
try {
|
|
248
|
-
return db.transaction(
|
|
458
|
+
return db.transaction(stores, mode, transactionOptions);
|
|
249
459
|
} catch {
|
|
250
460
|
statsState.transactionOptionFallbacks += 1;
|
|
251
|
-
return db.transaction(
|
|
461
|
+
return db.transaction(stores, mode);
|
|
252
462
|
}
|
|
253
463
|
};
|
|
254
464
|
const withStore = async (mode, run) => {
|
|
@@ -265,6 +475,57 @@ function indexedDbTransport(options = {}) {
|
|
|
265
475
|
throw error;
|
|
266
476
|
}
|
|
267
477
|
};
|
|
478
|
+
const withLogAndSessionStores = async (mode, run) => {
|
|
479
|
+
const tx = createTransaction(await openDb(), mode, [storeName, sessionStoreName]);
|
|
480
|
+
const settled = transactionToPromise(tx);
|
|
481
|
+
try {
|
|
482
|
+
const result = await run(tx.objectStore(storeName), tx.objectStore(sessionStoreName), tx);
|
|
483
|
+
await settled;
|
|
484
|
+
return result;
|
|
485
|
+
} catch (error) {
|
|
486
|
+
if (tx.error === null) try {
|
|
487
|
+
tx.abort();
|
|
488
|
+
} catch {}
|
|
489
|
+
throw error;
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
const withSessionStore = async (mode, run) => {
|
|
493
|
+
const tx = createTransaction(await openDb(), mode, sessionStoreName);
|
|
494
|
+
const settled = transactionToPromise(tx);
|
|
495
|
+
try {
|
|
496
|
+
const result = await run(tx.objectStore(sessionStoreName), tx);
|
|
497
|
+
await settled;
|
|
498
|
+
return result;
|
|
499
|
+
} catch (error) {
|
|
500
|
+
if (tx.error === null) try {
|
|
501
|
+
tx.abort();
|
|
502
|
+
} catch {}
|
|
503
|
+
throw error;
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
const mergeSessionSummaries = async (sessionStore, entries) => {
|
|
507
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
508
|
+
for (const entry of entries) addSessionSummary(summaries, entry);
|
|
509
|
+
for (const summary of summaries.values()) {
|
|
510
|
+
const current = await requestToPromise(sessionStore.get(summary.sessionId));
|
|
511
|
+
await requestToPromise(sessionStore.put(mergeSessionSummary(current, summary)));
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
const rebuildSessionSummaries = async (store, sessionStore, sessionIds) => {
|
|
515
|
+
if (sessionIds.size === 0) return;
|
|
516
|
+
const index = getStoreIndex(store, SESSION_ORDER_INDEX_NAME);
|
|
517
|
+
for (const sessionId of sessionIds) {
|
|
518
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
519
|
+
const range = orderRangeForSessionId(sessionId);
|
|
520
|
+
await iterateCursor(index && range ? index.openCursor(range) : store.openCursor(), (cursor) => {
|
|
521
|
+
const entry = cursor.value;
|
|
522
|
+
if (entry.sessionId === sessionId) addSessionSummary(summaries, entry);
|
|
523
|
+
});
|
|
524
|
+
const summary = summaries.get(sessionId);
|
|
525
|
+
if (summary) await requestToPromise(sessionStore.put(summary));
|
|
526
|
+
else await requestToPromise(sessionStore.delete(sessionId));
|
|
527
|
+
}
|
|
528
|
+
};
|
|
268
529
|
const getEntries = async () => orderEntries(await withStore("readonly", (store) => requestToPromise(store.getAll())));
|
|
269
530
|
const countEntries = async () => withStore("readonly", (store) => {
|
|
270
531
|
const maybeStore = store;
|
|
@@ -273,25 +534,31 @@ function indexedDbTransport(options = {}) {
|
|
|
273
534
|
});
|
|
274
535
|
const deleteEntries = async (entries, reason) => {
|
|
275
536
|
if (entries.length === 0) return;
|
|
276
|
-
await
|
|
537
|
+
await withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
538
|
+
const affectedSessionIds = /* @__PURE__ */ new Set();
|
|
277
539
|
for (const entry of entries) {
|
|
278
540
|
await requestToPromise(store.delete(entry.id));
|
|
541
|
+
if (entry.sessionId) affectedSessionIds.add(entry.sessionId);
|
|
279
542
|
dropPersistedEntry(entry, reason);
|
|
280
543
|
}
|
|
544
|
+
await rebuildSessionSummaries(store, sessionStore, affectedSessionIds);
|
|
281
545
|
});
|
|
282
546
|
};
|
|
283
|
-
const deleteByCursor = async (reason, openCursor, shouldDelete) =>
|
|
547
|
+
const deleteByCursor = async (reason, openCursor, shouldDelete) => withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
284
548
|
const request = openCursor(store);
|
|
285
549
|
if (!request) return void 0;
|
|
286
550
|
let deleted = 0;
|
|
551
|
+
const affectedSessionIds = /* @__PURE__ */ new Set();
|
|
287
552
|
await iterateCursor(request, (cursor) => {
|
|
288
553
|
const entry = cursor.value;
|
|
289
554
|
if (!shouldDelete(entry, deleted)) return false;
|
|
290
555
|
cursor.delete();
|
|
291
556
|
deleted += 1;
|
|
557
|
+
if (entry.sessionId) affectedSessionIds.add(entry.sessionId);
|
|
292
558
|
dropPersistedEntry(entry, reason);
|
|
293
559
|
return true;
|
|
294
560
|
});
|
|
561
|
+
await rebuildSessionSummaries(store, sessionStore, affectedSessionIds);
|
|
295
562
|
return deleted;
|
|
296
563
|
});
|
|
297
564
|
const deleteExpiredByCursor = async (cutoff) => {
|
|
@@ -322,7 +589,7 @@ function indexedDbTransport(options = {}) {
|
|
|
322
589
|
return total;
|
|
323
590
|
});
|
|
324
591
|
const queryEntriesByCursor = async (queryOptions) => withStore("readonly", async (store) => {
|
|
325
|
-
const index = getStoreIndex(store, ORDER_INDEX_NAME);
|
|
592
|
+
const index = getStoreIndex(store, queryOptions.sessionId === void 0 ? ORDER_INDEX_NAME : SESSION_ORDER_INDEX_NAME);
|
|
326
593
|
if (!index) return void 0;
|
|
327
594
|
const entries = [];
|
|
328
595
|
const limit = queryOptions.limit;
|
|
@@ -335,6 +602,20 @@ function indexedDbTransport(options = {}) {
|
|
|
335
602
|
});
|
|
336
603
|
return entries;
|
|
337
604
|
});
|
|
605
|
+
const listSessions = async (sessionQueryOptions = {}) => {
|
|
606
|
+
const limit = sessionQueryOptions.limit;
|
|
607
|
+
if (limit !== void 0 && limit <= 0) return [];
|
|
608
|
+
return withSessionStore("readonly", async (sessionStore) => {
|
|
609
|
+
const sessions = [];
|
|
610
|
+
const index = getStoreIndex(sessionStore, SESSION_LAST_SEEN_INDEX_NAME);
|
|
611
|
+
if (!index) return orderSessions(await requestToPromise(sessionStore.getAll()), sessionQueryOptions.order).slice(0, limit);
|
|
612
|
+
await iterateCursor(index.openCursor(void 0, sessionQueryOptions.order === "asc" ? "next" : "prev"), (cursor) => {
|
|
613
|
+
sessions.push(cursor.value);
|
|
614
|
+
return limit === void 0 || sessions.length < limit;
|
|
615
|
+
});
|
|
616
|
+
return sessions;
|
|
617
|
+
});
|
|
618
|
+
};
|
|
338
619
|
const pruneWithEntries = async () => {
|
|
339
620
|
const now = Date.now();
|
|
340
621
|
let entries = await getEntries();
|
|
@@ -390,10 +671,34 @@ function indexedDbTransport(options = {}) {
|
|
|
390
671
|
statsState.lastPruneDurationMs = nowMs() - startedAt;
|
|
391
672
|
}
|
|
392
673
|
};
|
|
674
|
+
const sessionIdForEvent = (event) => {
|
|
675
|
+
if (!sessionOptions) return void 0;
|
|
676
|
+
return normalizeSessionId(sessionOptions.getId?.(event)) ?? normalizeSessionId(event.context?.[sessionOptions.contextKey]) ?? sessionOptions.id;
|
|
677
|
+
};
|
|
678
|
+
const eventWithSessionContext = (event, sessionId) => {
|
|
679
|
+
if (!sessionId || !sessionOptions?.contextKey) return event;
|
|
680
|
+
if (event.context?.[sessionOptions.contextKey] === sessionId) return event;
|
|
681
|
+
return {
|
|
682
|
+
...event,
|
|
683
|
+
context: {
|
|
684
|
+
...event.context,
|
|
685
|
+
[sessionOptions.contextKey]: sessionId
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
};
|
|
689
|
+
const eventWithResolvedSession = (event) => {
|
|
690
|
+
const sessionId = sessionIdForEvent(event);
|
|
691
|
+
return {
|
|
692
|
+
event: eventWithSessionContext(event, sessionId),
|
|
693
|
+
sessionId
|
|
694
|
+
};
|
|
695
|
+
};
|
|
393
696
|
const eventToEntry = (event) => {
|
|
394
|
-
const
|
|
697
|
+
const resolved = eventWithResolvedSession(event);
|
|
698
|
+
const payload = codec.encode(resolved.event);
|
|
395
699
|
return {
|
|
396
700
|
id: event.id,
|
|
701
|
+
sessionId: resolved.sessionId,
|
|
397
702
|
seq: event.seq,
|
|
398
703
|
createdAt: event.time,
|
|
399
704
|
level: event.level,
|
|
@@ -404,17 +709,50 @@ function indexedDbTransport(options = {}) {
|
|
|
404
709
|
payload
|
|
405
710
|
};
|
|
406
711
|
};
|
|
712
|
+
const clearLocalStorageSpill = (expectedValue) => {
|
|
713
|
+
if (!spillOptions) return false;
|
|
714
|
+
try {
|
|
715
|
+
if (expectedValue !== void 0 && spillOptions.storage.getItem(spillOptions.key) !== expectedValue) return false;
|
|
716
|
+
spillOptions.storage.removeItem(spillOptions.key);
|
|
717
|
+
if (expectedValue === void 0 || localStorageSpillClearCandidate?.value === expectedValue) localStorageSpillClearCandidate = void 0;
|
|
718
|
+
return true;
|
|
719
|
+
} catch (error) {
|
|
720
|
+
reportSpillError(error, "clear");
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
const rememberLocalStorageSpill = (value, events) => {
|
|
725
|
+
localStorageSpillClearCandidate = {
|
|
726
|
+
pendingIds: new Set(events.map((event) => event.id)),
|
|
727
|
+
value
|
|
728
|
+
};
|
|
729
|
+
};
|
|
730
|
+
const markLocalStorageSpillPersisted = (events) => {
|
|
731
|
+
const candidate = localStorageSpillClearCandidate;
|
|
732
|
+
if (!candidate || events.length === 0) return;
|
|
733
|
+
for (const event of events) candidate.pendingIds.delete(event.id);
|
|
734
|
+
if (candidate.pendingIds.size === 0) clearLocalStorageSpill(candidate.value);
|
|
735
|
+
};
|
|
407
736
|
const writeBatch = async (events) => {
|
|
408
737
|
if (events.length === 0) return;
|
|
409
738
|
const startedAt = nowMs();
|
|
410
739
|
statsState.lastFlushBatchSize = events.length;
|
|
411
740
|
const entries = events.map(eventToEntry);
|
|
412
741
|
try {
|
|
413
|
-
await
|
|
414
|
-
|
|
742
|
+
await withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
743
|
+
const rebuildSessionIds = /* @__PURE__ */ new Set();
|
|
744
|
+
for (const entry of entries) {
|
|
745
|
+
const existing = await requestToPromise(store.get(entry.id));
|
|
746
|
+
if (existing?.sessionId) rebuildSessionIds.add(existing.sessionId);
|
|
747
|
+
if (existing && entry.sessionId) rebuildSessionIds.add(entry.sessionId);
|
|
748
|
+
await requestToPromise(store.put(entry));
|
|
749
|
+
}
|
|
750
|
+
await rebuildSessionSummaries(store, sessionStore, rebuildSessionIds);
|
|
751
|
+
await mergeSessionSummaries(sessionStore, entries.filter((entry) => !entry.sessionId || !rebuildSessionIds.has(entry.sessionId)));
|
|
415
752
|
});
|
|
416
753
|
statsState.persisted += entries.length;
|
|
417
754
|
incrementLoggerMetaCounter("transport.indexeddb.persisted", entries.length);
|
|
755
|
+
markLocalStorageSpillPersisted(events);
|
|
418
756
|
await prune();
|
|
419
757
|
statsState.flushes += 1;
|
|
420
758
|
} catch (error) {
|
|
@@ -424,6 +762,124 @@ function indexedDbTransport(options = {}) {
|
|
|
424
762
|
statsState.lastFlushDurationMs = nowMs() - startedAt;
|
|
425
763
|
}
|
|
426
764
|
};
|
|
765
|
+
const stringifySpillPayload = (events) => safeJsonStringify({
|
|
766
|
+
createdAt: Date.now(),
|
|
767
|
+
entries: events,
|
|
768
|
+
schema: SPILL_SCHEMA
|
|
769
|
+
}, { maxArrayLength: spillOptions?.maxEntries ?? DEFAULT_SPILL_MAX_ENTRIES });
|
|
770
|
+
const spillStorageBudget = (currentValue) => {
|
|
771
|
+
if (!spillOptions) return 0;
|
|
772
|
+
const ownBytes = currentValue ? storageByteLength(spillOptions.key, currentValue) : 0;
|
|
773
|
+
const originBytes = estimateLocalStorageBytes(spillOptions.storage);
|
|
774
|
+
if (originBytes === void 0) return spillOptions.maxBytes;
|
|
775
|
+
return Math.max(0, Math.min(spillOptions.maxBytes, spillOptions.maxOriginBytes - originBytes + ownBytes));
|
|
776
|
+
};
|
|
777
|
+
const pendingEventsForSpill = () => {
|
|
778
|
+
const events = [...pendingFlushBatch ?? [], ...buffer];
|
|
779
|
+
const seen = /* @__PURE__ */ new Set();
|
|
780
|
+
const deduped = [];
|
|
781
|
+
for (const event of events) {
|
|
782
|
+
if (seen.has(event.id)) continue;
|
|
783
|
+
seen.add(event.id);
|
|
784
|
+
if (spillOptions?.minLevelValue !== void 0 && event.level < spillOptions.minLevelValue) continue;
|
|
785
|
+
deduped.push(eventWithResolvedSession(event).event);
|
|
786
|
+
}
|
|
787
|
+
return deduped;
|
|
788
|
+
};
|
|
789
|
+
const trimSpillEventsToBudget = (events, budget) => {
|
|
790
|
+
if (!spillOptions) return void 0;
|
|
791
|
+
while (events.length > spillOptions.maxEntries) {
|
|
792
|
+
const dropped = events.shift();
|
|
793
|
+
if (dropped) dropSpillEvent(dropped, "max-entries");
|
|
794
|
+
}
|
|
795
|
+
while (events.length > 0) {
|
|
796
|
+
const value = stringifySpillPayload(events);
|
|
797
|
+
if (storageByteLength(spillOptions.key, value) <= budget) return value;
|
|
798
|
+
const dropped = events.shift();
|
|
799
|
+
if (dropped) dropSpillEvent(dropped, "max-bytes");
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
const writeLocalStorageSpill = () => {
|
|
803
|
+
if (!spillOptions) return;
|
|
804
|
+
const pendingEvents = pendingEventsForSpill();
|
|
805
|
+
if (pendingEvents.length === 0) return;
|
|
806
|
+
try {
|
|
807
|
+
const currentValue = spillOptions.storage.getItem(spillOptions.key);
|
|
808
|
+
let events = pendingEvents;
|
|
809
|
+
if (currentValue) {
|
|
810
|
+
const merged = /* @__PURE__ */ new Map();
|
|
811
|
+
try {
|
|
812
|
+
for (const event of parseSpillPayload(currentValue) ?? []) merged.set(event.id, event);
|
|
813
|
+
} catch {}
|
|
814
|
+
for (const event of pendingEvents) merged.set(event.id, event);
|
|
815
|
+
events = [...merged.values()];
|
|
816
|
+
}
|
|
817
|
+
const budget = spillStorageBudget(currentValue);
|
|
818
|
+
const value = trimSpillEventsToBudget(events, budget);
|
|
819
|
+
if (!value) return;
|
|
820
|
+
try {
|
|
821
|
+
spillOptions.storage.setItem(spillOptions.key, value);
|
|
822
|
+
} catch (error) {
|
|
823
|
+
if (!isQuotaExceededError(error)) throw error;
|
|
824
|
+
while (events.length > 0) {
|
|
825
|
+
const dropped = events.shift();
|
|
826
|
+
if (dropped) dropSpillEvent(dropped, "quota");
|
|
827
|
+
const retryValue = trimSpillEventsToBudget(events, budget);
|
|
828
|
+
if (!retryValue) return;
|
|
829
|
+
try {
|
|
830
|
+
spillOptions.storage.setItem(spillOptions.key, retryValue);
|
|
831
|
+
rememberLocalStorageSpill(retryValue, events);
|
|
832
|
+
statsState.localStorageSpillWrites += 1;
|
|
833
|
+
statsState.localStorageSpillEntries += events.length;
|
|
834
|
+
return;
|
|
835
|
+
} catch (retryError) {
|
|
836
|
+
if (!isQuotaExceededError(retryError)) throw retryError;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
rememberLocalStorageSpill(value, events);
|
|
842
|
+
statsState.localStorageSpillWrites += 1;
|
|
843
|
+
statsState.localStorageSpillEntries += events.length;
|
|
844
|
+
} catch (error) {
|
|
845
|
+
reportSpillError(error, "write");
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
const drainLocalStorageSpill = async () => {
|
|
849
|
+
if (!spillOptions) return;
|
|
850
|
+
let raw;
|
|
851
|
+
try {
|
|
852
|
+
raw = spillOptions.storage.getItem(spillOptions.key);
|
|
853
|
+
} catch (error) {
|
|
854
|
+
reportSpillError(error, "read");
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
if (!raw) return;
|
|
858
|
+
let events;
|
|
859
|
+
try {
|
|
860
|
+
events = parseSpillPayload(raw);
|
|
861
|
+
} catch (error) {
|
|
862
|
+
reportSpillError(error, "parse");
|
|
863
|
+
clearLocalStorageSpill();
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
if (!events || events.length === 0) {
|
|
867
|
+
clearLocalStorageSpill();
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
await writeBatch(events);
|
|
871
|
+
clearLocalStorageSpill(raw);
|
|
872
|
+
statsState.localStorageSpillDrains += 1;
|
|
873
|
+
statsState.localStorageSpillDrainedEntries += events.length;
|
|
874
|
+
};
|
|
875
|
+
const ensureSpillDrained = async () => {
|
|
876
|
+
const promise = spillDrainPromise;
|
|
877
|
+
if (!promise) return;
|
|
878
|
+
await promise;
|
|
879
|
+
};
|
|
880
|
+
if (spillOptions?.drainOnCreate) spillDrainPromise = drainLocalStorageSpill().catch((error) => reportSpillError(error, "drain")).finally(() => {
|
|
881
|
+
spillDrainPromise = void 0;
|
|
882
|
+
});
|
|
427
883
|
const clearTimer = () => {
|
|
428
884
|
if (timer) clearTimeout(timer);
|
|
429
885
|
timer = void 0;
|
|
@@ -431,7 +887,15 @@ function indexedDbTransport(options = {}) {
|
|
|
431
887
|
const flushPending = async () => {
|
|
432
888
|
if (flushPromise) return flushPromise;
|
|
433
889
|
clearTimer();
|
|
434
|
-
|
|
890
|
+
const batch = buffer.splice(0, buffer.length);
|
|
891
|
+
pendingFlushBatch = batch;
|
|
892
|
+
const clearBarrier = clearPromise;
|
|
893
|
+
flushPromise = (async () => {
|
|
894
|
+
await clearBarrier;
|
|
895
|
+
await ensureSpillDrained();
|
|
896
|
+
await writeBatch(batch);
|
|
897
|
+
})().finally(() => {
|
|
898
|
+
if (pendingFlushBatch === batch) pendingFlushBatch = void 0;
|
|
435
899
|
flushPromise = void 0;
|
|
436
900
|
if (buffer.length > 0 && !closed) schedule();
|
|
437
901
|
});
|
|
@@ -444,14 +908,28 @@ function indexedDbTransport(options = {}) {
|
|
|
444
908
|
}, flushIntervalMs);
|
|
445
909
|
};
|
|
446
910
|
const onPageHide = () => {
|
|
447
|
-
|
|
911
|
+
if (spillOptions?.spillOnPageHide) writeLocalStorageSpill();
|
|
912
|
+
if (flushOnPageHide) flushPending().catch((error) => reportInternalError(error, "pagehide-flush"));
|
|
448
913
|
};
|
|
449
914
|
const onVisibilityChange = () => {
|
|
450
|
-
if (typeof document !== "undefined" && document.visibilityState === "hidden")
|
|
915
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
916
|
+
if (spillOptions?.spillOnPageHide) writeLocalStorageSpill();
|
|
917
|
+
if (flushOnPageHide) flushPending().catch((error) => reportInternalError(error, "visibility-flush"));
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
const addVisibilityChangeListener = () => {
|
|
921
|
+
if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
|
|
922
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
923
|
+
return () => document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
924
|
+
}
|
|
925
|
+
if (typeof globalThis.addEventListener !== "function") return void 0;
|
|
926
|
+
globalThis.addEventListener("visibilitychange", onVisibilityChange);
|
|
927
|
+
return () => globalThis.removeEventListener?.("visibilitychange", onVisibilityChange);
|
|
451
928
|
};
|
|
452
|
-
|
|
929
|
+
let removeVisibilityChangeListener;
|
|
930
|
+
if (flushOnPageHide || spillOptions?.spillOnPageHide) {
|
|
453
931
|
globalThis.addEventListener?.("pagehide", onPageHide);
|
|
454
|
-
|
|
932
|
+
removeVisibilityChangeListener = addVisibilityChangeListener();
|
|
455
933
|
}
|
|
456
934
|
return {
|
|
457
935
|
name: options.name ?? "indexeddb",
|
|
@@ -482,16 +960,36 @@ function indexedDbTransport(options = {}) {
|
|
|
482
960
|
return await countEntries() ?? (await getEntries()).length;
|
|
483
961
|
},
|
|
484
962
|
async clear() {
|
|
963
|
+
if (clearPromise) return clearPromise;
|
|
964
|
+
const inFlightFlush = flushPromise;
|
|
965
|
+
const inFlightDrain = spillDrainPromise;
|
|
485
966
|
buffer.length = 0;
|
|
486
967
|
clearTimer();
|
|
487
|
-
|
|
968
|
+
clearLocalStorageSpill();
|
|
969
|
+
clearPromise = (async () => {
|
|
970
|
+
await inFlightFlush;
|
|
971
|
+
await inFlightDrain;
|
|
972
|
+
await withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
973
|
+
await requestToPromise(store.clear());
|
|
974
|
+
await requestToPromise(sessionStore.clear());
|
|
975
|
+
});
|
|
976
|
+
})().finally(() => {
|
|
977
|
+
clearPromise = void 0;
|
|
978
|
+
});
|
|
979
|
+
return clearPromise;
|
|
488
980
|
},
|
|
489
981
|
async remove(ids) {
|
|
490
982
|
await flushPending();
|
|
491
983
|
const items = typeof ids === "string" ? [ids] : ids;
|
|
492
984
|
if (items.length === 0) return;
|
|
493
|
-
await
|
|
494
|
-
|
|
985
|
+
await withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
986
|
+
const affectedSessionIds = /* @__PURE__ */ new Set();
|
|
987
|
+
for (const id of items) {
|
|
988
|
+
const entry = await requestToPromise(store.get(id));
|
|
989
|
+
if (entry?.sessionId) affectedSessionIds.add(entry.sessionId);
|
|
990
|
+
await requestToPromise(store.delete(id));
|
|
991
|
+
}
|
|
992
|
+
await rebuildSessionSummaries(store, sessionStore, affectedSessionIds);
|
|
495
993
|
});
|
|
496
994
|
},
|
|
497
995
|
async *query(queryOptions = {}) {
|
|
@@ -507,6 +1005,10 @@ function indexedDbTransport(options = {}) {
|
|
|
507
1005
|
if (event) yield event;
|
|
508
1006
|
}
|
|
509
1007
|
},
|
|
1008
|
+
async sessions(sessionQueryOptions = {}) {
|
|
1009
|
+
await flushPending();
|
|
1010
|
+
return listSessions(sessionQueryOptions);
|
|
1011
|
+
},
|
|
510
1012
|
stats() {
|
|
511
1013
|
return snapshotStats();
|
|
512
1014
|
},
|
|
@@ -514,12 +1016,12 @@ function indexedDbTransport(options = {}) {
|
|
|
514
1016
|
closed = true;
|
|
515
1017
|
clearTimer();
|
|
516
1018
|
globalThis.removeEventListener?.("pagehide", onPageHide);
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
promise?.then((db) => db.close());
|
|
1019
|
+
removeVisibilityChangeListener?.();
|
|
1020
|
+
removeVisibilityChangeListener = void 0;
|
|
1021
|
+
return flushPending().finally(() => {
|
|
1022
|
+
const promise = dbPromise;
|
|
1023
|
+
dbPromise = void 0;
|
|
1024
|
+
return promise?.then((db) => db.close());
|
|
523
1025
|
});
|
|
524
1026
|
}
|
|
525
1027
|
};
|
|
@@ -546,12 +1048,14 @@ function isBrowserOnline(options) {
|
|
|
546
1048
|
function offlineFirstTransport(remote, options = {}) {
|
|
547
1049
|
const name = options.name ?? `offline-first(${remote.name ?? "transport"})`;
|
|
548
1050
|
const replayBatchSize = normalizeBatchSize(options.replayBatchSize);
|
|
1051
|
+
const queueOptions = options.queueOptions;
|
|
549
1052
|
const queue = options.queue ?? indexedDbTransport({
|
|
550
1053
|
dbName: "loggerjs-offline",
|
|
551
1054
|
flushIntervalMs: 0,
|
|
552
1055
|
name: `${name}:queue`,
|
|
553
1056
|
storeName: "offline-events",
|
|
554
|
-
...
|
|
1057
|
+
...queueOptions,
|
|
1058
|
+
session: queueOptions?.session ?? false
|
|
555
1059
|
});
|
|
556
1060
|
const reliableRemote = retryTransport(remote, {
|
|
557
1061
|
maxRetries: 2,
|