@loggerjs/browser 0.6.0 → 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.
Files changed (59) hide show
  1. package/README.md +2 -2
  2. package/dist/chunks/http-capture-utils-DbhpUirh.cjs +83 -0
  3. package/dist/chunks/http-capture-utils-DbhpUirh.cjs.map +1 -0
  4. package/dist/chunks/http-capture-utils-_hG5wepA.js +48 -0
  5. package/dist/chunks/http-capture-utils-_hG5wepA.js.map +1 -0
  6. package/dist/context-propagation-integration.cjs +5 -4
  7. package/dist/context-propagation-integration.cjs.map +1 -1
  8. package/dist/context-propagation-integration.d.ts +2 -1
  9. package/dist/context-propagation-integration.d.ts.map +1 -1
  10. package/dist/context-propagation-integration.js +2 -1
  11. package/dist/context-propagation-integration.js.map +1 -1
  12. package/dist/fetch-integration.cjs +8 -52
  13. package/dist/fetch-integration.cjs.map +1 -1
  14. package/dist/fetch-integration.js +1 -45
  15. package/dist/fetch-integration.js.map +1 -1
  16. package/dist/http-transport.cjs +90 -16
  17. package/dist/http-transport.cjs.map +1 -1
  18. package/dist/http-transport.d.ts +17 -0
  19. package/dist/http-transport.d.ts.map +1 -1
  20. package/dist/http-transport.js +90 -16
  21. package/dist/http-transport.js.map +1 -1
  22. package/dist/index.cjs +58 -4167
  23. package/dist/index.d.ts +243 -17
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +25 -4135
  26. package/dist/indexeddb-offline-queue.cjs +8 -1
  27. package/dist/indexeddb-offline-queue.cjs.map +1 -1
  28. package/dist/indexeddb-offline-queue.d.ts.map +1 -1
  29. package/dist/indexeddb-offline-queue.js +8 -1
  30. package/dist/indexeddb-offline-queue.js.map +1 -1
  31. package/dist/indexeddb-transport.cjs +24 -4
  32. package/dist/indexeddb-transport.cjs.map +1 -1
  33. package/dist/indexeddb-transport.d.ts.map +1 -1
  34. package/dist/indexeddb-transport.js +24 -4
  35. package/dist/indexeddb-transport.js.map +1 -1
  36. package/dist/offline-first-transport.cjs +2 -1029
  37. package/dist/offline-first-transport.cjs.map +1 -1
  38. package/dist/offline-first-transport.js +2 -1029
  39. package/dist/offline-first-transport.js.map +1 -1
  40. package/dist/payload-transforms.cjs +3 -3
  41. package/dist/payload-transforms.cjs.map +1 -1
  42. package/dist/payload-transforms.d.ts.map +1 -1
  43. package/dist/payload-transforms.js +1 -1
  44. package/dist/payload-transforms.js.map +1 -1
  45. package/dist/websocket-integration.cjs +5 -32
  46. package/dist/websocket-integration.cjs.map +1 -1
  47. package/dist/websocket-integration.js +1 -28
  48. package/dist/websocket-integration.js.map +1 -1
  49. package/dist/websocket-transport.cjs +1 -1
  50. package/dist/websocket-transport.cjs.map +1 -1
  51. package/dist/websocket-transport.js +2 -2
  52. package/dist/websocket-transport.js.map +1 -1
  53. package/dist/xhr-integration.cjs +6 -33
  54. package/dist/xhr-integration.cjs.map +1 -1
  55. package/dist/xhr-integration.js +1 -28
  56. package/dist/xhr-integration.js.map +1 -1
  57. package/package.json +5 -2
  58. package/dist/index.cjs.map +0 -1
  59. package/dist/index.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _loggerjs_core = require("@loggerjs/core");
3
+ let _loggerjs_core_payload_transforms = require("@loggerjs/core/payload-transforms");
3
4
  //#region src/http-transport.ts
4
5
  const DEFAULT_BEACON_MAX_BYTES = 60 * 1024;
5
6
  const sleep = (delayMs) => delayMs <= 0 ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, delayMs));
@@ -57,13 +58,30 @@ function remainingEvents(chunks, startIndex) {
57
58
  }
58
59
  return events;
59
60
  }
61
+ function idempotencyKeyHeaders(header) {
62
+ if (!header) return () => void 0;
63
+ new Headers().append(header, "");
64
+ const prefix = Array.from(crypto.getRandomValues(new Uint32Array(2)), (word) => word.toString(36)).join("");
65
+ const keys = /* @__PURE__ */ new WeakMap();
66
+ let next = 0;
67
+ return (events) => {
68
+ const last = events[events.length - 1];
69
+ let key = keys.get(last);
70
+ if (key === void 0) {
71
+ key = `${prefix}-${(next++).toString(36)}`;
72
+ keys.set(last, key);
73
+ }
74
+ return { [header]: key };
75
+ };
76
+ }
60
77
  function browserHttpTransport(options) {
61
- const codec = options.codec ?? (0, _loggerjs_core.safeJsonCodec)();
78
+ const codec = options.codec ?? (0, _loggerjs_core.safeJsonEncoder)();
62
79
  const beaconCodec = options.beaconCodec ?? codec;
63
80
  const queue = [];
64
81
  const maxBatchSize = options.maxBatchSize ?? 50;
65
82
  if (!Number.isSafeInteger(maxBatchSize) || maxBatchSize <= 0) throw new RangeError("maxBatchSize must be a positive safe integer");
66
83
  const flushIntervalMs = options.flushIntervalMs ?? 2e3;
84
+ const keyHeaders = idempotencyKeyHeaders(options.idempotencyKeyHeader);
67
85
  const maxQueueSize = options.maxQueueSize ?? 1e3;
68
86
  const dropPolicy = options.dropPolicy ?? "drop-oldest";
69
87
  const beaconMaxBytes = options.beaconMaxBytes ?? DEFAULT_BEACON_MAX_BYTES;
@@ -73,11 +91,16 @@ function browserHttpTransport(options) {
73
91
  const offlineReplayMaxDelayMs = options.offlineReplayMaxDelayMs ?? 5e3;
74
92
  const random = options.random ?? Math.random;
75
93
  const fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);
94
+ const timeoutMs = options.timeoutMs ?? 1e4;
76
95
  let offlineEntrySeq = 0;
96
+ let closed = false;
97
+ let retryNotBefore = 0;
98
+ let replayTimer;
77
99
  let timer;
78
100
  let activeFlush;
79
101
  let flushingBeacon = false;
80
102
  let replayPromise;
103
+ let heldBatchSize = 0;
81
104
  let lastContext;
82
105
  const headers = (payloadHeaders, contentType = codec.contentType) => ({
83
106
  "content-type": contentType,
@@ -149,13 +172,18 @@ function browserHttpTransport(options) {
149
172
  const chunk = chunks[index];
150
173
  if (!chunk) continue;
151
174
  try {
152
- if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) return { remaining: remainingEvents(chunks, index) };
175
+ if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) {
176
+ (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.beacon.rejected", chunk.events.length);
177
+ return { remaining: remainingEvents(chunks, index) };
178
+ }
153
179
  } catch (error) {
180
+ (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.beacon.rejected", chunk.events.length);
154
181
  return {
155
182
  remaining: remainingEvents(chunks, index),
156
183
  failure: { error }
157
184
  };
158
185
  }
186
+ (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.beacon.accepted", chunk.events.length);
159
187
  }
160
188
  return { remaining: [] };
161
189
  };
@@ -166,12 +194,13 @@ function browserHttpTransport(options) {
166
194
  headers: entry.headers,
167
195
  body: payloadToBody(entry.body),
168
196
  credentials: entry.credentials,
169
- keepalive: entry.keepalive
197
+ keepalive: entry.keepalive,
198
+ signal: timeoutMs > 0 && typeof AbortSignal?.timeout === "function" ? AbortSignal.timeout(timeoutMs) : void 0
170
199
  });
171
- if (!response.ok) throw new Error(`browserHttpTransport failed with status ${response.status}`);
200
+ if (!response.ok) throw (0, _loggerjs_core.httpStatusError)("browserHttpTransport", response);
172
201
  };
173
202
  const encodeTransformedPayload = async (batch) => {
174
- return (0, _loggerjs_core.applyPayloadTransforms)(codec.encode(batch), {
203
+ return (0, _loggerjs_core_payload_transforms.applyPayloadTransforms)(codec.encode(batch), {
175
204
  contentType: codec.contentType,
176
205
  events: batch,
177
206
  transport: options.name ?? "browser-http"
@@ -196,18 +225,30 @@ function browserHttpTransport(options) {
196
225
  const isOnline = () => typeof navigator === "undefined" || navigator.onLine !== false;
197
226
  const sendFetchBatch = async (batch) => {
198
227
  const transformed = await encodeTransformedPayload(batch);
228
+ const payloadHeaders = {
229
+ ...transformed.headers,
230
+ ...keyHeaders(batch)
231
+ };
199
232
  if (offlineQueue && !isOnline()) {
200
- await enqueueOfflinePayload(transformed.payload, transformed.headers, transformed.contentType);
233
+ await enqueueOfflinePayload(transformed.payload, payloadHeaders, transformed.contentType);
201
234
  return "queued";
202
235
  }
203
236
  try {
204
- await sendPayload(createOfflineEntry(transformed.payload, transformed.headers, transformed.contentType));
237
+ await sendPayload(createOfflineEntry(transformed.payload, payloadHeaders, transformed.contentType));
205
238
  return "sent";
206
239
  } catch (error) {
207
- if (await enqueueOfflinePayload(transformed.payload, transformed.headers, transformed.contentType)) return "queued";
240
+ noteRetryAfter(error);
241
+ if (await enqueueOfflinePayload(transformed.payload, payloadHeaders, transformed.contentType)) return "queued";
208
242
  throw error;
209
243
  }
210
244
  };
245
+ const retryWaitMs = () => Math.max(0, retryNotBefore - Date.now());
246
+ const noteRetryAfter = (error) => {
247
+ const retryAfterMs = error?.retryAfterMs;
248
+ if (typeof retryAfterMs !== "number" || !(retryAfterMs >= 0)) return void 0;
249
+ retryNotBefore = Math.max(retryNotBefore, Date.now() + retryAfterMs);
250
+ return retryAfterMs;
251
+ };
211
252
  const replayRetryDelay = (attempt) => {
212
253
  const cap = Math.min(offlineReplayMaxDelayMs, offlineReplayBaseDelayMs * 2 ** attempt);
213
254
  return cap <= 0 ? 0 : random() * cap;
@@ -218,12 +259,13 @@ function browserHttpTransport(options) {
218
259
  (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.replayed");
219
260
  return;
220
261
  } catch (error) {
221
- if (attempt >= offlineReplayMaxRetries) {
262
+ const retryAfterMs = noteRetryAfter(error);
263
+ if (attempt >= offlineReplayMaxRetries || (retryAfterMs ?? 0) > offlineReplayMaxDelayMs) {
222
264
  (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.replay.failed");
223
265
  throw error;
224
266
  }
225
267
  (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.offline.retry");
226
- await sleep(replayRetryDelay(attempt));
268
+ await sleep(Math.max(replayRetryDelay(attempt), retryAfterMs ?? 0));
227
269
  }
228
270
  };
229
271
  const replayOfflineQueue = () => {
@@ -239,6 +281,14 @@ function browserHttpTransport(options) {
239
281
  };
240
282
  const replayOfflineQueueSafely = async (operation) => {
241
283
  if (!offlineQueue || !isOnline()) return;
284
+ const waitMs = retryWaitMs();
285
+ if (waitMs > 0) {
286
+ if (!closed && replayTimer === void 0) replayTimer = setTimeout(() => {
287
+ replayTimer = void 0;
288
+ replayOfflineQueueSafely(operation);
289
+ }, waitMs);
290
+ return;
291
+ }
242
292
  try {
243
293
  await replayOfflineQueue();
244
294
  } catch (error) {
@@ -248,13 +298,15 @@ function browserHttpTransport(options) {
248
298
  const flushBeaconQueue = () => {
249
299
  if (flushingBeacon || options.transformPayload || queue.length === 0) return;
250
300
  flushingBeacon = true;
251
- let pending = queue.splice(0, queue.length);
301
+ const taken = queue.length;
302
+ let pending = queue.splice(0, taken);
252
303
  try {
253
304
  const result = sendBeaconBatch(pending);
254
305
  pending = result.remaining;
255
306
  if (result.failure) throw result.failure.error;
256
307
  } finally {
257
308
  if (pending.length > 0) queue.unshift(...pending);
309
+ heldBatchSize = Math.max(0, heldBatchSize - (taken - pending.length));
258
310
  flushingBeacon = false;
259
311
  }
260
312
  };
@@ -270,6 +322,10 @@ function browserHttpTransport(options) {
270
322
  return activeFlush;
271
323
  }
272
324
  if (queue.length === 0) return preferBeacon ? Promise.resolve() : replayOfflineQueueSafely("replay");
325
+ if (!preferBeacon && retryWaitMs() > 0) {
326
+ schedule();
327
+ return Promise.resolve();
328
+ }
273
329
  clearTimer();
274
330
  let resolve;
275
331
  let reject;
@@ -284,12 +340,14 @@ function browserHttpTransport(options) {
284
340
  let sent = false;
285
341
  let queued = false;
286
342
  while (queue.length > 0) {
287
- const batch = queue.splice(0, maxBatchSize);
343
+ const batch = queue.splice(0, heldBatchSize || maxBatchSize);
344
+ heldBatchSize = 0;
288
345
  try {
289
346
  if (await sendFetchBatch(batch) === "sent") sent = true;
290
347
  else queued = true;
291
348
  } catch (error) {
292
349
  queue.unshift(...batch);
350
+ heldBatchSize = batch.length;
293
351
  throw error;
294
352
  }
295
353
  }
@@ -305,11 +363,13 @@ function browserHttpTransport(options) {
305
363
  return task;
306
364
  };
307
365
  const schedule = () => {
308
- if (activeFlush || timer || flushIntervalMs <= 0) return;
366
+ if (closed || activeFlush || timer) return;
367
+ const delayMs = Math.max(flushIntervalMs, retryWaitMs());
368
+ if (delayMs <= 0) return;
309
369
  timer = setTimeout(() => {
310
370
  timer = void 0;
311
371
  flush(false).catch((error) => reportInternalError(error, "flush"));
312
- }, flushIntervalMs);
372
+ }, delayMs);
313
373
  };
314
374
  const onPageHide = () => {
315
375
  flush(true).catch((error) => reportInternalError(error, "pagehide-flush"));
@@ -318,7 +378,7 @@ function browserHttpTransport(options) {
318
378
  if (typeof document !== "undefined" && document.visibilityState === "hidden") flush(true).catch((error) => reportInternalError(error, "visibility-flush"));
319
379
  };
320
380
  const onOnline = () => {
321
- replayOfflineQueue().catch((error) => reportInternalError(error, "replay"));
381
+ replayOfflineQueueSafely("replay");
322
382
  };
323
383
  if (options.useBeaconOnPageHide ?? true) {
324
384
  globalThis.addEventListener?.("pagehide", onPageHide);
@@ -334,12 +394,17 @@ function browserHttpTransport(options) {
334
394
  log(event, context) {
335
395
  if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
336
396
  lastContext = context;
397
+ if (closed) {
398
+ reportDrop(event, "closed");
399
+ return;
400
+ }
337
401
  if (queue.length >= maxQueueSize) {
338
402
  if (dropPolicy === "drop-newest") {
339
403
  reportDrop(event, "queue-full");
340
404
  return;
341
405
  }
342
406
  const dropped = queue.shift();
407
+ if (heldBatchSize > 0) heldBatchSize -= 1;
343
408
  if (dropped) reportDrop(dropped, "queue-full");
344
409
  }
345
410
  queue.push(event);
@@ -354,7 +419,16 @@ function browserHttpTransport(options) {
354
419
  globalThis.removeEventListener?.("pagehide", onPageHide);
355
420
  globalThis.removeEventListener?.("visibilitychange", onVisibilityChange);
356
421
  globalThis.removeEventListener?.("online", onOnline);
357
- return flush(true);
422
+ closed = true;
423
+ clearTimer();
424
+ if (replayTimer !== void 0) clearTimeout(replayTimer);
425
+ const dropUndelivered = () => {
426
+ for (const event of queue.splice(0)) reportDrop(event, "closed");
427
+ };
428
+ return flush(true).then(dropUndelivered, (error) => {
429
+ dropUndelivered();
430
+ throw error;
431
+ });
358
432
  }
359
433
  };
360
434
  }
@@ -1 +1 @@
1
- {"version":3,"file":"http-transport.cjs","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 offlineReplayOnStart?: boolean;\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 replayPromise: Promise<void> | undefined;\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 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 if (offlineQueue && !isOnline()) {\n await enqueueOfflinePayload(\n transformed.payload,\n transformed.headers,\n transformed.contentType,\n );\n return \"queued\";\n }\n try {\n await sendPayload(\n createOfflineEntry(transformed.payload, transformed.headers, transformed.contentType),\n );\n return \"sent\";\n } catch (error) {\n if (\n await enqueueOfflinePayload(\n transformed.payload,\n transformed.headers,\n transformed.contentType,\n )\n ) {\n return \"queued\";\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 = (): 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) => {\n if (!offlineQueue || !isOnline()) return;\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 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) {\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 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, maxBatchSize);\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 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 (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) {\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 (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":";;;AAgBA,MAAM,2BAA2B,KAAK;AAEtC,MAAM,SAAS,YACb,WAAW,IAAI,QAAQ,QAAQ,IAAI,IAAI,SAAe,YAAY,WAAW,SAAS,OAAO,CAAC;AAmDhG,SAAgB,8BACd,UAAgD,CAAC,GACC;CAClD,MAAM,UAAqC,CAAC;CAC5C,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,aAAa,QAAQ,cAAc;CAEzC,MAAM,QAAQ,OAAgC,WAAmB;EAC/D,CAAA,GAAA,eAAA,2BAAA,CAA2B,2BAA2B;EACtD,CAAA,GAAA,eAAA,2BAAA,CAA2B,6BAA6B,QAAQ;EAChE,QAAQ,SAAS,OAAO,MAAM;CAChC;CAEA,OAAO;EACL,QAAQ,OAAO;GACb,IAAI,QAAQ,UAAU,YAAY;IAChC,IAAI,eAAe,eAAe;KAChC,KAAK,OAAO,YAAY;KACxB;IACF;IACA,MAAM,UAAU,QAAQ,MAAM;IAC9B,IAAI,SAAS,KAAK,SAAS,YAAY;GACzC;GACA,QAAQ,KAAK,KAAK;EACpB;EACA,MAAM,OAAO,MAAM;GACjB,OAAO,QAAQ,SAAS,GAAG;IACzB,MAAM,QAAQ,QAAQ;IACtB,IAAI,CAAC,OAAO;IAEZ,MAAM,KAAK,KAAK;IAChB,QAAQ,MAAM;GAChB;EACF;EACA,OAAO;GACL,OAAO,QAAQ;EACjB;CACF;AACF;AAEA,SAAS,cAAc,SAAmC;CACxD,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,OAAO,WAAW,KAAK,OAAO;AAChC;AAEA,SAAS,kBAAkB,SAAiC;CAC1D,IAAI,OAAO,YAAY,UAAU,OAAO,QAAQ;CAChD,IAAI,OAAO,gBAAgB,aAAa,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC;CACjF,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAC7B;AAEA,SAAS,oBAAoB,SAAyB,aAA+B;CACnF,OAAO,IAAI,KAAK,CAAC,OAAO,YAAY,WAAW,UAAU,WAAW,KAAK,OAAO,CAAC,GAAG,EAClF,MAAM,YACR,CAAC;AACH;AAOA,SAAS,gBAAgB,QAAuB,YAAgC;CAC9E,MAAM,SAAqB,CAAC;CAC5B,KAAK,IAAI,QAAQ,YAAY,QAAQ,OAAO,QAAQ,SAAS;EAC3D,MAAM,QAAQ,OAAO;EACrB,IAAI,OAAO,OAAO,KAAK,GAAG,MAAM,MAAM;CACxC;CACA,OAAO;AACT;AAEA,SAAgB,qBAAqB,SAAiD;CACpF,MAAM,QAAQ,QAAQ,UAAA,GAAA,eAAA,cAAA,CAAuB;CAC7C,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,QAAoB,CAAC;CAC3B,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,IAAI,CAAC,OAAO,cAAc,YAAY,KAAK,gBAAgB,GACzD,MAAM,IAAI,WAAW,8CAA8C;CAErE,MAAM,kBAAkB,QAAQ,mBAAmB;CACnD,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,iBAAiB,QAAQ,kBAAkB;CACjD,MAAM,eAAe,QAAQ;CAC7B,MAAM,0BAA0B,QAAQ,2BAA2B;CACnE,MAAM,2BAA2B,QAAQ,4BAA4B;CACrE,MAAM,0BAA0B,QAAQ,2BAA2B;CACnE,MAAM,SAAS,QAAQ,UAAU,KAAK;CACtC,MAAM,UAAU,QAAQ,WAAW,WAAW,OAAO,KAAK,UAAU;CACpE,IAAI,kBAAkB;CACtB,IAAI;CACJ,IAAI;CACJ,IAAI,iBAAiB;CACrB,IAAI;CACJ,IAAI;CAEJ,MAAM,WAAW,gBAAyC,cAAc,MAAM,iBAAiB;EAC7F,gBAAgB;EAChB,GAAG;EACH,GAAG,QAAQ;CACb;CAEA,MAAM,mBAAmB;EACvB,IAAI,OAAO,aAAa,KAAK;EAC7B,QAAQ,KAAA;CACV;CAEA,MAAM,cAAc,OAAiB,WAAmB;EACtD,CAAA,GAAA,eAAA,2BAAA,CAA2B,mBAAmB;EAC9C,CAAA,GAAA,eAAA,2BAAA,CAA2B,qBAAqB,QAAQ;EACxD,QAAQ,SAAS,OAAO,MAAM;CAChC;CAEA,MAAM,uBAAuB,OAAgB,cAAsB;EACjE,aAAa,oBAAoB,OAAO;GACtC,OAAO;GACP,WAAW,QAAQ,QAAQ;GAC3B;EACF,CAAC;CACH;CAEA,MAAM,sBAAsB,UAAqC;EAC/D,MAAM,SAAwB,CAAC;EAC/B,MAAM,YAAwB,CAAC;EAC/B,IAAI,gBAA4B,CAAC;EACjC,IAAI;EAEJ,KAAK,MAAM,SAAS,OAAO;GACzB,IAAI,cAAc,WAAW,gBAAgB,mBAAmB,KAAA,GAAW;IACzE,OAAO,KAAK;KAAE,QAAQ;KAAe,SAAS;IAAe,CAAC;IAC9D,gBAAgB,CAAC;IACjB,iBAAiB,KAAA;GACnB;GACA,MAAM,kBAAkB,CAAC,GAAG,eAAe,KAAK;GAChD,MAAM,mBAAmB,YAAY,OAAO,eAAe;GAC3D,IAAI,kBAAkB,gBAAgB,KAAK,gBAAgB;IACzD,gBAAgB;IAChB,iBAAiB;IACjB;GACF;GAEA,IAAI,cAAc,SAAS,KAAK,mBAAmB,KAAA,GACjD,OAAO,KAAK;IAAE,QAAQ;IAAe,SAAS;GAAe,CAAC;GAGhE,MAAM,gBAAgB,YAAY,OAAO,CAAC,KAAK,CAAC;GAChD,IAAI,kBAAkB,aAAa,KAAK,gBAAgB;IACtD,gBAAgB,CAAC,KAAK;IACtB,iBAAiB;GACnB,OAAO;IACL,gBAAgB,CAAC;IACjB,iBAAiB,KAAA;IACjB,UAAU,KAAK,KAAK;GACtB;EACF;EAEA,IAAI,cAAc,SAAS,KAAK,mBAAmB,KAAA,GACjD,OAAO,KAAK;GAAE,QAAQ;GAAe,SAAS;EAAe,CAAC;EAGhE,KAAK,MAAM,SAAS,WAAW,WAAW,OAAO,kBAAkB;EACnE,OAAO;CACT;CAEA,MAAM,mBACJ,UAC4D;EAC5D,IAAI,OAAO,cAAc,eAAe,CAAC,UAAU,YACjD,OAAO,EAAE,WAAW,MAAM;EAG5B,MAAM,SAAS,mBAAmB,KAAK;EACvC,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS;GAClD,MAAM,QAAQ,OAAO;GACrB,IAAI,CAAC,OAAO;GACZ,IAAI;IAKF,IAAI,CAJO,UAAU,WACnB,QAAQ,KACR,oBAAoB,MAAM,SAAS,YAAY,WAAW,CAEtD,GAAG,OAAO,EAAE,WAAW,gBAAgB,QAAQ,KAAK,EAAE;GAC9D,SAAS,OAAO;IACd,OAAO;KAAE,WAAW,gBAAgB,QAAQ,KAAK;KAAG,SAAS,EAAE,MAAM;IAAE;GACzE;EACF;EAEA,OAAO,EAAE,WAAW,CAAC,EAAE;CACzB;CAEA,MAAM,cAAc,OAAO,UAAmC;EAC5D,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,iDAAiD;EAC/E,MAAM,WAAW,MAAM,QAAQ,MAAM,KAAK;GACxC,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,MAAM,cAAc,MAAM,IAAI;GAC9B,aAAa,MAAM;GACnB,WAAW,MAAM;EACnB,CAAC;EACD,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,2CAA2C,SAAS,QAAQ;CAChG;CAEA,MAAM,2BAA2B,OAAO,UAAsB;EAE5D,QAAA,GAAA,eAAA,uBAAA,CADgB,MAAM,OAAO,KAErB,GACN;GACE,aAAa,MAAM;GACnB,QAAQ;GACR,WAAW,QAAQ,QAAQ;EAC7B,GACA,QAAQ,gBACV;CACF;CAEA,MAAM,sBACJ,SACA,gBACA,iBAC6B;EAC7B,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,kBAAA,CAAmB,SAAS,EAAE;EACjE,KAAK,QAAQ;EACb,QAAQ,QAAQ,UAAU;EAC1B,SAAS,QAAQ,gBAAgB,WAAW;EAC5C,MAAM;EACN,aAAa,QAAQ;EACrB,WAAW,QAAQ,aAAa;EAChC,WAAW,KAAK,IAAI;CACtB;CAEA,MAAM,wBAAwB,OAC5B,SACA,gBACA,gBACG;EACH,IAAI,CAAC,cAAc,OAAO;EAC1B,MAAM,aAAa,QAAQ,mBAAmB,SAAS,gBAAgB,WAAW,CAAC;EACnF,CAAA,GAAA,eAAA,2BAAA,CAA2B,0BAA0B;EACrD,OAAO;CACT;CAEA,MAAM,iBAAiB,OAAO,cAAc,eAAe,UAAU,WAAW;CAIhF,MAAM,iBAAiB,OAAO,UAAkD;EAC9E,MAAM,cAAc,MAAM,yBAAyB,KAAK;EACxD,IAAI,gBAAgB,CAAC,SAAS,GAAG;GAC/B,MAAM,sBACJ,YAAY,SACZ,YAAY,SACZ,YAAY,WACd;GACA,OAAO;EACT;EACA,IAAI;GACF,MAAM,YACJ,mBAAmB,YAAY,SAAS,YAAY,SAAS,YAAY,WAAW,CACtF;GACA,OAAO;EACT,SAAS,OAAO;GACd,IACE,MAAM,sBACJ,YAAY,SACZ,YAAY,SACZ,YAAY,WACd,GAEA,OAAO;GAET,MAAM;EACR;CACF;CAEA,MAAM,oBAAoB,YAA4B;EACpD,MAAM,MAAM,KAAK,IAAI,yBAAyB,2BAA2B,KAAK,OAAO;EACrF,OAAO,OAAO,IAAI,IAAI,OAAO,IAAI;CACnC;CAEA,MAAM,4BAA4B,OAAO,UAAmC;EAC1E,KAAK,IAAI,UAAU,IAAK,WACtB,IAAI;GAEF,MAAM,YAAY,KAAK;GACvB,CAAA,GAAA,eAAA,2BAAA,CAA2B,4BAA4B;GACvD;EACF,SAAS,OAAO;GACd,IAAI,WAAW,yBAAyB;IACtC,CAAA,GAAA,eAAA,2BAAA,CAA2B,iCAAiC;IAC5D,MAAM;GACR;GACA,CAAA,GAAA,eAAA,2BAAA,CAA2B,yBAAyB;GAEpD,MAAM,MAAM,iBAAiB,OAAO,CAAC;EACvC;CAEJ;CAEA,MAAM,2BAA0C;EAC9C,IAAI,CAAC,cAAc,OAAO,QAAQ,QAAQ;EAC1C,mBAAmB,YAAY;GAC7B,IAAI;IACF,MAAM,aAAa,OAAO,yBAAyB;GACrD,UAAU;IACR,gBAAgB,KAAA;GAClB;EACF,EAAA,CAAG;EACH,OAAO;CACT;CAIA,MAAM,2BAA2B,OAAO,cAAsB;EAC5D,IAAI,CAAC,gBAAgB,CAAC,SAAS,GAAG;EAClC,IAAI;GACF,MAAM,mBAAmB;EAC3B,SAAS,OAAO;GACd,oBAAoB,OAAO,SAAS;EACtC;CACF;CAIA,MAAM,yBAAyB;EAC7B,IAAI,kBAAkB,QAAQ,oBAAoB,MAAM,WAAW,GAAG;EACtE,iBAAiB;EACjB,IAAI,UAAU,MAAM,OAAO,GAAG,MAAM,MAAM;EAC1C,IAAI;GACF,MAAM,SAAS,gBAAgB,OAAO;GACtC,UAAU,OAAO;GACjB,IAAI,OAAO,SAAS,MAAM,OAAO,QAAQ;EAC3C,UAAU;GACR,IAAI,QAAQ,SAAS,GAAG,MAAM,QAAQ,GAAG,OAAO;GAChD,iBAAiB;EACnB;CACF;CAEA,MAAM,SAAS,eAAe,UAAyB;EACrD,IAAI,aAAa;GACf,IAAI,cACF,IAAI;IACF,iBAAiB;GACnB,SAAS,OAAO;IAEd,OAAO,YAAY,WAAW;KAC5B,MAAM;IACR,CAAC;GACH;GAEF,OAAO;EACT;EACA,IAAI,MAAM,WAAW,GAGnB,OAAO,eAAe,QAAQ,QAAQ,IAAI,yBAAyB,QAAQ;EAE7E,WAAW;EAGX,IAAI;EACJ,IAAI;EACJ,MAAM,OAAO,IAAI,SAAe,WAAW,aAAa;GACtD,UAAU;GACV,SAAS;EACX,CAAC;EACD,cAAc;EACd,CAAM,YAAY;GAChB,IAAI;IACF,IAAI,cAAc,iBAAiB;IACnC,IAAI,OAAO;IACX,IAAI,SAAS;IACb,OAAO,MAAM,SAAS,GAAG;KACvB,MAAM,QAAQ,MAAM,OAAO,GAAG,YAAY;KAC1C,IAAI;MAGF,IAAI,MADkB,eAAe,KAAK,MAC1B,QAAQ,OAAO;WAC1B,SAAS;KAChB,SAAS,OAAO;MACd,MAAM,QAAQ,GAAG,KAAK;MACtB,MAAM;KACR;IACF;IAGA,IAAI,CAAC,gBAAgB,QAAQ,CAAC,QAAQ,MAAM,yBAAyB,QAAQ;IAC7E,QAAQ;GACV,SAAS,OAAO;IACd,OAAO,KAAK;GACd,UAAU;IACR,cAAc,KAAA;IACd,IAAI,MAAM,SAAS,GAAG,SAAS;GACjC;EACF,EAAA,CAAG;EACH,OAAO;CACT;CAEA,MAAM,iBAAiB;EACrB,IAAI,eAAe,SAAS,mBAAmB,GAAG;EAClD,QAAQ,iBAAiB;GACvB,QAAQ,KAAA;GACR,MAAW,KAAK,CAAC,CAAC,OAAO,UAAmB,oBAAoB,OAAO,OAAO,CAAC;EACjF,GAAG,eAAe;CACpB;CAEA,MAAM,mBAAmB;EACvB,MAAW,IAAI,CAAC,CAAC,OAAO,UAAmB,oBAAoB,OAAO,gBAAgB,CAAC;CACzF;CACA,MAAM,2BAA2B;EAC/B,IAAI,OAAO,aAAa,eAAe,SAAS,oBAAoB,UAClE,MAAW,IAAI,CAAC,CAAC,OAAO,UAAmB,oBAAoB,OAAO,kBAAkB,CAAC;CAE7F;CACA,MAAM,iBAAiB;EACrB,mBAAwB,CAAC,CAAC,OAAO,UAAmB,oBAAoB,OAAO,QAAQ,CAAC;CAC1F;CAEA,IAAI,QAAQ,uBAAuB,MAAM;EACvC,WAAW,mBAAmB,YAAY,UAAU;EACpD,WAAW,mBAAmB,oBAAoB,kBAAkB;CACtE;CACA,IAAI,cAAc;EAChB,WAAW,mBAAmB,UAAU,QAAQ;EAGhD,IAAI,QAAQ,wBAAwB,MAClC,iBAAiB,KAAK,yBAAyB,gBAAgB,GAAG,CAAC;CAEvE;CAEA,OAAO;EACL,MAAM,QAAQ,QAAQ;EACtB,UAAU,QAAQ;EAClB,IAAI,OAAO,SAAS;GAClB,IAAI,QAAQ,aAAa,KAAA,KAAa,MAAM,SAAA,GAAA,eAAA,aAAA,CAAqB,QAAQ,QAAQ,GAAG;GACpF,cAAc;GACd,IAAI,MAAM,UAAU,cAAc;IAChC,IAAI,eAAe,eAAe;KAChC,WAAW,OAAO,YAAY;KAC9B;IACF;IACA,MAAM,UAAU,MAAM,MAAM;IAC5B,IAAI,SAAS,WAAW,SAAS,YAAY;GAC/C;GACA,MAAM,KAAK,KAAK;GAChB,IAAI,aAAa;GACjB,IAAI,MAAM,UAAU,cAClB,MAAW,KAAK,CAAC,CAAC,OAAO,UAAmB,oBAAoB,OAAO,OAAO,CAAC;QAC1E,SAAS;EAClB;EACA,QAAQ;GACN,OAAO,MAAM,KAAK;EACpB;EACA,QAAQ;GACN,WAAW,sBAAsB,YAAY,UAAU;GACvD,WAAW,sBAAsB,oBAAoB,kBAAkB;GACvE,WAAW,sBAAsB,UAAU,QAAQ;GACnD,OAAO,MAAM,IAAI;EACnB;CACF;AACF"}
1
+ {"version":3,"file":"http-transport.cjs","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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"}
@@ -44,6 +44,23 @@ export interface BrowserHttpTransportOptions {
44
44
  fetchFn?: typeof fetch;
45
45
  transformPayload?: PayloadTransform | readonly PayloadTransform[];
46
46
  onDrop?: (event: LogEvent, reason: string) => void;
47
+ /**
48
+ * Abort a Fetch delivery after this many milliseconds. Browsers never time
49
+ * out a request on their own, so one stalled request would otherwise hold
50
+ * every later flush and close(). A timed-out batch falls back to the
51
+ * offline queue when one is configured. Set to 0 to disable. Defaults to
52
+ * 10000.
53
+ */
54
+ timeoutMs?: number;
55
+ /**
56
+ * Header that carries an idempotency key for each Fetch request, for
57
+ * example "Idempotency-Key". Every resend of a batch repeats its key, and
58
+ * offline-queue entries store it, so the collector can drop duplicates.
59
+ * Keys start with a random per-transport prefix, so senders never share
60
+ * one. Off by default because a cross-origin collector must allow the
61
+ * header in Access-Control-Allow-Headers. Beacon requests cannot carry it.
62
+ */
63
+ idempotencyKeyHeader?: string;
47
64
  }
48
65
  export declare function memoryBrowserHttpOfflineQueue(options?: MemoryBrowserHttpOfflineQueueOptions): BrowserHttpOfflineQueue & {
49
66
  size: () => number;
@@ -1 +1 @@
1
- {"version":3,"file":"http-transport.d.ts","sourceRoot":"","sources":["../src/http-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,KAAK,EAEV,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EAEf,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG,aAAa,CAAC;AAOlE,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3F;AAED,MAAM,WAAW,oCAAoC;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,qBAAqB,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACnE;AAED,MAAM,WAAW,2BAA2B;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,qBAAqB,CAAC;IACnC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,uBAAuB,CAAC;IACvC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,SAAS,gBAAgB,EAAE,CAAC;IAClE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,oCAAyC,GACjD,uBAAuB,GAAG;IAAE,IAAI,EAAE,MAAM,MAAM,CAAA;CAAE,CAoClD;AAiCD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,SAAS,CAoYpF"}
1
+ {"version":3,"file":"http-transport.d.ts","sourceRoot":"","sources":["../src/http-transport.ts"],"names":[],"mappings":"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"}