@loggerjs/browser 0.5.6 → 0.7.0
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 +13 -16
- package/dist/chunks/http-capture-utils-DbhpUirh.cjs +83 -0
- package/dist/chunks/http-capture-utils-DbhpUirh.cjs.map +1 -0
- package/dist/chunks/http-capture-utils-_hG5wepA.js +48 -0
- package/dist/chunks/http-capture-utils-_hG5wepA.js.map +1 -0
- package/dist/context-propagation-integration.cjs +5 -4
- package/dist/context-propagation-integration.cjs.map +1 -1
- package/dist/context-propagation-integration.d.ts +2 -1
- package/dist/context-propagation-integration.d.ts.map +1 -1
- package/dist/context-propagation-integration.js +2 -1
- package/dist/context-propagation-integration.js.map +1 -1
- package/dist/fetch-integration.cjs +8 -52
- package/dist/fetch-integration.cjs.map +1 -1
- package/dist/fetch-integration.js +1 -45
- package/dist/fetch-integration.js.map +1 -1
- package/dist/http-transport.cjs +121 -29
- package/dist/http-transport.cjs.map +1 -1
- package/dist/http-transport.d.ts +18 -0
- package/dist/http-transport.d.ts.map +1 -1
- package/dist/http-transport.js +121 -29
- package/dist/http-transport.js.map +1 -1
- package/dist/index.cjs +58 -4148
- package/dist/index.d.ts +243 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -4116
- package/dist/indexeddb-offline-queue.cjs +8 -1
- package/dist/indexeddb-offline-queue.cjs.map +1 -1
- package/dist/indexeddb-offline-queue.d.ts.map +1 -1
- package/dist/indexeddb-offline-queue.js +8 -1
- package/dist/indexeddb-offline-queue.js.map +1 -1
- package/dist/indexeddb-transport.cjs +24 -4
- package/dist/indexeddb-transport.cjs.map +1 -1
- package/dist/indexeddb-transport.d.ts.map +1 -1
- package/dist/indexeddb-transport.js +24 -4
- package/dist/indexeddb-transport.js.map +1 -1
- package/dist/offline-first-transport.cjs +3 -1029
- 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 +3 -1029
- package/dist/offline-first-transport.js.map +1 -1
- package/dist/payload-transforms.cjs +3 -3
- package/dist/payload-transforms.cjs.map +1 -1
- package/dist/payload-transforms.d.ts.map +1 -1
- package/dist/payload-transforms.js +1 -1
- package/dist/payload-transforms.js.map +1 -1
- package/dist/websocket-integration.cjs +5 -32
- package/dist/websocket-integration.cjs.map +1 -1
- package/dist/websocket-integration.js +1 -28
- package/dist/websocket-integration.js.map +1 -1
- package/dist/websocket-transport.cjs +1 -1
- package/dist/websocket-transport.cjs.map +1 -1
- package/dist/websocket-transport.js +2 -2
- package/dist/websocket-transport.js.map +1 -1
- package/dist/xhr-integration.cjs +6 -33
- package/dist/xhr-integration.cjs.map +1 -1
- package/dist/xhr-integration.js +1 -28
- package/dist/xhr-integration.js.map +1 -1
- package/package.json +5 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,4150 +1,62 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const getChannel = (context) => {
|
|
61
|
-
if (closed) return void 0;
|
|
62
|
-
if (channel) return channel;
|
|
63
|
-
try {
|
|
64
|
-
channel = channelFactory(options.channelName);
|
|
65
|
-
return channel;
|
|
66
|
-
} catch (error) {
|
|
67
|
-
reportError(error, context, {
|
|
68
|
-
droppedEvents: 1,
|
|
69
|
-
operation: "create-channel"
|
|
70
|
-
});
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
const postMessage = (message, context, droppedEvents) => {
|
|
75
|
-
const currentChannel = getChannel(context);
|
|
76
|
-
if (!currentChannel) return;
|
|
77
|
-
try {
|
|
78
|
-
currentChannel.postMessage(message);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
reportError(error, context, {
|
|
81
|
-
droppedEvents,
|
|
82
|
-
operation: "post-message"
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
return {
|
|
87
|
-
name: transportName,
|
|
88
|
-
minLevel: options.minLevel,
|
|
89
|
-
log(event, context) {
|
|
90
|
-
postMessage(mapEvent(event, mapContext), context, 1);
|
|
91
|
-
},
|
|
92
|
-
logBatch(events, context) {
|
|
93
|
-
if (events.length === 0) return;
|
|
94
|
-
postMessage(mapBatch(events, mapContext), context, events.length);
|
|
95
|
-
},
|
|
96
|
-
async close() {
|
|
97
|
-
closed = true;
|
|
98
|
-
if (!channel || !closeChannelOnClose) return;
|
|
99
|
-
try {
|
|
100
|
-
channel.close?.();
|
|
101
|
-
} catch (error) {
|
|
102
|
-
reportError(error, void 0, {
|
|
103
|
-
droppedEvents: 0,
|
|
104
|
-
operation: "close-channel"
|
|
105
|
-
});
|
|
106
|
-
} finally {
|
|
107
|
-
channel = void 0;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
//#endregion
|
|
113
|
-
//#region src/http-transport.ts
|
|
114
|
-
const DEFAULT_BEACON_MAX_BYTES = 60 * 1024;
|
|
115
|
-
const sleep = (delayMs) => delayMs <= 0 ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
116
|
-
function memoryBrowserHttpOfflineQueue(options = {}) {
|
|
117
|
-
const entries = [];
|
|
118
|
-
const maxEntries = options.maxEntries ?? 1e3;
|
|
119
|
-
const dropPolicy = options.dropPolicy ?? "drop-oldest";
|
|
120
|
-
const drop = (entry, reason) => {
|
|
121
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.dropped");
|
|
122
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.offline.dropped.${reason}`);
|
|
123
|
-
options.onDrop?.(entry, reason);
|
|
124
|
-
};
|
|
125
|
-
return {
|
|
126
|
-
enqueue(entry) {
|
|
127
|
-
if (entries.length >= maxEntries) {
|
|
128
|
-
if (dropPolicy === "drop-newest") {
|
|
129
|
-
drop(entry, "queue-full");
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const dropped = entries.shift();
|
|
133
|
-
if (dropped) drop(dropped, "queue-full");
|
|
134
|
-
}
|
|
135
|
-
entries.push(entry);
|
|
136
|
-
},
|
|
137
|
-
async replay(send) {
|
|
138
|
-
while (entries.length > 0) {
|
|
139
|
-
const entry = entries[0];
|
|
140
|
-
if (!entry) return;
|
|
141
|
-
await send(entry);
|
|
142
|
-
entries.shift();
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
size() {
|
|
146
|
-
return entries.length;
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
function payloadToBody(payload) {
|
|
151
|
-
if (typeof payload === "string") return payload;
|
|
152
|
-
return Uint8Array.from(payload);
|
|
153
|
-
}
|
|
154
|
-
function payloadByteLength$1(payload) {
|
|
155
|
-
if (typeof payload !== "string") return payload.byteLength;
|
|
156
|
-
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(payload).byteLength;
|
|
157
|
-
return new Blob([payload]).size;
|
|
158
|
-
}
|
|
159
|
-
function payloadToBeaconBody(payload, contentType) {
|
|
160
|
-
return new Blob([typeof payload === "string" ? payload : Uint8Array.from(payload)], { type: contentType });
|
|
161
|
-
}
|
|
162
|
-
function remainingEvents(chunks, startIndex) {
|
|
163
|
-
const events = [];
|
|
164
|
-
for (let index = startIndex; index < chunks.length; index++) {
|
|
165
|
-
const chunk = chunks[index];
|
|
166
|
-
if (chunk) events.push(...chunk.events);
|
|
167
|
-
}
|
|
168
|
-
return events;
|
|
169
|
-
}
|
|
170
|
-
function browserHttpTransport(options) {
|
|
171
|
-
const codec = options.codec ?? (0, _loggerjs_core.safeJsonCodec)();
|
|
172
|
-
const beaconCodec = options.beaconCodec ?? codec;
|
|
173
|
-
const queue = [];
|
|
174
|
-
const maxBatchSize = options.maxBatchSize ?? 50;
|
|
175
|
-
if (!Number.isSafeInteger(maxBatchSize) || maxBatchSize <= 0) throw new RangeError("maxBatchSize must be a positive safe integer");
|
|
176
|
-
const flushIntervalMs = options.flushIntervalMs ?? 2e3;
|
|
177
|
-
const maxQueueSize = options.maxQueueSize ?? 1e3;
|
|
178
|
-
const dropPolicy = options.dropPolicy ?? "drop-oldest";
|
|
179
|
-
const beaconMaxBytes = options.beaconMaxBytes ?? DEFAULT_BEACON_MAX_BYTES;
|
|
180
|
-
const offlineQueue = options.offlineQueue;
|
|
181
|
-
const offlineReplayMaxRetries = options.offlineReplayMaxRetries ?? 3;
|
|
182
|
-
const offlineReplayBaseDelayMs = options.offlineReplayBaseDelayMs ?? 250;
|
|
183
|
-
const offlineReplayMaxDelayMs = options.offlineReplayMaxDelayMs ?? 5e3;
|
|
184
|
-
const random = options.random ?? Math.random;
|
|
185
|
-
const fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);
|
|
186
|
-
let offlineEntrySeq = 0;
|
|
187
|
-
let timer;
|
|
188
|
-
let activeFlush;
|
|
189
|
-
let flushingBeacon = false;
|
|
190
|
-
let replayingOffline = false;
|
|
191
|
-
let lastContext;
|
|
192
|
-
const headers = (payloadHeaders, contentType = codec.contentType) => ({
|
|
193
|
-
"content-type": contentType,
|
|
194
|
-
...payloadHeaders,
|
|
195
|
-
...options.headers
|
|
196
|
-
});
|
|
197
|
-
const clearTimer = () => {
|
|
198
|
-
if (timer) clearTimeout(timer);
|
|
199
|
-
timer = void 0;
|
|
200
|
-
};
|
|
201
|
-
const reportDrop = (event, reason) => {
|
|
202
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.dropped");
|
|
203
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.dropped.${reason}`);
|
|
204
|
-
options.onDrop?.(event, reason);
|
|
205
|
-
};
|
|
206
|
-
const reportInternalError = (error, operation) => {
|
|
207
|
-
lastContext?.reportInternalError(error, {
|
|
208
|
-
phase: "transport",
|
|
209
|
-
transport: options.name ?? "browser-http",
|
|
210
|
-
operation
|
|
211
|
-
});
|
|
212
|
-
};
|
|
213
|
-
const createBeaconChunks = (batch) => {
|
|
214
|
-
const chunks = [];
|
|
215
|
-
const oversized = [];
|
|
216
|
-
let currentEvents = [];
|
|
217
|
-
let currentPayload;
|
|
218
|
-
for (const event of batch) {
|
|
219
|
-
if (currentEvents.length === maxBatchSize && currentPayload !== void 0) {
|
|
220
|
-
chunks.push({
|
|
221
|
-
events: currentEvents,
|
|
222
|
-
payload: currentPayload
|
|
223
|
-
});
|
|
224
|
-
currentEvents = [];
|
|
225
|
-
currentPayload = void 0;
|
|
226
|
-
}
|
|
227
|
-
const candidateEvents = [...currentEvents, event];
|
|
228
|
-
const candidatePayload = beaconCodec.encode(candidateEvents);
|
|
229
|
-
if (payloadByteLength$1(candidatePayload) <= beaconMaxBytes) {
|
|
230
|
-
currentEvents = candidateEvents;
|
|
231
|
-
currentPayload = candidatePayload;
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
if (currentEvents.length > 0 && currentPayload !== void 0) chunks.push({
|
|
235
|
-
events: currentEvents,
|
|
236
|
-
payload: currentPayload
|
|
237
|
-
});
|
|
238
|
-
const singlePayload = beaconCodec.encode([event]);
|
|
239
|
-
if (payloadByteLength$1(singlePayload) <= beaconMaxBytes) {
|
|
240
|
-
currentEvents = [event];
|
|
241
|
-
currentPayload = singlePayload;
|
|
242
|
-
} else {
|
|
243
|
-
currentEvents = [];
|
|
244
|
-
currentPayload = void 0;
|
|
245
|
-
oversized.push(event);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (currentEvents.length > 0 && currentPayload !== void 0) chunks.push({
|
|
249
|
-
events: currentEvents,
|
|
250
|
-
payload: currentPayload
|
|
251
|
-
});
|
|
252
|
-
for (const event of oversized) reportDrop(event, "beacon-too-large");
|
|
253
|
-
return chunks;
|
|
254
|
-
};
|
|
255
|
-
const sendBeaconBatch = (batch) => {
|
|
256
|
-
if (typeof navigator === "undefined" || !navigator.sendBeacon) return { remaining: batch };
|
|
257
|
-
const chunks = createBeaconChunks(batch);
|
|
258
|
-
for (let index = 0; index < chunks.length; index++) {
|
|
259
|
-
const chunk = chunks[index];
|
|
260
|
-
if (!chunk) continue;
|
|
261
|
-
try {
|
|
262
|
-
if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) return { remaining: remainingEvents(chunks, index) };
|
|
263
|
-
} catch (error) {
|
|
264
|
-
return {
|
|
265
|
-
remaining: remainingEvents(chunks, index),
|
|
266
|
-
failure: { error }
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
return { remaining: [] };
|
|
271
|
-
};
|
|
272
|
-
const sendPayload = async (entry) => {
|
|
273
|
-
if (!fetchFn) throw new Error("fetch is not available for browserHttpTransport");
|
|
274
|
-
const response = await fetchFn(entry.url, {
|
|
275
|
-
method: entry.method,
|
|
276
|
-
headers: entry.headers,
|
|
277
|
-
body: payloadToBody(entry.body),
|
|
278
|
-
credentials: entry.credentials,
|
|
279
|
-
keepalive: entry.keepalive
|
|
280
|
-
});
|
|
281
|
-
if (!response.ok) throw new Error(`browserHttpTransport failed with status ${response.status}`);
|
|
282
|
-
};
|
|
283
|
-
const encodeTransformedPayload = async (batch) => {
|
|
284
|
-
return (0, _loggerjs_core.applyPayloadTransforms)(codec.encode(batch), {
|
|
285
|
-
contentType: codec.contentType,
|
|
286
|
-
events: batch,
|
|
287
|
-
transport: options.name ?? "browser-http"
|
|
288
|
-
}, options.transformPayload);
|
|
289
|
-
};
|
|
290
|
-
const createOfflineEntry = (payload, payloadHeaders, contentType) => ({
|
|
291
|
-
id: `${Date.now().toString(36)}-${(offlineEntrySeq++).toString(36)}`,
|
|
292
|
-
url: options.url,
|
|
293
|
-
method: options.method ?? "POST",
|
|
294
|
-
headers: headers(payloadHeaders, contentType),
|
|
295
|
-
body: payload,
|
|
296
|
-
credentials: options.credentials,
|
|
297
|
-
keepalive: options.keepalive ?? true,
|
|
298
|
-
createdAt: Date.now()
|
|
299
|
-
});
|
|
300
|
-
const enqueueOfflinePayload = async (payload, payloadHeaders, contentType) => {
|
|
301
|
-
if (!offlineQueue) return false;
|
|
302
|
-
await offlineQueue.enqueue(createOfflineEntry(payload, payloadHeaders, contentType));
|
|
303
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.queued");
|
|
304
|
-
return true;
|
|
305
|
-
};
|
|
306
|
-
const sendFetchBatch = async (batch) => {
|
|
307
|
-
if (batch.length === 0) return;
|
|
308
|
-
const transformed = await encodeTransformedPayload(batch);
|
|
309
|
-
if (offlineQueue && typeof navigator !== "undefined" && navigator.onLine === false) {
|
|
310
|
-
await enqueueOfflinePayload(transformed.payload, transformed.headers, transformed.contentType);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
try {
|
|
314
|
-
await sendPayload(createOfflineEntry(transformed.payload, transformed.headers, transformed.contentType));
|
|
315
|
-
} catch (error) {
|
|
316
|
-
if (await enqueueOfflinePayload(transformed.payload, transformed.headers, transformed.contentType)) return;
|
|
317
|
-
throw error;
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
const replayRetryDelay = (attempt) => {
|
|
321
|
-
const cap = Math.min(offlineReplayMaxDelayMs, offlineReplayBaseDelayMs * 2 ** attempt);
|
|
322
|
-
return cap <= 0 ? 0 : random() * cap;
|
|
323
|
-
};
|
|
324
|
-
const sendOfflineEntryWithRetry = async (entry) => {
|
|
325
|
-
for (let attempt = 0;; attempt++) try {
|
|
326
|
-
await sendPayload(entry);
|
|
327
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.replayed");
|
|
328
|
-
return;
|
|
329
|
-
} catch (error) {
|
|
330
|
-
if (attempt >= offlineReplayMaxRetries) {
|
|
331
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.replay.failed");
|
|
332
|
-
throw error;
|
|
333
|
-
}
|
|
334
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.retry");
|
|
335
|
-
await sleep(replayRetryDelay(attempt));
|
|
336
|
-
}
|
|
337
|
-
};
|
|
338
|
-
const replayOfflineQueue = async () => {
|
|
339
|
-
if (!offlineQueue || replayingOffline) return;
|
|
340
|
-
replayingOffline = true;
|
|
341
|
-
try {
|
|
342
|
-
await offlineQueue.replay(sendOfflineEntryWithRetry);
|
|
343
|
-
} finally {
|
|
344
|
-
replayingOffline = false;
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
const flushBeaconQueue = () => {
|
|
348
|
-
if (flushingBeacon || options.transformPayload || queue.length === 0) return;
|
|
349
|
-
flushingBeacon = true;
|
|
350
|
-
let pending = queue.splice(0, queue.length);
|
|
351
|
-
try {
|
|
352
|
-
const result = sendBeaconBatch(pending);
|
|
353
|
-
pending = result.remaining;
|
|
354
|
-
if (result.failure) throw result.failure.error;
|
|
355
|
-
} finally {
|
|
356
|
-
if (pending.length > 0) queue.unshift(...pending);
|
|
357
|
-
flushingBeacon = false;
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
const flush = (preferBeacon = false) => {
|
|
361
|
-
if (activeFlush) {
|
|
362
|
-
if (preferBeacon) try {
|
|
363
|
-
flushBeaconQueue();
|
|
364
|
-
} catch (error) {
|
|
365
|
-
return activeFlush.then(() => {
|
|
366
|
-
throw error;
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
return activeFlush;
|
|
370
|
-
}
|
|
371
|
-
if (queue.length === 0) return Promise.resolve();
|
|
372
|
-
clearTimer();
|
|
373
|
-
let resolve;
|
|
374
|
-
let reject;
|
|
375
|
-
const task = new Promise((onResolve, onReject) => {
|
|
376
|
-
resolve = onResolve;
|
|
377
|
-
reject = onReject;
|
|
378
|
-
});
|
|
379
|
-
activeFlush = task;
|
|
380
|
-
(async () => {
|
|
381
|
-
try {
|
|
382
|
-
if (preferBeacon) flushBeaconQueue();
|
|
383
|
-
while (queue.length > 0) {
|
|
384
|
-
const batch = queue.splice(0, maxBatchSize);
|
|
385
|
-
try {
|
|
386
|
-
await sendFetchBatch(batch);
|
|
387
|
-
} catch (error) {
|
|
388
|
-
queue.unshift(...batch);
|
|
389
|
-
throw error;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
resolve();
|
|
393
|
-
} catch (error) {
|
|
394
|
-
reject(error);
|
|
395
|
-
} finally {
|
|
396
|
-
activeFlush = void 0;
|
|
397
|
-
if (queue.length > 0) schedule();
|
|
398
|
-
}
|
|
399
|
-
})();
|
|
400
|
-
return task;
|
|
401
|
-
};
|
|
402
|
-
const schedule = () => {
|
|
403
|
-
if (activeFlush || timer || flushIntervalMs <= 0) return;
|
|
404
|
-
timer = setTimeout(() => {
|
|
405
|
-
timer = void 0;
|
|
406
|
-
flush(false).catch((error) => reportInternalError(error, "flush"));
|
|
407
|
-
}, flushIntervalMs);
|
|
408
|
-
};
|
|
409
|
-
const onPageHide = () => {
|
|
410
|
-
flush(true).catch((error) => reportInternalError(error, "pagehide-flush"));
|
|
411
|
-
};
|
|
412
|
-
const onVisibilityChange = () => {
|
|
413
|
-
if (typeof document !== "undefined" && document.visibilityState === "hidden") flush(true).catch((error) => reportInternalError(error, "visibility-flush"));
|
|
414
|
-
};
|
|
415
|
-
const onOnline = () => {
|
|
416
|
-
replayOfflineQueue().catch((error) => reportInternalError(error, "replay"));
|
|
417
|
-
};
|
|
418
|
-
if (options.useBeaconOnPageHide ?? true) {
|
|
419
|
-
globalThis.addEventListener?.("pagehide", onPageHide);
|
|
420
|
-
globalThis.addEventListener?.("visibilitychange", onVisibilityChange);
|
|
421
|
-
}
|
|
422
|
-
if (offlineQueue) globalThis.addEventListener?.("online", onOnline);
|
|
423
|
-
return {
|
|
424
|
-
name: options.name ?? "browser-http",
|
|
425
|
-
minLevel: options.minLevel,
|
|
426
|
-
log(event, context) {
|
|
427
|
-
if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
|
|
428
|
-
lastContext = context;
|
|
429
|
-
if (queue.length >= maxQueueSize) {
|
|
430
|
-
if (dropPolicy === "drop-newest") {
|
|
431
|
-
reportDrop(event, "queue-full");
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
const dropped = queue.shift();
|
|
435
|
-
if (dropped) reportDrop(dropped, "queue-full");
|
|
436
|
-
}
|
|
437
|
-
queue.push(event);
|
|
438
|
-
if (activeFlush) return;
|
|
439
|
-
if (queue.length >= maxBatchSize) flush(false).catch((error) => reportInternalError(error, "flush"));
|
|
440
|
-
else schedule();
|
|
441
|
-
},
|
|
442
|
-
flush() {
|
|
443
|
-
return flush(false);
|
|
444
|
-
},
|
|
445
|
-
close() {
|
|
446
|
-
globalThis.removeEventListener?.("pagehide", onPageHide);
|
|
447
|
-
globalThis.removeEventListener?.("visibilitychange", onVisibilityChange);
|
|
448
|
-
globalThis.removeEventListener?.("online", onOnline);
|
|
449
|
-
return flush(true);
|
|
450
|
-
}
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
//#endregion
|
|
454
|
-
//#region src/payload-transforms.ts
|
|
455
|
-
async function compressWithStream(payload, format, streamFactory) {
|
|
456
|
-
const stream = (streamFactory ?? ((nextFormat) => {
|
|
457
|
-
if (typeof CompressionStream === "undefined") throw new Error("CompressionStream is not available in this browser");
|
|
458
|
-
return new CompressionStream(nextFormat);
|
|
459
|
-
}))(format);
|
|
460
|
-
const writer = stream.writable.getWriter();
|
|
461
|
-
await writer.write(Uint8Array.from(payload));
|
|
462
|
-
await writer.close();
|
|
463
|
-
return new Uint8Array(await new Response(stream.readable).arrayBuffer());
|
|
464
|
-
}
|
|
465
|
-
function browserCompressionPayloadTransform(options = {}) {
|
|
466
|
-
const format = options.format ?? "gzip";
|
|
467
|
-
return async (payload, context) => ({
|
|
468
|
-
payload: await (options.compress ? options.compress((0, _loggerjs_core.encodedPayloadToUint8Array)(payload), {
|
|
469
|
-
...context,
|
|
470
|
-
format
|
|
471
|
-
}) : compressWithStream((0, _loggerjs_core.encodedPayloadToUint8Array)(payload), format, options.streamFactory)),
|
|
472
|
-
headers: {
|
|
473
|
-
"content-encoding": format,
|
|
474
|
-
...options.headers
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
//#endregion
|
|
479
|
-
//#region src/service-worker-transport.ts
|
|
480
|
-
let sourceSequence = 0;
|
|
481
|
-
function createSourceId() {
|
|
482
|
-
sourceSequence += 1;
|
|
483
|
-
return `loggerjs-${Date.now().toString(36)}-${sourceSequence.toString(36)}`;
|
|
484
|
-
}
|
|
485
|
-
function defaultMapEvent(event, context) {
|
|
486
|
-
return {
|
|
487
|
-
type: "loggerjs.event",
|
|
488
|
-
source: context.source,
|
|
489
|
-
event
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
function defaultMapBatch(events, context) {
|
|
493
|
-
return {
|
|
494
|
-
type: "loggerjs.batch",
|
|
495
|
-
source: context.source,
|
|
496
|
-
events
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
function serviceWorkerContainer() {
|
|
500
|
-
return typeof navigator === "undefined" ? void 0 : navigator.serviceWorker;
|
|
501
|
-
}
|
|
502
|
-
function workerFromRegistration(registration) {
|
|
503
|
-
return registration.active ?? registration.waiting ?? registration.installing ?? void 0;
|
|
504
|
-
}
|
|
505
|
-
function browserServiceWorkerTransport(options = {}) {
|
|
506
|
-
const transportName = options.name ?? "browser-service-worker";
|
|
507
|
-
const target = options.target ?? "controller";
|
|
508
|
-
const source = options.source ?? createSourceId();
|
|
509
|
-
const maxQueueSize = options.maxQueueSize ?? 1e3;
|
|
510
|
-
const dropPolicy = options.dropPolicy ?? "drop-oldest";
|
|
511
|
-
const mapContext = {
|
|
512
|
-
source,
|
|
513
|
-
target
|
|
514
|
-
};
|
|
515
|
-
const mapEvent = options.mapEvent ?? defaultMapEvent;
|
|
516
|
-
const mapBatch = options.mapBatch ?? defaultMapBatch;
|
|
517
|
-
const queue = [];
|
|
518
|
-
let readyWorker;
|
|
519
|
-
let readyPromise;
|
|
520
|
-
let lastContext;
|
|
521
|
-
const reportError = (error, context, operation, droppedEvents = 0) => {
|
|
522
|
-
try {
|
|
523
|
-
options.onError?.(error, {
|
|
524
|
-
droppedEvents,
|
|
525
|
-
operation
|
|
526
|
-
});
|
|
527
|
-
} catch (onErrorError) {
|
|
528
|
-
context?.reportInternalError(onErrorError, {
|
|
529
|
-
operation: "on-error",
|
|
530
|
-
phase: "transport",
|
|
531
|
-
transport: transportName
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
context?.reportInternalError(error, {
|
|
535
|
-
operation,
|
|
536
|
-
phase: "transport",
|
|
537
|
-
transport: transportName
|
|
538
|
-
});
|
|
539
|
-
};
|
|
540
|
-
const reportDrop = (item, reason) => {
|
|
541
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.dropped", item.events.length);
|
|
542
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.dropped.${reason}`, item.events.length);
|
|
543
|
-
for (const event of item.events) options.onDrop?.(event, reason);
|
|
544
|
-
};
|
|
545
|
-
const currentWorker = () => {
|
|
546
|
-
const container = options.serviceWorker ?? serviceWorkerContainer();
|
|
547
|
-
if (!container) return void 0;
|
|
548
|
-
if (target === "controller") return container.controller ?? void 0;
|
|
549
|
-
return readyWorker;
|
|
550
|
-
};
|
|
551
|
-
const post = (worker, item, context) => {
|
|
552
|
-
try {
|
|
553
|
-
worker.postMessage(item.message, options.transfer?.(item.message));
|
|
554
|
-
return true;
|
|
555
|
-
} catch (error) {
|
|
556
|
-
reportDrop(item, "post-message");
|
|
557
|
-
reportError(error, context, "post-message", item.events.length);
|
|
558
|
-
return false;
|
|
559
|
-
}
|
|
560
|
-
};
|
|
561
|
-
const drain = (context = lastContext) => {
|
|
562
|
-
const worker = currentWorker();
|
|
563
|
-
if (!worker || !context) return;
|
|
564
|
-
while (queue.length > 0) {
|
|
565
|
-
const item = queue.shift();
|
|
566
|
-
if (!item) return;
|
|
567
|
-
post(worker, item, context);
|
|
568
|
-
}
|
|
569
|
-
};
|
|
570
|
-
const dropQueued = (reason) => {
|
|
571
|
-
const pending = queue.splice(0);
|
|
572
|
-
for (const item of pending) reportDrop(item, reason);
|
|
573
|
-
return pending.reduce((total, item) => total + item.events.length, 0);
|
|
574
|
-
};
|
|
575
|
-
const waitForReady = (context) => {
|
|
576
|
-
if (currentWorker()) return Promise.resolve();
|
|
577
|
-
if (target !== "ready") return Promise.reject(/* @__PURE__ */ new Error("service worker controller is not available"));
|
|
578
|
-
if (readyPromise) return readyPromise;
|
|
579
|
-
const container = options.serviceWorker ?? serviceWorkerContainer();
|
|
580
|
-
if (!container?.ready) {
|
|
581
|
-
const error = /* @__PURE__ */ new Error("serviceWorker.ready is not available");
|
|
582
|
-
reportError(error, context, "ready", dropQueued("ready"));
|
|
583
|
-
return Promise.reject(error);
|
|
584
|
-
}
|
|
585
|
-
readyPromise = container.ready.then((registration) => {
|
|
586
|
-
readyWorker = workerFromRegistration(registration);
|
|
587
|
-
if (!readyWorker) throw new Error("service worker registration has no active worker");
|
|
588
|
-
drain(context ?? lastContext);
|
|
589
|
-
}).catch((error) => {
|
|
590
|
-
readyPromise = void 0;
|
|
591
|
-
const droppedEvents = dropQueued("ready");
|
|
592
|
-
reportError(error, context ?? lastContext, "ready", droppedEvents);
|
|
593
|
-
throw error;
|
|
594
|
-
});
|
|
595
|
-
return readyPromise;
|
|
596
|
-
};
|
|
597
|
-
const startReady = (context) => {
|
|
598
|
-
waitForReady(context).catch(() => {});
|
|
599
|
-
};
|
|
600
|
-
const enqueue = (item) => {
|
|
601
|
-
if (queue.length >= maxQueueSize) {
|
|
602
|
-
if (dropPolicy === "drop-newest") {
|
|
603
|
-
reportDrop(item, "queue-full");
|
|
604
|
-
return false;
|
|
605
|
-
}
|
|
606
|
-
const dropped = queue.shift();
|
|
607
|
-
if (dropped) reportDrop(dropped, "queue-full");
|
|
608
|
-
}
|
|
609
|
-
queue.push(item);
|
|
610
|
-
return true;
|
|
611
|
-
};
|
|
612
|
-
const send = (item, context) => {
|
|
613
|
-
lastContext = context;
|
|
614
|
-
const worker = currentWorker();
|
|
615
|
-
if (worker) {
|
|
616
|
-
post(worker, item, context);
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
if (target === "ready") {
|
|
620
|
-
if (enqueue(item)) startReady(context);
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
reportDrop(item, "unavailable");
|
|
624
|
-
reportError(/* @__PURE__ */ new Error("service worker controller is not available"), context, "unavailable", item.events.length);
|
|
625
|
-
};
|
|
626
|
-
return {
|
|
627
|
-
name: transportName,
|
|
628
|
-
minLevel: options.minLevel,
|
|
629
|
-
queueSize() {
|
|
630
|
-
return queue.length;
|
|
631
|
-
},
|
|
632
|
-
ready() {
|
|
633
|
-
return waitForReady();
|
|
634
|
-
},
|
|
635
|
-
log(event, context) {
|
|
636
|
-
send({
|
|
637
|
-
events: [event],
|
|
638
|
-
message: mapEvent(event, mapContext)
|
|
639
|
-
}, context);
|
|
640
|
-
},
|
|
641
|
-
logBatch(events, context) {
|
|
642
|
-
if (events.length === 0) return;
|
|
643
|
-
send({
|
|
644
|
-
events,
|
|
645
|
-
message: mapBatch(events, mapContext)
|
|
646
|
-
}, context);
|
|
647
|
-
},
|
|
648
|
-
flush() {
|
|
649
|
-
drain();
|
|
650
|
-
},
|
|
651
|
-
close() {
|
|
652
|
-
const pending = queue.splice(0);
|
|
653
|
-
for (const item of pending) reportDrop(item, "closed");
|
|
654
|
-
}
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
//#endregion
|
|
658
|
-
//#region src/websocket-transport.ts
|
|
659
|
-
const WS_CONNECTING = 0;
|
|
660
|
-
const WS_OPEN = 1;
|
|
661
|
-
function defaultWebSocketFactory(url, protocols) {
|
|
662
|
-
if (typeof WebSocket === "undefined") throw new Error("WebSocket is not available");
|
|
663
|
-
return new WebSocket(url, protocols);
|
|
664
|
-
}
|
|
665
|
-
function toSendPayload(payload) {
|
|
666
|
-
if (typeof payload === "string") return payload;
|
|
667
|
-
return Uint8Array.from(payload);
|
|
668
|
-
}
|
|
669
|
-
function browserWebSocketTransport(options) {
|
|
670
|
-
const transportName = options.name ?? "browser-websocket";
|
|
671
|
-
const codec = options.codec ?? (0, _loggerjs_core.safeJsonCodec)();
|
|
672
|
-
const maxQueueSize = options.maxQueueSize ?? 1e3;
|
|
673
|
-
const dropPolicy = options.dropPolicy ?? "drop-oldest";
|
|
674
|
-
const webSocketFactory = options.webSocketFactory ?? defaultWebSocketFactory;
|
|
675
|
-
const queue = [];
|
|
676
|
-
let active;
|
|
677
|
-
let closed = false;
|
|
678
|
-
let lastContext;
|
|
679
|
-
const reportDrop = (item, reason) => {
|
|
680
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.dropped", item.events.length);
|
|
681
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.dropped.${reason}`, item.events.length);
|
|
682
|
-
for (const event of item.events) options.onDrop?.(event, reason);
|
|
683
|
-
};
|
|
684
|
-
const reportError = (error, context, detail) => {
|
|
685
|
-
try {
|
|
686
|
-
options.onError?.(error, detail);
|
|
687
|
-
} catch (onErrorError) {
|
|
688
|
-
context?.reportInternalError(onErrorError, {
|
|
689
|
-
operation: "on-error",
|
|
690
|
-
phase: "transport",
|
|
691
|
-
transport: transportName
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
context?.reportInternalError(error, {
|
|
695
|
-
operation: detail.operation,
|
|
696
|
-
phase: "transport",
|
|
697
|
-
transport: transportName
|
|
698
|
-
});
|
|
699
|
-
};
|
|
700
|
-
const detachSocket = () => {
|
|
701
|
-
if (!active) return;
|
|
702
|
-
const current = active;
|
|
703
|
-
current.socket.removeEventListener("open", current.onOpen);
|
|
704
|
-
current.socket.removeEventListener("close", current.onClose);
|
|
705
|
-
current.socket.removeEventListener("error", current.onError);
|
|
706
|
-
active = void 0;
|
|
707
|
-
};
|
|
708
|
-
const drainQueue = (context = lastContext) => {
|
|
709
|
-
const socket = active?.socket;
|
|
710
|
-
if (!socket || socket.readyState !== WS_OPEN) return;
|
|
711
|
-
while (queue.length > 0) {
|
|
712
|
-
const item = queue.shift();
|
|
713
|
-
if (!item) return;
|
|
714
|
-
try {
|
|
715
|
-
socket.send(toSendPayload(item.payload));
|
|
716
|
-
} catch (error) {
|
|
717
|
-
reportDrop(item, "send");
|
|
718
|
-
reportError(error, context, {
|
|
719
|
-
droppedEvents: item.events.length,
|
|
720
|
-
operation: "send"
|
|
721
|
-
});
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
const createSocket = (context, droppedEvents) => {
|
|
727
|
-
if (closed) return void 0;
|
|
728
|
-
if (active && (active.socket.readyState === WS_CONNECTING || active.socket.readyState === WS_OPEN)) return active.socket;
|
|
729
|
-
detachSocket();
|
|
730
|
-
try {
|
|
731
|
-
const socket = webSocketFactory(options.url, options.protocols);
|
|
732
|
-
const nextActive = {
|
|
733
|
-
socket,
|
|
734
|
-
onOpen() {
|
|
735
|
-
drainQueue(context);
|
|
736
|
-
},
|
|
737
|
-
onClose() {
|
|
738
|
-
if (active?.socket === socket) detachSocket();
|
|
739
|
-
},
|
|
740
|
-
onError(errorEvent) {
|
|
741
|
-
reportError(errorEvent, context, {
|
|
742
|
-
droppedEvents: 0,
|
|
743
|
-
operation: "socket-error"
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
};
|
|
747
|
-
active = nextActive;
|
|
748
|
-
socket.addEventListener("open", nextActive.onOpen);
|
|
749
|
-
socket.addEventListener("close", nextActive.onClose);
|
|
750
|
-
socket.addEventListener("error", nextActive.onError);
|
|
751
|
-
return socket;
|
|
752
|
-
} catch (error) {
|
|
753
|
-
reportError(error, context, {
|
|
754
|
-
droppedEvents,
|
|
755
|
-
operation: "create-socket"
|
|
756
|
-
});
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
};
|
|
760
|
-
const enqueue = (item) => {
|
|
761
|
-
if (queue.length >= maxQueueSize) {
|
|
762
|
-
if (dropPolicy === "drop-newest") {
|
|
763
|
-
reportDrop(item, "queue-full");
|
|
764
|
-
return false;
|
|
765
|
-
}
|
|
766
|
-
const dropped = queue.shift();
|
|
767
|
-
if (dropped) reportDrop(dropped, "queue-full");
|
|
768
|
-
}
|
|
769
|
-
queue.push(item);
|
|
770
|
-
return true;
|
|
771
|
-
};
|
|
772
|
-
const sendOrQueue = (item, context) => {
|
|
773
|
-
lastContext = context;
|
|
774
|
-
if (closed) {
|
|
775
|
-
reportDrop(item, "closed");
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
const socket = createSocket(context, item.events.length);
|
|
779
|
-
if (!socket) {
|
|
780
|
-
reportDrop(item, "create-socket");
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
if (socket.readyState === WS_OPEN) {
|
|
784
|
-
try {
|
|
785
|
-
socket.send(toSendPayload(item.payload));
|
|
786
|
-
} catch (error) {
|
|
787
|
-
reportDrop(item, "send");
|
|
788
|
-
reportError(error, context, {
|
|
789
|
-
droppedEvents: item.events.length,
|
|
790
|
-
operation: "send"
|
|
791
|
-
});
|
|
792
|
-
}
|
|
793
|
-
return;
|
|
794
|
-
}
|
|
795
|
-
enqueue(item);
|
|
796
|
-
};
|
|
797
|
-
return {
|
|
798
|
-
name: transportName,
|
|
799
|
-
minLevel: options.minLevel,
|
|
800
|
-
queueSize() {
|
|
801
|
-
return queue.length;
|
|
802
|
-
},
|
|
803
|
-
log(event, context) {
|
|
804
|
-
sendOrQueue({
|
|
805
|
-
events: [event],
|
|
806
|
-
payload: codec.encode(event)
|
|
807
|
-
}, context);
|
|
808
|
-
},
|
|
809
|
-
logBatch(events, context) {
|
|
810
|
-
if (events.length === 0) return;
|
|
811
|
-
sendOrQueue({
|
|
812
|
-
events,
|
|
813
|
-
payload: codec.encode(events)
|
|
814
|
-
}, context);
|
|
815
|
-
},
|
|
816
|
-
flush() {
|
|
817
|
-
drainQueue();
|
|
818
|
-
},
|
|
819
|
-
async close() {
|
|
820
|
-
closed = true;
|
|
821
|
-
while (queue.length > 0) {
|
|
822
|
-
const item = queue.shift();
|
|
823
|
-
if (item) reportDrop(item, "closed");
|
|
824
|
-
}
|
|
825
|
-
const socket = active?.socket;
|
|
826
|
-
detachSocket();
|
|
827
|
-
if (!socket) return;
|
|
828
|
-
try {
|
|
829
|
-
socket.close(options.closeCode, options.closeReason);
|
|
830
|
-
} catch (error) {
|
|
831
|
-
reportError(error, lastContext, {
|
|
832
|
-
droppedEvents: 0,
|
|
833
|
-
operation: "close-socket"
|
|
834
|
-
});
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
//#endregion
|
|
840
|
-
//#region src/indexeddb-offline-queue.ts
|
|
841
|
-
const DEFAULT_DB_NAME$1 = "loggerjs";
|
|
842
|
-
const DEFAULT_STORE_NAME$1 = "browser-http-offline";
|
|
843
|
-
function normalizeMaxEntries(value) {
|
|
844
|
-
if (value === void 0 || !Number.isFinite(value)) return 1e3;
|
|
845
|
-
return Math.max(0, Math.floor(value));
|
|
846
|
-
}
|
|
847
|
-
function requestToPromise$1(request) {
|
|
848
|
-
return new Promise((resolve, reject) => {
|
|
849
|
-
request.addEventListener("success", () => resolve(request.result), { once: true });
|
|
850
|
-
request.addEventListener("error", () => reject(request.error ?? /* @__PURE__ */ new Error("IndexedDB request failed")), { once: true });
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
function compareEntries$1(a, b) {
|
|
854
|
-
if (a.createdAt !== b.createdAt) return a.createdAt - b.createdAt;
|
|
855
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
856
|
-
}
|
|
857
|
-
function orderEntries$1(entries) {
|
|
858
|
-
const ordered = [];
|
|
859
|
-
for (const entry of entries) {
|
|
860
|
-
let index = 0;
|
|
861
|
-
while (index < ordered.length && compareEntries$1(ordered[index], entry) <= 0) index += 1;
|
|
862
|
-
ordered.splice(index, 0, entry);
|
|
863
|
-
}
|
|
864
|
-
return ordered;
|
|
865
|
-
}
|
|
866
|
-
function indexedDbBrowserHttpOfflineQueue(options = {}) {
|
|
867
|
-
const dbName = options.dbName ?? DEFAULT_DB_NAME$1;
|
|
868
|
-
const storeName = options.storeName ?? DEFAULT_STORE_NAME$1;
|
|
869
|
-
const maxEntries = normalizeMaxEntries(options.maxEntries);
|
|
870
|
-
const dropPolicy = options.dropPolicy ?? "drop-oldest";
|
|
871
|
-
const idb = options.indexedDB ?? globalThis.indexedDB;
|
|
872
|
-
let dbPromise;
|
|
873
|
-
const drop = (entry, reason) => {
|
|
874
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.dropped");
|
|
875
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.offline.dropped.${reason}`);
|
|
876
|
-
options.onDrop?.(entry, reason);
|
|
877
|
-
};
|
|
878
|
-
const openDb = () => {
|
|
879
|
-
if (!idb) throw new Error("IndexedDB is not available for loggerjs offline queue");
|
|
880
|
-
dbPromise ??= new Promise((resolve, reject) => {
|
|
881
|
-
const request = idb.open(dbName, 1);
|
|
882
|
-
request.addEventListener("upgradeneeded", () => {
|
|
883
|
-
const db = request.result;
|
|
884
|
-
if (!db.objectStoreNames.contains(storeName)) db.createObjectStore(storeName, { keyPath: "id" });
|
|
885
|
-
});
|
|
886
|
-
request.addEventListener("success", () => resolve(request.result), { once: true });
|
|
887
|
-
request.addEventListener("error", () => reject(request.error ?? /* @__PURE__ */ new Error("IndexedDB open failed")), { once: true });
|
|
888
|
-
});
|
|
889
|
-
return dbPromise;
|
|
890
|
-
};
|
|
891
|
-
const withStore = async (mode, run) => {
|
|
892
|
-
return run((await openDb()).transaction(storeName, mode).objectStore(storeName));
|
|
893
|
-
};
|
|
894
|
-
const getEntries = async () => orderEntries$1(await withStore("readonly", (store) => requestToPromise$1(store.getAll())));
|
|
895
|
-
const putEntry = (entry) => withStore("readwrite", (store) => requestToPromise$1(store.put(entry)));
|
|
896
|
-
const deleteEntry = (id) => withStore("readwrite", (store) => requestToPromise$1(store.delete(id)));
|
|
897
|
-
return {
|
|
898
|
-
async enqueue(entry) {
|
|
899
|
-
if (maxEntries === 0) {
|
|
900
|
-
drop(entry, "queue-full");
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
903
|
-
const entries = await getEntries();
|
|
904
|
-
if (entries.length >= maxEntries) {
|
|
905
|
-
if (dropPolicy === "drop-newest") {
|
|
906
|
-
drop(entry, "queue-full");
|
|
907
|
-
return;
|
|
908
|
-
}
|
|
909
|
-
const dropCount = entries.length - maxEntries + 1;
|
|
910
|
-
for (let index = 0; index < dropCount; index += 1) {
|
|
911
|
-
const dropped = entries[index];
|
|
912
|
-
if (!dropped) continue;
|
|
913
|
-
await deleteEntry(dropped.id);
|
|
914
|
-
drop(dropped, "queue-full");
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
await putEntry(entry);
|
|
918
|
-
},
|
|
919
|
-
async replay(send) {
|
|
920
|
-
const entries = await getEntries();
|
|
921
|
-
for (const entry of entries) {
|
|
922
|
-
await send(entry);
|
|
923
|
-
await deleteEntry(entry.id);
|
|
924
|
-
}
|
|
925
|
-
},
|
|
926
|
-
async size() {
|
|
927
|
-
return (await getEntries()).length;
|
|
928
|
-
},
|
|
929
|
-
clear() {
|
|
930
|
-
return withStore("readwrite", (store) => requestToPromise$1(store.clear()));
|
|
931
|
-
},
|
|
932
|
-
close() {
|
|
933
|
-
const promise = dbPromise;
|
|
934
|
-
dbPromise = void 0;
|
|
935
|
-
promise?.then((db) => db.close());
|
|
936
|
-
}
|
|
937
|
-
};
|
|
938
|
-
}
|
|
939
|
-
//#endregion
|
|
940
|
-
//#region src/indexeddb-transport.ts
|
|
941
|
-
const DEFAULT_DB_NAME = "loggerjs";
|
|
942
|
-
const DEFAULT_STORE_NAME = "logs";
|
|
943
|
-
const DEFAULT_SESSION_CONTEXT_KEY$1 = "sessionId";
|
|
944
|
-
const DEFAULT_SPILL_MAX_ENTRIES = 200;
|
|
945
|
-
const DEFAULT_SPILL_MAX_BYTES = 512 * 1024;
|
|
946
|
-
const DEFAULT_SPILL_MAX_ORIGIN_BYTES = 5 * 1024 * 1024;
|
|
947
|
-
const DB_VERSION = 4;
|
|
948
|
-
const ORDER_INDEX_NAME = "createdAtSeq";
|
|
949
|
-
const SESSION_INDEX_NAME = "sessionId";
|
|
950
|
-
const SESSION_ORDER_INDEX_NAME = "sessionIdCreatedAtSeq";
|
|
951
|
-
const SESSION_STORE_SUFFIX = "__sessions";
|
|
952
|
-
const SESSION_LAST_SEEN_INDEX_NAME = "lastSeenSessionId";
|
|
953
|
-
const SPILL_SCHEMA = "loggerjs.indexeddb-spill.v1";
|
|
954
|
-
function normalizePositiveInteger(value, fallback) {
|
|
955
|
-
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
956
|
-
return Math.max(0, Math.floor(value));
|
|
957
|
-
}
|
|
958
|
-
let fallbackSessionSeq = 0;
|
|
959
|
-
function defaultPageSessionId() {
|
|
960
|
-
const randomUUID = globalThis.crypto?.randomUUID;
|
|
961
|
-
if (randomUUID) return randomUUID.call(globalThis.crypto);
|
|
962
|
-
return `${Date.now().toString(36)}-${(fallbackSessionSeq++).toString(36)}`;
|
|
963
|
-
}
|
|
964
|
-
function normalizeSessionId(value) {
|
|
965
|
-
if (typeof value !== "string") return void 0;
|
|
966
|
-
const normalized = value.trim();
|
|
967
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
968
|
-
}
|
|
969
|
-
function normalizeSessionOptions(session) {
|
|
970
|
-
if (session === false) return void 0;
|
|
971
|
-
if (typeof session === "string") return {
|
|
972
|
-
contextKey: DEFAULT_SESSION_CONTEXT_KEY$1,
|
|
973
|
-
id: normalizeSessionId(session) ?? defaultPageSessionId()
|
|
974
|
-
};
|
|
975
|
-
return {
|
|
976
|
-
contextKey: session?.contextKey ?? DEFAULT_SESSION_CONTEXT_KEY$1,
|
|
977
|
-
getId: session?.getId,
|
|
978
|
-
id: normalizeSessionId(session?.id) ?? defaultPageSessionId()
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
function resolveLocalStorage(storage) {
|
|
982
|
-
if (storage) return storage;
|
|
983
|
-
try {
|
|
984
|
-
return globalThis.localStorage;
|
|
985
|
-
} catch {
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
function normalizeLocalStorageSpillOptions(spill, dbName, storeName) {
|
|
990
|
-
if (!spill) return void 0;
|
|
991
|
-
const storage = resolveLocalStorage(spill.storage);
|
|
992
|
-
if (!storage) return void 0;
|
|
993
|
-
const namespace = spill.namespace ?? `${dbName}:${storeName}`;
|
|
994
|
-
return {
|
|
995
|
-
drainOnCreate: spill.drainOnCreate ?? true,
|
|
996
|
-
key: `loggerjs:spill:v1:${namespace}`,
|
|
997
|
-
maxBytes: normalizePositiveInteger(spill.maxBytes, DEFAULT_SPILL_MAX_BYTES),
|
|
998
|
-
maxEntries: normalizePositiveInteger(spill.maxEntries, DEFAULT_SPILL_MAX_ENTRIES),
|
|
999
|
-
maxOriginBytes: normalizePositiveInteger(spill.maxOriginBytes, DEFAULT_SPILL_MAX_ORIGIN_BYTES),
|
|
1000
|
-
minLevelValue: spill.minLevel === void 0 ? void 0 : (0, _loggerjs_core.toLevelValue)(spill.minLevel),
|
|
1001
|
-
onDrop: spill.onDrop,
|
|
1002
|
-
spillOnPageHide: spill.spillOnPageHide ?? true,
|
|
1003
|
-
storage
|
|
1004
|
-
};
|
|
1005
|
-
}
|
|
1006
|
-
function storageByteLength(key, value) {
|
|
1007
|
-
return 2 * (key.length + value.length);
|
|
1008
|
-
}
|
|
1009
|
-
function estimateLocalStorageBytes(storage) {
|
|
1010
|
-
try {
|
|
1011
|
-
let total = 0;
|
|
1012
|
-
for (let index = 0; index < storage.length; index += 1) {
|
|
1013
|
-
const key = storage.key(index);
|
|
1014
|
-
if (key === null) continue;
|
|
1015
|
-
total += storageByteLength(key, storage.getItem(key) ?? "");
|
|
1016
|
-
}
|
|
1017
|
-
return total;
|
|
1018
|
-
} catch {
|
|
1019
|
-
return;
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
function isQuotaExceededError(error) {
|
|
1023
|
-
return typeof DOMException !== "undefined" && error instanceof DOMException && (error.name === "QuotaExceededError" || error.name === "NS_ERROR_DOM_QUOTA_REACHED");
|
|
1024
|
-
}
|
|
1025
|
-
function isSpillLogEvent(value) {
|
|
1026
|
-
if (!value || typeof value !== "object") return false;
|
|
1027
|
-
const event = value;
|
|
1028
|
-
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";
|
|
1029
|
-
}
|
|
1030
|
-
function parseSpillPayload(raw) {
|
|
1031
|
-
const parsed = JSON.parse(raw);
|
|
1032
|
-
if (parsed.schema !== SPILL_SCHEMA || !Array.isArray(parsed.entries)) return void 0;
|
|
1033
|
-
return parsed.entries.filter(isSpillLogEvent);
|
|
1034
|
-
}
|
|
1035
|
-
function nowMs$1() {
|
|
1036
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
1037
|
-
}
|
|
1038
|
-
function incrementRecord(record, key, amount = 1) {
|
|
1039
|
-
record[key] = (record[key] ?? 0) + amount;
|
|
1040
|
-
}
|
|
1041
|
-
function requestToPromise(request) {
|
|
1042
|
-
return new Promise((resolve, reject) => {
|
|
1043
|
-
request.addEventListener("success", () => resolve(request.result), { once: true });
|
|
1044
|
-
request.addEventListener("error", () => reject(request.error ?? /* @__PURE__ */ new Error("IndexedDB request failed")), { once: true });
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1047
|
-
function transactionToPromise(transaction) {
|
|
1048
|
-
return new Promise((resolve, reject) => {
|
|
1049
|
-
transaction.addEventListener("complete", () => resolve(), { once: true });
|
|
1050
|
-
transaction.addEventListener("abort", () => reject(transaction.error ?? /* @__PURE__ */ new Error("IndexedDB transaction aborted")), { once: true });
|
|
1051
|
-
transaction.addEventListener("error", () => reject(transaction.error ?? /* @__PURE__ */ new Error("IndexedDB transaction failed")), { once: true });
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
function payloadByteLength(payload) {
|
|
1055
|
-
if (typeof payload !== "string") return payload.byteLength;
|
|
1056
|
-
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(payload).byteLength;
|
|
1057
|
-
return new Blob([payload]).size;
|
|
1058
|
-
}
|
|
1059
|
-
function compareEntries(a, b) {
|
|
1060
|
-
if (a.createdAt !== b.createdAt) return a.createdAt - b.createdAt;
|
|
1061
|
-
if (a.seq !== b.seq) return a.seq - b.seq;
|
|
1062
|
-
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
1063
|
-
}
|
|
1064
|
-
function orderEntries(entries, order = "asc") {
|
|
1065
|
-
const sorted = [...entries].sort(compareEntries);
|
|
1066
|
-
if (order === "asc") return sorted;
|
|
1067
|
-
const reversed = [];
|
|
1068
|
-
for (let index = sorted.length - 1; index >= 0; index -= 1) reversed.push(sorted[index]);
|
|
1069
|
-
return reversed;
|
|
1070
|
-
}
|
|
1071
|
-
function indexExists(store, name) {
|
|
1072
|
-
return store.indexNames.contains(name);
|
|
1073
|
-
}
|
|
1074
|
-
function ensureIndex(store, name, keyPath) {
|
|
1075
|
-
if (!indexExists(store, name)) store.createIndex(name, keyPath);
|
|
1076
|
-
}
|
|
1077
|
-
function sessionStoreNameFor(storeName) {
|
|
1078
|
-
return `${storeName}${SESSION_STORE_SUFFIX}`;
|
|
1079
|
-
}
|
|
1080
|
-
function getStoreIndex(store, name) {
|
|
1081
|
-
const maybeStore = store;
|
|
1082
|
-
if (typeof maybeStore.index !== "function" || !indexExists(store, name)) return void 0;
|
|
1083
|
-
return maybeStore.index(name);
|
|
1084
|
-
}
|
|
1085
|
-
function getStorageBucketManager() {
|
|
1086
|
-
return globalThis.navigator?.storageBuckets;
|
|
1087
|
-
}
|
|
1088
|
-
function storageBucketOpenOptions(options) {
|
|
1089
|
-
if (options.storageBucketDurability === void 0 && options.storageBucketPersisted === void 0) return;
|
|
1090
|
-
return {
|
|
1091
|
-
durability: options.storageBucketDurability,
|
|
1092
|
-
persisted: options.storageBucketPersisted
|
|
1093
|
-
};
|
|
1094
|
-
}
|
|
1095
|
-
function decodeEntry(entry, codec) {
|
|
1096
|
-
if (!codec.decode) throw new Error("indexedDbTransport query requires a codec with decode");
|
|
1097
|
-
const decoded = codec.decode(entry.payload);
|
|
1098
|
-
return Array.isArray(decoded) ? decoded[0] : decoded;
|
|
1099
|
-
}
|
|
1100
|
-
function shouldKeepEntry(entry, options) {
|
|
1101
|
-
if (options.from !== void 0 && entry.createdAt < options.from) return false;
|
|
1102
|
-
if (options.to !== void 0 && entry.createdAt > options.to) return false;
|
|
1103
|
-
if (options.sessionId !== void 0 && entry.sessionId !== options.sessionId) return false;
|
|
1104
|
-
if (options.minLevel !== void 0 && entry.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return false;
|
|
1105
|
-
if (options.logger !== void 0 && entry.logger !== options.logger) return false;
|
|
1106
|
-
if (options.type !== void 0 && entry.type !== options.type) return false;
|
|
1107
|
-
return true;
|
|
1108
|
-
}
|
|
1109
|
-
function addSessionSummary(sessions, entry) {
|
|
1110
|
-
if (!entry.sessionId) return;
|
|
1111
|
-
const current = sessions.get(entry.sessionId);
|
|
1112
|
-
if (current) {
|
|
1113
|
-
current.firstSeen = Math.min(current.firstSeen, entry.createdAt);
|
|
1114
|
-
current.lastSeen = Math.max(current.lastSeen, entry.createdAt);
|
|
1115
|
-
current.count += 1;
|
|
1116
|
-
current.byteLength += entry.byteLength;
|
|
1117
|
-
return;
|
|
1118
|
-
}
|
|
1119
|
-
sessions.set(entry.sessionId, {
|
|
1120
|
-
byteLength: entry.byteLength,
|
|
1121
|
-
count: 1,
|
|
1122
|
-
firstSeen: entry.createdAt,
|
|
1123
|
-
lastSeen: entry.createdAt,
|
|
1124
|
-
sessionId: entry.sessionId
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
function mergeSessionSummary(current, incoming) {
|
|
1128
|
-
if (!current) return incoming;
|
|
1129
|
-
return {
|
|
1130
|
-
byteLength: current.byteLength + incoming.byteLength,
|
|
1131
|
-
count: current.count + incoming.count,
|
|
1132
|
-
firstSeen: Math.min(current.firstSeen, incoming.firstSeen),
|
|
1133
|
-
lastSeen: Math.max(current.lastSeen, incoming.lastSeen),
|
|
1134
|
-
sessionId: incoming.sessionId
|
|
1135
|
-
};
|
|
1136
|
-
}
|
|
1137
|
-
function orderSessions(sessions, order = "desc") {
|
|
1138
|
-
const sorted = [...sessions].sort((left, right) => {
|
|
1139
|
-
if (left.lastSeen !== right.lastSeen) return left.lastSeen - right.lastSeen;
|
|
1140
|
-
return left.sessionId < right.sessionId ? -1 : left.sessionId > right.sessionId ? 1 : 0;
|
|
1141
|
-
});
|
|
1142
|
-
if (order === "asc") return sorted;
|
|
1143
|
-
const reversed = [];
|
|
1144
|
-
for (let index = sorted.length - 1; index >= 0; index -= 1) reversed.push(sorted[index]);
|
|
1145
|
-
return reversed;
|
|
1146
|
-
}
|
|
1147
|
-
function orderRangeForQuery(options) {
|
|
1148
|
-
if (typeof IDBKeyRange === "undefined") return void 0;
|
|
1149
|
-
if (options.sessionId !== void 0) {
|
|
1150
|
-
const lower = [
|
|
1151
|
-
options.sessionId,
|
|
1152
|
-
options.from ?? Number.MIN_SAFE_INTEGER,
|
|
1153
|
-
Number.MIN_SAFE_INTEGER,
|
|
1154
|
-
""
|
|
1155
|
-
];
|
|
1156
|
-
const upper = [
|
|
1157
|
-
options.sessionId,
|
|
1158
|
-
options.to ?? Number.MAX_SAFE_INTEGER,
|
|
1159
|
-
Number.MAX_SAFE_INTEGER,
|
|
1160
|
-
""
|
|
1161
|
-
];
|
|
1162
|
-
return IDBKeyRange.bound(lower, upper);
|
|
1163
|
-
}
|
|
1164
|
-
const lower = options.from === void 0 ? void 0 : [
|
|
1165
|
-
options.from,
|
|
1166
|
-
Number.MIN_SAFE_INTEGER,
|
|
1167
|
-
""
|
|
1168
|
-
];
|
|
1169
|
-
const upper = options.to === void 0 ? void 0 : [
|
|
1170
|
-
options.to,
|
|
1171
|
-
Number.MAX_SAFE_INTEGER,
|
|
1172
|
-
""
|
|
1173
|
-
];
|
|
1174
|
-
if (lower && upper) return IDBKeyRange.bound(lower, upper);
|
|
1175
|
-
if (lower) return IDBKeyRange.lowerBound(lower);
|
|
1176
|
-
if (upper) return IDBKeyRange.upperBound(upper);
|
|
1177
|
-
}
|
|
1178
|
-
function orderRangeForSessionId(sessionId) {
|
|
1179
|
-
if (typeof IDBKeyRange === "undefined") return void 0;
|
|
1180
|
-
return IDBKeyRange.bound([
|
|
1181
|
-
sessionId,
|
|
1182
|
-
Number.MIN_SAFE_INTEGER,
|
|
1183
|
-
Number.MIN_SAFE_INTEGER,
|
|
1184
|
-
""
|
|
1185
|
-
], [
|
|
1186
|
-
sessionId,
|
|
1187
|
-
Number.MAX_SAFE_INTEGER,
|
|
1188
|
-
Number.MAX_SAFE_INTEGER,
|
|
1189
|
-
""
|
|
1190
|
-
]);
|
|
1191
|
-
}
|
|
1192
|
-
function migrateSessionSummaries(logStore, sessionStore) {
|
|
1193
|
-
const summaries = /* @__PURE__ */ new Map();
|
|
1194
|
-
const request = logStore.openCursor();
|
|
1195
|
-
request.addEventListener("success", () => {
|
|
1196
|
-
const cursor = request.result;
|
|
1197
|
-
if (!cursor) {
|
|
1198
|
-
for (const summary of summaries.values()) sessionStore.put(summary);
|
|
1199
|
-
return;
|
|
1200
|
-
}
|
|
1201
|
-
addSessionSummary(summaries, cursor.value);
|
|
1202
|
-
cursor.continue();
|
|
1203
|
-
});
|
|
1204
|
-
}
|
|
1205
|
-
function iterateCursor(request, visit) {
|
|
1206
|
-
return new Promise((resolve, reject) => {
|
|
1207
|
-
request.addEventListener("success", () => {
|
|
1208
|
-
const cursor = request.result;
|
|
1209
|
-
if (!cursor) {
|
|
1210
|
-
resolve();
|
|
1211
|
-
return;
|
|
1212
|
-
}
|
|
1213
|
-
try {
|
|
1214
|
-
if (visit(cursor) === false) {
|
|
1215
|
-
resolve();
|
|
1216
|
-
return;
|
|
1217
|
-
}
|
|
1218
|
-
cursor.continue();
|
|
1219
|
-
} catch (error) {
|
|
1220
|
-
reject(error);
|
|
1221
|
-
}
|
|
1222
|
-
}, false);
|
|
1223
|
-
request.addEventListener("error", () => reject(request.error ?? /* @__PURE__ */ new Error("IndexedDB cursor failed")), { once: true });
|
|
1224
|
-
});
|
|
1225
|
-
}
|
|
1226
|
-
function indexedDbTransport(options = {}) {
|
|
1227
|
-
const dbName = options.dbName ?? DEFAULT_DB_NAME;
|
|
1228
|
-
const storeName = options.storeName ?? DEFAULT_STORE_NAME;
|
|
1229
|
-
const sessionStoreName = sessionStoreNameFor(storeName);
|
|
1230
|
-
const codec = options.codec ?? (0, _loggerjs_core.safeJsonCodec)();
|
|
1231
|
-
const batchSize = normalizePositiveInteger(options.batchSize, 100);
|
|
1232
|
-
const flushIntervalMs = normalizePositiveInteger(options.flushIntervalMs, 1e3);
|
|
1233
|
-
const maxBufferSize = normalizePositiveInteger(options.maxBufferSize, 1e3);
|
|
1234
|
-
const maxEntries = normalizePositiveInteger(options.maxEntries, 5e4);
|
|
1235
|
-
const maxBytes = normalizePositiveInteger(options.maxBytes, 50 * 1024 * 1024);
|
|
1236
|
-
const dropPolicy = options.dropPolicy ?? "drop-oldest";
|
|
1237
|
-
const flushOnPageHide = options.flushOnPageHide ?? true;
|
|
1238
|
-
const durability = options.durability ?? "default";
|
|
1239
|
-
const rootIndexedDB = options.indexedDB ?? globalThis.indexedDB;
|
|
1240
|
-
const sessionOptions = normalizeSessionOptions(options.session);
|
|
1241
|
-
const spillOptions = normalizeLocalStorageSpillOptions(options.localStorageSpill, dbName, storeName);
|
|
1242
|
-
const transactionOptions = durability === "default" ? void 0 : { durability };
|
|
1243
|
-
const buffer = [];
|
|
1244
|
-
let dbPromise;
|
|
1245
|
-
let indexedDBFactoryPromise;
|
|
1246
|
-
let timer;
|
|
1247
|
-
let flushPromise;
|
|
1248
|
-
let clearPromise;
|
|
1249
|
-
let pendingFlushBatch;
|
|
1250
|
-
let spillDrainPromise;
|
|
1251
|
-
let localStorageSpillClearCandidate;
|
|
1252
|
-
let lastContext;
|
|
1253
|
-
let closed = false;
|
|
1254
|
-
const statsState = {
|
|
1255
|
-
bufferDepth: 0,
|
|
1256
|
-
maxBufferDepth: 0,
|
|
1257
|
-
pendingFlush: false,
|
|
1258
|
-
enqueued: 0,
|
|
1259
|
-
persisted: 0,
|
|
1260
|
-
dropped: 0,
|
|
1261
|
-
droppedByReason: {},
|
|
1262
|
-
persistedDropped: 0,
|
|
1263
|
-
persistedDroppedByReason: {},
|
|
1264
|
-
flushes: 0,
|
|
1265
|
-
flushErrors: 0,
|
|
1266
|
-
lastFlushBatchSize: 0,
|
|
1267
|
-
lastFlushDurationMs: 0,
|
|
1268
|
-
prunes: 0,
|
|
1269
|
-
pruneFallbacks: 0,
|
|
1270
|
-
lastPruneDurationMs: 0,
|
|
1271
|
-
queries: 0,
|
|
1272
|
-
queryFallbacks: 0,
|
|
1273
|
-
lastQueryDurationMs: 0,
|
|
1274
|
-
databaseOpenCount: 0,
|
|
1275
|
-
storageBucketFallbacks: 0,
|
|
1276
|
-
transactionOptionFallbacks: 0,
|
|
1277
|
-
errors: 0,
|
|
1278
|
-
errorsByOperation: {},
|
|
1279
|
-
localStorageSpillWrites: 0,
|
|
1280
|
-
localStorageSpillEntries: 0,
|
|
1281
|
-
localStorageSpillDrains: 0,
|
|
1282
|
-
localStorageSpillDrainedEntries: 0,
|
|
1283
|
-
localStorageSpillDropped: 0,
|
|
1284
|
-
localStorageSpillDroppedByReason: {},
|
|
1285
|
-
localStorageSpillErrors: 0,
|
|
1286
|
-
localStorageSpillErrorsByOperation: {}
|
|
1287
|
-
};
|
|
1288
|
-
const snapshotStats = () => ({
|
|
1289
|
-
...statsState,
|
|
1290
|
-
bufferDepth: buffer.length,
|
|
1291
|
-
droppedByReason: { ...statsState.droppedByReason },
|
|
1292
|
-
errorsByOperation: { ...statsState.errorsByOperation },
|
|
1293
|
-
localStorageSpillDroppedByReason: { ...statsState.localStorageSpillDroppedByReason },
|
|
1294
|
-
localStorageSpillErrorsByOperation: { ...statsState.localStorageSpillErrorsByOperation },
|
|
1295
|
-
pendingFlush: flushPromise !== void 0,
|
|
1296
|
-
persistedDroppedByReason: { ...statsState.persistedDroppedByReason }
|
|
1297
|
-
});
|
|
1298
|
-
const reportInternalError = (error, operation) => {
|
|
1299
|
-
statsState.errors += 1;
|
|
1300
|
-
incrementRecord(statsState.errorsByOperation, operation);
|
|
1301
|
-
lastContext?.reportInternalError(error, {
|
|
1302
|
-
operation,
|
|
1303
|
-
phase: "transport",
|
|
1304
|
-
transport: options.name ?? "indexeddb"
|
|
1305
|
-
});
|
|
1306
|
-
};
|
|
1307
|
-
const dropEvent = (event, reason) => {
|
|
1308
|
-
statsState.dropped += 1;
|
|
1309
|
-
incrementRecord(statsState.droppedByReason, reason);
|
|
1310
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.indexeddb.dropped");
|
|
1311
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.indexeddb.dropped.${reason}`);
|
|
1312
|
-
options.onDrop?.(event, reason);
|
|
1313
|
-
};
|
|
1314
|
-
const dropPersistedEntry = (entry, reason) => {
|
|
1315
|
-
statsState.persistedDropped += 1;
|
|
1316
|
-
incrementRecord(statsState.persistedDroppedByReason, reason);
|
|
1317
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.indexeddb.persisted.dropped");
|
|
1318
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.indexeddb.persisted.dropped.${reason}`);
|
|
1319
|
-
options.onPersistedDrop?.(entry, reason);
|
|
1320
|
-
};
|
|
1321
|
-
const reportSpillError = (error, operation) => {
|
|
1322
|
-
statsState.localStorageSpillErrors += 1;
|
|
1323
|
-
incrementRecord(statsState.localStorageSpillErrorsByOperation, operation);
|
|
1324
|
-
reportInternalError(error, `localstorage-spill-${operation}`);
|
|
1325
|
-
};
|
|
1326
|
-
const dropSpillEvent = (event, reason) => {
|
|
1327
|
-
statsState.localStorageSpillDropped += 1;
|
|
1328
|
-
incrementRecord(statsState.localStorageSpillDroppedByReason, reason);
|
|
1329
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.indexeddb.localstorage_spill.dropped");
|
|
1330
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.indexeddb.localstorage_spill.dropped.${reason}`);
|
|
1331
|
-
spillOptions?.onDrop?.(event, reason);
|
|
1332
|
-
};
|
|
1333
|
-
const resolveIndexedDBFactory = async () => {
|
|
1334
|
-
if (options.indexedDB || !options.storageBucketName) return rootIndexedDB;
|
|
1335
|
-
indexedDBFactoryPromise ??= (async () => {
|
|
1336
|
-
const manager = getStorageBucketManager();
|
|
1337
|
-
if (!manager) {
|
|
1338
|
-
statsState.storageBucketFallbacks += 1;
|
|
1339
|
-
return rootIndexedDB;
|
|
1340
|
-
}
|
|
1341
|
-
try {
|
|
1342
|
-
const bucket = await manager.open(options.storageBucketName, storageBucketOpenOptions(options));
|
|
1343
|
-
if (bucket.indexedDB) return bucket.indexedDB;
|
|
1344
|
-
statsState.storageBucketFallbacks += 1;
|
|
1345
|
-
return rootIndexedDB;
|
|
1346
|
-
} catch (error) {
|
|
1347
|
-
statsState.storageBucketFallbacks += 1;
|
|
1348
|
-
reportInternalError(error, "storage-bucket-open");
|
|
1349
|
-
return rootIndexedDB;
|
|
1350
|
-
}
|
|
1351
|
-
})();
|
|
1352
|
-
return indexedDBFactoryPromise;
|
|
1353
|
-
};
|
|
1354
|
-
const openDb = () => {
|
|
1355
|
-
dbPromise ??= resolveIndexedDBFactory().then((idb) => {
|
|
1356
|
-
if (!idb) throw new Error("IndexedDB is not available for indexedDbTransport");
|
|
1357
|
-
return new Promise((resolve, reject) => {
|
|
1358
|
-
const request = idb.open(dbName, DB_VERSION);
|
|
1359
|
-
request.addEventListener("upgradeneeded", (event) => {
|
|
1360
|
-
const db = request.result;
|
|
1361
|
-
const store = db.objectStoreNames.contains(storeName) ? request.transaction?.objectStore(storeName) : db.createObjectStore(storeName, { keyPath: "id" });
|
|
1362
|
-
if (!store) return;
|
|
1363
|
-
ensureIndex(store, "createdAt", "createdAt");
|
|
1364
|
-
ensureIndex(store, "level", "level");
|
|
1365
|
-
ensureIndex(store, "logger", "logger");
|
|
1366
|
-
ensureIndex(store, "type", "type");
|
|
1367
|
-
ensureIndex(store, ORDER_INDEX_NAME, [
|
|
1368
|
-
"createdAt",
|
|
1369
|
-
"seq",
|
|
1370
|
-
"id"
|
|
1371
|
-
]);
|
|
1372
|
-
ensureIndex(store, SESSION_INDEX_NAME, "sessionId");
|
|
1373
|
-
ensureIndex(store, SESSION_ORDER_INDEX_NAME, [
|
|
1374
|
-
"sessionId",
|
|
1375
|
-
"createdAt",
|
|
1376
|
-
"seq",
|
|
1377
|
-
"id"
|
|
1378
|
-
]);
|
|
1379
|
-
const sessionStore = db.objectStoreNames.contains(sessionStoreName) ? request.transaction?.objectStore(sessionStoreName) : db.createObjectStore(sessionStoreName, { keyPath: "sessionId" });
|
|
1380
|
-
if (!sessionStore) return;
|
|
1381
|
-
ensureIndex(sessionStore, SESSION_LAST_SEEN_INDEX_NAME, ["lastSeen", "sessionId"]);
|
|
1382
|
-
if (event.oldVersion < 4) migrateSessionSummaries(store, sessionStore);
|
|
1383
|
-
});
|
|
1384
|
-
request.addEventListener("success", () => {
|
|
1385
|
-
statsState.databaseOpenCount += 1;
|
|
1386
|
-
resolve(request.result);
|
|
1387
|
-
}, { once: true });
|
|
1388
|
-
request.addEventListener("error", () => reject(request.error ?? /* @__PURE__ */ new Error("IndexedDB open failed")), { once: true });
|
|
1389
|
-
});
|
|
1390
|
-
});
|
|
1391
|
-
return dbPromise;
|
|
1392
|
-
};
|
|
1393
|
-
const createTransaction = (db, mode, stores = storeName) => {
|
|
1394
|
-
if (mode !== "readwrite" || !transactionOptions) return db.transaction(stores, mode);
|
|
1395
|
-
try {
|
|
1396
|
-
return db.transaction(stores, mode, transactionOptions);
|
|
1397
|
-
} catch {
|
|
1398
|
-
statsState.transactionOptionFallbacks += 1;
|
|
1399
|
-
return db.transaction(stores, mode);
|
|
1400
|
-
}
|
|
1401
|
-
};
|
|
1402
|
-
const withStore = async (mode, run) => {
|
|
1403
|
-
const tx = createTransaction(await openDb(), mode);
|
|
1404
|
-
const settled = transactionToPromise(tx);
|
|
1405
|
-
try {
|
|
1406
|
-
const result = await run(tx.objectStore(storeName), tx);
|
|
1407
|
-
await settled;
|
|
1408
|
-
return result;
|
|
1409
|
-
} catch (error) {
|
|
1410
|
-
if (tx.error === null) try {
|
|
1411
|
-
tx.abort();
|
|
1412
|
-
} catch {}
|
|
1413
|
-
throw error;
|
|
1414
|
-
}
|
|
1415
|
-
};
|
|
1416
|
-
const withLogAndSessionStores = async (mode, run) => {
|
|
1417
|
-
const tx = createTransaction(await openDb(), mode, [storeName, sessionStoreName]);
|
|
1418
|
-
const settled = transactionToPromise(tx);
|
|
1419
|
-
try {
|
|
1420
|
-
const result = await run(tx.objectStore(storeName), tx.objectStore(sessionStoreName), tx);
|
|
1421
|
-
await settled;
|
|
1422
|
-
return result;
|
|
1423
|
-
} catch (error) {
|
|
1424
|
-
if (tx.error === null) try {
|
|
1425
|
-
tx.abort();
|
|
1426
|
-
} catch {}
|
|
1427
|
-
throw error;
|
|
1428
|
-
}
|
|
1429
|
-
};
|
|
1430
|
-
const withSessionStore = async (mode, run) => {
|
|
1431
|
-
const tx = createTransaction(await openDb(), mode, sessionStoreName);
|
|
1432
|
-
const settled = transactionToPromise(tx);
|
|
1433
|
-
try {
|
|
1434
|
-
const result = await run(tx.objectStore(sessionStoreName), tx);
|
|
1435
|
-
await settled;
|
|
1436
|
-
return result;
|
|
1437
|
-
} catch (error) {
|
|
1438
|
-
if (tx.error === null) try {
|
|
1439
|
-
tx.abort();
|
|
1440
|
-
} catch {}
|
|
1441
|
-
throw error;
|
|
1442
|
-
}
|
|
1443
|
-
};
|
|
1444
|
-
const mergeSessionSummaries = async (sessionStore, entries) => {
|
|
1445
|
-
const summaries = /* @__PURE__ */ new Map();
|
|
1446
|
-
for (const entry of entries) addSessionSummary(summaries, entry);
|
|
1447
|
-
for (const summary of summaries.values()) {
|
|
1448
|
-
const current = await requestToPromise(sessionStore.get(summary.sessionId));
|
|
1449
|
-
await requestToPromise(sessionStore.put(mergeSessionSummary(current, summary)));
|
|
1450
|
-
}
|
|
1451
|
-
};
|
|
1452
|
-
const rebuildSessionSummaries = async (store, sessionStore, sessionIds) => {
|
|
1453
|
-
if (sessionIds.size === 0) return;
|
|
1454
|
-
const index = getStoreIndex(store, SESSION_ORDER_INDEX_NAME);
|
|
1455
|
-
for (const sessionId of sessionIds) {
|
|
1456
|
-
const summaries = /* @__PURE__ */ new Map();
|
|
1457
|
-
const range = orderRangeForSessionId(sessionId);
|
|
1458
|
-
await iterateCursor(index && range ? index.openCursor(range) : store.openCursor(), (cursor) => {
|
|
1459
|
-
const entry = cursor.value;
|
|
1460
|
-
if (entry.sessionId === sessionId) addSessionSummary(summaries, entry);
|
|
1461
|
-
});
|
|
1462
|
-
const summary = summaries.get(sessionId);
|
|
1463
|
-
if (summary) await requestToPromise(sessionStore.put(summary));
|
|
1464
|
-
else await requestToPromise(sessionStore.delete(sessionId));
|
|
1465
|
-
}
|
|
1466
|
-
};
|
|
1467
|
-
const getEntries = async () => orderEntries(await withStore("readonly", (store) => requestToPromise(store.getAll())));
|
|
1468
|
-
const countEntries = async () => withStore("readonly", (store) => {
|
|
1469
|
-
const maybeStore = store;
|
|
1470
|
-
if (typeof maybeStore.count !== "function") return void 0;
|
|
1471
|
-
return requestToPromise(maybeStore.count.call(store));
|
|
1472
|
-
});
|
|
1473
|
-
const deleteEntries = async (entries, reason) => {
|
|
1474
|
-
if (entries.length === 0) return;
|
|
1475
|
-
await withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
1476
|
-
const affectedSessionIds = /* @__PURE__ */ new Set();
|
|
1477
|
-
for (const entry of entries) {
|
|
1478
|
-
await requestToPromise(store.delete(entry.id));
|
|
1479
|
-
if (entry.sessionId) affectedSessionIds.add(entry.sessionId);
|
|
1480
|
-
dropPersistedEntry(entry, reason);
|
|
1481
|
-
}
|
|
1482
|
-
await rebuildSessionSummaries(store, sessionStore, affectedSessionIds);
|
|
1483
|
-
});
|
|
1484
|
-
};
|
|
1485
|
-
const deleteByCursor = async (reason, openCursor, shouldDelete) => withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
1486
|
-
const request = openCursor(store);
|
|
1487
|
-
if (!request) return void 0;
|
|
1488
|
-
let deleted = 0;
|
|
1489
|
-
const affectedSessionIds = /* @__PURE__ */ new Set();
|
|
1490
|
-
await iterateCursor(request, (cursor) => {
|
|
1491
|
-
const entry = cursor.value;
|
|
1492
|
-
if (!shouldDelete(entry, deleted)) return false;
|
|
1493
|
-
cursor.delete();
|
|
1494
|
-
deleted += 1;
|
|
1495
|
-
if (entry.sessionId) affectedSessionIds.add(entry.sessionId);
|
|
1496
|
-
dropPersistedEntry(entry, reason);
|
|
1497
|
-
return true;
|
|
1498
|
-
});
|
|
1499
|
-
await rebuildSessionSummaries(store, sessionStore, affectedSessionIds);
|
|
1500
|
-
return deleted;
|
|
1501
|
-
});
|
|
1502
|
-
const deleteExpiredByCursor = async (cutoff) => {
|
|
1503
|
-
if (typeof IDBKeyRange === "undefined") return void 0;
|
|
1504
|
-
const range = IDBKeyRange.upperBound(cutoff, true);
|
|
1505
|
-
return deleteByCursor("ttl", (store) => getStoreIndex(store, "createdAt")?.openCursor(range), () => true);
|
|
1506
|
-
};
|
|
1507
|
-
const deleteOldestByCursor = async (reason, count) => {
|
|
1508
|
-
if (count <= 0) return 0;
|
|
1509
|
-
return deleteByCursor(reason, (store) => getStoreIndex(store, ORDER_INDEX_NAME)?.openCursor(), (_entry, deleted) => deleted < count);
|
|
1510
|
-
};
|
|
1511
|
-
const deleteOldestBytesByCursor = async (bytesToFree) => {
|
|
1512
|
-
if (bytesToFree <= 0) return 0;
|
|
1513
|
-
let freed = 0;
|
|
1514
|
-
return deleteByCursor("max-bytes", (store) => getStoreIndex(store, ORDER_INDEX_NAME)?.openCursor(), (entry) => {
|
|
1515
|
-
if (freed >= bytesToFree) return false;
|
|
1516
|
-
freed += entry.byteLength;
|
|
1517
|
-
return true;
|
|
1518
|
-
});
|
|
1519
|
-
};
|
|
1520
|
-
const sumBytesByCursor = async () => withStore("readonly", async (store) => {
|
|
1521
|
-
const index = getStoreIndex(store, ORDER_INDEX_NAME);
|
|
1522
|
-
if (!index) return void 0;
|
|
1523
|
-
let total = 0;
|
|
1524
|
-
await iterateCursor(index.openCursor(), (cursor) => {
|
|
1525
|
-
total += cursor.value.byteLength;
|
|
1526
|
-
});
|
|
1527
|
-
return total;
|
|
1528
|
-
});
|
|
1529
|
-
const queryEntriesByCursor = async (queryOptions) => withStore("readonly", async (store) => {
|
|
1530
|
-
const index = getStoreIndex(store, queryOptions.sessionId === void 0 ? ORDER_INDEX_NAME : SESSION_ORDER_INDEX_NAME);
|
|
1531
|
-
if (!index) return void 0;
|
|
1532
|
-
const entries = [];
|
|
1533
|
-
const limit = queryOptions.limit;
|
|
1534
|
-
if (limit !== void 0 && limit <= 0) return entries;
|
|
1535
|
-
await iterateCursor(index.openCursor(orderRangeForQuery(queryOptions), queryOptions.order === "desc" ? "prev" : "next"), (cursor) => {
|
|
1536
|
-
const entry = cursor.value;
|
|
1537
|
-
if (!shouldKeepEntry(entry, queryOptions)) return true;
|
|
1538
|
-
entries.push(entry);
|
|
1539
|
-
return limit === void 0 || entries.length < limit;
|
|
1540
|
-
});
|
|
1541
|
-
return entries;
|
|
1542
|
-
});
|
|
1543
|
-
const listSessions = async (sessionQueryOptions = {}) => {
|
|
1544
|
-
const limit = sessionQueryOptions.limit;
|
|
1545
|
-
if (limit !== void 0 && limit <= 0) return [];
|
|
1546
|
-
return withSessionStore("readonly", async (sessionStore) => {
|
|
1547
|
-
const sessions = [];
|
|
1548
|
-
const index = getStoreIndex(sessionStore, SESSION_LAST_SEEN_INDEX_NAME);
|
|
1549
|
-
if (!index) return orderSessions(await requestToPromise(sessionStore.getAll()), sessionQueryOptions.order).slice(0, limit);
|
|
1550
|
-
await iterateCursor(index.openCursor(void 0, sessionQueryOptions.order === "asc" ? "next" : "prev"), (cursor) => {
|
|
1551
|
-
sessions.push(cursor.value);
|
|
1552
|
-
return limit === void 0 || sessions.length < limit;
|
|
1553
|
-
});
|
|
1554
|
-
return sessions;
|
|
1555
|
-
});
|
|
1556
|
-
};
|
|
1557
|
-
const pruneWithEntries = async () => {
|
|
1558
|
-
const now = Date.now();
|
|
1559
|
-
let entries = await getEntries();
|
|
1560
|
-
const expired = options.ttlMs === void 0 ? [] : entries.filter((entry) => now - entry.createdAt > options.ttlMs);
|
|
1561
|
-
if (expired.length > 0) {
|
|
1562
|
-
await deleteEntries(expired, "ttl");
|
|
1563
|
-
const expiredIds = new Set(expired.map((entry) => entry.id));
|
|
1564
|
-
entries = entries.filter((entry) => !expiredIds.has(entry.id));
|
|
1565
|
-
}
|
|
1566
|
-
if (maxEntries >= 0 && entries.length > maxEntries) {
|
|
1567
|
-
await deleteEntries(entries.slice(0, entries.length - maxEntries), "max-entries");
|
|
1568
|
-
entries = entries.slice(entries.length - maxEntries);
|
|
1569
|
-
}
|
|
1570
|
-
if (maxBytes >= 0) {
|
|
1571
|
-
let total = entries.reduce((sum, entry) => sum + entry.byteLength, 0);
|
|
1572
|
-
const dropped = [];
|
|
1573
|
-
for (const entry of entries) {
|
|
1574
|
-
if (total <= maxBytes) break;
|
|
1575
|
-
total -= entry.byteLength;
|
|
1576
|
-
dropped.push(entry);
|
|
1577
|
-
}
|
|
1578
|
-
await deleteEntries(dropped, "max-bytes");
|
|
1579
|
-
}
|
|
1580
|
-
};
|
|
1581
|
-
const pruneWithCursors = async () => {
|
|
1582
|
-
if (options.ttlMs !== void 0) {
|
|
1583
|
-
if (await deleteExpiredByCursor(Date.now() - options.ttlMs) === void 0) return false;
|
|
1584
|
-
}
|
|
1585
|
-
if (maxEntries >= 0) {
|
|
1586
|
-
const persistedCount = await countEntries();
|
|
1587
|
-
if (persistedCount === void 0) return false;
|
|
1588
|
-
if (persistedCount > maxEntries) {
|
|
1589
|
-
if (await deleteOldestByCursor("max-entries", persistedCount - maxEntries) === void 0) return false;
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
if (maxBytes >= 0) {
|
|
1593
|
-
const totalBytes = await sumBytesByCursor();
|
|
1594
|
-
if (totalBytes === void 0) return false;
|
|
1595
|
-
if (totalBytes > maxBytes) {
|
|
1596
|
-
if (await deleteOldestBytesByCursor(totalBytes - maxBytes) === void 0) return false;
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
return true;
|
|
1600
|
-
};
|
|
1601
|
-
const prune = async () => {
|
|
1602
|
-
const startedAt = nowMs$1();
|
|
1603
|
-
try {
|
|
1604
|
-
if (await pruneWithCursors()) return;
|
|
1605
|
-
statsState.pruneFallbacks += 1;
|
|
1606
|
-
await pruneWithEntries();
|
|
1607
|
-
} finally {
|
|
1608
|
-
statsState.prunes += 1;
|
|
1609
|
-
statsState.lastPruneDurationMs = nowMs$1() - startedAt;
|
|
1610
|
-
}
|
|
1611
|
-
};
|
|
1612
|
-
const sessionIdForEvent = (event) => {
|
|
1613
|
-
if (!sessionOptions) return void 0;
|
|
1614
|
-
return normalizeSessionId(sessionOptions.getId?.(event)) ?? normalizeSessionId(event.context?.[sessionOptions.contextKey]) ?? sessionOptions.id;
|
|
1615
|
-
};
|
|
1616
|
-
const eventWithSessionContext = (event, sessionId) => {
|
|
1617
|
-
if (!sessionId || !sessionOptions?.contextKey) return event;
|
|
1618
|
-
if (event.context?.[sessionOptions.contextKey] === sessionId) return event;
|
|
1619
|
-
return {
|
|
1620
|
-
...event,
|
|
1621
|
-
context: {
|
|
1622
|
-
...event.context,
|
|
1623
|
-
[sessionOptions.contextKey]: sessionId
|
|
1624
|
-
}
|
|
1625
|
-
};
|
|
1626
|
-
};
|
|
1627
|
-
const eventWithResolvedSession = (event) => {
|
|
1628
|
-
const sessionId = sessionIdForEvent(event);
|
|
1629
|
-
return {
|
|
1630
|
-
event: eventWithSessionContext(event, sessionId),
|
|
1631
|
-
sessionId
|
|
1632
|
-
};
|
|
1633
|
-
};
|
|
1634
|
-
const eventToEntry = (event) => {
|
|
1635
|
-
const resolved = eventWithResolvedSession(event);
|
|
1636
|
-
const payload = codec.encode(resolved.event);
|
|
1637
|
-
return {
|
|
1638
|
-
id: event.id,
|
|
1639
|
-
sessionId: resolved.sessionId,
|
|
1640
|
-
seq: event.seq,
|
|
1641
|
-
createdAt: event.time,
|
|
1642
|
-
level: event.level,
|
|
1643
|
-
levelName: event.levelName,
|
|
1644
|
-
logger: event.logger,
|
|
1645
|
-
type: event.type,
|
|
1646
|
-
byteLength: payloadByteLength(payload),
|
|
1647
|
-
payload
|
|
1648
|
-
};
|
|
1649
|
-
};
|
|
1650
|
-
const clearLocalStorageSpill = (expectedValue) => {
|
|
1651
|
-
if (!spillOptions) return false;
|
|
1652
|
-
try {
|
|
1653
|
-
if (expectedValue !== void 0 && spillOptions.storage.getItem(spillOptions.key) !== expectedValue) return false;
|
|
1654
|
-
spillOptions.storage.removeItem(spillOptions.key);
|
|
1655
|
-
if (expectedValue === void 0 || localStorageSpillClearCandidate?.value === expectedValue) localStorageSpillClearCandidate = void 0;
|
|
1656
|
-
return true;
|
|
1657
|
-
} catch (error) {
|
|
1658
|
-
reportSpillError(error, "clear");
|
|
1659
|
-
return false;
|
|
1660
|
-
}
|
|
1661
|
-
};
|
|
1662
|
-
const rememberLocalStorageSpill = (value, events) => {
|
|
1663
|
-
localStorageSpillClearCandidate = {
|
|
1664
|
-
pendingIds: new Set(events.map((event) => event.id)),
|
|
1665
|
-
value
|
|
1666
|
-
};
|
|
1667
|
-
};
|
|
1668
|
-
const markLocalStorageSpillPersisted = (events) => {
|
|
1669
|
-
const candidate = localStorageSpillClearCandidate;
|
|
1670
|
-
if (!candidate || events.length === 0) return;
|
|
1671
|
-
for (const event of events) candidate.pendingIds.delete(event.id);
|
|
1672
|
-
if (candidate.pendingIds.size === 0) clearLocalStorageSpill(candidate.value);
|
|
1673
|
-
};
|
|
1674
|
-
const writeBatch = async (events) => {
|
|
1675
|
-
if (events.length === 0) return;
|
|
1676
|
-
const startedAt = nowMs$1();
|
|
1677
|
-
statsState.lastFlushBatchSize = events.length;
|
|
1678
|
-
const entries = events.map(eventToEntry);
|
|
1679
|
-
try {
|
|
1680
|
-
await withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
1681
|
-
const rebuildSessionIds = /* @__PURE__ */ new Set();
|
|
1682
|
-
for (const entry of entries) {
|
|
1683
|
-
const existing = await requestToPromise(store.get(entry.id));
|
|
1684
|
-
if (existing?.sessionId) rebuildSessionIds.add(existing.sessionId);
|
|
1685
|
-
if (existing && entry.sessionId) rebuildSessionIds.add(entry.sessionId);
|
|
1686
|
-
await requestToPromise(store.put(entry));
|
|
1687
|
-
}
|
|
1688
|
-
await rebuildSessionSummaries(store, sessionStore, rebuildSessionIds);
|
|
1689
|
-
await mergeSessionSummaries(sessionStore, entries.filter((entry) => !entry.sessionId || !rebuildSessionIds.has(entry.sessionId)));
|
|
1690
|
-
});
|
|
1691
|
-
statsState.persisted += entries.length;
|
|
1692
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.indexeddb.persisted", entries.length);
|
|
1693
|
-
markLocalStorageSpillPersisted(events);
|
|
1694
|
-
await prune();
|
|
1695
|
-
statsState.flushes += 1;
|
|
1696
|
-
} catch (error) {
|
|
1697
|
-
statsState.flushErrors += 1;
|
|
1698
|
-
throw error;
|
|
1699
|
-
} finally {
|
|
1700
|
-
statsState.lastFlushDurationMs = nowMs$1() - startedAt;
|
|
1701
|
-
}
|
|
1702
|
-
};
|
|
1703
|
-
const stringifySpillPayload = (events) => (0, _loggerjs_core.safeJsonStringify)({
|
|
1704
|
-
createdAt: Date.now(),
|
|
1705
|
-
entries: events,
|
|
1706
|
-
schema: SPILL_SCHEMA
|
|
1707
|
-
}, { maxArrayLength: spillOptions?.maxEntries ?? DEFAULT_SPILL_MAX_ENTRIES });
|
|
1708
|
-
const spillStorageBudget = (currentValue) => {
|
|
1709
|
-
if (!spillOptions) return 0;
|
|
1710
|
-
const ownBytes = currentValue ? storageByteLength(spillOptions.key, currentValue) : 0;
|
|
1711
|
-
const originBytes = estimateLocalStorageBytes(spillOptions.storage);
|
|
1712
|
-
if (originBytes === void 0) return spillOptions.maxBytes;
|
|
1713
|
-
return Math.max(0, Math.min(spillOptions.maxBytes, spillOptions.maxOriginBytes - originBytes + ownBytes));
|
|
1714
|
-
};
|
|
1715
|
-
const pendingEventsForSpill = () => {
|
|
1716
|
-
const events = [...pendingFlushBatch ?? [], ...buffer];
|
|
1717
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1718
|
-
const deduped = [];
|
|
1719
|
-
for (const event of events) {
|
|
1720
|
-
if (seen.has(event.id)) continue;
|
|
1721
|
-
seen.add(event.id);
|
|
1722
|
-
if (spillOptions?.minLevelValue !== void 0 && event.level < spillOptions.minLevelValue) continue;
|
|
1723
|
-
deduped.push(eventWithResolvedSession(event).event);
|
|
1724
|
-
}
|
|
1725
|
-
return deduped;
|
|
1726
|
-
};
|
|
1727
|
-
const trimSpillEventsToBudget = (events, budget) => {
|
|
1728
|
-
if (!spillOptions) return void 0;
|
|
1729
|
-
while (events.length > spillOptions.maxEntries) {
|
|
1730
|
-
const dropped = events.shift();
|
|
1731
|
-
if (dropped) dropSpillEvent(dropped, "max-entries");
|
|
1732
|
-
}
|
|
1733
|
-
while (events.length > 0) {
|
|
1734
|
-
const value = stringifySpillPayload(events);
|
|
1735
|
-
if (storageByteLength(spillOptions.key, value) <= budget) return value;
|
|
1736
|
-
const dropped = events.shift();
|
|
1737
|
-
if (dropped) dropSpillEvent(dropped, "max-bytes");
|
|
1738
|
-
}
|
|
1739
|
-
};
|
|
1740
|
-
const writeLocalStorageSpill = () => {
|
|
1741
|
-
if (!spillOptions) return;
|
|
1742
|
-
const pendingEvents = pendingEventsForSpill();
|
|
1743
|
-
if (pendingEvents.length === 0) return;
|
|
1744
|
-
try {
|
|
1745
|
-
const currentValue = spillOptions.storage.getItem(spillOptions.key);
|
|
1746
|
-
let events = pendingEvents;
|
|
1747
|
-
if (currentValue) {
|
|
1748
|
-
const merged = /* @__PURE__ */ new Map();
|
|
1749
|
-
try {
|
|
1750
|
-
for (const event of parseSpillPayload(currentValue) ?? []) merged.set(event.id, event);
|
|
1751
|
-
} catch {}
|
|
1752
|
-
for (const event of pendingEvents) merged.set(event.id, event);
|
|
1753
|
-
events = [...merged.values()];
|
|
1754
|
-
}
|
|
1755
|
-
const budget = spillStorageBudget(currentValue);
|
|
1756
|
-
const value = trimSpillEventsToBudget(events, budget);
|
|
1757
|
-
if (!value) return;
|
|
1758
|
-
try {
|
|
1759
|
-
spillOptions.storage.setItem(spillOptions.key, value);
|
|
1760
|
-
} catch (error) {
|
|
1761
|
-
if (!isQuotaExceededError(error)) throw error;
|
|
1762
|
-
while (events.length > 0) {
|
|
1763
|
-
const dropped = events.shift();
|
|
1764
|
-
if (dropped) dropSpillEvent(dropped, "quota");
|
|
1765
|
-
const retryValue = trimSpillEventsToBudget(events, budget);
|
|
1766
|
-
if (!retryValue) return;
|
|
1767
|
-
try {
|
|
1768
|
-
spillOptions.storage.setItem(spillOptions.key, retryValue);
|
|
1769
|
-
rememberLocalStorageSpill(retryValue, events);
|
|
1770
|
-
statsState.localStorageSpillWrites += 1;
|
|
1771
|
-
statsState.localStorageSpillEntries += events.length;
|
|
1772
|
-
return;
|
|
1773
|
-
} catch (retryError) {
|
|
1774
|
-
if (!isQuotaExceededError(retryError)) throw retryError;
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
return;
|
|
1778
|
-
}
|
|
1779
|
-
rememberLocalStorageSpill(value, events);
|
|
1780
|
-
statsState.localStorageSpillWrites += 1;
|
|
1781
|
-
statsState.localStorageSpillEntries += events.length;
|
|
1782
|
-
} catch (error) {
|
|
1783
|
-
reportSpillError(error, "write");
|
|
1784
|
-
}
|
|
1785
|
-
};
|
|
1786
|
-
const drainLocalStorageSpill = async () => {
|
|
1787
|
-
if (!spillOptions) return;
|
|
1788
|
-
let raw;
|
|
1789
|
-
try {
|
|
1790
|
-
raw = spillOptions.storage.getItem(spillOptions.key);
|
|
1791
|
-
} catch (error) {
|
|
1792
|
-
reportSpillError(error, "read");
|
|
1793
|
-
return;
|
|
1794
|
-
}
|
|
1795
|
-
if (!raw) return;
|
|
1796
|
-
let events;
|
|
1797
|
-
try {
|
|
1798
|
-
events = parseSpillPayload(raw);
|
|
1799
|
-
} catch (error) {
|
|
1800
|
-
reportSpillError(error, "parse");
|
|
1801
|
-
clearLocalStorageSpill();
|
|
1802
|
-
return;
|
|
1803
|
-
}
|
|
1804
|
-
if (!events || events.length === 0) {
|
|
1805
|
-
clearLocalStorageSpill();
|
|
1806
|
-
return;
|
|
1807
|
-
}
|
|
1808
|
-
await writeBatch(events);
|
|
1809
|
-
clearLocalStorageSpill(raw);
|
|
1810
|
-
statsState.localStorageSpillDrains += 1;
|
|
1811
|
-
statsState.localStorageSpillDrainedEntries += events.length;
|
|
1812
|
-
};
|
|
1813
|
-
const ensureSpillDrained = async () => {
|
|
1814
|
-
const promise = spillDrainPromise;
|
|
1815
|
-
if (!promise) return;
|
|
1816
|
-
await promise;
|
|
1817
|
-
};
|
|
1818
|
-
if (spillOptions?.drainOnCreate) spillDrainPromise = drainLocalStorageSpill().catch((error) => reportSpillError(error, "drain")).finally(() => {
|
|
1819
|
-
spillDrainPromise = void 0;
|
|
1820
|
-
});
|
|
1821
|
-
const clearTimer = () => {
|
|
1822
|
-
if (timer) clearTimeout(timer);
|
|
1823
|
-
timer = void 0;
|
|
1824
|
-
};
|
|
1825
|
-
const flushPending = async () => {
|
|
1826
|
-
if (flushPromise) return flushPromise;
|
|
1827
|
-
clearTimer();
|
|
1828
|
-
const batch = buffer.splice(0, buffer.length);
|
|
1829
|
-
pendingFlushBatch = batch;
|
|
1830
|
-
const clearBarrier = clearPromise;
|
|
1831
|
-
flushPromise = (async () => {
|
|
1832
|
-
await clearBarrier;
|
|
1833
|
-
await ensureSpillDrained();
|
|
1834
|
-
await writeBatch(batch);
|
|
1835
|
-
})().finally(() => {
|
|
1836
|
-
if (pendingFlushBatch === batch) pendingFlushBatch = void 0;
|
|
1837
|
-
flushPromise = void 0;
|
|
1838
|
-
if (buffer.length > 0 && !closed) schedule();
|
|
1839
|
-
});
|
|
1840
|
-
return flushPromise;
|
|
1841
|
-
};
|
|
1842
|
-
const schedule = () => {
|
|
1843
|
-
if (timer || flushIntervalMs <= 0 || closed) return;
|
|
1844
|
-
timer = setTimeout(() => {
|
|
1845
|
-
flushPending().catch((error) => reportInternalError(error, "flush"));
|
|
1846
|
-
}, flushIntervalMs);
|
|
1847
|
-
};
|
|
1848
|
-
const onPageHide = () => {
|
|
1849
|
-
if (spillOptions?.spillOnPageHide) writeLocalStorageSpill();
|
|
1850
|
-
if (flushOnPageHide) flushPending().catch((error) => reportInternalError(error, "pagehide-flush"));
|
|
1851
|
-
};
|
|
1852
|
-
const onVisibilityChange = () => {
|
|
1853
|
-
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
1854
|
-
if (spillOptions?.spillOnPageHide) writeLocalStorageSpill();
|
|
1855
|
-
if (flushOnPageHide) flushPending().catch((error) => reportInternalError(error, "visibility-flush"));
|
|
1856
|
-
}
|
|
1857
|
-
};
|
|
1858
|
-
const addVisibilityChangeListener = () => {
|
|
1859
|
-
if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
|
|
1860
|
-
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
1861
|
-
return () => document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1862
|
-
}
|
|
1863
|
-
if (typeof globalThis.addEventListener !== "function") return void 0;
|
|
1864
|
-
globalThis.addEventListener("visibilitychange", onVisibilityChange);
|
|
1865
|
-
return () => globalThis.removeEventListener?.("visibilitychange", onVisibilityChange);
|
|
1866
|
-
};
|
|
1867
|
-
let removeVisibilityChangeListener;
|
|
1868
|
-
if (flushOnPageHide || spillOptions?.spillOnPageHide) {
|
|
1869
|
-
globalThis.addEventListener?.("pagehide", onPageHide);
|
|
1870
|
-
removeVisibilityChangeListener = addVisibilityChangeListener();
|
|
1871
|
-
}
|
|
1872
|
-
return {
|
|
1873
|
-
name: options.name ?? "indexeddb",
|
|
1874
|
-
minLevel: options.minLevel,
|
|
1875
|
-
log(event, context) {
|
|
1876
|
-
if (closed) return;
|
|
1877
|
-
if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
|
|
1878
|
-
lastContext = context;
|
|
1879
|
-
if (maxBufferSize === 0 || buffer.length >= maxBufferSize) {
|
|
1880
|
-
if (dropPolicy === "drop-newest") {
|
|
1881
|
-
dropEvent(event, "buffer-full");
|
|
1882
|
-
return;
|
|
1883
|
-
}
|
|
1884
|
-
const dropped = buffer.shift();
|
|
1885
|
-
if (dropped) dropEvent(dropped, "buffer-full");
|
|
1886
|
-
}
|
|
1887
|
-
buffer.push(event);
|
|
1888
|
-
statsState.enqueued += 1;
|
|
1889
|
-
statsState.maxBufferDepth = Math.max(statsState.maxBufferDepth, buffer.length);
|
|
1890
|
-
if (buffer.length >= batchSize || flushIntervalMs === 0) flushPending().catch((error) => reportInternalError(error, "flush"));
|
|
1891
|
-
else schedule();
|
|
1892
|
-
},
|
|
1893
|
-
flush() {
|
|
1894
|
-
return flushPending();
|
|
1895
|
-
},
|
|
1896
|
-
async count() {
|
|
1897
|
-
await flushPending();
|
|
1898
|
-
return await countEntries() ?? (await getEntries()).length;
|
|
1899
|
-
},
|
|
1900
|
-
async clear() {
|
|
1901
|
-
if (clearPromise) return clearPromise;
|
|
1902
|
-
const inFlightFlush = flushPromise;
|
|
1903
|
-
const inFlightDrain = spillDrainPromise;
|
|
1904
|
-
buffer.length = 0;
|
|
1905
|
-
clearTimer();
|
|
1906
|
-
clearLocalStorageSpill();
|
|
1907
|
-
clearPromise = (async () => {
|
|
1908
|
-
await inFlightFlush;
|
|
1909
|
-
await inFlightDrain;
|
|
1910
|
-
await withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
1911
|
-
await requestToPromise(store.clear());
|
|
1912
|
-
await requestToPromise(sessionStore.clear());
|
|
1913
|
-
});
|
|
1914
|
-
})().finally(() => {
|
|
1915
|
-
clearPromise = void 0;
|
|
1916
|
-
});
|
|
1917
|
-
return clearPromise;
|
|
1918
|
-
},
|
|
1919
|
-
async remove(ids) {
|
|
1920
|
-
await flushPending();
|
|
1921
|
-
const items = typeof ids === "string" ? [ids] : ids;
|
|
1922
|
-
if (items.length === 0) return;
|
|
1923
|
-
await withLogAndSessionStores("readwrite", async (store, sessionStore) => {
|
|
1924
|
-
const affectedSessionIds = /* @__PURE__ */ new Set();
|
|
1925
|
-
for (const id of items) {
|
|
1926
|
-
const entry = await requestToPromise(store.get(id));
|
|
1927
|
-
if (entry?.sessionId) affectedSessionIds.add(entry.sessionId);
|
|
1928
|
-
await requestToPromise(store.delete(id));
|
|
1929
|
-
}
|
|
1930
|
-
await rebuildSessionSummaries(store, sessionStore, affectedSessionIds);
|
|
1931
|
-
});
|
|
1932
|
-
},
|
|
1933
|
-
async *query(queryOptions = {}) {
|
|
1934
|
-
await flushPending();
|
|
1935
|
-
const startedAt = nowMs$1();
|
|
1936
|
-
const cursorEntries = await queryEntriesByCursor(queryOptions);
|
|
1937
|
-
if (cursorEntries === void 0) statsState.queryFallbacks += 1;
|
|
1938
|
-
const entries = cursorEntries ?? orderEntries(await getEntries(), queryOptions.order ?? "asc").filter((entry) => shouldKeepEntry(entry, queryOptions)).slice(0, queryOptions.limit);
|
|
1939
|
-
statsState.queries += 1;
|
|
1940
|
-
statsState.lastQueryDurationMs = nowMs$1() - startedAt;
|
|
1941
|
-
for (const entry of entries) {
|
|
1942
|
-
const event = decodeEntry(entry, codec);
|
|
1943
|
-
if (event) yield event;
|
|
1944
|
-
}
|
|
1945
|
-
},
|
|
1946
|
-
async sessions(sessionQueryOptions = {}) {
|
|
1947
|
-
await flushPending();
|
|
1948
|
-
return listSessions(sessionQueryOptions);
|
|
1949
|
-
},
|
|
1950
|
-
stats() {
|
|
1951
|
-
return snapshotStats();
|
|
1952
|
-
},
|
|
1953
|
-
close() {
|
|
1954
|
-
closed = true;
|
|
1955
|
-
clearTimer();
|
|
1956
|
-
globalThis.removeEventListener?.("pagehide", onPageHide);
|
|
1957
|
-
removeVisibilityChangeListener?.();
|
|
1958
|
-
removeVisibilityChangeListener = void 0;
|
|
1959
|
-
return flushPending().finally(() => {
|
|
1960
|
-
const promise = dbPromise;
|
|
1961
|
-
dbPromise = void 0;
|
|
1962
|
-
return promise?.then((db) => db.close());
|
|
1963
|
-
});
|
|
1964
|
-
}
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
1967
|
-
//#endregion
|
|
1968
|
-
//#region src/offline-first-transport.ts
|
|
1969
|
-
function normalizeBatchSize(value) {
|
|
1970
|
-
if (value === void 0 || !Number.isFinite(value) || value <= 0) return 100;
|
|
1971
|
-
return Math.floor(value);
|
|
1972
|
-
}
|
|
1973
|
-
async function collectBatch(queue, replayBatchSize) {
|
|
1974
|
-
const events = [];
|
|
1975
|
-
for await (const event of queue.query({
|
|
1976
|
-
order: "asc",
|
|
1977
|
-
limit: replayBatchSize
|
|
1978
|
-
})) events.push(event);
|
|
1979
|
-
return events;
|
|
1980
|
-
}
|
|
1981
|
-
function isBrowserOnline(options) {
|
|
1982
|
-
if (options.online) return options.online();
|
|
1983
|
-
if (typeof navigator === "undefined") return true;
|
|
1984
|
-
return navigator.onLine !== false;
|
|
1985
|
-
}
|
|
1986
|
-
function offlineFirstTransport(remote, options = {}) {
|
|
1987
|
-
const name = options.name ?? `offline-first(${remote.name ?? "transport"})`;
|
|
1988
|
-
const replayBatchSize = normalizeBatchSize(options.replayBatchSize);
|
|
1989
|
-
const queueOptions = options.queueOptions;
|
|
1990
|
-
const queue = options.queue ?? indexedDbTransport({
|
|
1991
|
-
dbName: "loggerjs-offline",
|
|
1992
|
-
flushIntervalMs: 0,
|
|
1993
|
-
name: `${name}:queue`,
|
|
1994
|
-
storeName: "offline-events",
|
|
1995
|
-
...queueOptions,
|
|
1996
|
-
session: queueOptions?.session ?? false
|
|
1997
|
-
});
|
|
1998
|
-
const reliableRemote = (0, _loggerjs_core.retryTransport)(remote, {
|
|
1999
|
-
maxRetries: 2,
|
|
2000
|
-
retryBaseDelayMs: 250,
|
|
2001
|
-
retryMaxDelayMs: 5e3,
|
|
2002
|
-
...options.retry,
|
|
2003
|
-
name: `${name}:remote`
|
|
2004
|
-
});
|
|
2005
|
-
let lastContext;
|
|
2006
|
-
let replayPromise;
|
|
2007
|
-
const replayContext = {
|
|
2008
|
-
loggerName: name,
|
|
2009
|
-
now: () => Date.now(),
|
|
2010
|
-
toEvent: _loggerjs_core.recordToEvent,
|
|
2011
|
-
reportInternalError() {}
|
|
2012
|
-
};
|
|
2013
|
-
const contextForReplay = () => lastContext ?? replayContext;
|
|
2014
|
-
const reportInternalError = (error, operation) => {
|
|
2015
|
-
contextForReplay().reportInternalError(error, {
|
|
2016
|
-
phase: "transport",
|
|
2017
|
-
transport: name,
|
|
2018
|
-
operation
|
|
2019
|
-
});
|
|
2020
|
-
};
|
|
2021
|
-
const queueEvent = async (event, context, error) => {
|
|
2022
|
-
await queue.log(event, context);
|
|
2023
|
-
await queue.flush?.();
|
|
2024
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.queued");
|
|
2025
|
-
options.onQueued?.(event, error);
|
|
2026
|
-
};
|
|
2027
|
-
const sendRemoteBatch = async (events, context) => {
|
|
2028
|
-
await reliableRemote.logBatch?.([...events], context);
|
|
2029
|
-
};
|
|
2030
|
-
const sendOrQueue = async (event, context) => {
|
|
2031
|
-
lastContext = context;
|
|
2032
|
-
if (!isBrowserOnline(options)) {
|
|
2033
|
-
await queueEvent(event, context);
|
|
2034
|
-
return;
|
|
2035
|
-
}
|
|
2036
|
-
try {
|
|
2037
|
-
await reliableRemote.log?.(event, context);
|
|
2038
|
-
} catch (error) {
|
|
2039
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.remote.failed");
|
|
2040
|
-
await queueEvent(event, context, error);
|
|
2041
|
-
}
|
|
2042
|
-
};
|
|
2043
|
-
const replay = async () => {
|
|
2044
|
-
if (replayPromise) return replayPromise;
|
|
2045
|
-
replayPromise = (async () => {
|
|
2046
|
-
if (!isBrowserOnline(options)) return;
|
|
2047
|
-
const context = contextForReplay();
|
|
2048
|
-
await queue.flush?.();
|
|
2049
|
-
for (;;) {
|
|
2050
|
-
const batch = await collectBatch(queue, replayBatchSize);
|
|
2051
|
-
if (batch.length === 0) return;
|
|
2052
|
-
try {
|
|
2053
|
-
await sendRemoteBatch(batch, context);
|
|
2054
|
-
await queue.remove(batch.map((event) => event.id));
|
|
2055
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.replayed", batch.length);
|
|
2056
|
-
options.onReplayed?.(batch);
|
|
2057
|
-
} catch (error) {
|
|
2058
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.replay.failed");
|
|
2059
|
-
reportInternalError(error, "replay");
|
|
2060
|
-
throw error;
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
})().finally(() => {
|
|
2064
|
-
replayPromise = void 0;
|
|
2065
|
-
});
|
|
2066
|
-
return replayPromise;
|
|
2067
|
-
};
|
|
2068
|
-
const onOnline = () => {
|
|
2069
|
-
replay().catch((error) => reportInternalError(error, "online-replay"));
|
|
2070
|
-
};
|
|
2071
|
-
if (options.replayOnOnline ?? true) globalThis.addEventListener?.("online", onOnline);
|
|
2072
|
-
return {
|
|
2073
|
-
name,
|
|
2074
|
-
minLevel: options.minLevel ?? remote.minLevel,
|
|
2075
|
-
queue,
|
|
2076
|
-
write(record, context) {
|
|
2077
|
-
return sendOrQueue(context.toEvent(record), context);
|
|
2078
|
-
},
|
|
2079
|
-
writeBatch(records, context) {
|
|
2080
|
-
return Promise.all(records.map((record) => sendOrQueue(context.toEvent(record), context))).then(() => void 0);
|
|
2081
|
-
},
|
|
2082
|
-
log(event, context) {
|
|
2083
|
-
if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
|
|
2084
|
-
return sendOrQueue(event, context);
|
|
2085
|
-
},
|
|
2086
|
-
logBatch(events, context) {
|
|
2087
|
-
return Promise.all(events.map((event) => options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel) ? void 0 : sendOrQueue(event, context))).then(() => void 0);
|
|
2088
|
-
},
|
|
2089
|
-
replay,
|
|
2090
|
-
async flush() {
|
|
2091
|
-
await replay();
|
|
2092
|
-
await reliableRemote.flush?.();
|
|
2093
|
-
await queue.flush?.();
|
|
2094
|
-
},
|
|
2095
|
-
flushSync() {
|
|
2096
|
-
reliableRemote.flushSync?.();
|
|
2097
|
-
},
|
|
2098
|
-
async close() {
|
|
2099
|
-
globalThis.removeEventListener?.("online", onOnline);
|
|
2100
|
-
await replay();
|
|
2101
|
-
await reliableRemote.close?.();
|
|
2102
|
-
await queue.close?.();
|
|
2103
|
-
},
|
|
2104
|
-
async queuedCount() {
|
|
2105
|
-
await queue.flush?.();
|
|
2106
|
-
return queue.count?.();
|
|
2107
|
-
},
|
|
2108
|
-
async clearQueue() {
|
|
2109
|
-
await queue.clear?.();
|
|
2110
|
-
}
|
|
2111
|
-
};
|
|
2112
|
-
}
|
|
2113
|
-
//#endregion
|
|
2114
|
-
//#region src/zip-export.ts
|
|
2115
|
-
const ZIP_UTF8_FLAG = 2048;
|
|
2116
|
-
const ZIP_STORE_METHOD = 0;
|
|
2117
|
-
const ZIP_VERSION = 20;
|
|
2118
|
-
const ZIP_MAX_UINT16 = 65535;
|
|
2119
|
-
const ZIP_MAX_UINT32 = 4294967295;
|
|
2120
|
-
const LOCAL_FILE_HEADER_SIGNATURE = 67324752;
|
|
2121
|
-
const CENTRAL_DIRECTORY_SIGNATURE = 33639248;
|
|
2122
|
-
const END_OF_CENTRAL_DIRECTORY_SIGNATURE = 101010256;
|
|
2123
|
-
const DEFAULT_SESSION_CONTEXT_KEY = "sessionId";
|
|
2124
|
-
let crc32Table;
|
|
2125
|
-
function getCrc32Table() {
|
|
2126
|
-
if (crc32Table) return crc32Table;
|
|
2127
|
-
const table = new Uint32Array(256);
|
|
2128
|
-
for (let index = 0; index < table.length; index += 1) {
|
|
2129
|
-
let crc = index;
|
|
2130
|
-
for (let bit = 0; bit < 8; bit += 1) crc = crc & 1 ? 3988292384 ^ crc >>> 1 : crc >>> 1;
|
|
2131
|
-
table[index] = crc >>> 0;
|
|
2132
|
-
}
|
|
2133
|
-
crc32Table = table;
|
|
2134
|
-
return table;
|
|
2135
|
-
}
|
|
2136
|
-
function crc32(bytes) {
|
|
2137
|
-
const table = getCrc32Table();
|
|
2138
|
-
let crc = 4294967295;
|
|
2139
|
-
for (const byte of bytes) crc = crc >>> 8 ^ (table[(crc ^ byte) & 255] ?? 0);
|
|
2140
|
-
return (crc ^ 4294967295) >>> 0;
|
|
2141
|
-
}
|
|
2142
|
-
function encodeText(value) {
|
|
2143
|
-
return new TextEncoder().encode(value);
|
|
2144
|
-
}
|
|
2145
|
-
function normalizeFileContent(content) {
|
|
2146
|
-
if (typeof content === "string") return encodeText(content);
|
|
2147
|
-
if (content instanceof Uint8Array) return content;
|
|
2148
|
-
return new Uint8Array(content);
|
|
2149
|
-
}
|
|
2150
|
-
function normalizeZipFileName(name) {
|
|
2151
|
-
const normalized = name.replace(/\\/g, "/").split("/").filter((part) => part.length > 0 && part !== "." && part !== "..").join("/");
|
|
2152
|
-
if (!normalized) throw new Error("ZIP file name cannot be empty");
|
|
2153
|
-
return normalized;
|
|
2154
|
-
}
|
|
2155
|
-
function toDosDateTime(timestamp) {
|
|
2156
|
-
const date = new Date(Number.isFinite(timestamp) ? timestamp : Date.now());
|
|
2157
|
-
const year = Math.min(2107, Math.max(1980, date.getFullYear()));
|
|
2158
|
-
return {
|
|
2159
|
-
time: date.getHours() << 11 | date.getMinutes() << 5 | Math.floor(date.getSeconds() / 2),
|
|
2160
|
-
date: year - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
|
|
2161
|
-
};
|
|
2162
|
-
}
|
|
2163
|
-
function assertZipUint16(value, label) {
|
|
2164
|
-
if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT16) throw new Error(`${label} exceeds ZIP32 limit`);
|
|
2165
|
-
}
|
|
2166
|
-
function assertZipUint32(value, label) {
|
|
2167
|
-
if (!Number.isSafeInteger(value) || value < 0 || value > ZIP_MAX_UINT32) throw new Error(`${label} exceeds ZIP32 limit`);
|
|
2168
|
-
}
|
|
2169
|
-
function createLocalFileHeader(file) {
|
|
2170
|
-
const header = new Uint8Array(30);
|
|
2171
|
-
const view = new DataView(header.buffer);
|
|
2172
|
-
view.setUint32(0, LOCAL_FILE_HEADER_SIGNATURE, true);
|
|
2173
|
-
view.setUint16(4, ZIP_VERSION, true);
|
|
2174
|
-
view.setUint16(6, ZIP_UTF8_FLAG, true);
|
|
2175
|
-
view.setUint16(8, ZIP_STORE_METHOD, true);
|
|
2176
|
-
view.setUint16(10, file.modifiedTime, true);
|
|
2177
|
-
view.setUint16(12, file.modifiedDate, true);
|
|
2178
|
-
view.setUint32(14, file.crc32, true);
|
|
2179
|
-
view.setUint32(18, file.contentBytes.byteLength, true);
|
|
2180
|
-
view.setUint32(22, file.contentBytes.byteLength, true);
|
|
2181
|
-
view.setUint16(26, file.nameBytes.byteLength, true);
|
|
2182
|
-
view.setUint16(28, 0, true);
|
|
2183
|
-
return header;
|
|
2184
|
-
}
|
|
2185
|
-
function createCentralDirectoryHeader(file) {
|
|
2186
|
-
const header = new Uint8Array(46);
|
|
2187
|
-
const view = new DataView(header.buffer);
|
|
2188
|
-
view.setUint32(0, CENTRAL_DIRECTORY_SIGNATURE, true);
|
|
2189
|
-
view.setUint16(4, ZIP_VERSION, true);
|
|
2190
|
-
view.setUint16(6, ZIP_VERSION, true);
|
|
2191
|
-
view.setUint16(8, ZIP_UTF8_FLAG, true);
|
|
2192
|
-
view.setUint16(10, ZIP_STORE_METHOD, true);
|
|
2193
|
-
view.setUint16(12, file.modifiedTime, true);
|
|
2194
|
-
view.setUint16(14, file.modifiedDate, true);
|
|
2195
|
-
view.setUint32(16, file.crc32, true);
|
|
2196
|
-
view.setUint32(20, file.contentBytes.byteLength, true);
|
|
2197
|
-
view.setUint32(24, file.contentBytes.byteLength, true);
|
|
2198
|
-
view.setUint16(28, file.nameBytes.byteLength, true);
|
|
2199
|
-
view.setUint16(30, 0, true);
|
|
2200
|
-
view.setUint16(32, 0, true);
|
|
2201
|
-
view.setUint16(34, 0, true);
|
|
2202
|
-
view.setUint16(36, 0, true);
|
|
2203
|
-
view.setUint32(38, 0, true);
|
|
2204
|
-
view.setUint32(42, file.localHeaderOffset, true);
|
|
2205
|
-
return header;
|
|
2206
|
-
}
|
|
2207
|
-
function createEndOfCentralDirectory(fileCount, centralDirectorySize, centralDirectoryOffset) {
|
|
2208
|
-
assertZipUint16(fileCount, "ZIP file count");
|
|
2209
|
-
assertZipUint32(centralDirectorySize, "ZIP central directory size");
|
|
2210
|
-
assertZipUint32(centralDirectoryOffset, "ZIP central directory offset");
|
|
2211
|
-
const footer = new Uint8Array(22);
|
|
2212
|
-
const view = new DataView(footer.buffer);
|
|
2213
|
-
view.setUint32(0, END_OF_CENTRAL_DIRECTORY_SIGNATURE, true);
|
|
2214
|
-
view.setUint16(4, 0, true);
|
|
2215
|
-
view.setUint16(6, 0, true);
|
|
2216
|
-
view.setUint16(8, fileCount, true);
|
|
2217
|
-
view.setUint16(10, fileCount, true);
|
|
2218
|
-
view.setUint32(12, centralDirectorySize, true);
|
|
2219
|
-
view.setUint32(16, centralDirectoryOffset, true);
|
|
2220
|
-
view.setUint16(20, 0, true);
|
|
2221
|
-
return footer;
|
|
2222
|
-
}
|
|
2223
|
-
function createNormalizedZipFiles(files, options) {
|
|
2224
|
-
const names = /* @__PURE__ */ new Set();
|
|
2225
|
-
let offset = 0;
|
|
2226
|
-
return files.map((file) => {
|
|
2227
|
-
const name = normalizeZipFileName(file.name);
|
|
2228
|
-
if (names.has(name)) throw new Error(`Duplicate ZIP file name: ${name}`);
|
|
2229
|
-
names.add(name);
|
|
2230
|
-
const nameBytes = encodeText(name);
|
|
2231
|
-
const contentBytes = normalizeFileContent(file.content);
|
|
2232
|
-
assertZipUint16(nameBytes.byteLength, `ZIP file name ${name}`);
|
|
2233
|
-
assertZipUint32(contentBytes.byteLength, `ZIP file ${name}`);
|
|
2234
|
-
assertZipUint32(offset, "ZIP local header offset");
|
|
2235
|
-
const modified = toDosDateTime(file.lastModified ?? options.lastModified ?? Date.now());
|
|
2236
|
-
const normalized = {
|
|
2237
|
-
name,
|
|
2238
|
-
nameBytes,
|
|
2239
|
-
contentBytes,
|
|
2240
|
-
crc32: crc32(contentBytes),
|
|
2241
|
-
modifiedTime: modified.time,
|
|
2242
|
-
modifiedDate: modified.date,
|
|
2243
|
-
localHeaderOffset: offset
|
|
2244
|
-
};
|
|
2245
|
-
offset += 30 + nameBytes.byteLength + contentBytes.byteLength;
|
|
2246
|
-
assertZipUint32(offset, "ZIP local data size");
|
|
2247
|
-
return normalized;
|
|
2248
|
-
});
|
|
2249
|
-
}
|
|
2250
|
-
function isQuerySource(source) {
|
|
2251
|
-
return "query" in source && typeof source.query === "function";
|
|
2252
|
-
}
|
|
2253
|
-
function resolveSource(source, query) {
|
|
2254
|
-
if (isQuerySource(source)) return source.query(query);
|
|
2255
|
-
if (query) throw new Error("Log ZIP export query options require a source with query()");
|
|
2256
|
-
return source;
|
|
2257
|
-
}
|
|
2258
|
-
function normalizeMaxEvents(value) {
|
|
2259
|
-
if (value === void 0) return void 0;
|
|
2260
|
-
if (!Number.isFinite(value)) return void 0;
|
|
2261
|
-
return Math.max(0, Math.floor(value));
|
|
2262
|
-
}
|
|
2263
|
-
function normalizeSessionExportOptions(value, logFileName) {
|
|
2264
|
-
if (!value) return void 0;
|
|
2265
|
-
const options = value === true ? {} : value;
|
|
2266
|
-
return {
|
|
2267
|
-
contextKey: options.contextKey ?? DEFAULT_SESSION_CONTEXT_KEY,
|
|
2268
|
-
directory: options.directory ?? "sessions",
|
|
2269
|
-
fallbackSessionId: options.fallbackSessionId ?? "unknown",
|
|
2270
|
-
logFileName: options.logFileName ?? logFileName
|
|
2271
|
-
};
|
|
2272
|
-
}
|
|
2273
|
-
function normalizeRecentExportOptions(value, format) {
|
|
2274
|
-
if (!value) return void 0;
|
|
2275
|
-
const options = value === true ? {} : value;
|
|
2276
|
-
return {
|
|
2277
|
-
logFileName: options.logFileName ?? (format === "json" ? "recent.json" : "recent.ndjson"),
|
|
2278
|
-
maxEvents: normalizeMaxEvents(options.maxEvents) ?? 100
|
|
2279
|
-
};
|
|
2280
|
-
}
|
|
2281
|
-
function logContentForLines(lines, format) {
|
|
2282
|
-
if (format === "json") return `[${lines.join(",")}]`;
|
|
2283
|
-
return lines.length > 0 ? `${lines.join("\n")}\n` : "";
|
|
2284
|
-
}
|
|
2285
|
-
function eventTimeRange(items) {
|
|
2286
|
-
let from;
|
|
2287
|
-
let to;
|
|
2288
|
-
for (const item of items) {
|
|
2289
|
-
from = from === void 0 ? item.event.time : Math.min(from, item.event.time);
|
|
2290
|
-
to = to === void 0 ? item.event.time : Math.max(to, item.event.time);
|
|
2291
|
-
}
|
|
2292
|
-
return from === void 0 || to === void 0 ? void 0 : {
|
|
2293
|
-
from,
|
|
2294
|
-
to
|
|
2295
|
-
};
|
|
2296
|
-
}
|
|
2297
|
-
function sessionIdForEvent(event, options) {
|
|
2298
|
-
const value = event.context?.[options.contextKey];
|
|
2299
|
-
return typeof value === "string" && value.length > 0 ? value : options.fallbackSessionId;
|
|
2300
|
-
}
|
|
2301
|
-
function safeZipPathSegment(value) {
|
|
2302
|
-
const encoded = encodeURIComponent(value).replace(/%/g, "_");
|
|
2303
|
-
if (!encoded || encoded === "." || encoded === "..") return "_";
|
|
2304
|
-
return encoded;
|
|
2305
|
-
}
|
|
2306
|
-
function sortByEventTime(items) {
|
|
2307
|
-
return [...items].sort((left, right) => {
|
|
2308
|
-
if (left.event.time !== right.event.time) return left.event.time - right.event.time;
|
|
2309
|
-
if (left.event.seq !== right.event.seq) return left.event.seq - right.event.seq;
|
|
2310
|
-
return left.event.id < right.event.id ? -1 : left.event.id > right.event.id ? 1 : 0;
|
|
2311
|
-
});
|
|
2312
|
-
}
|
|
2313
|
-
function createLogZipBlob(files, options = {}) {
|
|
2314
|
-
const normalizedFiles = createNormalizedZipFiles(files, options);
|
|
2315
|
-
const parts = [];
|
|
2316
|
-
let centralDirectoryOffset = 0;
|
|
2317
|
-
for (const file of normalizedFiles) {
|
|
2318
|
-
parts.push(createLocalFileHeader(file), file.nameBytes, file.contentBytes);
|
|
2319
|
-
centralDirectoryOffset += 30 + file.nameBytes.byteLength + file.contentBytes.byteLength;
|
|
2320
|
-
}
|
|
2321
|
-
let centralDirectorySize = 0;
|
|
2322
|
-
for (const file of normalizedFiles) {
|
|
2323
|
-
const header = createCentralDirectoryHeader(file);
|
|
2324
|
-
parts.push(header, file.nameBytes);
|
|
2325
|
-
centralDirectorySize += header.byteLength + file.nameBytes.byteLength;
|
|
2326
|
-
}
|
|
2327
|
-
parts.push(createEndOfCentralDirectory(normalizedFiles.length, centralDirectorySize, centralDirectoryOffset));
|
|
2328
|
-
return new Blob(parts, { type: options.type ?? "application/zip" });
|
|
2329
|
-
}
|
|
2330
|
-
async function exportLogsToZip(source, options = {}) {
|
|
2331
|
-
const format = options.format ?? "ndjson";
|
|
2332
|
-
const createdAt = options.createdAt ?? Date.now();
|
|
2333
|
-
const logFileName = options.logFileName ?? (format === "json" ? "logs.json" : "logs.ndjson");
|
|
2334
|
-
const maxEvents = normalizeMaxEvents(options.maxEvents);
|
|
2335
|
-
const sessionExportOptions = normalizeSessionExportOptions(options.groupBySession, logFileName);
|
|
2336
|
-
const recentExportOptions = normalizeRecentExportOptions(options.includeRecent, format);
|
|
2337
|
-
const serialize = options.serializeEvent ?? ((event) => (0, _loggerjs_core.safeJsonStringify)(event, options.stringify ?? {}));
|
|
2338
|
-
const exported = [];
|
|
2339
|
-
let skippedCount = 0;
|
|
2340
|
-
for await (const event of resolveSource(source, options.query)) {
|
|
2341
|
-
if (maxEvents !== void 0 && exported.length >= maxEvents) break;
|
|
2342
|
-
const mapped = options.mapEvent ? options.mapEvent(event) : event;
|
|
2343
|
-
if (!mapped) {
|
|
2344
|
-
skippedCount += 1;
|
|
2345
|
-
continue;
|
|
2346
|
-
}
|
|
2347
|
-
exported.push({
|
|
2348
|
-
event: mapped,
|
|
2349
|
-
line: serialize(mapped)
|
|
2350
|
-
});
|
|
2351
|
-
}
|
|
2352
|
-
const files = [{
|
|
2353
|
-
name: logFileName,
|
|
2354
|
-
content: logContentForLines(exported.map((item) => item.line), format),
|
|
2355
|
-
lastModified: createdAt
|
|
2356
|
-
}];
|
|
2357
|
-
const sessionManifests = [];
|
|
2358
|
-
if (sessionExportOptions) {
|
|
2359
|
-
const groups = /* @__PURE__ */ new Map();
|
|
2360
|
-
for (const item of exported) {
|
|
2361
|
-
const sessionId = sessionIdForEvent(item.event, sessionExportOptions);
|
|
2362
|
-
const group = groups.get(sessionId) ?? [];
|
|
2363
|
-
group.push(item);
|
|
2364
|
-
groups.set(sessionId, group);
|
|
2365
|
-
}
|
|
2366
|
-
const usedFileNames = new Set(files.map((file) => normalizeZipFileName(file.name)));
|
|
2367
|
-
const sortedGroups = [...groups.entries()];
|
|
2368
|
-
sortedGroups.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
2369
|
-
for (const [sessionId, group] of sortedGroups) {
|
|
2370
|
-
let fileName = normalizeZipFileName(`${sessionExportOptions.directory}/${safeZipPathSegment(sessionId)}/${sessionExportOptions.logFileName}`);
|
|
2371
|
-
let suffix = 1;
|
|
2372
|
-
while (usedFileNames.has(fileName)) {
|
|
2373
|
-
fileName = normalizeZipFileName(`${sessionExportOptions.directory}/${safeZipPathSegment(sessionId)}-${suffix}/${sessionExportOptions.logFileName}`);
|
|
2374
|
-
suffix += 1;
|
|
2375
|
-
}
|
|
2376
|
-
usedFileNames.add(fileName);
|
|
2377
|
-
const orderedGroup = sortByEventTime(group);
|
|
2378
|
-
files.push({
|
|
2379
|
-
name: fileName,
|
|
2380
|
-
content: logContentForLines(orderedGroup.map((item) => item.line), format),
|
|
2381
|
-
lastModified: createdAt
|
|
2382
|
-
});
|
|
2383
|
-
sessionManifests.push({
|
|
2384
|
-
logCount: orderedGroup.length,
|
|
2385
|
-
logFileName: fileName,
|
|
2386
|
-
sessionId,
|
|
2387
|
-
timeRange: eventTimeRange(orderedGroup)
|
|
2388
|
-
});
|
|
2389
|
-
}
|
|
2390
|
-
}
|
|
2391
|
-
let recentLogFileName;
|
|
2392
|
-
if (recentExportOptions) {
|
|
2393
|
-
const recentItems = recentExportOptions.maxEvents <= 0 ? [] : sortByEventTime(exported).slice(-recentExportOptions.maxEvents);
|
|
2394
|
-
recentLogFileName = normalizeZipFileName(recentExportOptions.logFileName);
|
|
2395
|
-
files.push({
|
|
2396
|
-
name: recentLogFileName,
|
|
2397
|
-
content: logContentForLines(recentItems.map((item) => item.line), format),
|
|
2398
|
-
lastModified: createdAt
|
|
2399
|
-
});
|
|
2400
|
-
}
|
|
2401
|
-
if (options.includeManifest ?? true) {
|
|
2402
|
-
const manifest = {
|
|
2403
|
-
schema: "loggerjs.log-export.v1",
|
|
2404
|
-
createdAt: new Date(createdAt).toISOString(),
|
|
2405
|
-
source: options.source,
|
|
2406
|
-
format,
|
|
2407
|
-
logFileName: normalizeZipFileName(logFileName),
|
|
2408
|
-
logCount: exported.length,
|
|
2409
|
-
skippedCount,
|
|
2410
|
-
query: options.query,
|
|
2411
|
-
recentLogFileName,
|
|
2412
|
-
sessionCount: sessionExportOptions ? sessionManifests.length : void 0,
|
|
2413
|
-
sessions: sessionExportOptions ? sessionManifests : void 0,
|
|
2414
|
-
timeRange: eventTimeRange(exported)
|
|
2415
|
-
};
|
|
2416
|
-
files.push({
|
|
2417
|
-
name: options.manifestFileName ?? "manifest.json",
|
|
2418
|
-
content: `${(0, _loggerjs_core.safeJsonStringify)(manifest, { space: 2 })}\n`,
|
|
2419
|
-
lastModified: createdAt
|
|
2420
|
-
});
|
|
2421
|
-
}
|
|
2422
|
-
return createLogZipBlob(files, { lastModified: createdAt });
|
|
2423
|
-
}
|
|
2424
|
-
function downloadBlob(blob, filenameOrOptions = {}) {
|
|
2425
|
-
const options = typeof filenameOrOptions === "string" ? { filename: filenameOrOptions } : filenameOrOptions;
|
|
2426
|
-
const documentRef = options.document ?? globalThis.document;
|
|
2427
|
-
const urlRef = options.url ?? globalThis.URL;
|
|
2428
|
-
if (!documentRef?.body || !urlRef?.createObjectURL || !urlRef.revokeObjectURL) throw new Error("downloadBlob requires document and URL.createObjectURL");
|
|
2429
|
-
const href = urlRef.createObjectURL(blob);
|
|
2430
|
-
const anchor = documentRef.createElement("a");
|
|
2431
|
-
anchor.href = href;
|
|
2432
|
-
anchor.download = options.filename ?? "loggerjs-logs.zip";
|
|
2433
|
-
anchor.style.display = "none";
|
|
2434
|
-
documentRef.body.appendChild(anchor);
|
|
2435
|
-
anchor.click();
|
|
2436
|
-
anchor.remove();
|
|
2437
|
-
const revoke = () => urlRef.revokeObjectURL(href);
|
|
2438
|
-
const revokeDelayMs = options.revokeDelayMs ?? 1e3;
|
|
2439
|
-
if (revokeDelayMs <= 0) revoke();
|
|
2440
|
-
else setTimeout(revoke, revokeDelayMs);
|
|
2441
|
-
return href;
|
|
2442
|
-
}
|
|
2443
|
-
//#endregion
|
|
2444
|
-
//#region src/console-integration.ts
|
|
2445
|
-
const defaultLevels = [
|
|
2446
|
-
"debug",
|
|
2447
|
-
"info",
|
|
2448
|
-
"log",
|
|
2449
|
-
"trace",
|
|
2450
|
-
"warn",
|
|
2451
|
-
"error"
|
|
2452
|
-
];
|
|
2453
|
-
const levelMap = {
|
|
2454
|
-
debug: "debug",
|
|
2455
|
-
info: "info",
|
|
2456
|
-
log: "info",
|
|
2457
|
-
trace: "trace",
|
|
2458
|
-
warn: "warn",
|
|
2459
|
-
error: "error"
|
|
2460
|
-
};
|
|
2461
|
-
function formatConsoleMessage(args) {
|
|
2462
|
-
return args.map((arg) => {
|
|
2463
|
-
if (typeof arg === "string") return arg;
|
|
2464
|
-
if (arg instanceof Error) return arg.message;
|
|
2465
|
-
try {
|
|
2466
|
-
return JSON.stringify((0, _loggerjs_core.normalizeValue)(arg, {
|
|
2467
|
-
maxDepth: 3,
|
|
2468
|
-
maxArrayLength: 20,
|
|
2469
|
-
maxObjectKeys: 40
|
|
2470
|
-
}));
|
|
2471
|
-
} catch {
|
|
2472
|
-
return String(arg);
|
|
2473
|
-
}
|
|
2474
|
-
}).join(" ");
|
|
2475
|
-
}
|
|
2476
|
-
function reportRateLimitDrop() {
|
|
2477
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("integration.dropped");
|
|
2478
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("integration.dropped.rate-limit");
|
|
2479
|
-
}
|
|
2480
|
-
function captureConsoleIntegration(options = {}) {
|
|
2481
|
-
const levels = options.levels ?? defaultLevels;
|
|
2482
|
-
const preserveConsole = options.preserveConsole ?? true;
|
|
2483
|
-
const captureArguments = options.captureArguments ?? false;
|
|
2484
|
-
const maxCapturesPerSecond = options.maxCapturesPerSecond ?? 100;
|
|
2485
|
-
return {
|
|
2486
|
-
name: "capture-console",
|
|
2487
|
-
setup(api) {
|
|
2488
|
-
if (typeof console === "undefined") return;
|
|
2489
|
-
const originals = {};
|
|
2490
|
-
const boundOriginals = {};
|
|
2491
|
-
let windowStarted = Date.now();
|
|
2492
|
-
let capturesInWindow = 0;
|
|
2493
|
-
let disposed = false;
|
|
2494
|
-
const shouldCapture = () => {
|
|
2495
|
-
const now = Date.now();
|
|
2496
|
-
if (now - windowStarted >= 1e3) {
|
|
2497
|
-
windowStarted = now;
|
|
2498
|
-
capturesInWindow = 0;
|
|
2499
|
-
}
|
|
2500
|
-
if (capturesInWindow >= maxCapturesPerSecond) {
|
|
2501
|
-
reportRateLimitDrop();
|
|
2502
|
-
return false;
|
|
2503
|
-
}
|
|
2504
|
-
capturesInWindow += 1;
|
|
2505
|
-
return true;
|
|
2506
|
-
};
|
|
2507
|
-
for (const level of levels) {
|
|
2508
|
-
const original = console[level] ?? console.log.bind(console);
|
|
2509
|
-
originals[level] = original;
|
|
2510
|
-
const unpatched = api.unpatched.console[level] ?? original;
|
|
2511
|
-
api.unpatched.console[level] ??= original;
|
|
2512
|
-
boundOriginals[level] = unpatched.bind(console);
|
|
2513
|
-
const capture = api.guard((args) => {
|
|
2514
|
-
if (!shouldCapture()) return;
|
|
2515
|
-
const consoleData = { level };
|
|
2516
|
-
if (captureArguments) consoleData.arguments = (0, _loggerjs_core.normalizeValue)(args, { maxDepth: 4 });
|
|
2517
|
-
api.capture({
|
|
2518
|
-
level: levelMap[level],
|
|
2519
|
-
message: formatConsoleMessage(args),
|
|
2520
|
-
props: { console: consoleData }
|
|
2521
|
-
});
|
|
2522
|
-
});
|
|
2523
|
-
console[level] = (...args) => {
|
|
2524
|
-
capture(args);
|
|
2525
|
-
if (preserveConsole) boundOriginals[level]?.(...args);
|
|
2526
|
-
};
|
|
2527
|
-
}
|
|
2528
|
-
return () => {
|
|
2529
|
-
if (disposed) return;
|
|
2530
|
-
disposed = true;
|
|
2531
|
-
for (const level of levels) if (originals[level]) console[level] = originals[level];
|
|
2532
|
-
};
|
|
2533
|
-
}
|
|
2534
|
-
};
|
|
2535
|
-
}
|
|
2536
|
-
//#endregion
|
|
2537
|
-
//#region src/context-propagation-integration.ts
|
|
2538
|
-
const defaultActionEvents = [
|
|
2539
|
-
"click",
|
|
2540
|
-
"input",
|
|
2541
|
-
"change",
|
|
2542
|
-
"submit"
|
|
2543
|
-
];
|
|
2544
|
-
let fallbackIdSeq = 0;
|
|
2545
|
-
function defaultIdFactory() {
|
|
2546
|
-
const cryptoRandomUUID = globalThis.crypto?.randomUUID;
|
|
2547
|
-
if (cryptoRandomUUID) return cryptoRandomUUID.call(globalThis.crypto);
|
|
2548
|
-
return `${Date.now().toString(36)}-${(fallbackIdSeq++).toString(36)}`;
|
|
2549
|
-
}
|
|
2550
|
-
function resolveValue(value) {
|
|
2551
|
-
return typeof value === "function" ? value() : value;
|
|
2552
|
-
}
|
|
2553
|
-
function normalizeBaggage(value) {
|
|
2554
|
-
if (typeof value === "string") return (0, _loggerjs_core.parseBaggage)(value);
|
|
2555
|
-
return value;
|
|
2556
|
-
}
|
|
2557
|
-
function actionTargetName(target) {
|
|
2558
|
-
const item = target;
|
|
2559
|
-
if (!item) return void 0;
|
|
2560
|
-
return item.id || item.name || item.tagName?.toLowerCase();
|
|
2561
|
-
}
|
|
2562
|
-
function browserContextPropagationIntegration(options = {}) {
|
|
2563
|
-
const actionEvents = options.actionEvents ?? defaultActionEvents;
|
|
2564
|
-
const actionTtlMs = Math.max(0, Math.floor(options.actionTtlMs ?? 5e3));
|
|
2565
|
-
const clock = options.clock ?? Date.now;
|
|
2566
|
-
const idFactory = options.idFactory ?? defaultIdFactory;
|
|
2567
|
-
let lastAction;
|
|
2568
|
-
return {
|
|
2569
|
-
name: "browser-context-propagation",
|
|
2570
|
-
setup() {
|
|
2571
|
-
const root = options.root ?? globalThis.document;
|
|
2572
|
-
const disposeProvider = (0, _loggerjs_core.addContextProvider)(() => {
|
|
2573
|
-
const sessionId = resolveValue(options.sessionId);
|
|
2574
|
-
const requestId = options.requestId?.();
|
|
2575
|
-
const trace = resolveValue(options.trace) ?? (0, _loggerjs_core.parseTraceparent)(resolveValue(options.traceparent));
|
|
2576
|
-
const baggage = normalizeBaggage(resolveValue(options.baggage)) ?? trace?.baggage;
|
|
2577
|
-
const traceparent = (0, _loggerjs_core.formatTraceparent)(trace);
|
|
2578
|
-
const now = clock();
|
|
2579
|
-
const action = lastAction && now - lastAction.time <= actionTtlMs ? {
|
|
2580
|
-
actionId: lastAction.id,
|
|
2581
|
-
actionType: lastAction.type,
|
|
2582
|
-
actionTarget: lastAction.target
|
|
2583
|
-
} : void 0;
|
|
2584
|
-
return {
|
|
2585
|
-
sessionId,
|
|
2586
|
-
requestId,
|
|
2587
|
-
traceparent,
|
|
2588
|
-
baggage: (0, _loggerjs_core.formatBaggage)(baggage),
|
|
2589
|
-
...action
|
|
2590
|
-
};
|
|
2591
|
-
});
|
|
2592
|
-
if (!root?.addEventListener || !root.removeEventListener || actionEvents.length === 0) return disposeProvider;
|
|
2593
|
-
const onAction = (event) => {
|
|
2594
|
-
lastAction = {
|
|
2595
|
-
id: idFactory(),
|
|
2596
|
-
type: event.type,
|
|
2597
|
-
target: actionTargetName(event.target),
|
|
2598
|
-
time: clock()
|
|
2599
|
-
};
|
|
2600
|
-
};
|
|
2601
|
-
for (const event of actionEvents) root.addEventListener(event, onAction, true);
|
|
2602
|
-
return () => {
|
|
2603
|
-
for (const event of actionEvents) root.removeEventListener(event, onAction, true);
|
|
2604
|
-
disposeProvider();
|
|
2605
|
-
};
|
|
2606
|
-
}
|
|
2607
|
-
};
|
|
2608
|
-
}
|
|
2609
|
-
//#endregion
|
|
2610
|
-
//#region src/error-integration.ts
|
|
2611
|
-
function resourceInfo(target) {
|
|
2612
|
-
const element = target;
|
|
2613
|
-
return {
|
|
2614
|
-
tagName: element?.tagName,
|
|
2615
|
-
url: element?.src || element?.currentSrc || element?.href,
|
|
2616
|
-
html: element?.outerHTML?.slice(0, 500)
|
|
2617
|
-
};
|
|
2618
|
-
}
|
|
2619
|
-
function reportDrop(reason) {
|
|
2620
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)("integration.dropped");
|
|
2621
|
-
(0, _loggerjs_core.incrementLoggerMetaCounter)(`integration.dropped.${reason}`);
|
|
2622
|
-
}
|
|
2623
|
-
function captureBrowserErrorsIntegration(options = {}) {
|
|
2624
|
-
const captureWindowError = options.captureWindowError ?? true;
|
|
2625
|
-
const captureUnhandledRejection = options.captureUnhandledRejection ?? true;
|
|
2626
|
-
const captureResourceErrors = options.captureResourceErrors ?? true;
|
|
2627
|
-
const captureSecurityPolicyViolation = options.captureSecurityPolicyViolation ?? false;
|
|
2628
|
-
const scriptErrorDedupeWindowMs = options.scriptErrorDedupeWindowMs ?? 1e3;
|
|
2629
|
-
return {
|
|
2630
|
-
name: "capture-browser-errors",
|
|
2631
|
-
setup(api) {
|
|
2632
|
-
const disposers = [];
|
|
2633
|
-
let lastScriptErrorAt = 0;
|
|
2634
|
-
let disposed = false;
|
|
2635
|
-
const shouldDropScriptError = (message) => {
|
|
2636
|
-
if (message !== "Script error.") return false;
|
|
2637
|
-
const now = Date.now();
|
|
2638
|
-
if (now - lastScriptErrorAt <= scriptErrorDedupeWindowMs) {
|
|
2639
|
-
reportDrop("script-error-duplicate");
|
|
2640
|
-
return true;
|
|
2641
|
-
}
|
|
2642
|
-
lastScriptErrorAt = now;
|
|
2643
|
-
return false;
|
|
2644
|
-
};
|
|
2645
|
-
if (captureWindowError && typeof addEventListener !== "undefined") {
|
|
2646
|
-
const onError = api.guard((event) => {
|
|
2647
|
-
const errorEvent = event;
|
|
2648
|
-
if ("message" in errorEvent && shouldDropScriptError(errorEvent.message)) return;
|
|
2649
|
-
if ("error" in errorEvent && errorEvent.error) {
|
|
2650
|
-
api.capture({
|
|
2651
|
-
level: "error",
|
|
2652
|
-
message: errorEvent.message || errorEvent.error.message,
|
|
2653
|
-
error: errorEvent.error,
|
|
2654
|
-
props: { browser: {
|
|
2655
|
-
kind: "script-error",
|
|
2656
|
-
file: errorEvent.filename,
|
|
2657
|
-
line: errorEvent.lineno,
|
|
2658
|
-
column: errorEvent.colno
|
|
2659
|
-
} }
|
|
2660
|
-
});
|
|
2661
|
-
return;
|
|
2662
|
-
}
|
|
2663
|
-
if ("message" in errorEvent && errorEvent.message) {
|
|
2664
|
-
api.capture({
|
|
2665
|
-
level: "error",
|
|
2666
|
-
message: errorEvent.message,
|
|
2667
|
-
props: { browser: {
|
|
2668
|
-
kind: "script-error",
|
|
2669
|
-
file: errorEvent.filename,
|
|
2670
|
-
line: errorEvent.lineno,
|
|
2671
|
-
column: errorEvent.colno
|
|
2672
|
-
} }
|
|
2673
|
-
});
|
|
2674
|
-
return;
|
|
2675
|
-
}
|
|
2676
|
-
if (captureResourceErrors) api.capture({
|
|
2677
|
-
level: "error",
|
|
2678
|
-
message: "Browser resource load error",
|
|
2679
|
-
props: { browser: {
|
|
2680
|
-
kind: "resource-error",
|
|
2681
|
-
...resourceInfo(event.target)
|
|
2682
|
-
} }
|
|
2683
|
-
});
|
|
2684
|
-
});
|
|
2685
|
-
addEventListener("error", onError, true);
|
|
2686
|
-
disposers.push(() => removeEventListener("error", onError, true));
|
|
2687
|
-
}
|
|
2688
|
-
if (captureUnhandledRejection && typeof addEventListener !== "undefined") {
|
|
2689
|
-
const onUnhandledRejection = api.guard((event) => {
|
|
2690
|
-
const reason = event.reason;
|
|
2691
|
-
api.capture({
|
|
2692
|
-
level: "error",
|
|
2693
|
-
message: reason instanceof Error ? reason.message : "Unhandled promise rejection",
|
|
2694
|
-
error: reason,
|
|
2695
|
-
props: {
|
|
2696
|
-
browser: { kind: "unhandledrejection" },
|
|
2697
|
-
reason: (0, _loggerjs_core.normalizeValue)(reason, { maxDepth: 5 })
|
|
2698
|
-
}
|
|
2699
|
-
});
|
|
2700
|
-
});
|
|
2701
|
-
addEventListener("unhandledrejection", onUnhandledRejection);
|
|
2702
|
-
disposers.push(() => removeEventListener("unhandledrejection", onUnhandledRejection));
|
|
2703
|
-
}
|
|
2704
|
-
if (captureSecurityPolicyViolation && typeof addEventListener !== "undefined") {
|
|
2705
|
-
const onSecurityPolicyViolation = api.guard((event) => {
|
|
2706
|
-
api.capture({
|
|
2707
|
-
level: "warn",
|
|
2708
|
-
message: `Security policy violation: ${event.violatedDirective}`,
|
|
2709
|
-
props: { browser: {
|
|
2710
|
-
kind: "securitypolicyviolation",
|
|
2711
|
-
blockedURI: event.blockedURI,
|
|
2712
|
-
documentURI: event.documentURI,
|
|
2713
|
-
file: event.sourceFile,
|
|
2714
|
-
line: event.lineNumber,
|
|
2715
|
-
column: event.columnNumber,
|
|
2716
|
-
originalPolicy: event.originalPolicy,
|
|
2717
|
-
effectiveDirective: event.effectiveDirective
|
|
2718
|
-
} }
|
|
2719
|
-
});
|
|
2720
|
-
});
|
|
2721
|
-
addEventListener("securitypolicyviolation", onSecurityPolicyViolation);
|
|
2722
|
-
disposers.push(() => removeEventListener("securitypolicyviolation", onSecurityPolicyViolation));
|
|
2723
|
-
}
|
|
2724
|
-
return () => {
|
|
2725
|
-
if (disposed) return;
|
|
2726
|
-
disposed = true;
|
|
2727
|
-
for (const dispose of disposers) dispose();
|
|
2728
|
-
};
|
|
2729
|
-
}
|
|
2730
|
-
};
|
|
2731
|
-
}
|
|
2732
|
-
//#endregion
|
|
2733
|
-
//#region src/http-capture-utils.ts
|
|
2734
|
-
function nowMs() {
|
|
2735
|
-
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
2736
|
-
}
|
|
2737
|
-
function durationMs(started) {
|
|
2738
|
-
return nowMs() - started;
|
|
2739
|
-
}
|
|
2740
|
-
function sanitizeHttpUrl(rawUrl, sanitizer) {
|
|
2741
|
-
const stripped = stripSensitiveUrlParts(rawUrl);
|
|
2742
|
-
return sanitizer ? sanitizer(stripped) : stripped;
|
|
2743
|
-
}
|
|
2744
|
-
function pickAllowedHeaders(headers, allowList) {
|
|
2745
|
-
if (!headers || !allowList || allowList.length === 0) return void 0;
|
|
2746
|
-
const out = {};
|
|
2747
|
-
for (const name of allowList) {
|
|
2748
|
-
const value = headers.get(name);
|
|
2749
|
-
if (value !== null) out[name.toLowerCase()] = value;
|
|
2750
|
-
}
|
|
2751
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
2752
|
-
}
|
|
2753
|
-
function headersFromInit(headers) {
|
|
2754
|
-
if (!headers) return void 0;
|
|
2755
|
-
try {
|
|
2756
|
-
return new Headers(headers);
|
|
2757
|
-
} catch {
|
|
2758
|
-
return;
|
|
2759
|
-
}
|
|
2760
|
-
}
|
|
2761
|
-
function shouldSample(sampleRate, random) {
|
|
2762
|
-
if (sampleRate >= 1) return true;
|
|
2763
|
-
if (sampleRate <= 0) return false;
|
|
2764
|
-
return random() < sampleRate;
|
|
2765
|
-
}
|
|
2766
|
-
function stripSensitiveUrlParts(rawUrl) {
|
|
2767
|
-
if (!/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(rawUrl)) return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
2768
|
-
try {
|
|
2769
|
-
const url = new URL(rawUrl);
|
|
2770
|
-
url.search = "";
|
|
2771
|
-
url.hash = "";
|
|
2772
|
-
return url.toString();
|
|
2773
|
-
} catch {
|
|
2774
|
-
return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
2775
|
-
}
|
|
2776
|
-
}
|
|
2777
|
-
//#endregion
|
|
2778
|
-
//#region src/fetch-integration.ts
|
|
2779
|
-
function requestUrl(input) {
|
|
2780
|
-
if (typeof input === "string") return input;
|
|
2781
|
-
if (input instanceof URL) return input.toString();
|
|
2782
|
-
return input.url;
|
|
2783
|
-
}
|
|
2784
|
-
function requestMethod(input, init) {
|
|
2785
|
-
if (init?.method) return init.method.toUpperCase();
|
|
2786
|
-
if (typeof input === "object" && "method" in input && input.method) return input.method.toUpperCase();
|
|
2787
|
-
return "GET";
|
|
2788
|
-
}
|
|
2789
|
-
function captureFetchIntegration(options = {}) {
|
|
2790
|
-
const minStatus = options.minStatus ?? 400;
|
|
2791
|
-
const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
|
|
2792
|
-
const sampleRate = options.sampleRate ?? 1;
|
|
2793
|
-
const random = options.random ?? Math.random;
|
|
2794
|
-
return {
|
|
2795
|
-
name: "capture-fetch",
|
|
2796
|
-
setup(api) {
|
|
2797
|
-
const current = globalThis.fetch;
|
|
2798
|
-
if (!current) return;
|
|
2799
|
-
const original = api.unpatched.fetch ?? current.bind(globalThis);
|
|
2800
|
-
api.unpatched.fetch ??= original;
|
|
2801
|
-
const capture = api.guard((input) => api.capture(input));
|
|
2802
|
-
globalThis.fetch = async (input, init) => {
|
|
2803
|
-
const started = nowMs();
|
|
2804
|
-
const url = sanitizeHttpUrl(requestUrl(input), options.sanitizeUrl);
|
|
2805
|
-
const method = requestMethod(input, init);
|
|
2806
|
-
try {
|
|
2807
|
-
const response = await original(input, init);
|
|
2808
|
-
if (response.status >= minStatus || captureAll && shouldSample(sampleRate, random)) capture({
|
|
2809
|
-
level: response.status >= minStatus ? "warn" : "debug",
|
|
2810
|
-
message: `Fetch ${response.status} ${method} ${url}`,
|
|
2811
|
-
props: { http: {
|
|
2812
|
-
kind: "fetch",
|
|
2813
|
-
method,
|
|
2814
|
-
url,
|
|
2815
|
-
status: response.status,
|
|
2816
|
-
ok: response.ok,
|
|
2817
|
-
durationMs: durationMs(started),
|
|
2818
|
-
requestHeaders: pickAllowedHeaders(headersFromInit(init?.headers), options.captureRequestHeaders),
|
|
2819
|
-
responseHeaders: pickAllowedHeaders(response.headers, options.captureResponseHeaders)
|
|
2820
|
-
} }
|
|
2821
|
-
});
|
|
2822
|
-
return response;
|
|
2823
|
-
} catch (error) {
|
|
2824
|
-
capture({
|
|
2825
|
-
level: "error",
|
|
2826
|
-
message: `Fetch network error ${method} ${url}`,
|
|
2827
|
-
error,
|
|
2828
|
-
props: {
|
|
2829
|
-
http: {
|
|
2830
|
-
kind: "fetch",
|
|
2831
|
-
method,
|
|
2832
|
-
url,
|
|
2833
|
-
durationMs: durationMs(started)
|
|
2834
|
-
},
|
|
2835
|
-
input: (0, _loggerjs_core.normalizeValue)(input, { maxDepth: 3 })
|
|
2836
|
-
}
|
|
2837
|
-
});
|
|
2838
|
-
throw error;
|
|
2839
|
-
}
|
|
2840
|
-
};
|
|
2841
|
-
return () => {
|
|
2842
|
-
globalThis.fetch = current;
|
|
2843
|
-
};
|
|
2844
|
-
}
|
|
2845
|
-
};
|
|
2846
|
-
}
|
|
2847
|
-
//#endregion
|
|
2848
|
-
//#region src/xhr-integration.ts
|
|
2849
|
-
const XHR_META = "__LOGGERJS_XHR_META__";
|
|
2850
|
-
function captureXHRIntegration(options = {}) {
|
|
2851
|
-
const minStatus = options.minStatus ?? 400;
|
|
2852
|
-
const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
|
|
2853
|
-
const sampleRate = options.sampleRate ?? 1;
|
|
2854
|
-
const random = options.random ?? Math.random;
|
|
2855
|
-
return {
|
|
2856
|
-
name: "capture-xhr",
|
|
2857
|
-
setup(api) {
|
|
2858
|
-
if (typeof XMLHttpRequest === "undefined") return;
|
|
2859
|
-
const proto = XMLHttpRequest.prototype;
|
|
2860
|
-
const originalOpen = proto.open;
|
|
2861
|
-
const originalSend = proto.send;
|
|
2862
|
-
api.unpatched.XMLHttpRequest ??= XMLHttpRequest;
|
|
2863
|
-
const capture = api.guard((input) => api.capture(input));
|
|
2864
|
-
proto.open = function patchedOpen(method, url, ...rest) {
|
|
2865
|
-
this[XHR_META] = {
|
|
2866
|
-
method: method.toUpperCase(),
|
|
2867
|
-
url: sanitizeHttpUrl(String(url), options.sanitizeUrl),
|
|
2868
|
-
started: 0
|
|
2869
|
-
};
|
|
2870
|
-
return originalOpen.apply(this, [
|
|
2871
|
-
method,
|
|
2872
|
-
url,
|
|
2873
|
-
...rest
|
|
2874
|
-
]);
|
|
2875
|
-
};
|
|
2876
|
-
proto.send = function patchedSend(body) {
|
|
2877
|
-
const meta = this[XHR_META];
|
|
2878
|
-
if (meta) meta.started = nowMs();
|
|
2879
|
-
let captured = false;
|
|
2880
|
-
const onLoadEnd = () => {
|
|
2881
|
-
if (!meta || captured) return;
|
|
2882
|
-
if (this.status >= minStatus || captureAll && shouldSample(sampleRate, random)) {
|
|
2883
|
-
captured = true;
|
|
2884
|
-
capture({
|
|
2885
|
-
level: this.status >= minStatus ? "warn" : "debug",
|
|
2886
|
-
message: `XHR ${this.status} ${meta.method} ${meta.url}`,
|
|
2887
|
-
props: { http: {
|
|
2888
|
-
kind: "xhr",
|
|
2889
|
-
method: meta.method,
|
|
2890
|
-
url: meta.url,
|
|
2891
|
-
status: this.status,
|
|
2892
|
-
durationMs: durationMs(meta.started)
|
|
2893
|
-
} }
|
|
2894
|
-
});
|
|
2895
|
-
}
|
|
2896
|
-
};
|
|
2897
|
-
const onError = () => {
|
|
2898
|
-
if (!meta || captured) return;
|
|
2899
|
-
captured = true;
|
|
2900
|
-
capture({
|
|
2901
|
-
level: "error",
|
|
2902
|
-
message: `XHR network error ${meta.method} ${meta.url}`,
|
|
2903
|
-
props: { http: {
|
|
2904
|
-
kind: "xhr",
|
|
2905
|
-
method: meta.method,
|
|
2906
|
-
url: meta.url,
|
|
2907
|
-
durationMs: durationMs(meta.started)
|
|
2908
|
-
} }
|
|
2909
|
-
});
|
|
2910
|
-
};
|
|
2911
|
-
this.addEventListener("loadend", onLoadEnd, { once: true });
|
|
2912
|
-
this.addEventListener("error", onError, { once: true });
|
|
2913
|
-
return originalSend.apply(this, [body]);
|
|
2914
|
-
};
|
|
2915
|
-
return () => {
|
|
2916
|
-
proto.open = originalOpen;
|
|
2917
|
-
proto.send = originalSend;
|
|
2918
|
-
};
|
|
2919
|
-
}
|
|
2920
|
-
};
|
|
2921
|
-
}
|
|
2922
|
-
//#endregion
|
|
2923
|
-
//#region src/framework-error-integration.ts
|
|
2924
|
-
function errorMessage(error) {
|
|
2925
|
-
return error instanceof Error ? error.message : "Framework error";
|
|
2926
|
-
}
|
|
2927
|
-
function instanceComponentName(instance) {
|
|
2928
|
-
const record = instance;
|
|
2929
|
-
return record?.$options?.name ?? record?.type?.displayName ?? record?.type?.name ?? record?.constructor?.name;
|
|
2930
|
-
}
|
|
2931
|
-
function normalizeFrameworkInfo(input, fallbackFramework) {
|
|
2932
|
-
if (typeof input === "string") return {
|
|
2933
|
-
framework: fallbackFramework,
|
|
2934
|
-
info: input
|
|
2935
|
-
};
|
|
2936
|
-
return {
|
|
2937
|
-
framework: fallbackFramework,
|
|
2938
|
-
...input
|
|
2939
|
-
};
|
|
2940
|
-
}
|
|
2941
|
-
function captureFrameworkErrorsIntegration(options = {}) {
|
|
2942
|
-
const integrationName = options.name ?? "capture-framework-errors";
|
|
2943
|
-
const fallbackFramework = options.framework ?? "framework";
|
|
2944
|
-
const level = options.level ?? "error";
|
|
2945
|
-
const maxPending = Math.max(0, Math.floor(options.maxPending ?? 10));
|
|
2946
|
-
const infoMaxDepth = options.infoMaxDepth ?? 6;
|
|
2947
|
-
const pending = [];
|
|
2948
|
-
let api;
|
|
2949
|
-
let started = false;
|
|
2950
|
-
let disposed = false;
|
|
2951
|
-
const emit = (error, rawInfo) => {
|
|
2952
|
-
const info = normalizeFrameworkInfo(rawInfo, fallbackFramework);
|
|
2953
|
-
const message = options.getMessage?.(error, info) ?? errorMessage(error);
|
|
2954
|
-
api?.capture({
|
|
2955
|
-
level,
|
|
2956
|
-
message,
|
|
2957
|
-
error,
|
|
2958
|
-
props: { browser: {
|
|
2959
|
-
kind: "framework-error",
|
|
2960
|
-
framework: info.framework,
|
|
2961
|
-
componentName: info.componentName,
|
|
2962
|
-
componentStack: info.componentStack,
|
|
2963
|
-
info: (0, _loggerjs_core.normalizeValue)(info.info, {
|
|
2964
|
-
maxDepth: infoMaxDepth,
|
|
2965
|
-
maxObjectKeys: 80
|
|
2966
|
-
}),
|
|
2967
|
-
props: (0, _loggerjs_core.normalizeValue)(info.props, {
|
|
2968
|
-
maxDepth: infoMaxDepth,
|
|
2969
|
-
maxObjectKeys: 80
|
|
2970
|
-
})
|
|
2971
|
-
} }
|
|
2972
|
-
});
|
|
2973
|
-
};
|
|
2974
|
-
const capture = (error, info) => {
|
|
2975
|
-
if (api) {
|
|
2976
|
-
emit(error, info);
|
|
2977
|
-
return;
|
|
2978
|
-
}
|
|
2979
|
-
if (started || disposed || pending.length >= maxPending) return;
|
|
2980
|
-
pending.push({
|
|
2981
|
-
error,
|
|
2982
|
-
info
|
|
2983
|
-
});
|
|
2984
|
-
};
|
|
2985
|
-
return {
|
|
2986
|
-
name: integrationName,
|
|
2987
|
-
setup(nextApi) {
|
|
2988
|
-
api = nextApi;
|
|
2989
|
-
started = true;
|
|
2990
|
-
disposed = false;
|
|
2991
|
-
for (const item of pending.splice(0)) emit(item.error, item.info);
|
|
2992
|
-
return () => {
|
|
2993
|
-
api = void 0;
|
|
2994
|
-
disposed = true;
|
|
2995
|
-
};
|
|
2996
|
-
},
|
|
2997
|
-
capture,
|
|
2998
|
-
reactComponentDidCatch(error, errorInfo) {
|
|
2999
|
-
capture(error, {
|
|
3000
|
-
framework: "react",
|
|
3001
|
-
componentStack: errorInfo?.componentStack
|
|
3002
|
-
});
|
|
3003
|
-
},
|
|
3004
|
-
vueErrorHandler(error, instance, info) {
|
|
3005
|
-
capture(error, {
|
|
3006
|
-
framework: "vue",
|
|
3007
|
-
componentName: instanceComponentName(instance),
|
|
3008
|
-
info
|
|
3009
|
-
});
|
|
3010
|
-
},
|
|
3011
|
-
solidErrorHandler(error) {
|
|
3012
|
-
capture(error, { framework: "solid" });
|
|
3013
|
-
},
|
|
3014
|
-
svelteErrorHandler(error, info) {
|
|
3015
|
-
capture(error, normalizeFrameworkInfo(info, "svelte"));
|
|
3016
|
-
}
|
|
3017
|
-
};
|
|
3018
|
-
}
|
|
3019
|
-
//#endregion
|
|
3020
|
-
//#region src/framework-router-integrations.ts
|
|
3021
|
-
function routePath(input) {
|
|
3022
|
-
if (typeof input === "string") return input;
|
|
3023
|
-
if (input && typeof input === "object") {
|
|
3024
|
-
const route = input;
|
|
3025
|
-
if (typeof route.fullPath === "string") return route.fullPath;
|
|
3026
|
-
if (typeof route.pathname === "string") return route.pathname;
|
|
3027
|
-
if (typeof route.path === "string") return route.path;
|
|
3028
|
-
if (route.location) return routePath(route.location);
|
|
3029
|
-
}
|
|
3030
|
-
return "";
|
|
3031
|
-
}
|
|
3032
|
-
function emitRouteChange(api, framework, level, to, from, options) {
|
|
3033
|
-
const sanitize = options.sanitizeUrl;
|
|
3034
|
-
const toPath = sanitize ? sanitize(routePath(to)) : routePath(to);
|
|
3035
|
-
const fromPath = from === void 0 ? void 0 : sanitize ? sanitize(routePath(from)) : routePath(from);
|
|
3036
|
-
api.capture({
|
|
3037
|
-
level,
|
|
3038
|
-
message: `${framework} route ${toPath}`,
|
|
3039
|
-
props: { browser: {
|
|
3040
|
-
kind: "framework-route-change",
|
|
3041
|
-
framework,
|
|
3042
|
-
route: {
|
|
3043
|
-
to: toPath,
|
|
3044
|
-
from: fromPath,
|
|
3045
|
-
state: options.includeState ? (0, _loggerjs_core.normalizeValue)(to, { maxDepth: 4 }) : void 0
|
|
3046
|
-
}
|
|
3047
|
-
} }
|
|
3048
|
-
});
|
|
3049
|
-
}
|
|
3050
|
-
function nextRouterIntegration(options) {
|
|
3051
|
-
const level = options.level ?? "info";
|
|
3052
|
-
return {
|
|
3053
|
-
name: "next-router",
|
|
3054
|
-
setup(api) {
|
|
3055
|
-
const router = options.router;
|
|
3056
|
-
if (!router.events?.on || !router.events.off) return;
|
|
3057
|
-
const onRoute = api.guard((url) => emitRouteChange(api, "next", level, url, router.asPath, options));
|
|
3058
|
-
router.events.on("routeChangeComplete", onRoute);
|
|
3059
|
-
router.events.on("hashChangeComplete", onRoute);
|
|
3060
|
-
return () => {
|
|
3061
|
-
router.events?.off?.("routeChangeComplete", onRoute);
|
|
3062
|
-
router.events?.off?.("hashChangeComplete", onRoute);
|
|
3063
|
-
};
|
|
3064
|
-
}
|
|
3065
|
-
};
|
|
3066
|
-
}
|
|
3067
|
-
function reactRouterIntegration(options) {
|
|
3068
|
-
const level = options.level ?? "info";
|
|
3069
|
-
return {
|
|
3070
|
-
name: "react-router",
|
|
3071
|
-
setup(api) {
|
|
3072
|
-
let previous = options.history.location;
|
|
3073
|
-
const dispose = options.history.listen?.(api.guard((update) => {
|
|
3074
|
-
const next = update?.location ?? update;
|
|
3075
|
-
emitRouteChange(api, "react-router", level, next, previous, options);
|
|
3076
|
-
previous = next;
|
|
3077
|
-
}));
|
|
3078
|
-
return typeof dispose === "function" ? dispose : void 0;
|
|
3079
|
-
}
|
|
3080
|
-
};
|
|
3081
|
-
}
|
|
3082
|
-
function vueRouterIntegration(options) {
|
|
3083
|
-
const level = options.level ?? "info";
|
|
3084
|
-
const framework = options.framework ?? "vue-router";
|
|
3085
|
-
return {
|
|
3086
|
-
name: framework,
|
|
3087
|
-
setup(api) {
|
|
3088
|
-
const dispose = options.router.afterEach?.(api.guard((to, from) => emitRouteChange(api, framework, level, to, from, options)));
|
|
3089
|
-
return typeof dispose === "function" ? dispose : void 0;
|
|
3090
|
-
}
|
|
3091
|
-
};
|
|
3092
|
-
}
|
|
3093
|
-
function nuxtRouterIntegration(options) {
|
|
3094
|
-
return vueRouterIntegration({
|
|
3095
|
-
...options,
|
|
3096
|
-
framework: "nuxt"
|
|
3097
|
-
});
|
|
3098
|
-
}
|
|
3099
|
-
//#endregion
|
|
3100
|
-
//#region src/reporting-integration.ts
|
|
3101
|
-
const defaultReportTypes = [
|
|
3102
|
-
"csp-violation",
|
|
3103
|
-
"deprecation",
|
|
3104
|
-
"intervention",
|
|
3105
|
-
"crash"
|
|
3106
|
-
];
|
|
3107
|
-
function levelFor$1(level, report) {
|
|
3108
|
-
if (typeof level === "function") return level(report);
|
|
3109
|
-
if (level) return level;
|
|
3110
|
-
return report.type === "deprecation" ? "info" : "warn";
|
|
3111
|
-
}
|
|
3112
|
-
function maybeSanitize(url, sanitizeUrl) {
|
|
3113
|
-
return url && sanitizeUrl ? sanitizeUrl(url) : url;
|
|
3114
|
-
}
|
|
3115
|
-
function normalizeReport(report, sanitizeUrl) {
|
|
3116
|
-
const raw = typeof report.toJSON === "function" ? report.toJSON() : report;
|
|
3117
|
-
const record = raw && typeof raw === "object" ? raw : {};
|
|
3118
|
-
const body = "body" in record ? record.body : report.body;
|
|
3119
|
-
const url = typeof record.url === "string" ? record.url : report.url;
|
|
3120
|
-
return {
|
|
3121
|
-
type: typeof record.type === "string" ? record.type : report.type ?? "unknown",
|
|
3122
|
-
url: maybeSanitize(url, sanitizeUrl),
|
|
3123
|
-
body: (0, _loggerjs_core.normalizeValue)(body, {
|
|
3124
|
-
maxDepth: 6,
|
|
3125
|
-
maxObjectKeys: 80
|
|
3126
|
-
})
|
|
3127
|
-
};
|
|
3128
|
-
}
|
|
3129
|
-
function normalizeCspViolation(event, sanitizeUrl) {
|
|
3130
|
-
return {
|
|
3131
|
-
type: "securitypolicyviolation",
|
|
3132
|
-
blockedURI: maybeSanitize(event.blockedURI, sanitizeUrl),
|
|
3133
|
-
documentURI: maybeSanitize(event.documentURI, sanitizeUrl),
|
|
3134
|
-
effectiveDirective: event.effectiveDirective,
|
|
3135
|
-
violatedDirective: event.violatedDirective,
|
|
3136
|
-
disposition: event.disposition,
|
|
3137
|
-
sourceFile: maybeSanitize(event.sourceFile, sanitizeUrl),
|
|
3138
|
-
lineNumber: event.lineNumber,
|
|
3139
|
-
columnNumber: event.columnNumber,
|
|
3140
|
-
statusCode: event.statusCode,
|
|
3141
|
-
sample: event.sample
|
|
3142
|
-
};
|
|
3143
|
-
}
|
|
3144
|
-
function captureReportingIntegration(options = {}) {
|
|
3145
|
-
const captureSecurityPolicyViolation = options.captureSecurityPolicyViolation ?? true;
|
|
3146
|
-
const captureReportingObserver = options.captureReportingObserver ?? true;
|
|
3147
|
-
const reportTypes = options.reportTypes ?? defaultReportTypes;
|
|
3148
|
-
const buffered = options.buffered ?? true;
|
|
3149
|
-
return {
|
|
3150
|
-
name: "capture-reporting",
|
|
3151
|
-
setup(api) {
|
|
3152
|
-
const add = options.addEventListener ?? globalThis.addEventListener?.bind(globalThis);
|
|
3153
|
-
const remove = options.removeEventListener ?? globalThis.removeEventListener?.bind(globalThis);
|
|
3154
|
-
const Observer = options.ReportingObserver ?? globalThis.ReportingObserver;
|
|
3155
|
-
const disposers = [];
|
|
3156
|
-
let disposed = false;
|
|
3157
|
-
const captureReport = api.guard((report) => {
|
|
3158
|
-
if (disposed) return;
|
|
3159
|
-
api.capture({
|
|
3160
|
-
level: levelFor$1(options.level, report),
|
|
3161
|
-
message: `Browser report ${report.type}`,
|
|
3162
|
-
props: { browser: {
|
|
3163
|
-
kind: "report",
|
|
3164
|
-
report
|
|
3165
|
-
} }
|
|
3166
|
-
});
|
|
3167
|
-
});
|
|
3168
|
-
if (captureSecurityPolicyViolation && add && remove) {
|
|
3169
|
-
const onViolation = api.guard((event) => {
|
|
3170
|
-
const report = normalizeCspViolation(event, options.sanitizeUrl);
|
|
3171
|
-
api.capture({
|
|
3172
|
-
level: levelFor$1(options.level, report),
|
|
3173
|
-
message: `Security policy violation: ${report.effectiveDirective ?? report.violatedDirective ?? "unknown"}`,
|
|
3174
|
-
props: { browser: {
|
|
3175
|
-
kind: "securitypolicyviolation",
|
|
3176
|
-
report
|
|
3177
|
-
} }
|
|
3178
|
-
});
|
|
3179
|
-
});
|
|
3180
|
-
add("securitypolicyviolation", onViolation);
|
|
3181
|
-
disposers.push(() => remove("securitypolicyviolation", onViolation));
|
|
3182
|
-
}
|
|
3183
|
-
if (captureReportingObserver && Observer) try {
|
|
3184
|
-
const observer = new Observer((reports) => {
|
|
3185
|
-
for (const report of reports) captureReport(normalizeReport(report, options.sanitizeUrl));
|
|
3186
|
-
}, {
|
|
3187
|
-
buffered,
|
|
3188
|
-
types: reportTypes
|
|
3189
|
-
});
|
|
3190
|
-
observer.observe();
|
|
3191
|
-
disposers.push(() => {
|
|
3192
|
-
for (const report of observer.takeRecords?.() ?? []) captureReport(normalizeReport(report, options.sanitizeUrl));
|
|
3193
|
-
observer.disconnect();
|
|
3194
|
-
});
|
|
3195
|
-
} catch {}
|
|
3196
|
-
return () => {
|
|
3197
|
-
if (disposed) return;
|
|
3198
|
-
for (const dispose of disposers) dispose();
|
|
3199
|
-
disposed = true;
|
|
3200
|
-
};
|
|
3201
|
-
}
|
|
3202
|
-
};
|
|
3203
|
-
}
|
|
3204
|
-
//#endregion
|
|
3205
|
-
//#region src/router-integration.ts
|
|
3206
|
-
function locationHref(location) {
|
|
3207
|
-
if (!location) return "http://localhost/";
|
|
3208
|
-
if (location.href) return location.href;
|
|
3209
|
-
return `${location.pathname ?? "/"}${location.search ?? ""}${location.hash ?? ""}`;
|
|
3210
|
-
}
|
|
3211
|
-
function normalizeRouteUrl(url, location, mode, sanitizeUrl) {
|
|
3212
|
-
const fallback = locationHref(location);
|
|
3213
|
-
const raw = url === void 0 || url === null ? fallback : String(url);
|
|
3214
|
-
let normalized = raw;
|
|
3215
|
-
try {
|
|
3216
|
-
const parsed = new URL(raw, fallback);
|
|
3217
|
-
normalized = mode === "href" ? parsed.href : `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
3218
|
-
} catch {
|
|
3219
|
-
normalized = raw;
|
|
3220
|
-
}
|
|
3221
|
-
return sanitizeUrl ? sanitizeUrl(normalized) : normalized;
|
|
3222
|
-
}
|
|
3223
|
-
function routeState(includeState, state, maxDepth) {
|
|
3224
|
-
return includeState ? (0, _loggerjs_core.normalizeValue)(state, {
|
|
3225
|
-
maxDepth,
|
|
3226
|
-
maxObjectKeys: 80
|
|
3227
|
-
}) : void 0;
|
|
3228
|
-
}
|
|
3229
|
-
function captureRouterIntegration(options = {}) {
|
|
3230
|
-
const level = options.level ?? "info";
|
|
3231
|
-
const captureInitial = options.captureInitial ?? true;
|
|
3232
|
-
const includeState = options.includeState ?? false;
|
|
3233
|
-
const stateMaxDepth = options.stateMaxDepth ?? 4;
|
|
3234
|
-
const urlMode = options.urlMode ?? "path";
|
|
3235
|
-
return {
|
|
3236
|
-
name: "capture-router",
|
|
3237
|
-
setup(api) {
|
|
3238
|
-
const history = options.history ?? globalThis.history;
|
|
3239
|
-
const location = options.location ?? globalThis.location;
|
|
3240
|
-
const add = options.addEventListener ?? globalThis.addEventListener?.bind(globalThis);
|
|
3241
|
-
const remove = options.removeEventListener ?? globalThis.removeEventListener?.bind(globalThis);
|
|
3242
|
-
const originalPushState = history?.pushState;
|
|
3243
|
-
const originalReplaceState = history?.replaceState;
|
|
3244
|
-
let current = normalizeRouteUrl(void 0, location, urlMode, options.sanitizeUrl);
|
|
3245
|
-
let disposed = false;
|
|
3246
|
-
const capture = (trigger, to, state) => {
|
|
3247
|
-
if (disposed) return;
|
|
3248
|
-
const previous = current;
|
|
3249
|
-
current = to;
|
|
3250
|
-
if (trigger !== "initial" && previous === to) return;
|
|
3251
|
-
const route = {
|
|
3252
|
-
trigger,
|
|
3253
|
-
from: trigger === "initial" ? void 0 : previous,
|
|
3254
|
-
to,
|
|
3255
|
-
state: routeState(includeState, state, stateMaxDepth)
|
|
3256
|
-
};
|
|
3257
|
-
api.capture({
|
|
3258
|
-
level,
|
|
3259
|
-
message: trigger === "initial" ? `Route initial ${to}` : `Route change ${to}`,
|
|
3260
|
-
props: { browser: {
|
|
3261
|
-
kind: "route-change",
|
|
3262
|
-
route
|
|
3263
|
-
} }
|
|
3264
|
-
});
|
|
3265
|
-
};
|
|
3266
|
-
if (captureInitial) capture("initial", current, history?.state);
|
|
3267
|
-
if (history && originalPushState) history.pushState = function pushState(data, unused, url) {
|
|
3268
|
-
const result = originalPushState.call(this, data, unused, url);
|
|
3269
|
-
capture("pushState", normalizeRouteUrl(url, location, urlMode, options.sanitizeUrl), data);
|
|
3270
|
-
return result;
|
|
3271
|
-
};
|
|
3272
|
-
if (history && originalReplaceState) history.replaceState = function replaceState(data, unused, url) {
|
|
3273
|
-
const result = originalReplaceState.call(this, data, unused, url);
|
|
3274
|
-
capture("replaceState", normalizeRouteUrl(url, location, urlMode, options.sanitizeUrl), data);
|
|
3275
|
-
return result;
|
|
3276
|
-
};
|
|
3277
|
-
const onPopState = api.guard((event) => {
|
|
3278
|
-
capture("popstate", normalizeRouteUrl(void 0, location, urlMode, options.sanitizeUrl), event.state);
|
|
3279
|
-
});
|
|
3280
|
-
const onHashChange = api.guard((event) => {
|
|
3281
|
-
capture("hashchange", normalizeRouteUrl(event.newURL, location, urlMode, options.sanitizeUrl), history?.state);
|
|
3282
|
-
});
|
|
3283
|
-
add?.("popstate", onPopState);
|
|
3284
|
-
add?.("hashchange", onHashChange);
|
|
3285
|
-
return () => {
|
|
3286
|
-
if (disposed) return;
|
|
3287
|
-
disposed = true;
|
|
3288
|
-
if (history && originalPushState) history.pushState = originalPushState;
|
|
3289
|
-
if (history && originalReplaceState) history.replaceState = originalReplaceState;
|
|
3290
|
-
remove?.("popstate", onPopState);
|
|
3291
|
-
remove?.("hashchange", onHashChange);
|
|
3292
|
-
};
|
|
3293
|
-
}
|
|
3294
|
-
};
|
|
3295
|
-
}
|
|
3296
|
-
//#endregion
|
|
3297
|
-
//#region src/runtime-host-integration.ts
|
|
3298
|
-
const textEncoder$2 = new TextEncoder();
|
|
3299
|
-
function byteLength(value) {
|
|
3300
|
-
if (typeof value === "string") return textEncoder$2.encode(value).byteLength;
|
|
3301
|
-
if (value instanceof ArrayBuffer) return value.byteLength;
|
|
3302
|
-
if (ArrayBuffer.isView(value)) return value.byteLength;
|
|
3303
|
-
}
|
|
3304
|
-
function valueSummary(value, includeData) {
|
|
3305
|
-
return {
|
|
3306
|
-
dataType: typeof value === "object" && value ? value.constructor?.name ?? "object" : typeof value,
|
|
3307
|
-
byteLength: byteLength(value),
|
|
3308
|
-
data: includeData ? (0, _loggerjs_core.normalizeValue)(value, {
|
|
3309
|
-
maxDepth: 4,
|
|
3310
|
-
maxObjectKeys: 80
|
|
3311
|
-
}) : void 0
|
|
3312
|
-
};
|
|
3313
|
-
}
|
|
3314
|
-
function senderInfo(sender, sanitizeUrl) {
|
|
3315
|
-
if (!sender) return void 0;
|
|
3316
|
-
const sanitize = (url) => url && sanitizeUrl ? sanitizeUrl(url) : url;
|
|
3317
|
-
return {
|
|
3318
|
-
id: sender.id,
|
|
3319
|
-
origin: sender.origin,
|
|
3320
|
-
url: sanitize(sender.url),
|
|
3321
|
-
tab: sender.tab ? {
|
|
3322
|
-
id: sender.tab.id,
|
|
3323
|
-
url: sanitize(sender.tab.url)
|
|
3324
|
-
} : void 0
|
|
3325
|
-
};
|
|
3326
|
-
}
|
|
3327
|
-
function defaultExtensionRuntime() {
|
|
3328
|
-
const global = globalThis;
|
|
3329
|
-
return global.browser?.runtime ?? global.chrome?.runtime;
|
|
3330
|
-
}
|
|
3331
|
-
function defaultIpcRenderer() {
|
|
3332
|
-
const global = globalThis;
|
|
3333
|
-
if (global.electron?.ipcRenderer) return global.electron.ipcRenderer;
|
|
3334
|
-
try {
|
|
3335
|
-
return (global.require?.("electron"))?.ipcRenderer;
|
|
3336
|
-
} catch {
|
|
3337
|
-
return;
|
|
3338
|
-
}
|
|
3339
|
-
}
|
|
3340
|
-
function captureRuntimeHostIntegration(options = {}) {
|
|
3341
|
-
const level = options.level ?? "debug";
|
|
3342
|
-
const captureExtensionMessages = options.captureExtensionMessages ?? true;
|
|
3343
|
-
const captureExtensionInstalled = options.captureExtensionInstalled ?? true;
|
|
3344
|
-
const captureExtensionMessageData = options.captureExtensionMessageData ?? false;
|
|
3345
|
-
const captureElectronMessages = options.captureElectronMessages ?? true;
|
|
3346
|
-
const captureElectronSend = options.captureElectronSend ?? true;
|
|
3347
|
-
const captureElectronInvoke = options.captureElectronInvoke ?? true;
|
|
3348
|
-
const captureElectronMessageData = options.captureElectronMessageData ?? false;
|
|
3349
|
-
const electronChannels = options.electronChannels ?? [];
|
|
3350
|
-
return {
|
|
3351
|
-
name: "capture-runtime-host",
|
|
3352
|
-
setup(api) {
|
|
3353
|
-
const extensionRuntime = options.extensionRuntime ?? defaultExtensionRuntime();
|
|
3354
|
-
const ipcRenderer = options.ipcRenderer ?? defaultIpcRenderer();
|
|
3355
|
-
const disposers = [];
|
|
3356
|
-
let disposed = false;
|
|
3357
|
-
const capture = api.guard((input) => {
|
|
3358
|
-
if (!disposed) api.capture(input);
|
|
3359
|
-
});
|
|
3360
|
-
if (extensionRuntime) {
|
|
3361
|
-
const manifest = extensionRuntime.getManifest?.();
|
|
3362
|
-
if (captureExtensionMessages && extensionRuntime.onMessage?.addListener) {
|
|
3363
|
-
const onMessage = (message, sender) => {
|
|
3364
|
-
capture({
|
|
3365
|
-
level,
|
|
3366
|
-
message: "Browser extension message",
|
|
3367
|
-
props: { browser: {
|
|
3368
|
-
kind: "extension-message",
|
|
3369
|
-
extension: {
|
|
3370
|
-
id: extensionRuntime.id,
|
|
3371
|
-
name: manifest?.name,
|
|
3372
|
-
version: manifest?.version
|
|
3373
|
-
},
|
|
3374
|
-
message: valueSummary(message, captureExtensionMessageData),
|
|
3375
|
-
sender: senderInfo(sender, options.sanitizeUrl)
|
|
3376
|
-
} }
|
|
3377
|
-
});
|
|
3378
|
-
};
|
|
3379
|
-
extensionRuntime.onMessage.addListener(onMessage);
|
|
3380
|
-
disposers.push(() => extensionRuntime.onMessage?.removeListener?.(onMessage));
|
|
3381
|
-
}
|
|
3382
|
-
if (captureExtensionInstalled && extensionRuntime.onInstalled?.addListener) {
|
|
3383
|
-
const onInstalled = (details) => {
|
|
3384
|
-
capture({
|
|
3385
|
-
level,
|
|
3386
|
-
message: "Browser extension installed",
|
|
3387
|
-
props: { browser: {
|
|
3388
|
-
kind: "extension-installed",
|
|
3389
|
-
extension: {
|
|
3390
|
-
id: extensionRuntime.id,
|
|
3391
|
-
name: manifest?.name,
|
|
3392
|
-
version: manifest?.version
|
|
3393
|
-
},
|
|
3394
|
-
details: (0, _loggerjs_core.normalizeValue)(details, {
|
|
3395
|
-
maxDepth: 4,
|
|
3396
|
-
maxObjectKeys: 80
|
|
3397
|
-
})
|
|
3398
|
-
} }
|
|
3399
|
-
});
|
|
3400
|
-
};
|
|
3401
|
-
extensionRuntime.onInstalled.addListener(onInstalled);
|
|
3402
|
-
disposers.push(() => extensionRuntime.onInstalled?.removeListener?.(onInstalled));
|
|
3403
|
-
}
|
|
3404
|
-
}
|
|
3405
|
-
if (ipcRenderer) {
|
|
3406
|
-
if (captureElectronMessages && ipcRenderer.on) for (const channel of electronChannels) {
|
|
3407
|
-
const listener = (_event, ...args) => {
|
|
3408
|
-
capture({
|
|
3409
|
-
level,
|
|
3410
|
-
message: `Electron IPC message ${channel}`,
|
|
3411
|
-
props: { browser: {
|
|
3412
|
-
kind: "electron-ipc",
|
|
3413
|
-
direction: "incoming",
|
|
3414
|
-
channel,
|
|
3415
|
-
args: args.map((arg) => valueSummary(arg, captureElectronMessageData))
|
|
3416
|
-
} }
|
|
3417
|
-
});
|
|
3418
|
-
};
|
|
3419
|
-
ipcRenderer.on(channel, listener);
|
|
3420
|
-
disposers.push(() => {
|
|
3421
|
-
if (ipcRenderer.off) ipcRenderer.off(channel, listener);
|
|
3422
|
-
else ipcRenderer.removeListener?.(channel, listener);
|
|
3423
|
-
});
|
|
3424
|
-
}
|
|
3425
|
-
if (captureElectronSend && ipcRenderer.send) {
|
|
3426
|
-
const originalSend = ipcRenderer.send;
|
|
3427
|
-
ipcRenderer.send = (channel, ...args) => {
|
|
3428
|
-
capture({
|
|
3429
|
-
level,
|
|
3430
|
-
message: `Electron IPC send ${channel}`,
|
|
3431
|
-
props: { browser: {
|
|
3432
|
-
kind: "electron-ipc",
|
|
3433
|
-
direction: "outgoing",
|
|
3434
|
-
channel,
|
|
3435
|
-
args: args.map((arg) => valueSummary(arg, captureElectronMessageData))
|
|
3436
|
-
} }
|
|
3437
|
-
});
|
|
3438
|
-
return originalSend.call(ipcRenderer, channel, ...args);
|
|
3439
|
-
};
|
|
3440
|
-
disposers.push(() => {
|
|
3441
|
-
ipcRenderer.send = originalSend;
|
|
3442
|
-
});
|
|
3443
|
-
}
|
|
3444
|
-
if (captureElectronInvoke && ipcRenderer.invoke) {
|
|
3445
|
-
const originalInvoke = ipcRenderer.invoke;
|
|
3446
|
-
ipcRenderer.invoke = (channel, ...args) => {
|
|
3447
|
-
capture({
|
|
3448
|
-
level,
|
|
3449
|
-
message: `Electron IPC invoke ${channel}`,
|
|
3450
|
-
props: { browser: {
|
|
3451
|
-
kind: "electron-ipc",
|
|
3452
|
-
direction: "invoke",
|
|
3453
|
-
channel,
|
|
3454
|
-
args: args.map((arg) => valueSummary(arg, captureElectronMessageData))
|
|
3455
|
-
} }
|
|
3456
|
-
});
|
|
3457
|
-
return originalInvoke.call(ipcRenderer, channel, ...args);
|
|
3458
|
-
};
|
|
3459
|
-
disposers.push(() => {
|
|
3460
|
-
ipcRenderer.invoke = originalInvoke;
|
|
3461
|
-
});
|
|
3462
|
-
}
|
|
3463
|
-
}
|
|
3464
|
-
return () => {
|
|
3465
|
-
if (disposed) return;
|
|
3466
|
-
disposed = true;
|
|
3467
|
-
for (const dispose of disposers) dispose();
|
|
3468
|
-
};
|
|
3469
|
-
}
|
|
3470
|
-
};
|
|
3471
|
-
}
|
|
3472
|
-
//#endregion
|
|
3473
|
-
//#region src/service-worker-integration.ts
|
|
3474
|
-
const textEncoder$1 = new TextEncoder();
|
|
3475
|
-
function dataType$1(data) {
|
|
3476
|
-
if (typeof data === "string") return "string";
|
|
3477
|
-
if (data instanceof ArrayBuffer) return "arraybuffer";
|
|
3478
|
-
if (ArrayBuffer.isView(data)) return data.constructor.name;
|
|
3479
|
-
return data?.constructor?.name ?? typeof data;
|
|
3480
|
-
}
|
|
3481
|
-
function dataByteLength$1(data) {
|
|
3482
|
-
if (typeof data === "string") return textEncoder$1.encode(data).byteLength;
|
|
3483
|
-
if (data instanceof ArrayBuffer) return data.byteLength;
|
|
3484
|
-
if (ArrayBuffer.isView(data)) return data.byteLength;
|
|
3485
|
-
const size = data?.size;
|
|
3486
|
-
return typeof size === "number" && Number.isFinite(size) ? size : void 0;
|
|
3487
|
-
}
|
|
3488
|
-
function controllerInfo(controller, sanitizeUrl) {
|
|
3489
|
-
if (!controller) return void 0;
|
|
3490
|
-
return {
|
|
3491
|
-
scriptURL: controller.scriptURL && sanitizeUrl ? sanitizeUrl(controller.scriptURL) : controller.scriptURL,
|
|
3492
|
-
state: controller.state
|
|
3493
|
-
};
|
|
3494
|
-
}
|
|
3495
|
-
function messagePayload$1(event, captureMessageData) {
|
|
3496
|
-
return {
|
|
3497
|
-
dataType: dataType$1(event.data),
|
|
3498
|
-
byteLength: dataByteLength$1(event.data),
|
|
3499
|
-
data: captureMessageData ? (0, _loggerjs_core.normalizeValue)(event.data, {
|
|
3500
|
-
maxDepth: 4,
|
|
3501
|
-
maxObjectKeys: 80
|
|
3502
|
-
}) : void 0,
|
|
3503
|
-
origin: event.origin,
|
|
3504
|
-
lastEventId: event.lastEventId
|
|
3505
|
-
};
|
|
3506
|
-
}
|
|
3507
|
-
function captureServiceWorkerIntegration(options = {}) {
|
|
3508
|
-
const level = options.level ?? "debug";
|
|
3509
|
-
const captureControllerChange = options.captureControllerChange ?? true;
|
|
3510
|
-
const captureMessages = options.captureMessages ?? true;
|
|
3511
|
-
const captureMessageErrors = options.captureMessageErrors ?? true;
|
|
3512
|
-
const captureMessageData = options.captureMessageData ?? false;
|
|
3513
|
-
return {
|
|
3514
|
-
name: "capture-service-worker",
|
|
3515
|
-
setup(api) {
|
|
3516
|
-
const serviceWorker = options.serviceWorker ?? (typeof navigator === "undefined" ? void 0 : navigator.serviceWorker);
|
|
3517
|
-
if (!serviceWorker?.addEventListener || !serviceWorker.removeEventListener) return;
|
|
3518
|
-
const disposers = [];
|
|
3519
|
-
let disposed = false;
|
|
3520
|
-
const capture = api.guard((input) => {
|
|
3521
|
-
if (!disposed) api.capture(input);
|
|
3522
|
-
});
|
|
3523
|
-
if (captureControllerChange) {
|
|
3524
|
-
const onControllerChange = () => {
|
|
3525
|
-
capture({
|
|
3526
|
-
level,
|
|
3527
|
-
message: "Service worker controller change",
|
|
3528
|
-
props: { browser: {
|
|
3529
|
-
kind: "service-worker",
|
|
3530
|
-
event: "controllerchange",
|
|
3531
|
-
controller: controllerInfo(serviceWorker.controller, options.sanitizeUrl)
|
|
3532
|
-
} }
|
|
3533
|
-
});
|
|
3534
|
-
};
|
|
3535
|
-
serviceWorker.addEventListener("controllerchange", onControllerChange);
|
|
3536
|
-
disposers.push(() => serviceWorker.removeEventListener("controllerchange", onControllerChange));
|
|
3537
|
-
}
|
|
3538
|
-
if (captureMessages) {
|
|
3539
|
-
const onMessage = (event) => {
|
|
3540
|
-
capture({
|
|
3541
|
-
level,
|
|
3542
|
-
message: "Service worker message",
|
|
3543
|
-
props: { browser: {
|
|
3544
|
-
kind: "service-worker",
|
|
3545
|
-
event: "message",
|
|
3546
|
-
message: messagePayload$1(event, captureMessageData)
|
|
3547
|
-
} }
|
|
3548
|
-
});
|
|
3549
|
-
};
|
|
3550
|
-
serviceWorker.addEventListener("message", onMessage);
|
|
3551
|
-
disposers.push(() => serviceWorker.removeEventListener("message", onMessage));
|
|
3552
|
-
}
|
|
3553
|
-
if (captureMessageErrors) {
|
|
3554
|
-
const onMessageError = (event) => {
|
|
3555
|
-
capture({
|
|
3556
|
-
level: "warn",
|
|
3557
|
-
message: "Service worker message error",
|
|
3558
|
-
props: { browser: {
|
|
3559
|
-
kind: "service-worker",
|
|
3560
|
-
event: "messageerror",
|
|
3561
|
-
type: event.type
|
|
3562
|
-
} }
|
|
3563
|
-
});
|
|
3564
|
-
};
|
|
3565
|
-
serviceWorker.addEventListener("messageerror", onMessageError);
|
|
3566
|
-
disposers.push(() => serviceWorker.removeEventListener("messageerror", onMessageError));
|
|
3567
|
-
}
|
|
3568
|
-
return () => {
|
|
3569
|
-
if (disposed) return;
|
|
3570
|
-
disposed = true;
|
|
3571
|
-
for (const dispose of disposers) dispose();
|
|
3572
|
-
};
|
|
3573
|
-
}
|
|
3574
|
-
};
|
|
3575
|
-
}
|
|
3576
|
-
//#endregion
|
|
3577
|
-
//#region src/user-action-integration.ts
|
|
3578
|
-
const defaultEvents = [
|
|
3579
|
-
"click",
|
|
3580
|
-
"input",
|
|
3581
|
-
"change",
|
|
3582
|
-
"submit"
|
|
3583
|
-
];
|
|
3584
|
-
const defaultLabelAttributes = [
|
|
3585
|
-
"data-loggerjs-label",
|
|
3586
|
-
"aria-label",
|
|
3587
|
-
"name",
|
|
3588
|
-
"id"
|
|
3589
|
-
];
|
|
3590
|
-
function truncate(value, maxLength) {
|
|
3591
|
-
if (!value) return void 0;
|
|
3592
|
-
return value.length > maxLength ? value.slice(0, maxLength) : value;
|
|
3593
|
-
}
|
|
3594
|
-
function readAttribute(target, name) {
|
|
3595
|
-
return target.getAttribute?.(name) || void 0;
|
|
3596
|
-
}
|
|
3597
|
-
function sanitizeField(value, field, sanitize) {
|
|
3598
|
-
if (!value) return void 0;
|
|
3599
|
-
return sanitize ? sanitize(value, field) : value;
|
|
3600
|
-
}
|
|
3601
|
-
function firstLabel(target, attributes) {
|
|
3602
|
-
for (const name of attributes) {
|
|
3603
|
-
const value = readAttribute(target, name) ?? target[name];
|
|
3604
|
-
if (typeof value === "string" && value) return value;
|
|
3605
|
-
}
|
|
3606
|
-
}
|
|
3607
|
-
function targetInfo(target, options) {
|
|
3608
|
-
const element = target;
|
|
3609
|
-
if (!element || typeof element !== "object") return {};
|
|
3610
|
-
const maxTextLength = options.maxTextLength ?? 120;
|
|
3611
|
-
const sanitize = options.sanitize;
|
|
3612
|
-
const info = {
|
|
3613
|
-
tagName: sanitizeField(element.tagName?.toLowerCase(), "tagName", sanitize),
|
|
3614
|
-
id: sanitizeField(element.id, "id", sanitize),
|
|
3615
|
-
name: sanitizeField(element.name, "name", sanitize),
|
|
3616
|
-
role: sanitizeField(element.role ?? readAttribute(element, "role"), "role", sanitize),
|
|
3617
|
-
type: sanitizeField(element.type, "type", sanitize),
|
|
3618
|
-
href: sanitizeField(element.href, "href", sanitize),
|
|
3619
|
-
label: sanitizeField(firstLabel(element, options.labelAttributes ?? defaultLabelAttributes), "label", sanitize)
|
|
3620
|
-
};
|
|
3621
|
-
if (options.captureText) info.text = sanitizeField(truncate(element.textContent ?? void 0, maxTextLength), "text", sanitize);
|
|
3622
|
-
if (options.captureValue) info.value = sanitizeField(truncate(element.value, maxTextLength), "value", sanitize);
|
|
3623
|
-
return info;
|
|
3624
|
-
}
|
|
3625
|
-
function throttleKey(event, target) {
|
|
3626
|
-
return `${event.type}:${target.tagName ?? ""}:${target.id ?? ""}:${target.name ?? ""}:${target.label ?? ""}`;
|
|
3627
|
-
}
|
|
3628
|
-
function captureUserActionsIntegration(options = {}) {
|
|
3629
|
-
const events = options.events ?? defaultEvents;
|
|
3630
|
-
const level = options.level ?? "info";
|
|
3631
|
-
const listenerCapture = options.listenerCapture ?? true;
|
|
3632
|
-
const throttleMs = Math.max(0, Math.floor(options.throttleMs ?? 250));
|
|
3633
|
-
const clock = options.clock ?? Date.now;
|
|
3634
|
-
return {
|
|
3635
|
-
name: "capture-user-actions",
|
|
3636
|
-
setup(api) {
|
|
3637
|
-
const root = options.root ?? globalThis.document;
|
|
3638
|
-
if (!root?.addEventListener || !root.removeEventListener) return;
|
|
3639
|
-
const lastSeen = /* @__PURE__ */ new Map();
|
|
3640
|
-
let disposed = false;
|
|
3641
|
-
const onAction = api.guard((event) => {
|
|
3642
|
-
if (disposed) return;
|
|
3643
|
-
const target = targetInfo(event.target, options);
|
|
3644
|
-
if (options.ignore?.(event, target)) return;
|
|
3645
|
-
const now = clock();
|
|
3646
|
-
const key = throttleKey(event, target);
|
|
3647
|
-
const last = lastSeen.get(key) ?? 0;
|
|
3648
|
-
if (throttleMs > 0 && now - last < throttleMs) return;
|
|
3649
|
-
lastSeen.set(key, now);
|
|
3650
|
-
const action = {
|
|
3651
|
-
type: event.type,
|
|
3652
|
-
target
|
|
3653
|
-
};
|
|
3654
|
-
api.capture({
|
|
3655
|
-
level,
|
|
3656
|
-
message: `User action ${event.type}${target.label ? ` ${target.label}` : ""}`,
|
|
3657
|
-
props: { browser: {
|
|
3658
|
-
kind: "user-action",
|
|
3659
|
-
action
|
|
3660
|
-
} }
|
|
3661
|
-
});
|
|
3662
|
-
});
|
|
3663
|
-
for (const event of events) root.addEventListener(event, onAction, listenerCapture);
|
|
3664
|
-
return () => {
|
|
3665
|
-
if (disposed) return;
|
|
3666
|
-
disposed = true;
|
|
3667
|
-
for (const event of events) root.removeEventListener(event, onAction, listenerCapture);
|
|
3668
|
-
};
|
|
3669
|
-
}
|
|
3670
|
-
};
|
|
3671
|
-
}
|
|
3672
|
-
//#endregion
|
|
3673
|
-
//#region src/websocket-integration.ts
|
|
3674
|
-
const registryKey = "browser.WebSocket";
|
|
3675
|
-
const textEncoder = new TextEncoder();
|
|
3676
|
-
function dataType(data) {
|
|
3677
|
-
if (typeof data === "string") return "string";
|
|
3678
|
-
if (data instanceof ArrayBuffer) return "arraybuffer";
|
|
3679
|
-
if (ArrayBuffer.isView(data)) return data.constructor.name;
|
|
3680
|
-
return data?.constructor?.name ?? typeof data;
|
|
3681
|
-
}
|
|
3682
|
-
function dataByteLength(data) {
|
|
3683
|
-
if (typeof data === "string") return textEncoder.encode(data).byteLength;
|
|
3684
|
-
if (data instanceof ArrayBuffer) return data.byteLength;
|
|
3685
|
-
if (ArrayBuffer.isView(data)) return data.byteLength;
|
|
3686
|
-
const size = data?.size;
|
|
3687
|
-
return typeof size === "number" && Number.isFinite(size) ? size : void 0;
|
|
3688
|
-
}
|
|
3689
|
-
function messagePayload(direction, data, captureMessageData) {
|
|
3690
|
-
return {
|
|
3691
|
-
direction,
|
|
3692
|
-
dataType: dataType(data),
|
|
3693
|
-
byteLength: dataByteLength(data),
|
|
3694
|
-
data: captureMessageData ? (0, _loggerjs_core.normalizeValue)(data, {
|
|
3695
|
-
maxDepth: 4,
|
|
3696
|
-
maxObjectKeys: 80
|
|
3697
|
-
}) : void 0
|
|
3698
|
-
};
|
|
3699
|
-
}
|
|
3700
|
-
function closeEventPayload(event) {
|
|
3701
|
-
const close = event;
|
|
3702
|
-
return {
|
|
3703
|
-
code: close.code,
|
|
3704
|
-
reason: close.reason,
|
|
3705
|
-
wasClean: close.wasClean
|
|
3706
|
-
};
|
|
3707
|
-
}
|
|
3708
|
-
function copyStaticConstants(target, source) {
|
|
3709
|
-
for (const key of [
|
|
3710
|
-
"CONNECTING",
|
|
3711
|
-
"OPEN",
|
|
3712
|
-
"CLOSING",
|
|
3713
|
-
"CLOSED"
|
|
3714
|
-
]) if (source[key] !== void 0) target[key] = source[key];
|
|
3715
|
-
}
|
|
3716
|
-
function captureWebSocketIntegration(options = {}) {
|
|
3717
|
-
const level = options.level ?? "debug";
|
|
3718
|
-
const captureConnect = options.captureConnect ?? true;
|
|
3719
|
-
const captureOpen = options.captureOpen ?? true;
|
|
3720
|
-
const captureClose = options.captureClose ?? true;
|
|
3721
|
-
const captureError = options.captureError ?? true;
|
|
3722
|
-
const captureMessages = options.captureMessages ?? false;
|
|
3723
|
-
const captureSentMessages = options.captureSentMessages ?? false;
|
|
3724
|
-
const captureMessageData = options.captureMessageData ?? false;
|
|
3725
|
-
const sampleRate = options.sampleRate ?? 1;
|
|
3726
|
-
const random = options.random ?? Math.random;
|
|
3727
|
-
return {
|
|
3728
|
-
name: "capture-websocket",
|
|
3729
|
-
setup(api) {
|
|
3730
|
-
const globalWebSocket = globalThis.WebSocket;
|
|
3731
|
-
const current = options.WebSocket ?? globalWebSocket;
|
|
3732
|
-
if (!current) return;
|
|
3733
|
-
const original = api.unpatched.get(registryKey) ?? current;
|
|
3734
|
-
api.unpatched.set(registryKey, original);
|
|
3735
|
-
const capture = api.guard((input) => api.capture(input));
|
|
3736
|
-
const PatchedWebSocket = function WebSocket(url, protocols) {
|
|
3737
|
-
const started = nowMs();
|
|
3738
|
-
const sanitizedUrl = sanitizeHttpUrl(String(url), options.sanitizeUrl);
|
|
3739
|
-
const socket = new original(url, protocols);
|
|
3740
|
-
const captureLifecycle = (event, extra = {}, error) => {
|
|
3741
|
-
if (!shouldSample(sampleRate, random)) return;
|
|
3742
|
-
capture({
|
|
3743
|
-
level: event === "error" ? "error" : level,
|
|
3744
|
-
message: `WebSocket ${event} ${sanitizedUrl}`,
|
|
3745
|
-
error,
|
|
3746
|
-
props: { websocket: {
|
|
3747
|
-
kind: "websocket",
|
|
3748
|
-
event,
|
|
3749
|
-
url: sanitizedUrl,
|
|
3750
|
-
durationMs: durationMs(started),
|
|
3751
|
-
...extra
|
|
3752
|
-
} }
|
|
3753
|
-
});
|
|
3754
|
-
};
|
|
3755
|
-
if (captureConnect) captureLifecycle("connect");
|
|
3756
|
-
if (captureOpen) socket.addEventListener?.("open", () => captureLifecycle("open"));
|
|
3757
|
-
if (captureClose) socket.addEventListener?.("close", (event) => captureLifecycle("close", closeEventPayload(event)));
|
|
3758
|
-
if (captureError) socket.addEventListener?.("error", (event) => captureLifecycle("error", { type: event.type }, event));
|
|
3759
|
-
if (captureMessages) socket.addEventListener?.("message", (event) => {
|
|
3760
|
-
const message = event;
|
|
3761
|
-
capture({
|
|
3762
|
-
level,
|
|
3763
|
-
message: `WebSocket message ${sanitizedUrl}`,
|
|
3764
|
-
props: { websocket: {
|
|
3765
|
-
kind: "websocket-message",
|
|
3766
|
-
url: sanitizedUrl,
|
|
3767
|
-
message: messagePayload("incoming", message.data, captureMessageData)
|
|
3768
|
-
} }
|
|
3769
|
-
});
|
|
3770
|
-
});
|
|
3771
|
-
if (captureSentMessages && socket.send) {
|
|
3772
|
-
const originalSend = socket.send.bind(socket);
|
|
3773
|
-
socket.send = (data) => {
|
|
3774
|
-
capture({
|
|
3775
|
-
level,
|
|
3776
|
-
message: `WebSocket send ${sanitizedUrl}`,
|
|
3777
|
-
props: { websocket: {
|
|
3778
|
-
kind: "websocket-message",
|
|
3779
|
-
url: sanitizedUrl,
|
|
3780
|
-
message: messagePayload("outgoing", data, captureMessageData)
|
|
3781
|
-
} }
|
|
3782
|
-
});
|
|
3783
|
-
return originalSend(data);
|
|
3784
|
-
};
|
|
3785
|
-
}
|
|
3786
|
-
return socket;
|
|
3787
|
-
};
|
|
3788
|
-
PatchedWebSocket.prototype = original.prototype;
|
|
3789
|
-
copyStaticConstants(PatchedWebSocket, original);
|
|
3790
|
-
globalThis.WebSocket = PatchedWebSocket;
|
|
3791
|
-
return () => {
|
|
3792
|
-
globalThis.WebSocket = current;
|
|
3793
|
-
};
|
|
3794
|
-
}
|
|
3795
|
-
};
|
|
3796
|
-
}
|
|
3797
|
-
//#endregion
|
|
3798
|
-
//#region src/web-vitals-integration.ts
|
|
3799
|
-
const defaultMetrics = [
|
|
3800
|
-
"CLS",
|
|
3801
|
-
"FCP",
|
|
3802
|
-
"INP",
|
|
3803
|
-
"LCP",
|
|
3804
|
-
"TTFB"
|
|
3805
|
-
];
|
|
3806
|
-
const thresholds = {
|
|
3807
|
-
CLS: [.1, .25],
|
|
3808
|
-
FCP: [1800, 3e3],
|
|
3809
|
-
INP: [200, 500],
|
|
3810
|
-
LCP: [2500, 4e3],
|
|
3811
|
-
TTFB: [800, 1800]
|
|
3812
|
-
};
|
|
3813
|
-
function ratingFor(name, value) {
|
|
3814
|
-
const [good, needsImprovement] = thresholds[name];
|
|
3815
|
-
if (value <= good) return "good";
|
|
3816
|
-
if (value <= needsImprovement) return "needs-improvement";
|
|
3817
|
-
return "poor";
|
|
3818
|
-
}
|
|
3819
|
-
function metricId(name) {
|
|
3820
|
-
return `${name.toLowerCase()}-${Date.now().toString(36)}`;
|
|
3821
|
-
}
|
|
3822
|
-
function entryValue(entry, field) {
|
|
3823
|
-
const value = entry[field];
|
|
3824
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3825
|
-
}
|
|
3826
|
-
function hasMetric(metrics, name) {
|
|
3827
|
-
return metrics.includes(name);
|
|
3828
|
-
}
|
|
3829
|
-
function supportsEntryType$1(Observer, type) {
|
|
3830
|
-
const supported = Observer?.supportedEntryTypes;
|
|
3831
|
-
return !supported || supported.includes(type);
|
|
3832
|
-
}
|
|
3833
|
-
function captureWebVitalsIntegration(options = {}) {
|
|
3834
|
-
const metrics = options.metrics ?? defaultMetrics;
|
|
3835
|
-
const level = options.level ?? "info";
|
|
3836
|
-
const reportAllChanges = options.reportAllChanges ?? false;
|
|
3837
|
-
const flushOnHidden = options.flushOnHidden ?? true;
|
|
3838
|
-
return {
|
|
3839
|
-
name: "capture-web-vitals",
|
|
3840
|
-
setup(api) {
|
|
3841
|
-
const Observer = options.PerformanceObserver ?? globalThis.PerformanceObserver;
|
|
3842
|
-
const perf = options.performance ?? globalThis.performance;
|
|
3843
|
-
const add = options.addEventListener ?? globalThis.addEventListener?.bind(globalThis);
|
|
3844
|
-
const remove = options.removeEventListener ?? globalThis.removeEventListener?.bind(globalThis);
|
|
3845
|
-
const disposers = [];
|
|
3846
|
-
const currentValues = /* @__PURE__ */ new Map();
|
|
3847
|
-
const reportedValues = /* @__PURE__ */ new Map();
|
|
3848
|
-
const emitted = /* @__PURE__ */ new Set();
|
|
3849
|
-
let disposed = false;
|
|
3850
|
-
const emit = (name, value, final) => {
|
|
3851
|
-
if (!Number.isFinite(value)) return;
|
|
3852
|
-
const finalKey = `${name}:final`;
|
|
3853
|
-
if (final && emitted.has(finalKey)) return;
|
|
3854
|
-
const previous = reportedValues.get(name) ?? 0;
|
|
3855
|
-
currentValues.set(name, value);
|
|
3856
|
-
reportedValues.set(name, value);
|
|
3857
|
-
if (final) emitted.add(finalKey);
|
|
3858
|
-
const metric = {
|
|
3859
|
-
name,
|
|
3860
|
-
value,
|
|
3861
|
-
delta: value - previous,
|
|
3862
|
-
rating: ratingFor(name, value),
|
|
3863
|
-
id: metricId(name),
|
|
3864
|
-
final
|
|
3865
|
-
};
|
|
3866
|
-
api.capture({
|
|
3867
|
-
level,
|
|
3868
|
-
message: `Web vital ${metric.name} ${metric.value}`,
|
|
3869
|
-
props: { webVital: metric }
|
|
3870
|
-
});
|
|
3871
|
-
};
|
|
3872
|
-
const observe = (type, callback, init = {
|
|
3873
|
-
type,
|
|
3874
|
-
buffered: true
|
|
3875
|
-
}) => {
|
|
3876
|
-
if (!Observer || !supportsEntryType$1(Observer, type)) return;
|
|
3877
|
-
try {
|
|
3878
|
-
const observer = new Observer((list) => {
|
|
3879
|
-
for (const entry of list.getEntries()) callback(entry);
|
|
3880
|
-
});
|
|
3881
|
-
observer.observe(init);
|
|
3882
|
-
disposers.push(() => observer.disconnect());
|
|
3883
|
-
} catch {}
|
|
3884
|
-
};
|
|
3885
|
-
if (hasMetric(metrics, "TTFB") && perf) {
|
|
3886
|
-
const navigation = perf.getEntriesByType("navigation")[0];
|
|
3887
|
-
if (navigation) {
|
|
3888
|
-
const responseStart = entryValue(navigation, "responseStart");
|
|
3889
|
-
const startTime = entryValue(navigation, "startTime") ?? 0;
|
|
3890
|
-
if (responseStart !== void 0) emit("TTFB", responseStart - startTime, true);
|
|
3891
|
-
}
|
|
3892
|
-
}
|
|
3893
|
-
if (hasMetric(metrics, "FCP") && perf) {
|
|
3894
|
-
const existing = perf.getEntriesByName("first-contentful-paint")[0];
|
|
3895
|
-
if (existing) emit("FCP", existing.startTime, true);
|
|
3896
|
-
observe("paint", (entry) => {
|
|
3897
|
-
if (entry.name === "first-contentful-paint") emit("FCP", entry.startTime, true);
|
|
3898
|
-
});
|
|
3899
|
-
}
|
|
3900
|
-
if (hasMetric(metrics, "CLS")) observe("layout-shift", (entry) => {
|
|
3901
|
-
if (entry.hadRecentInput === true) return;
|
|
3902
|
-
const value = entryValue(entry, "value") ?? 0;
|
|
3903
|
-
const next = (currentValues.get("CLS") ?? 0) + value;
|
|
3904
|
-
if (reportAllChanges) emit("CLS", next, false);
|
|
3905
|
-
else currentValues.set("CLS", next);
|
|
3906
|
-
});
|
|
3907
|
-
if (hasMetric(metrics, "LCP")) observe("largest-contentful-paint", (entry) => {
|
|
3908
|
-
currentValues.set("LCP", entry.startTime);
|
|
3909
|
-
if (reportAllChanges) emit("LCP", entry.startTime, false);
|
|
3910
|
-
});
|
|
3911
|
-
if (hasMetric(metrics, "INP")) observe("event", (entry) => {
|
|
3912
|
-
const duration = entryValue(entry, "duration") ?? 0;
|
|
3913
|
-
if (duration <= (currentValues.get("INP") ?? 0)) return;
|
|
3914
|
-
currentValues.set("INP", duration);
|
|
3915
|
-
if (reportAllChanges) emit("INP", duration, false);
|
|
3916
|
-
}, {
|
|
3917
|
-
type: "event",
|
|
3918
|
-
buffered: true,
|
|
3919
|
-
durationThreshold: 40
|
|
3920
|
-
});
|
|
3921
|
-
const flushFinal = api.guard(() => {
|
|
3922
|
-
const cls = currentValues.get("CLS");
|
|
3923
|
-
if (cls !== void 0 && cls > 0) emit("CLS", cls, true);
|
|
3924
|
-
const lcp = currentValues.get("LCP");
|
|
3925
|
-
if (lcp !== void 0) emit("LCP", lcp, true);
|
|
3926
|
-
const inp = currentValues.get("INP");
|
|
3927
|
-
if (inp !== void 0) emit("INP", inp, true);
|
|
3928
|
-
});
|
|
3929
|
-
if (flushOnHidden && add && remove) {
|
|
3930
|
-
const onPageHide = () => flushFinal();
|
|
3931
|
-
const onVisibilityChange = () => {
|
|
3932
|
-
if (typeof document !== "undefined" && document.visibilityState === "hidden") flushFinal();
|
|
3933
|
-
};
|
|
3934
|
-
add("pagehide", onPageHide);
|
|
3935
|
-
add("visibilitychange", onVisibilityChange);
|
|
3936
|
-
disposers.push(() => {
|
|
3937
|
-
remove("pagehide", onPageHide);
|
|
3938
|
-
remove("visibilitychange", onVisibilityChange);
|
|
3939
|
-
});
|
|
3940
|
-
}
|
|
3941
|
-
return () => {
|
|
3942
|
-
if (disposed) return;
|
|
3943
|
-
disposed = true;
|
|
3944
|
-
flushFinal();
|
|
3945
|
-
for (const dispose of disposers) dispose();
|
|
3946
|
-
};
|
|
3947
|
-
}
|
|
3948
|
-
};
|
|
3949
|
-
}
|
|
3950
|
-
//#endregion
|
|
3951
|
-
//#region src/performance-integration.ts
|
|
3952
|
-
const defaultEntryTypes = [
|
|
3953
|
-
"navigation",
|
|
3954
|
-
"resource",
|
|
3955
|
-
"longtask",
|
|
3956
|
-
"measure",
|
|
3957
|
-
"mark"
|
|
3958
|
-
];
|
|
3959
|
-
const numberFields = [
|
|
3960
|
-
"transferSize",
|
|
3961
|
-
"encodedBodySize",
|
|
3962
|
-
"decodedBodySize",
|
|
3963
|
-
"workerStart",
|
|
3964
|
-
"redirectStart",
|
|
3965
|
-
"redirectEnd",
|
|
3966
|
-
"fetchStart",
|
|
3967
|
-
"domainLookupStart",
|
|
3968
|
-
"domainLookupEnd",
|
|
3969
|
-
"connectStart",
|
|
3970
|
-
"connectEnd",
|
|
3971
|
-
"requestStart",
|
|
3972
|
-
"responseStart",
|
|
3973
|
-
"responseEnd"
|
|
3974
|
-
];
|
|
3975
|
-
function supportsEntryType(Observer, type) {
|
|
3976
|
-
const supported = Observer?.supportedEntryTypes;
|
|
3977
|
-
return !supported || supported.includes(type);
|
|
3978
|
-
}
|
|
3979
|
-
function finiteNumber(value) {
|
|
3980
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3981
|
-
}
|
|
3982
|
-
function stringValue(value) {
|
|
3983
|
-
return typeof value === "string" && value ? value : void 0;
|
|
3984
|
-
}
|
|
3985
|
-
function normalizeBrowserPerformanceEntry(entry, options = {}) {
|
|
3986
|
-
const raw = entry;
|
|
3987
|
-
const payload = {
|
|
3988
|
-
name: options.sanitizeName ? options.sanitizeName(entry.name, entry.entryType) : entry.name,
|
|
3989
|
-
entryType: entry.entryType,
|
|
3990
|
-
startTime: entry.startTime,
|
|
3991
|
-
duration: entry.duration
|
|
3992
|
-
};
|
|
3993
|
-
for (const field of numberFields) {
|
|
3994
|
-
const value = finiteNumber(raw[field]);
|
|
3995
|
-
if (value !== void 0) payload[field] = value;
|
|
3996
|
-
}
|
|
3997
|
-
const responseStatus = finiteNumber(raw.responseStatus);
|
|
3998
|
-
if (responseStatus !== void 0) payload.responseStatus = responseStatus;
|
|
3999
|
-
const initiatorType = stringValue(raw.initiatorType);
|
|
4000
|
-
if (initiatorType) payload.initiatorType = initiatorType;
|
|
4001
|
-
const nextHopProtocol = stringValue(raw.nextHopProtocol);
|
|
4002
|
-
if (nextHopProtocol) payload.nextHopProtocol = nextHopProtocol;
|
|
4003
|
-
const renderBlockingStatus = stringValue(raw.renderBlockingStatus);
|
|
4004
|
-
if (renderBlockingStatus) payload.renderBlockingStatus = renderBlockingStatus;
|
|
4005
|
-
if (options.captureDetail && raw.detail !== void 0) payload.detail = raw.detail;
|
|
4006
|
-
return payload;
|
|
4007
|
-
}
|
|
4008
|
-
function minDurationFor(value, entryType) {
|
|
4009
|
-
if (typeof value === "number") return value;
|
|
4010
|
-
return value?.[entryType] ?? 0;
|
|
4011
|
-
}
|
|
4012
|
-
function levelFor(value, entry) {
|
|
4013
|
-
if (typeof value === "function") return value(entry);
|
|
4014
|
-
if (value) return value;
|
|
4015
|
-
return entry.entryType === "longtask" ? "warn" : "info";
|
|
4016
|
-
}
|
|
4017
|
-
function entryKey(entry) {
|
|
4018
|
-
return `${entry.entryType}:${entry.name}:${entry.startTime}:${entry.duration}`;
|
|
4019
|
-
}
|
|
4020
|
-
function capturePerformanceIntegration(options = {}) {
|
|
4021
|
-
const entryTypes = options.entryTypes ?? defaultEntryTypes;
|
|
4022
|
-
const buffered = options.buffered ?? true;
|
|
4023
|
-
const emitExisting = options.emitExisting ?? true;
|
|
4024
|
-
const maxEntries = Math.max(0, Math.floor(options.maxEntries ?? 200));
|
|
4025
|
-
const sampleRate = options.sampleRate ?? 1;
|
|
4026
|
-
const random = options.random ?? Math.random;
|
|
4027
|
-
return {
|
|
4028
|
-
name: "capture-performance",
|
|
4029
|
-
setup(api) {
|
|
4030
|
-
const Observer = options.PerformanceObserver ?? globalThis.PerformanceObserver;
|
|
4031
|
-
const perf = options.performance ?? globalThis.performance;
|
|
4032
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4033
|
-
const disposers = [];
|
|
4034
|
-
let emitted = 0;
|
|
4035
|
-
let disposed = false;
|
|
4036
|
-
const capture = api.guard((entry) => {
|
|
4037
|
-
if (disposed || emitted >= maxEntries) return;
|
|
4038
|
-
const payload = normalizeBrowserPerformanceEntry(entry, options);
|
|
4039
|
-
if (payload.duration < minDurationFor(options.minDurationMs, payload.entryType)) return;
|
|
4040
|
-
if (sampleRate < 1 && random() >= sampleRate) return;
|
|
4041
|
-
if (options.ignore?.(payload)) return;
|
|
4042
|
-
const key = entryKey(payload);
|
|
4043
|
-
if (seen.has(key)) return;
|
|
4044
|
-
seen.add(key);
|
|
4045
|
-
emitted += 1;
|
|
4046
|
-
api.capture({
|
|
4047
|
-
level: levelFor(options.level, payload),
|
|
4048
|
-
message: `Performance ${payload.entryType} ${payload.name}`,
|
|
4049
|
-
props: { performance: payload }
|
|
4050
|
-
});
|
|
4051
|
-
});
|
|
4052
|
-
const emitExistingEntries = (type) => {
|
|
4053
|
-
if (!emitExisting || !perf) return;
|
|
4054
|
-
try {
|
|
4055
|
-
for (const entry of perf.getEntriesByType(type)) capture(entry);
|
|
4056
|
-
} catch {}
|
|
4057
|
-
};
|
|
4058
|
-
for (const type of entryTypes) {
|
|
4059
|
-
emitExistingEntries(type);
|
|
4060
|
-
if (!Observer || !supportsEntryType(Observer, type)) continue;
|
|
4061
|
-
try {
|
|
4062
|
-
const observer = new Observer((list) => {
|
|
4063
|
-
for (const entry of list.getEntries()) capture(entry);
|
|
4064
|
-
});
|
|
4065
|
-
observer.observe({
|
|
4066
|
-
type,
|
|
4067
|
-
buffered
|
|
4068
|
-
});
|
|
4069
|
-
disposers.push(() => observer.disconnect());
|
|
4070
|
-
} catch {}
|
|
4071
|
-
}
|
|
4072
|
-
return () => {
|
|
4073
|
-
if (disposed) return;
|
|
4074
|
-
disposed = true;
|
|
4075
|
-
for (const dispose of disposers) dispose();
|
|
4076
|
-
};
|
|
4077
|
-
}
|
|
4078
|
-
};
|
|
4079
|
-
}
|
|
4080
|
-
//#endregion
|
|
4081
|
-
//#region src/page-lifecycle.ts
|
|
4082
|
-
function pageLifecycleIntegration(options = {}) {
|
|
4083
|
-
const flushOnPageHide = options.flushOnPageHide ?? true;
|
|
4084
|
-
const flushOnHidden = options.flushOnHidden ?? true;
|
|
4085
|
-
const coalesceMs = options.coalesceMs ?? 250;
|
|
4086
|
-
return {
|
|
4087
|
-
name: "page-lifecycle",
|
|
4088
|
-
setup(api) {
|
|
4089
|
-
if (typeof addEventListener === "undefined") return;
|
|
4090
|
-
let disposed = false;
|
|
4091
|
-
let flushInFlight;
|
|
4092
|
-
let lastFlushAt = 0;
|
|
4093
|
-
const flush = () => {
|
|
4094
|
-
const now = Date.now();
|
|
4095
|
-
if (flushInFlight || now - lastFlushAt < coalesceMs) return;
|
|
4096
|
-
lastFlushAt = now;
|
|
4097
|
-
flushInFlight = Promise.resolve(api.flush()).catch(() => {}).finally(() => {
|
|
4098
|
-
flushInFlight = void 0;
|
|
4099
|
-
});
|
|
4100
|
-
};
|
|
4101
|
-
const onVisibility = () => {
|
|
4102
|
-
if (typeof document !== "undefined" && document.visibilityState === "hidden") flush();
|
|
4103
|
-
};
|
|
4104
|
-
if (flushOnPageHide) addEventListener("pagehide", flush);
|
|
4105
|
-
if (flushOnHidden) addEventListener("visibilitychange", onVisibility);
|
|
4106
|
-
return () => {
|
|
4107
|
-
if (disposed) return;
|
|
4108
|
-
disposed = true;
|
|
4109
|
-
if (flushOnPageHide) removeEventListener("pagehide", flush);
|
|
4110
|
-
if (flushOnHidden) removeEventListener("visibilitychange", onVisibility);
|
|
4111
|
-
};
|
|
4112
|
-
}
|
|
4113
|
-
};
|
|
4114
|
-
}
|
|
4115
|
-
//#endregion
|
|
4116
|
-
exports.browserBroadcastChannelTransport = browserBroadcastChannelTransport;
|
|
4117
|
-
exports.browserCompressionPayloadTransform = browserCompressionPayloadTransform;
|
|
4118
|
-
exports.browserContextPropagationIntegration = browserContextPropagationIntegration;
|
|
4119
|
-
exports.browserHttpTransport = browserHttpTransport;
|
|
4120
|
-
exports.browserServiceWorkerTransport = browserServiceWorkerTransport;
|
|
4121
|
-
exports.browserWebSocketTransport = browserWebSocketTransport;
|
|
4122
|
-
exports.captureBrowserErrorsIntegration = captureBrowserErrorsIntegration;
|
|
4123
|
-
exports.captureConsoleIntegration = captureConsoleIntegration;
|
|
4124
|
-
exports.captureFetchIntegration = captureFetchIntegration;
|
|
4125
|
-
exports.captureFrameworkErrorsIntegration = captureFrameworkErrorsIntegration;
|
|
4126
|
-
exports.capturePerformanceIntegration = capturePerformanceIntegration;
|
|
4127
|
-
exports.captureReportingIntegration = captureReportingIntegration;
|
|
4128
|
-
exports.captureRouterIntegration = captureRouterIntegration;
|
|
4129
|
-
exports.captureRuntimeHostIntegration = captureRuntimeHostIntegration;
|
|
4130
|
-
exports.captureServiceWorkerIntegration = captureServiceWorkerIntegration;
|
|
4131
|
-
exports.captureUserActionsIntegration = captureUserActionsIntegration;
|
|
4132
|
-
exports.captureWebSocketIntegration = captureWebSocketIntegration;
|
|
4133
|
-
exports.captureWebVitalsIntegration = captureWebVitalsIntegration;
|
|
4134
|
-
exports.captureXHRIntegration = captureXHRIntegration;
|
|
4135
|
-
exports.createLogZipBlob = createLogZipBlob;
|
|
4136
|
-
exports.downloadBlob = downloadBlob;
|
|
4137
|
-
exports.exportLogsToZip = exportLogsToZip;
|
|
4138
|
-
exports.indexedDbBrowserHttpOfflineQueue = indexedDbBrowserHttpOfflineQueue;
|
|
4139
|
-
exports.indexedDbTransport = indexedDbTransport;
|
|
4140
|
-
exports.memoryBrowserHttpOfflineQueue = memoryBrowserHttpOfflineQueue;
|
|
4141
|
-
exports.nextRouterIntegration = nextRouterIntegration;
|
|
4142
|
-
exports.normalizeBrowserPerformanceEntry = normalizeBrowserPerformanceEntry;
|
|
4143
|
-
exports.nuxtRouterIntegration = nuxtRouterIntegration;
|
|
4144
|
-
exports.offlineFirstTransport = offlineFirstTransport;
|
|
4145
|
-
exports.pageLifecycleIntegration = pageLifecycleIntegration;
|
|
4146
|
-
exports.reactRouterIntegration = reactRouterIntegration;
|
|
4147
|
-
exports.vueRouterIntegration = vueRouterIntegration;
|
|
2
|
+
const require_broadcast_channel_transport = require("./broadcast-channel-transport.cjs");
|
|
3
|
+
const require_http_transport = require("./http-transport.cjs");
|
|
4
|
+
const require_payload_transforms = require("./payload-transforms.cjs");
|
|
5
|
+
const require_service_worker_transport = require("./service-worker-transport.cjs");
|
|
6
|
+
const require_websocket_transport = require("./websocket-transport.cjs");
|
|
7
|
+
const require_indexeddb_offline_queue = require("./indexeddb-offline-queue.cjs");
|
|
8
|
+
const require_indexeddb_transport = require("./indexeddb-transport.cjs");
|
|
9
|
+
const require_offline_first_transport = require("./offline-first-transport.cjs");
|
|
10
|
+
const require_zip_export = require("./zip-export.cjs");
|
|
11
|
+
const require_console_integration = require("./console-integration.cjs");
|
|
12
|
+
const require_context_propagation_integration = require("./context-propagation-integration.cjs");
|
|
13
|
+
const require_error_integration = require("./error-integration.cjs");
|
|
14
|
+
const require_fetch_integration = require("./fetch-integration.cjs");
|
|
15
|
+
const require_xhr_integration = require("./xhr-integration.cjs");
|
|
16
|
+
const require_framework_error_integration = require("./framework-error-integration.cjs");
|
|
17
|
+
const require_framework_router_integrations = require("./framework-router-integrations.cjs");
|
|
18
|
+
const require_reporting_integration = require("./reporting-integration.cjs");
|
|
19
|
+
const require_router_integration = require("./router-integration.cjs");
|
|
20
|
+
const require_runtime_host_integration = require("./runtime-host-integration.cjs");
|
|
21
|
+
const require_service_worker_integration = require("./service-worker-integration.cjs");
|
|
22
|
+
const require_user_action_integration = require("./user-action-integration.cjs");
|
|
23
|
+
const require_websocket_integration = require("./websocket-integration.cjs");
|
|
24
|
+
const require_web_vitals_integration = require("./web-vitals-integration.cjs");
|
|
25
|
+
const require_performance_integration = require("./performance-integration.cjs");
|
|
26
|
+
const require_page_lifecycle = require("./page-lifecycle.cjs");
|
|
27
|
+
exports.browserBroadcastChannelTransport = require_broadcast_channel_transport.browserBroadcastChannelTransport;
|
|
28
|
+
exports.browserCompressionPayloadTransform = require_payload_transforms.browserCompressionPayloadTransform;
|
|
29
|
+
exports.browserContextPropagationIntegration = require_context_propagation_integration.browserContextPropagationIntegration;
|
|
30
|
+
exports.browserHttpTransport = require_http_transport.browserHttpTransport;
|
|
31
|
+
exports.browserServiceWorkerTransport = require_service_worker_transport.browserServiceWorkerTransport;
|
|
32
|
+
exports.browserWebSocketTransport = require_websocket_transport.browserWebSocketTransport;
|
|
33
|
+
exports.captureBrowserErrorsIntegration = require_error_integration.captureBrowserErrorsIntegration;
|
|
34
|
+
exports.captureConsoleIntegration = require_console_integration.captureConsoleIntegration;
|
|
35
|
+
exports.captureFetchIntegration = require_fetch_integration.captureFetchIntegration;
|
|
36
|
+
exports.captureFrameworkErrorsIntegration = require_framework_error_integration.captureFrameworkErrorsIntegration;
|
|
37
|
+
exports.capturePerformanceIntegration = require_performance_integration.capturePerformanceIntegration;
|
|
38
|
+
exports.captureReportingIntegration = require_reporting_integration.captureReportingIntegration;
|
|
39
|
+
exports.captureRouterIntegration = require_router_integration.captureRouterIntegration;
|
|
40
|
+
exports.captureRuntimeHostIntegration = require_runtime_host_integration.captureRuntimeHostIntegration;
|
|
41
|
+
exports.captureServiceWorkerIntegration = require_service_worker_integration.captureServiceWorkerIntegration;
|
|
42
|
+
exports.captureUserActionsIntegration = require_user_action_integration.captureUserActionsIntegration;
|
|
43
|
+
exports.captureWebSocketIntegration = require_websocket_integration.captureWebSocketIntegration;
|
|
44
|
+
exports.captureWebVitalsIntegration = require_web_vitals_integration.captureWebVitalsIntegration;
|
|
45
|
+
exports.captureXHRIntegration = require_xhr_integration.captureXHRIntegration;
|
|
46
|
+
exports.createLogZipBlob = require_zip_export.createLogZipBlob;
|
|
47
|
+
exports.downloadBlob = require_zip_export.downloadBlob;
|
|
48
|
+
exports.exportLogsToZip = require_zip_export.exportLogsToZip;
|
|
49
|
+
exports.indexedDbBrowserHttpOfflineQueue = require_indexeddb_offline_queue.indexedDbBrowserHttpOfflineQueue;
|
|
50
|
+
exports.indexedDbTransport = require_indexeddb_transport.indexedDbTransport;
|
|
51
|
+
exports.memoryBrowserHttpOfflineQueue = require_http_transport.memoryBrowserHttpOfflineQueue;
|
|
52
|
+
exports.nextRouterIntegration = require_framework_router_integrations.nextRouterIntegration;
|
|
53
|
+
exports.normalizeBrowserPerformanceEntry = require_performance_integration.normalizeBrowserPerformanceEntry;
|
|
54
|
+
exports.nuxtRouterIntegration = require_framework_router_integrations.nuxtRouterIntegration;
|
|
55
|
+
exports.offlineFirstTransport = require_offline_first_transport.offlineFirstTransport;
|
|
56
|
+
exports.pageLifecycleIntegration = require_page_lifecycle.pageLifecycleIntegration;
|
|
57
|
+
exports.reactRouterIntegration = require_framework_router_integrations.reactRouterIntegration;
|
|
58
|
+
exports.vueRouterIntegration = require_framework_router_integrations.vueRouterIntegration;
|
|
59
|
+
var _loggerjs_core = require("@loggerjs/core");
|
|
4148
60
|
Object.keys(_loggerjs_core).forEach(function(k) {
|
|
4149
61
|
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
4150
62
|
enumerable: true,
|
|
@@ -4153,5 +65,3 @@ Object.keys(_loggerjs_core).forEach(function(k) {
|
|
|
4153
65
|
}
|
|
4154
66
|
});
|
|
4155
67
|
});
|
|
4156
|
-
|
|
4157
|
-
//# sourceMappingURL=index.cjs.map
|