@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/http-transport.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { httpStatusError, incrementLoggerMetaCounter, safeJsonEncoder, toLevelValue } from "@loggerjs/core";
|
|
2
|
+
import { applyPayloadTransforms } from "@loggerjs/core/payload-transforms";
|
|
2
3
|
//#region src/http-transport.ts
|
|
3
4
|
const DEFAULT_BEACON_MAX_BYTES = 60 * 1024;
|
|
4
5
|
const sleep = (delayMs) => delayMs <= 0 ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
@@ -56,13 +57,30 @@ function remainingEvents(chunks, startIndex) {
|
|
|
56
57
|
}
|
|
57
58
|
return events;
|
|
58
59
|
}
|
|
60
|
+
function idempotencyKeyHeaders(header) {
|
|
61
|
+
if (!header) return () => void 0;
|
|
62
|
+
new Headers().append(header, "");
|
|
63
|
+
const prefix = Array.from(crypto.getRandomValues(new Uint32Array(2)), (word) => word.toString(36)).join("");
|
|
64
|
+
const keys = /* @__PURE__ */ new WeakMap();
|
|
65
|
+
let next = 0;
|
|
66
|
+
return (events) => {
|
|
67
|
+
const last = events[events.length - 1];
|
|
68
|
+
let key = keys.get(last);
|
|
69
|
+
if (key === void 0) {
|
|
70
|
+
key = `${prefix}-${(next++).toString(36)}`;
|
|
71
|
+
keys.set(last, key);
|
|
72
|
+
}
|
|
73
|
+
return { [header]: key };
|
|
74
|
+
};
|
|
75
|
+
}
|
|
59
76
|
function browserHttpTransport(options) {
|
|
60
|
-
const codec = options.codec ??
|
|
77
|
+
const codec = options.codec ?? safeJsonEncoder();
|
|
61
78
|
const beaconCodec = options.beaconCodec ?? codec;
|
|
62
79
|
const queue = [];
|
|
63
80
|
const maxBatchSize = options.maxBatchSize ?? 50;
|
|
64
81
|
if (!Number.isSafeInteger(maxBatchSize) || maxBatchSize <= 0) throw new RangeError("maxBatchSize must be a positive safe integer");
|
|
65
82
|
const flushIntervalMs = options.flushIntervalMs ?? 2e3;
|
|
83
|
+
const keyHeaders = idempotencyKeyHeaders(options.idempotencyKeyHeader);
|
|
66
84
|
const maxQueueSize = options.maxQueueSize ?? 1e3;
|
|
67
85
|
const dropPolicy = options.dropPolicy ?? "drop-oldest";
|
|
68
86
|
const beaconMaxBytes = options.beaconMaxBytes ?? DEFAULT_BEACON_MAX_BYTES;
|
|
@@ -72,11 +90,16 @@ function browserHttpTransport(options) {
|
|
|
72
90
|
const offlineReplayMaxDelayMs = options.offlineReplayMaxDelayMs ?? 5e3;
|
|
73
91
|
const random = options.random ?? Math.random;
|
|
74
92
|
const fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);
|
|
93
|
+
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
75
94
|
let offlineEntrySeq = 0;
|
|
95
|
+
let closed = false;
|
|
96
|
+
let retryNotBefore = 0;
|
|
97
|
+
let replayTimer;
|
|
76
98
|
let timer;
|
|
77
99
|
let activeFlush;
|
|
78
100
|
let flushingBeacon = false;
|
|
79
|
-
let
|
|
101
|
+
let replayPromise;
|
|
102
|
+
let heldBatchSize = 0;
|
|
80
103
|
let lastContext;
|
|
81
104
|
const headers = (payloadHeaders, contentType = codec.contentType) => ({
|
|
82
105
|
"content-type": contentType,
|
|
@@ -148,13 +171,18 @@ function browserHttpTransport(options) {
|
|
|
148
171
|
const chunk = chunks[index];
|
|
149
172
|
if (!chunk) continue;
|
|
150
173
|
try {
|
|
151
|
-
if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType)))
|
|
174
|
+
if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) {
|
|
175
|
+
incrementLoggerMetaCounter("transport.beacon.rejected", chunk.events.length);
|
|
176
|
+
return { remaining: remainingEvents(chunks, index) };
|
|
177
|
+
}
|
|
152
178
|
} catch (error) {
|
|
179
|
+
incrementLoggerMetaCounter("transport.beacon.rejected", chunk.events.length);
|
|
153
180
|
return {
|
|
154
181
|
remaining: remainingEvents(chunks, index),
|
|
155
182
|
failure: { error }
|
|
156
183
|
};
|
|
157
184
|
}
|
|
185
|
+
incrementLoggerMetaCounter("transport.beacon.accepted", chunk.events.length);
|
|
158
186
|
}
|
|
159
187
|
return { remaining: [] };
|
|
160
188
|
};
|
|
@@ -165,9 +193,10 @@ function browserHttpTransport(options) {
|
|
|
165
193
|
headers: entry.headers,
|
|
166
194
|
body: payloadToBody(entry.body),
|
|
167
195
|
credentials: entry.credentials,
|
|
168
|
-
keepalive: entry.keepalive
|
|
196
|
+
keepalive: entry.keepalive,
|
|
197
|
+
signal: timeoutMs > 0 && typeof AbortSignal?.timeout === "function" ? AbortSignal.timeout(timeoutMs) : void 0
|
|
169
198
|
});
|
|
170
|
-
if (!response.ok) throw
|
|
199
|
+
if (!response.ok) throw httpStatusError("browserHttpTransport", response);
|
|
171
200
|
};
|
|
172
201
|
const encodeTransformedPayload = async (batch) => {
|
|
173
202
|
return applyPayloadTransforms(codec.encode(batch), {
|
|
@@ -192,20 +221,33 @@ function browserHttpTransport(options) {
|
|
|
192
221
|
incrementLoggerMetaCounter("transport.offline.queued");
|
|
193
222
|
return true;
|
|
194
223
|
};
|
|
224
|
+
const isOnline = () => typeof navigator === "undefined" || navigator.onLine !== false;
|
|
195
225
|
const sendFetchBatch = async (batch) => {
|
|
196
|
-
if (batch.length === 0) return;
|
|
197
226
|
const transformed = await encodeTransformedPayload(batch);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
227
|
+
const payloadHeaders = {
|
|
228
|
+
...transformed.headers,
|
|
229
|
+
...keyHeaders(batch)
|
|
230
|
+
};
|
|
231
|
+
if (offlineQueue && !isOnline()) {
|
|
232
|
+
await enqueueOfflinePayload(transformed.payload, payloadHeaders, transformed.contentType);
|
|
233
|
+
return "queued";
|
|
201
234
|
}
|
|
202
235
|
try {
|
|
203
|
-
await sendPayload(createOfflineEntry(transformed.payload,
|
|
236
|
+
await sendPayload(createOfflineEntry(transformed.payload, payloadHeaders, transformed.contentType));
|
|
237
|
+
return "sent";
|
|
204
238
|
} catch (error) {
|
|
205
|
-
|
|
239
|
+
noteRetryAfter(error);
|
|
240
|
+
if (await enqueueOfflinePayload(transformed.payload, payloadHeaders, transformed.contentType)) return "queued";
|
|
206
241
|
throw error;
|
|
207
242
|
}
|
|
208
243
|
};
|
|
244
|
+
const retryWaitMs = () => Math.max(0, retryNotBefore - Date.now());
|
|
245
|
+
const noteRetryAfter = (error) => {
|
|
246
|
+
const retryAfterMs = error?.retryAfterMs;
|
|
247
|
+
if (typeof retryAfterMs !== "number" || !(retryAfterMs >= 0)) return void 0;
|
|
248
|
+
retryNotBefore = Math.max(retryNotBefore, Date.now() + retryAfterMs);
|
|
249
|
+
return retryAfterMs;
|
|
250
|
+
};
|
|
209
251
|
const replayRetryDelay = (attempt) => {
|
|
210
252
|
const cap = Math.min(offlineReplayMaxDelayMs, offlineReplayBaseDelayMs * 2 ** attempt);
|
|
211
253
|
return cap <= 0 ? 0 : random() * cap;
|
|
@@ -216,33 +258,54 @@ function browserHttpTransport(options) {
|
|
|
216
258
|
incrementLoggerMetaCounter("transport.offline.replayed");
|
|
217
259
|
return;
|
|
218
260
|
} catch (error) {
|
|
219
|
-
|
|
261
|
+
const retryAfterMs = noteRetryAfter(error);
|
|
262
|
+
if (attempt >= offlineReplayMaxRetries || (retryAfterMs ?? 0) > offlineReplayMaxDelayMs) {
|
|
220
263
|
incrementLoggerMetaCounter("transport.offline.replay.failed");
|
|
221
264
|
throw error;
|
|
222
265
|
}
|
|
223
266
|
incrementLoggerMetaCounter("transport.offline.retry");
|
|
224
|
-
await sleep(replayRetryDelay(attempt));
|
|
267
|
+
await sleep(Math.max(replayRetryDelay(attempt), retryAfterMs ?? 0));
|
|
225
268
|
}
|
|
226
269
|
};
|
|
227
|
-
const replayOfflineQueue =
|
|
228
|
-
if (!offlineQueue
|
|
229
|
-
|
|
270
|
+
const replayOfflineQueue = () => {
|
|
271
|
+
if (!offlineQueue) return Promise.resolve();
|
|
272
|
+
replayPromise ??= (async () => {
|
|
273
|
+
try {
|
|
274
|
+
await offlineQueue.replay(sendOfflineEntryWithRetry);
|
|
275
|
+
} finally {
|
|
276
|
+
replayPromise = void 0;
|
|
277
|
+
}
|
|
278
|
+
})();
|
|
279
|
+
return replayPromise;
|
|
280
|
+
};
|
|
281
|
+
const replayOfflineQueueSafely = async (operation) => {
|
|
282
|
+
if (!offlineQueue || !isOnline()) return;
|
|
283
|
+
const waitMs = retryWaitMs();
|
|
284
|
+
if (waitMs > 0) {
|
|
285
|
+
if (!closed && replayTimer === void 0) replayTimer = setTimeout(() => {
|
|
286
|
+
replayTimer = void 0;
|
|
287
|
+
replayOfflineQueueSafely(operation);
|
|
288
|
+
}, waitMs);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
230
291
|
try {
|
|
231
|
-
await
|
|
232
|
-
}
|
|
233
|
-
|
|
292
|
+
await replayOfflineQueue();
|
|
293
|
+
} catch (error) {
|
|
294
|
+
reportInternalError(error, operation);
|
|
234
295
|
}
|
|
235
296
|
};
|
|
236
297
|
const flushBeaconQueue = () => {
|
|
237
298
|
if (flushingBeacon || options.transformPayload || queue.length === 0) return;
|
|
238
299
|
flushingBeacon = true;
|
|
239
|
-
|
|
300
|
+
const taken = queue.length;
|
|
301
|
+
let pending = queue.splice(0, taken);
|
|
240
302
|
try {
|
|
241
303
|
const result = sendBeaconBatch(pending);
|
|
242
304
|
pending = result.remaining;
|
|
243
305
|
if (result.failure) throw result.failure.error;
|
|
244
306
|
} finally {
|
|
245
307
|
if (pending.length > 0) queue.unshift(...pending);
|
|
308
|
+
heldBatchSize = Math.max(0, heldBatchSize - (taken - pending.length));
|
|
246
309
|
flushingBeacon = false;
|
|
247
310
|
}
|
|
248
311
|
};
|
|
@@ -257,7 +320,11 @@ function browserHttpTransport(options) {
|
|
|
257
320
|
}
|
|
258
321
|
return activeFlush;
|
|
259
322
|
}
|
|
260
|
-
if (queue.length === 0) return Promise.resolve();
|
|
323
|
+
if (queue.length === 0) return preferBeacon ? Promise.resolve() : replayOfflineQueueSafely("replay");
|
|
324
|
+
if (!preferBeacon && retryWaitMs() > 0) {
|
|
325
|
+
schedule();
|
|
326
|
+
return Promise.resolve();
|
|
327
|
+
}
|
|
261
328
|
clearTimer();
|
|
262
329
|
let resolve;
|
|
263
330
|
let reject;
|
|
@@ -269,15 +336,21 @@ function browserHttpTransport(options) {
|
|
|
269
336
|
(async () => {
|
|
270
337
|
try {
|
|
271
338
|
if (preferBeacon) flushBeaconQueue();
|
|
339
|
+
let sent = false;
|
|
340
|
+
let queued = false;
|
|
272
341
|
while (queue.length > 0) {
|
|
273
|
-
const batch = queue.splice(0, maxBatchSize);
|
|
342
|
+
const batch = queue.splice(0, heldBatchSize || maxBatchSize);
|
|
343
|
+
heldBatchSize = 0;
|
|
274
344
|
try {
|
|
275
|
-
await sendFetchBatch(batch);
|
|
345
|
+
if (await sendFetchBatch(batch) === "sent") sent = true;
|
|
346
|
+
else queued = true;
|
|
276
347
|
} catch (error) {
|
|
277
348
|
queue.unshift(...batch);
|
|
349
|
+
heldBatchSize = batch.length;
|
|
278
350
|
throw error;
|
|
279
351
|
}
|
|
280
352
|
}
|
|
353
|
+
if (!preferBeacon && sent && !queued) await replayOfflineQueueSafely("replay");
|
|
281
354
|
resolve();
|
|
282
355
|
} catch (error) {
|
|
283
356
|
reject(error);
|
|
@@ -289,11 +362,13 @@ function browserHttpTransport(options) {
|
|
|
289
362
|
return task;
|
|
290
363
|
};
|
|
291
364
|
const schedule = () => {
|
|
292
|
-
if (
|
|
365
|
+
if (closed || activeFlush || timer) return;
|
|
366
|
+
const delayMs = Math.max(flushIntervalMs, retryWaitMs());
|
|
367
|
+
if (delayMs <= 0) return;
|
|
293
368
|
timer = setTimeout(() => {
|
|
294
369
|
timer = void 0;
|
|
295
370
|
flush(false).catch((error) => reportInternalError(error, "flush"));
|
|
296
|
-
},
|
|
371
|
+
}, delayMs);
|
|
297
372
|
};
|
|
298
373
|
const onPageHide = () => {
|
|
299
374
|
flush(true).catch((error) => reportInternalError(error, "pagehide-flush"));
|
|
@@ -302,25 +377,33 @@ function browserHttpTransport(options) {
|
|
|
302
377
|
if (typeof document !== "undefined" && document.visibilityState === "hidden") flush(true).catch((error) => reportInternalError(error, "visibility-flush"));
|
|
303
378
|
};
|
|
304
379
|
const onOnline = () => {
|
|
305
|
-
|
|
380
|
+
replayOfflineQueueSafely("replay");
|
|
306
381
|
};
|
|
307
382
|
if (options.useBeaconOnPageHide ?? true) {
|
|
308
383
|
globalThis.addEventListener?.("pagehide", onPageHide);
|
|
309
384
|
globalThis.addEventListener?.("visibilitychange", onVisibilityChange);
|
|
310
385
|
}
|
|
311
|
-
if (offlineQueue)
|
|
386
|
+
if (offlineQueue) {
|
|
387
|
+
globalThis.addEventListener?.("online", onOnline);
|
|
388
|
+
if (options.offlineReplayOnStart ?? true) setTimeout(() => void replayOfflineQueueSafely("startup-replay"), 0);
|
|
389
|
+
}
|
|
312
390
|
return {
|
|
313
391
|
name: options.name ?? "browser-http",
|
|
314
392
|
minLevel: options.minLevel,
|
|
315
393
|
log(event, context) {
|
|
316
394
|
if (options.minLevel !== void 0 && event.level < toLevelValue(options.minLevel)) return;
|
|
317
395
|
lastContext = context;
|
|
396
|
+
if (closed) {
|
|
397
|
+
reportDrop(event, "closed");
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
318
400
|
if (queue.length >= maxQueueSize) {
|
|
319
401
|
if (dropPolicy === "drop-newest") {
|
|
320
402
|
reportDrop(event, "queue-full");
|
|
321
403
|
return;
|
|
322
404
|
}
|
|
323
405
|
const dropped = queue.shift();
|
|
406
|
+
if (heldBatchSize > 0) heldBatchSize -= 1;
|
|
324
407
|
if (dropped) reportDrop(dropped, "queue-full");
|
|
325
408
|
}
|
|
326
409
|
queue.push(event);
|
|
@@ -335,7 +418,16 @@ function browserHttpTransport(options) {
|
|
|
335
418
|
globalThis.removeEventListener?.("pagehide", onPageHide);
|
|
336
419
|
globalThis.removeEventListener?.("visibilitychange", onVisibilityChange);
|
|
337
420
|
globalThis.removeEventListener?.("online", onOnline);
|
|
338
|
-
|
|
421
|
+
closed = true;
|
|
422
|
+
clearTimer();
|
|
423
|
+
if (replayTimer !== void 0) clearTimeout(replayTimer);
|
|
424
|
+
const dropUndelivered = () => {
|
|
425
|
+
for (const event of queue.splice(0)) reportDrop(event, "closed");
|
|
426
|
+
};
|
|
427
|
+
return flush(true).then(dropUndelivered, (error) => {
|
|
428
|
+
dropUndelivered();
|
|
429
|
+
throw error;
|
|
430
|
+
});
|
|
339
431
|
}
|
|
340
432
|
};
|
|
341
433
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-transport.js","names":[],"sources":["../src/http-transport.ts"],"sourcesContent":["import {\n applyPayloadTransforms,\n incrementLoggerMetaCounter,\n safeJsonCodec,\n toLevelValue,\n type Codec,\n type EncodedPayload,\n type LogEvent,\n type LoggerLevel,\n type PayloadTransform,\n type Transport,\n type TransportContext,\n} from \"@loggerjs/core\";\n\nexport type BrowserHttpDropPolicy = \"drop-oldest\" | \"drop-newest\";\n\nconst DEFAULT_BEACON_MAX_BYTES = 60 * 1024;\n\nconst sleep = (delayMs: number) =>\n delayMs <= 0 ? Promise.resolve() : new Promise<void>((resolve) => setTimeout(resolve, delayMs));\n\nexport interface BrowserHttpOfflineEntry {\n id: string;\n url: string;\n method: \"POST\" | \"PUT\";\n headers: Record<string, string>;\n body: string | Uint8Array;\n credentials?: RequestCredentials;\n keepalive: boolean;\n createdAt: number;\n}\n\nexport interface BrowserHttpOfflineQueue {\n enqueue: (entry: BrowserHttpOfflineEntry) => void | Promise<void>;\n replay: (send: (entry: BrowserHttpOfflineEntry) => Promise<void>) => void | Promise<void>;\n}\n\nexport interface MemoryBrowserHttpOfflineQueueOptions {\n maxEntries?: number;\n dropPolicy?: BrowserHttpDropPolicy;\n onDrop?: (entry: BrowserHttpOfflineEntry, reason: string) => void;\n}\n\nexport interface BrowserHttpTransportOptions {\n url: string;\n name?: string;\n method?: \"POST\" | \"PUT\";\n headers?: Record<string, string>;\n credentials?: RequestCredentials;\n keepalive?: boolean;\n codec?: Codec<string | Uint8Array>;\n beaconCodec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n maxBatchSize?: number;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n dropPolicy?: BrowserHttpDropPolicy;\n useBeaconOnPageHide?: boolean;\n beaconMaxBytes?: number;\n offlineQueue?: BrowserHttpOfflineQueue;\n offlineReplayMaxRetries?: number;\n offlineReplayBaseDelayMs?: number;\n offlineReplayMaxDelayMs?: number;\n random?: () => number;\n fetchFn?: typeof fetch;\n transformPayload?: PayloadTransform | readonly PayloadTransform[];\n onDrop?: (event: LogEvent, reason: string) => void;\n}\n\nexport function memoryBrowserHttpOfflineQueue(\n options: MemoryBrowserHttpOfflineQueueOptions = {},\n): BrowserHttpOfflineQueue & { size: () => number } {\n const entries: BrowserHttpOfflineEntry[] = [];\n const maxEntries = options.maxEntries ?? 1000;\n const dropPolicy = options.dropPolicy ?? \"drop-oldest\";\n\n const drop = (entry: BrowserHttpOfflineEntry, reason: string) => {\n incrementLoggerMetaCounter(\"transport.offline.dropped\");\n incrementLoggerMetaCounter(`transport.offline.dropped.${reason}`);\n options.onDrop?.(entry, reason);\n };\n\n return {\n enqueue(entry) {\n if (entries.length >= maxEntries) {\n if (dropPolicy === \"drop-newest\") {\n drop(entry, \"queue-full\");\n return;\n }\n const dropped = entries.shift();\n if (dropped) drop(dropped, \"queue-full\");\n }\n entries.push(entry);\n },\n async replay(send) {\n while (entries.length > 0) {\n const entry = entries[0];\n if (!entry) return;\n // oxlint-disable-next-line no-await-in-loop -- Replay must remove entries only after each send succeeds.\n await send(entry);\n entries.shift();\n }\n },\n size() {\n return entries.length;\n },\n };\n}\n\nfunction payloadToBody(payload: EncodedPayload): BodyInit {\n if (typeof payload === \"string\") return payload;\n return Uint8Array.from(payload);\n}\n\nfunction payloadByteLength(payload: EncodedPayload): number {\n if (typeof payload !== \"string\") return payload.byteLength;\n if (typeof TextEncoder !== \"undefined\") return new TextEncoder().encode(payload).byteLength;\n return new Blob([payload]).size;\n}\n\nfunction payloadToBeaconBody(payload: EncodedPayload, contentType: string): BodyInit {\n return new Blob([typeof payload === \"string\" ? payload : Uint8Array.from(payload)], {\n type: contentType,\n });\n}\n\ninterface BeaconChunk {\n events: LogEvent[];\n payload: string | Uint8Array;\n}\n\nfunction remainingEvents(chunks: BeaconChunk[], startIndex: number): LogEvent[] {\n const events: LogEvent[] = [];\n for (let index = startIndex; index < chunks.length; index++) {\n const chunk = chunks[index];\n if (chunk) events.push(...chunk.events);\n }\n return events;\n}\n\nexport function browserHttpTransport(options: BrowserHttpTransportOptions): Transport {\n const codec = options.codec ?? safeJsonCodec();\n const beaconCodec = options.beaconCodec ?? codec;\n const queue: LogEvent[] = [];\n const maxBatchSize = options.maxBatchSize ?? 50;\n if (!Number.isSafeInteger(maxBatchSize) || maxBatchSize <= 0) {\n throw new RangeError(\"maxBatchSize must be a positive safe integer\");\n }\n const flushIntervalMs = options.flushIntervalMs ?? 2000;\n const maxQueueSize = options.maxQueueSize ?? 1000;\n const dropPolicy = options.dropPolicy ?? \"drop-oldest\";\n const beaconMaxBytes = options.beaconMaxBytes ?? DEFAULT_BEACON_MAX_BYTES;\n const offlineQueue = options.offlineQueue;\n const offlineReplayMaxRetries = options.offlineReplayMaxRetries ?? 3;\n const offlineReplayBaseDelayMs = options.offlineReplayBaseDelayMs ?? 250;\n const offlineReplayMaxDelayMs = options.offlineReplayMaxDelayMs ?? 5000;\n const random = options.random ?? Math.random;\n const fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);\n let offlineEntrySeq = 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let activeFlush: Promise<void> | undefined;\n let flushingBeacon = false;\n let replayingOffline = false;\n let lastContext: TransportContext | undefined;\n\n const headers = (payloadHeaders?: Record<string, string>, contentType = codec.contentType) => ({\n \"content-type\": contentType,\n ...payloadHeaders,\n ...options.headers,\n });\n\n const clearTimer = () => {\n if (timer) clearTimeout(timer);\n timer = undefined;\n };\n\n const reportDrop = (event: LogEvent, reason: string) => {\n incrementLoggerMetaCounter(\"transport.dropped\");\n incrementLoggerMetaCounter(`transport.dropped.${reason}`);\n options.onDrop?.(event, reason);\n };\n\n const reportInternalError = (error: unknown, operation: string) => {\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name ?? \"browser-http\",\n operation,\n });\n };\n\n const createBeaconChunks = (batch: LogEvent[]): BeaconChunk[] => {\n const chunks: BeaconChunk[] = [];\n const oversized: LogEvent[] = [];\n let currentEvents: LogEvent[] = [];\n let currentPayload: string | Uint8Array | undefined;\n\n for (const event of batch) {\n if (currentEvents.length === maxBatchSize && currentPayload !== undefined) {\n chunks.push({ events: currentEvents, payload: currentPayload });\n currentEvents = [];\n currentPayload = undefined;\n }\n const candidateEvents = [...currentEvents, event];\n const candidatePayload = beaconCodec.encode(candidateEvents);\n if (payloadByteLength(candidatePayload) <= beaconMaxBytes) {\n currentEvents = candidateEvents;\n currentPayload = candidatePayload;\n continue;\n }\n\n if (currentEvents.length > 0 && currentPayload !== undefined) {\n chunks.push({ events: currentEvents, payload: currentPayload });\n }\n\n const singlePayload = beaconCodec.encode([event]);\n if (payloadByteLength(singlePayload) <= beaconMaxBytes) {\n currentEvents = [event];\n currentPayload = singlePayload;\n } else {\n currentEvents = [];\n currentPayload = undefined;\n oversized.push(event);\n }\n }\n\n if (currentEvents.length > 0 && currentPayload !== undefined) {\n chunks.push({ events: currentEvents, payload: currentPayload });\n }\n\n for (const event of oversized) reportDrop(event, \"beacon-too-large\");\n return chunks;\n };\n\n const sendBeaconBatch = (\n batch: LogEvent[],\n ): { remaining: LogEvent[]; failure?: { error: unknown } } => {\n if (typeof navigator === \"undefined\" || !navigator.sendBeacon) {\n return { remaining: batch };\n }\n\n const chunks = createBeaconChunks(batch);\n for (let index = 0; index < chunks.length; index++) {\n const chunk = chunks[index];\n if (!chunk) continue;\n try {\n const ok = navigator.sendBeacon(\n options.url,\n payloadToBeaconBody(chunk.payload, beaconCodec.contentType),\n );\n if (!ok) return { remaining: remainingEvents(chunks, index) };\n } catch (error) {\n return { remaining: remainingEvents(chunks, index), failure: { error } };\n }\n }\n\n return { remaining: [] };\n };\n\n const sendPayload = async (entry: BrowserHttpOfflineEntry) => {\n if (!fetchFn) throw new Error(\"fetch is not available for browserHttpTransport\");\n const response = await fetchFn(entry.url, {\n method: entry.method,\n headers: entry.headers,\n body: payloadToBody(entry.body),\n credentials: entry.credentials,\n keepalive: entry.keepalive,\n });\n if (!response.ok) throw new Error(`browserHttpTransport failed with status ${response.status}`);\n };\n\n const encodeTransformedPayload = async (batch: LogEvent[]) => {\n const encoded = codec.encode(batch);\n return applyPayloadTransforms(\n encoded,\n {\n contentType: codec.contentType,\n events: batch,\n transport: options.name ?? \"browser-http\",\n },\n options.transformPayload,\n );\n };\n\n const createOfflineEntry = (\n payload: string | Uint8Array,\n payloadHeaders?: Record<string, string>,\n contentType?: string,\n ): BrowserHttpOfflineEntry => ({\n id: `${Date.now().toString(36)}-${(offlineEntrySeq++).toString(36)}`,\n url: options.url,\n method: options.method ?? \"POST\",\n headers: headers(payloadHeaders, contentType),\n body: payload,\n credentials: options.credentials,\n keepalive: options.keepalive ?? true,\n createdAt: Date.now(),\n });\n\n const enqueueOfflinePayload = async (\n payload: string | Uint8Array,\n payloadHeaders?: Record<string, string>,\n contentType?: string,\n ) => {\n if (!offlineQueue) return false;\n await offlineQueue.enqueue(createOfflineEntry(payload, payloadHeaders, contentType));\n incrementLoggerMetaCounter(\"transport.offline.queued\");\n return true;\n };\n\n const sendFetchBatch = async (batch: LogEvent[]) => {\n if (batch.length === 0) return;\n const transformed = await encodeTransformedPayload(batch);\n if (offlineQueue && typeof navigator !== \"undefined\" && navigator.onLine === false) {\n await enqueueOfflinePayload(\n transformed.payload,\n transformed.headers,\n transformed.contentType,\n );\n return;\n }\n try {\n await sendPayload(\n createOfflineEntry(transformed.payload, transformed.headers, transformed.contentType),\n );\n } catch (error) {\n if (\n await enqueueOfflinePayload(\n transformed.payload,\n transformed.headers,\n transformed.contentType,\n )\n ) {\n return;\n }\n throw error;\n }\n };\n\n const replayRetryDelay = (attempt: number): number => {\n const cap = Math.min(offlineReplayMaxDelayMs, offlineReplayBaseDelayMs * 2 ** attempt);\n return cap <= 0 ? 0 : random() * cap;\n };\n\n const sendOfflineEntryWithRetry = async (entry: BrowserHttpOfflineEntry) => {\n for (let attempt = 0; ; attempt++) {\n try {\n // oxlint-disable-next-line no-await-in-loop -- Retry attempts must run sequentially.\n await sendPayload(entry);\n incrementLoggerMetaCounter(\"transport.offline.replayed\");\n return;\n } catch (error) {\n if (attempt >= offlineReplayMaxRetries) {\n incrementLoggerMetaCounter(\"transport.offline.replay.failed\");\n throw error;\n }\n incrementLoggerMetaCounter(\"transport.offline.retry\");\n // oxlint-disable-next-line no-await-in-loop -- Backoff must complete before the next retry.\n await sleep(replayRetryDelay(attempt));\n }\n }\n };\n\n const replayOfflineQueue = async () => {\n if (!offlineQueue || replayingOffline) return;\n replayingOffline = true;\n try {\n await offlineQueue.replay(sendOfflineEntryWithRetry);\n } finally {\n replayingOffline = false;\n }\n };\n\n // Beacon submission is synchronous, even while an earlier Fetch is pending.\n // Only queued events are eligible; the active Fetch batch keeps its ownership.\n const flushBeaconQueue = () => {\n if (flushingBeacon || options.transformPayload || queue.length === 0) return;\n flushingBeacon = true;\n let pending = queue.splice(0, queue.length);\n try {\n const result = sendBeaconBatch(pending);\n pending = result.remaining;\n if (result.failure) throw result.failure.error;\n } finally {\n if (pending.length > 0) queue.unshift(...pending);\n flushingBeacon = false;\n }\n };\n\n const flush = (preferBeacon = false): Promise<void> => {\n if (activeFlush) {\n if (preferBeacon) {\n try {\n flushBeaconQueue();\n } catch (error) {\n // close() must still wait for the active delivery before reporting a Beacon error.\n return activeFlush.then(() => {\n throw error;\n });\n }\n }\n return activeFlush;\n }\n if (queue.length === 0) return Promise.resolve();\n clearTimer();\n\n // Publish the task before invoking user codecs/transforms, which may reenter log().\n let resolve!: () => void;\n let reject!: (error: unknown) => void;\n const task = new Promise<void>((onResolve, onReject) => {\n resolve = onResolve;\n reject = onReject;\n });\n activeFlush = task;\n void (async () => {\n try {\n if (preferBeacon) flushBeaconQueue();\n while (queue.length > 0) {\n const batch = queue.splice(0, maxBatchSize);\n try {\n // oxlint-disable-next-line no-await-in-loop -- Preserve batch order and stop on the first unhandled failure.\n await sendFetchBatch(batch);\n } catch (error) {\n queue.unshift(...batch);\n throw error;\n }\n }\n resolve();\n } catch (error) {\n reject(error);\n } finally {\n activeFlush = undefined;\n if (queue.length > 0) schedule();\n }\n })();\n return task;\n };\n\n const schedule = () => {\n if (activeFlush || timer || flushIntervalMs <= 0) return;\n timer = setTimeout(() => {\n timer = undefined;\n void flush(false).catch((error: unknown) => reportInternalError(error, \"flush\"));\n }, flushIntervalMs);\n };\n\n const onPageHide = () => {\n void flush(true).catch((error: unknown) => reportInternalError(error, \"pagehide-flush\"));\n };\n const onVisibilityChange = () => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") {\n void flush(true).catch((error: unknown) => reportInternalError(error, \"visibility-flush\"));\n }\n };\n const onOnline = () => {\n void replayOfflineQueue().catch((error: unknown) => reportInternalError(error, \"replay\"));\n };\n\n if (options.useBeaconOnPageHide ?? true) {\n globalThis.addEventListener?.(\"pagehide\", onPageHide);\n globalThis.addEventListener?.(\"visibilitychange\", onVisibilityChange);\n }\n if (offlineQueue) globalThis.addEventListener?.(\"online\", onOnline);\n\n return {\n name: options.name ?? \"browser-http\",\n minLevel: options.minLevel,\n log(event, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n lastContext = context;\n if (queue.length >= maxQueueSize) {\n if (dropPolicy === \"drop-newest\") {\n reportDrop(event, \"queue-full\");\n return;\n }\n const dropped = queue.shift();\n if (dropped) reportDrop(dropped, \"queue-full\");\n }\n queue.push(event);\n if (activeFlush) return;\n if (queue.length >= maxBatchSize) {\n void flush(false).catch((error: unknown) => reportInternalError(error, \"flush\"));\n } else schedule();\n },\n flush() {\n return flush(false);\n },\n close() {\n globalThis.removeEventListener?.(\"pagehide\", onPageHide);\n globalThis.removeEventListener?.(\"visibilitychange\", onVisibilityChange);\n globalThis.removeEventListener?.(\"online\", onOnline);\n return flush(true);\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"http-transport.js","names":[],"sources":["../src/http-transport.ts"],"sourcesContent":["import {\n httpStatusError,\n incrementLoggerMetaCounter,\n safeJsonEncoder,\n toLevelValue,\n type Codec,\n type EncodedPayload,\n type LogEvent,\n type LoggerLevel,\n type PayloadTransform,\n type Transport,\n type TransportContext,\n type HttpStatusError,\n} from \"@loggerjs/core\";\nimport { applyPayloadTransforms } from \"@loggerjs/core/payload-transforms\";\n\nexport type BrowserHttpDropPolicy = \"drop-oldest\" | \"drop-newest\";\n\nconst DEFAULT_BEACON_MAX_BYTES = 60 * 1024;\n\nconst sleep = (delayMs: number) =>\n delayMs <= 0 ? Promise.resolve() : new Promise<void>((resolve) => setTimeout(resolve, delayMs));\n\nexport interface BrowserHttpOfflineEntry {\n id: string;\n url: string;\n method: \"POST\" | \"PUT\";\n headers: Record<string, string>;\n body: string | Uint8Array;\n credentials?: RequestCredentials;\n keepalive: boolean;\n createdAt: number;\n}\n\nexport interface BrowserHttpOfflineQueue {\n enqueue: (entry: BrowserHttpOfflineEntry) => void | Promise<void>;\n replay: (send: (entry: BrowserHttpOfflineEntry) => Promise<void>) => void | Promise<void>;\n}\n\nexport interface MemoryBrowserHttpOfflineQueueOptions {\n maxEntries?: number;\n dropPolicy?: BrowserHttpDropPolicy;\n onDrop?: (entry: BrowserHttpOfflineEntry, reason: string) => void;\n}\n\nexport interface BrowserHttpTransportOptions {\n url: string;\n name?: string;\n method?: \"POST\" | \"PUT\";\n headers?: Record<string, string>;\n credentials?: RequestCredentials;\n keepalive?: boolean;\n codec?: Codec<string | Uint8Array>;\n beaconCodec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n maxBatchSize?: number;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n dropPolicy?: BrowserHttpDropPolicy;\n useBeaconOnPageHide?: boolean;\n beaconMaxBytes?: number;\n offlineQueue?: BrowserHttpOfflineQueue;\n offlineReplayMaxRetries?: number;\n offlineReplayBaseDelayMs?: number;\n offlineReplayMaxDelayMs?: number;\n offlineReplayOnStart?: boolean;\n random?: () => number;\n fetchFn?: typeof fetch;\n transformPayload?: PayloadTransform | readonly PayloadTransform[];\n onDrop?: (event: LogEvent, reason: string) => void;\n /**\n * Abort a Fetch delivery after this many milliseconds. Browsers never time\n * out a request on their own, so one stalled request would otherwise hold\n * every later flush and close(). A timed-out batch falls back to the\n * offline queue when one is configured. Set to 0 to disable. Defaults to\n * 10000.\n */\n timeoutMs?: number;\n /**\n * Header that carries an idempotency key for each Fetch request, for\n * example \"Idempotency-Key\". Every resend of a batch repeats its key, and\n * offline-queue entries store it, so the collector can drop duplicates.\n * Keys start with a random per-transport prefix, so senders never share\n * one. Off by default because a cross-origin collector must allow the\n * header in Access-Control-Allow-Headers. Beacon requests cannot carry it.\n */\n idempotencyKeyHeader?: string;\n}\n\nexport function memoryBrowserHttpOfflineQueue(\n options: MemoryBrowserHttpOfflineQueueOptions = {},\n): BrowserHttpOfflineQueue & { size: () => number } {\n const entries: BrowserHttpOfflineEntry[] = [];\n const maxEntries = options.maxEntries ?? 1000;\n const dropPolicy = options.dropPolicy ?? \"drop-oldest\";\n\n const drop = (entry: BrowserHttpOfflineEntry, reason: string) => {\n incrementLoggerMetaCounter(\"transport.offline.dropped\");\n incrementLoggerMetaCounter(`transport.offline.dropped.${reason}`);\n options.onDrop?.(entry, reason);\n };\n\n return {\n enqueue(entry) {\n if (entries.length >= maxEntries) {\n if (dropPolicy === \"drop-newest\") {\n drop(entry, \"queue-full\");\n return;\n }\n const dropped = entries.shift();\n if (dropped) drop(dropped, \"queue-full\");\n }\n entries.push(entry);\n },\n async replay(send) {\n while (entries.length > 0) {\n const entry = entries[0];\n if (!entry) return;\n // oxlint-disable-next-line no-await-in-loop -- Replay must remove entries only after each send succeeds.\n await send(entry);\n entries.shift();\n }\n },\n size() {\n return entries.length;\n },\n };\n}\n\nfunction payloadToBody(payload: EncodedPayload): BodyInit {\n if (typeof payload === \"string\") return payload;\n return Uint8Array.from(payload);\n}\n\nfunction payloadByteLength(payload: EncodedPayload): number {\n if (typeof payload !== \"string\") return payload.byteLength;\n if (typeof TextEncoder !== \"undefined\") return new TextEncoder().encode(payload).byteLength;\n return new Blob([payload]).size;\n}\n\nfunction payloadToBeaconBody(payload: EncodedPayload, contentType: string): BodyInit {\n return new Blob([typeof payload === \"string\" ? payload : Uint8Array.from(payload)], {\n type: contentType,\n });\n}\n\ninterface BeaconChunk {\n events: LogEvent[];\n payload: string | Uint8Array;\n}\n\nfunction remainingEvents(chunks: BeaconChunk[], startIndex: number): LogEvent[] {\n const events: LogEvent[] = [];\n for (let index = startIndex; index < chunks.length; index++) {\n const chunk = chunks[index];\n if (chunk) events.push(...chunk.events);\n }\n return events;\n}\n\n// Keys are a random per-transport prefix, which keeps senders apart, and a\n// batch number. A batch's key is stored under its last event: resends of a\n// held batch keep that event, because a full queue and Beacon delivery only\n// remove events from the front. Mirrors @loggerjs/node.\nfunction idempotencyKeyHeaders(\n header: string | undefined,\n): (events: readonly LogEvent[]) => Record<string, string> | undefined {\n if (!header) return () => undefined;\n // Throws a TypeError for an invalid header name now instead of on every send.\n new Headers().append(header, \"\");\n const prefix = Array.from(crypto.getRandomValues(new Uint32Array(2)), (word) =>\n word.toString(36),\n ).join(\"\");\n const keys = new WeakMap<LogEvent, string>();\n let next = 0;\n return (events) => {\n const last = events[events.length - 1]!;\n let key = keys.get(last);\n if (key === undefined) {\n key = `${prefix}-${(next++).toString(36)}`;\n keys.set(last, key);\n }\n return { [header]: key };\n };\n}\n\nexport function browserHttpTransport(options: BrowserHttpTransportOptions): Transport {\n const codec = options.codec ?? safeJsonEncoder();\n const beaconCodec = options.beaconCodec ?? codec;\n const queue: LogEvent[] = [];\n const maxBatchSize = options.maxBatchSize ?? 50;\n if (!Number.isSafeInteger(maxBatchSize) || maxBatchSize <= 0) {\n throw new RangeError(\"maxBatchSize must be a positive safe integer\");\n }\n const flushIntervalMs = options.flushIntervalMs ?? 2000;\n const keyHeaders = idempotencyKeyHeaders(options.idempotencyKeyHeader);\n const maxQueueSize = options.maxQueueSize ?? 1000;\n const dropPolicy = options.dropPolicy ?? \"drop-oldest\";\n const beaconMaxBytes = options.beaconMaxBytes ?? DEFAULT_BEACON_MAX_BYTES;\n const offlineQueue = options.offlineQueue;\n const offlineReplayMaxRetries = options.offlineReplayMaxRetries ?? 3;\n const offlineReplayBaseDelayMs = options.offlineReplayBaseDelayMs ?? 250;\n const offlineReplayMaxDelayMs = options.offlineReplayMaxDelayMs ?? 5000;\n const random = options.random ?? Math.random;\n const fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);\n const timeoutMs = options.timeoutMs ?? 10_000;\n let offlineEntrySeq = 0;\n let closed = false;\n // Set from a Retry-After response: automatic sends and replays wait until then.\n let retryNotBefore = 0;\n let replayTimer: ReturnType<typeof setTimeout> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let activeFlush: Promise<void> | undefined;\n let flushingBeacon = false;\n let replayPromise: Promise<void> | undefined;\n // Events at the front of the queue that a failed send handed back. They are\n // resent as the same batch, so its idempotency key repeats.\n let heldBatchSize = 0;\n let lastContext: TransportContext | undefined;\n\n const headers = (payloadHeaders?: Record<string, string>, contentType = codec.contentType) => ({\n \"content-type\": contentType,\n ...payloadHeaders,\n ...options.headers,\n });\n\n const clearTimer = () => {\n if (timer) clearTimeout(timer);\n timer = undefined;\n };\n\n const reportDrop = (event: LogEvent, reason: string) => {\n incrementLoggerMetaCounter(\"transport.dropped\");\n incrementLoggerMetaCounter(`transport.dropped.${reason}`);\n options.onDrop?.(event, reason);\n };\n\n const reportInternalError = (error: unknown, operation: string) => {\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name ?? \"browser-http\",\n operation,\n });\n };\n\n const createBeaconChunks = (batch: LogEvent[]): BeaconChunk[] => {\n const chunks: BeaconChunk[] = [];\n const oversized: LogEvent[] = [];\n let currentEvents: LogEvent[] = [];\n let currentPayload: string | Uint8Array | undefined;\n\n for (const event of batch) {\n if (currentEvents.length === maxBatchSize && currentPayload !== undefined) {\n chunks.push({ events: currentEvents, payload: currentPayload });\n currentEvents = [];\n currentPayload = undefined;\n }\n const candidateEvents = [...currentEvents, event];\n const candidatePayload = beaconCodec.encode(candidateEvents);\n if (payloadByteLength(candidatePayload) <= beaconMaxBytes) {\n currentEvents = candidateEvents;\n currentPayload = candidatePayload;\n continue;\n }\n\n if (currentEvents.length > 0 && currentPayload !== undefined) {\n chunks.push({ events: currentEvents, payload: currentPayload });\n }\n\n const singlePayload = beaconCodec.encode([event]);\n if (payloadByteLength(singlePayload) <= beaconMaxBytes) {\n currentEvents = [event];\n currentPayload = singlePayload;\n } else {\n currentEvents = [];\n currentPayload = undefined;\n oversized.push(event);\n }\n }\n\n if (currentEvents.length > 0 && currentPayload !== undefined) {\n chunks.push({ events: currentEvents, payload: currentPayload });\n }\n\n for (const event of oversized) reportDrop(event, \"beacon-too-large\");\n return chunks;\n };\n\n const sendBeaconBatch = (\n batch: LogEvent[],\n ): { remaining: LogEvent[]; failure?: { error: unknown } } => {\n if (typeof navigator === \"undefined\" || !navigator.sendBeacon) {\n return { remaining: batch };\n }\n\n const chunks = createBeaconChunks(batch);\n for (let index = 0; index < chunks.length; index++) {\n const chunk = chunks[index];\n if (!chunk) continue;\n try {\n const ok = navigator.sendBeacon(\n options.url,\n payloadToBeaconBody(chunk.payload, beaconCodec.contentType),\n );\n if (!ok) {\n incrementLoggerMetaCounter(\"transport.beacon.rejected\", chunk.events.length);\n return { remaining: remainingEvents(chunks, index) };\n }\n } catch (error) {\n incrementLoggerMetaCounter(\"transport.beacon.rejected\", chunk.events.length);\n return { remaining: remainingEvents(chunks, index), failure: { error } };\n }\n // Accepted only means the browser queued the request: nothing reports\n // whether it reaches the collector, so these events are never dropped.\n incrementLoggerMetaCounter(\"transport.beacon.accepted\", chunk.events.length);\n }\n\n return { remaining: [] };\n };\n\n const sendPayload = async (entry: BrowserHttpOfflineEntry) => {\n if (!fetchFn) throw new Error(\"fetch is not available for browserHttpTransport\");\n const response = await fetchFn(entry.url, {\n method: entry.method,\n headers: entry.headers,\n body: payloadToBody(entry.body),\n credentials: entry.credentials,\n keepalive: entry.keepalive,\n signal:\n timeoutMs > 0 && typeof AbortSignal?.timeout === \"function\"\n ? AbortSignal.timeout(timeoutMs)\n : undefined,\n });\n if (!response.ok) throw httpStatusError(\"browserHttpTransport\", response);\n };\n\n const encodeTransformedPayload = async (batch: LogEvent[]) => {\n const encoded = codec.encode(batch);\n return applyPayloadTransforms(\n encoded,\n {\n contentType: codec.contentType,\n events: batch,\n transport: options.name ?? \"browser-http\",\n },\n options.transformPayload,\n );\n };\n\n const createOfflineEntry = (\n payload: string | Uint8Array,\n payloadHeaders?: Record<string, string>,\n contentType?: string,\n ): BrowserHttpOfflineEntry => ({\n id: `${Date.now().toString(36)}-${(offlineEntrySeq++).toString(36)}`,\n url: options.url,\n method: options.method ?? \"POST\",\n headers: headers(payloadHeaders, contentType),\n body: payload,\n credentials: options.credentials,\n keepalive: options.keepalive ?? true,\n createdAt: Date.now(),\n });\n\n const enqueueOfflinePayload = async (\n payload: string | Uint8Array,\n payloadHeaders?: Record<string, string>,\n contentType?: string,\n ) => {\n if (!offlineQueue) return false;\n await offlineQueue.enqueue(createOfflineEntry(payload, payloadHeaders, contentType));\n incrementLoggerMetaCounter(\"transport.offline.queued\");\n return true;\n };\n\n const isOnline = () => typeof navigator === \"undefined\" || navigator.onLine !== false;\n\n // Resolves to \"sent\" when the collector accepted the batch and \"queued\" when\n // the offline queue took it instead.\n const sendFetchBatch = async (batch: LogEvent[]): Promise<\"sent\" | \"queued\"> => {\n const transformed = await encodeTransformedPayload(batch);\n // The key travels in the stored headers, so replays repeat it.\n const payloadHeaders = { ...transformed.headers, ...keyHeaders(batch) };\n if (offlineQueue && !isOnline()) {\n await enqueueOfflinePayload(transformed.payload, payloadHeaders, transformed.contentType);\n return \"queued\";\n }\n try {\n await sendPayload(\n createOfflineEntry(transformed.payload, payloadHeaders, transformed.contentType),\n );\n return \"sent\";\n } catch (error) {\n noteRetryAfter(error);\n if (\n await enqueueOfflinePayload(transformed.payload, payloadHeaders, transformed.contentType)\n ) {\n return \"queued\";\n }\n throw error;\n }\n };\n\n const retryWaitMs = () => Math.max(0, retryNotBefore - Date.now());\n\n // Cross-origin collectors must expose Retry-After through\n // Access-Control-Expose-Headers for the browser to read it.\n const noteRetryAfter = (error: unknown): number | undefined => {\n const retryAfterMs = (error as Partial<HttpStatusError> | null)?.retryAfterMs;\n if (typeof retryAfterMs !== \"number\" || !(retryAfterMs >= 0)) return undefined;\n retryNotBefore = Math.max(retryNotBefore, Date.now() + retryAfterMs);\n return retryAfterMs;\n };\n\n const replayRetryDelay = (attempt: number): number => {\n const cap = Math.min(offlineReplayMaxDelayMs, offlineReplayBaseDelayMs * 2 ** attempt);\n return cap <= 0 ? 0 : random() * cap;\n };\n\n const sendOfflineEntryWithRetry = async (entry: BrowserHttpOfflineEntry) => {\n for (let attempt = 0; ; attempt++) {\n try {\n // oxlint-disable-next-line no-await-in-loop -- Retry attempts must run sequentially.\n await sendPayload(entry);\n incrementLoggerMetaCounter(\"transport.offline.replayed\");\n return;\n } catch (error) {\n const retryAfterMs = noteRetryAfter(error);\n // A longer server-requested wait ends this replay round; the entry\n // stays queued and replays after the wait.\n if (attempt >= offlineReplayMaxRetries || (retryAfterMs ?? 0) > offlineReplayMaxDelayMs) {\n incrementLoggerMetaCounter(\"transport.offline.replay.failed\");\n throw error;\n }\n incrementLoggerMetaCounter(\"transport.offline.retry\");\n // oxlint-disable-next-line no-await-in-loop -- Backoff must complete before the next retry.\n await sleep(Math.max(replayRetryDelay(attempt), retryAfterMs ?? 0));\n }\n }\n };\n\n const replayOfflineQueue = (): Promise<void> => {\n if (!offlineQueue) return Promise.resolve();\n replayPromise ??= (async () => {\n try {\n await offlineQueue.replay(sendOfflineEntryWithRetry);\n } finally {\n replayPromise = undefined;\n }\n })();\n return replayPromise;\n };\n\n // Stored entries stay in the offline queue when replay fails, so a failed\n // replay is reported instead of failing the flush that triggered it.\n const replayOfflineQueueSafely = async (operation: string): Promise<void> => {\n if (!offlineQueue || !isOnline()) return;\n const waitMs = retryWaitMs();\n if (waitMs > 0) {\n if (!closed && replayTimer === undefined) {\n replayTimer = setTimeout(() => {\n replayTimer = undefined;\n void replayOfflineQueueSafely(operation);\n }, waitMs);\n }\n return;\n }\n try {\n await replayOfflineQueue();\n } catch (error) {\n reportInternalError(error, operation);\n }\n };\n\n // Beacon submission is synchronous, even while an earlier Fetch is pending.\n // Only queued events are eligible; the active Fetch batch keeps its ownership.\n const flushBeaconQueue = () => {\n if (flushingBeacon || options.transformPayload || queue.length === 0) return;\n flushingBeacon = true;\n const taken = queue.length;\n let pending = queue.splice(0, taken);\n try {\n const result = sendBeaconBatch(pending);\n pending = result.remaining;\n if (result.failure) throw result.failure.error;\n } finally {\n if (pending.length > 0) queue.unshift(...pending);\n heldBatchSize = Math.max(0, heldBatchSize - (taken - pending.length));\n flushingBeacon = false;\n }\n };\n\n const flush = (preferBeacon = false): Promise<void> => {\n if (activeFlush) {\n if (preferBeacon) {\n try {\n flushBeaconQueue();\n } catch (error) {\n // close() must still wait for the active delivery before reporting a Beacon error.\n return activeFlush.then(() => {\n throw error;\n });\n }\n }\n return activeFlush;\n }\n if (queue.length === 0) {\n // An explicit flush with nothing live still retries stored payloads, so\n // entries queued during a server outage do not wait for an `online` event.\n return preferBeacon ? Promise.resolve() : replayOfflineQueueSafely(\"replay\");\n }\n // While the collector's Retry-After runs, keep events queued; the\n // scheduler sends them when it ends. Page-exit Beacon flushes still go out.\n if (!preferBeacon && retryWaitMs() > 0) {\n schedule();\n return Promise.resolve();\n }\n clearTimer();\n\n // Publish the task before invoking user codecs/transforms, which may reenter log().\n let resolve!: () => void;\n let reject!: (error: unknown) => void;\n const task = new Promise<void>((onResolve, onReject) => {\n resolve = onResolve;\n reject = onReject;\n });\n activeFlush = task;\n void (async () => {\n try {\n if (preferBeacon) flushBeaconQueue();\n let sent = false;\n let queued = false;\n while (queue.length > 0) {\n const batch = queue.splice(0, heldBatchSize || maxBatchSize);\n heldBatchSize = 0;\n try {\n // oxlint-disable-next-line no-await-in-loop -- Preserve batch order and stop on the first unhandled failure.\n const outcome = await sendFetchBatch(batch);\n if (outcome === \"sent\") sent = true;\n else queued = true;\n } catch (error) {\n queue.unshift(...batch);\n heldBatchSize = batch.length;\n throw error;\n }\n }\n // A live batch reached the collector and none fell back to the offline\n // queue, so the collector is reachable again: replay stored payloads.\n if (!preferBeacon && sent && !queued) await replayOfflineQueueSafely(\"replay\");\n resolve();\n } catch (error) {\n reject(error);\n } finally {\n activeFlush = undefined;\n if (queue.length > 0) schedule();\n }\n })();\n return task;\n };\n\n const schedule = () => {\n if (closed || activeFlush || timer) return;\n const delayMs = Math.max(flushIntervalMs, retryWaitMs());\n if (delayMs <= 0) return;\n timer = setTimeout(() => {\n timer = undefined;\n void flush(false).catch((error: unknown) => reportInternalError(error, \"flush\"));\n }, delayMs);\n };\n\n const onPageHide = () => {\n void flush(true).catch((error: unknown) => reportInternalError(error, \"pagehide-flush\"));\n };\n const onVisibilityChange = () => {\n if (typeof document !== \"undefined\" && document.visibilityState === \"hidden\") {\n void flush(true).catch((error: unknown) => reportInternalError(error, \"visibility-flush\"));\n }\n };\n const onOnline = () => {\n void replayOfflineQueueSafely(\"replay\");\n };\n\n if (options.useBeaconOnPageHide ?? true) {\n globalThis.addEventListener?.(\"pagehide\", onPageHide);\n globalThis.addEventListener?.(\"visibilitychange\", onVisibilityChange);\n }\n if (offlineQueue) {\n globalThis.addEventListener?.(\"online\", onOnline);\n // Payloads persisted by an earlier page load (for example in IndexedDB)\n // would otherwise wait for an `online` event that may never fire.\n if (options.offlineReplayOnStart ?? true) {\n setTimeout(() => void replayOfflineQueueSafely(\"startup-replay\"), 0);\n }\n }\n\n return {\n name: options.name ?? \"browser-http\",\n minLevel: options.minLevel,\n log(event, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n lastContext = context;\n if (closed) {\n reportDrop(event, \"closed\");\n return;\n }\n if (queue.length >= maxQueueSize) {\n if (dropPolicy === \"drop-newest\") {\n reportDrop(event, \"queue-full\");\n return;\n }\n const dropped = queue.shift();\n if (heldBatchSize > 0) heldBatchSize -= 1;\n if (dropped) reportDrop(dropped, \"queue-full\");\n }\n queue.push(event);\n if (activeFlush) return;\n if (queue.length >= maxBatchSize) {\n void flush(false).catch((error: unknown) => reportInternalError(error, \"flush\"));\n } else schedule();\n },\n flush() {\n return flush(false);\n },\n close() {\n globalThis.removeEventListener?.(\"pagehide\", onPageHide);\n globalThis.removeEventListener?.(\"visibilitychange\", onVisibilityChange);\n globalThis.removeEventListener?.(\"online\", onOnline);\n closed = true;\n clearTimer();\n if (replayTimer !== undefined) clearTimeout(replayTimer);\n // Events the final flush could not deliver or store are accounted for\n // as dropped instead of disappearing with the transport.\n const dropUndelivered = () => {\n for (const event of queue.splice(0)) reportDrop(event, \"closed\");\n };\n return flush(true).then(dropUndelivered, (error: unknown) => {\n dropUndelivered();\n throw error;\n });\n },\n };\n}\n"],"mappings":"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"}
|