@loggerjs/browser 0.5.5 → 0.5.6

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.
@@ -62,6 +62,7 @@ function browserHttpTransport(options) {
62
62
  const beaconCodec = options.beaconCodec ?? codec;
63
63
  const queue = [];
64
64
  const maxBatchSize = options.maxBatchSize ?? 50;
65
+ if (!Number.isSafeInteger(maxBatchSize) || maxBatchSize <= 0) throw new RangeError("maxBatchSize must be a positive safe integer");
65
66
  const flushIntervalMs = options.flushIntervalMs ?? 2e3;
66
67
  const maxQueueSize = options.maxQueueSize ?? 1e3;
67
68
  const dropPolicy = options.dropPolicy ?? "drop-oldest";
@@ -74,7 +75,8 @@ function browserHttpTransport(options) {
74
75
  const fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);
75
76
  let offlineEntrySeq = 0;
76
77
  let timer;
77
- let flushing = false;
78
+ let activeFlush;
79
+ let flushingBeacon = false;
78
80
  let replayingOffline = false;
79
81
  let lastContext;
80
82
  const headers = (payloadHeaders, contentType = codec.contentType) => ({
@@ -100,9 +102,18 @@ function browserHttpTransport(options) {
100
102
  };
101
103
  const createBeaconChunks = (batch) => {
102
104
  const chunks = [];
105
+ const oversized = [];
103
106
  let currentEvents = [];
104
107
  let currentPayload;
105
108
  for (const event of batch) {
109
+ if (currentEvents.length === maxBatchSize && currentPayload !== void 0) {
110
+ chunks.push({
111
+ events: currentEvents,
112
+ payload: currentPayload
113
+ });
114
+ currentEvents = [];
115
+ currentPayload = void 0;
116
+ }
106
117
  const candidateEvents = [...currentEvents, event];
107
118
  const candidatePayload = beaconCodec.encode(candidateEvents);
108
119
  if (payloadByteLength(candidatePayload) <= beaconMaxBytes) {
@@ -110,7 +121,7 @@ function browserHttpTransport(options) {
110
121
  currentPayload = candidatePayload;
111
122
  continue;
112
123
  }
113
- if (currentEvents.length > 0 && currentPayload) chunks.push({
124
+ if (currentEvents.length > 0 && currentPayload !== void 0) chunks.push({
114
125
  events: currentEvents,
115
126
  payload: currentPayload
116
127
  });
@@ -121,33 +132,32 @@ function browserHttpTransport(options) {
121
132
  } else {
122
133
  currentEvents = [];
123
134
  currentPayload = void 0;
124
- reportDrop(event, "beacon-too-large");
135
+ oversized.push(event);
125
136
  }
126
137
  }
127
- if (currentEvents.length > 0 && currentPayload) chunks.push({
138
+ if (currentEvents.length > 0 && currentPayload !== void 0) chunks.push({
128
139
  events: currentEvents,
129
140
  payload: currentPayload
130
141
  });
142
+ for (const event of oversized) reportDrop(event, "beacon-too-large");
131
143
  return chunks;
132
144
  };
133
145
  const sendBeaconBatch = (batch) => {
134
- if (typeof navigator === "undefined" || !navigator.sendBeacon) return {
135
- ok: false,
136
- remaining: batch
137
- };
146
+ if (typeof navigator === "undefined" || !navigator.sendBeacon) return { remaining: batch };
138
147
  const chunks = createBeaconChunks(batch);
139
148
  for (let index = 0; index < chunks.length; index++) {
140
149
  const chunk = chunks[index];
141
150
  if (!chunk) continue;
142
- if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) return {
143
- ok: false,
144
- remaining: remainingEvents(chunks, index)
145
- };
151
+ try {
152
+ if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) return { remaining: remainingEvents(chunks, index) };
153
+ } catch (error) {
154
+ return {
155
+ remaining: remainingEvents(chunks, index),
156
+ failure: { error }
157
+ };
158
+ }
146
159
  }
147
- return {
148
- ok: true,
149
- remaining: []
150
- };
160
+ return { remaining: [] };
151
161
  };
152
162
  const sendPayload = async (entry) => {
153
163
  if (!fetchFn) throw new Error("fetch is not available for browserHttpTransport");
@@ -224,31 +234,65 @@ function browserHttpTransport(options) {
224
234
  replayingOffline = false;
225
235
  }
226
236
  };
227
- const flush = async (preferBeacon = false) => {
228
- if (flushing || queue.length === 0) return;
229
- flushing = true;
230
- clearTimer();
231
- const batch = queue.splice(0, queue.length);
232
- let pendingBatch = batch;
237
+ const flushBeaconQueue = () => {
238
+ if (flushingBeacon || options.transformPayload || queue.length === 0) return;
239
+ flushingBeacon = true;
240
+ let pending = queue.splice(0, queue.length);
233
241
  try {
234
- if (preferBeacon && !options.transformPayload) {
235
- const beaconResult = sendBeaconBatch(batch);
236
- if (beaconResult.ok || beaconResult.remaining.length === 0) return;
237
- pendingBatch = beaconResult.remaining;
238
- }
239
- await sendFetchBatch(pendingBatch);
240
- pendingBatch = [];
241
- } catch (error) {
242
- if (pendingBatch.length > 0) queue.unshift(...pendingBatch);
243
- throw error;
242
+ const result = sendBeaconBatch(pending);
243
+ pending = result.remaining;
244
+ if (result.failure) throw result.failure.error;
244
245
  } finally {
245
- flushing = false;
246
- if (queue.length > 0) schedule();
246
+ if (pending.length > 0) queue.unshift(...pending);
247
+ flushingBeacon = false;
247
248
  }
248
249
  };
250
+ const flush = (preferBeacon = false) => {
251
+ if (activeFlush) {
252
+ if (preferBeacon) try {
253
+ flushBeaconQueue();
254
+ } catch (error) {
255
+ return activeFlush.then(() => {
256
+ throw error;
257
+ });
258
+ }
259
+ return activeFlush;
260
+ }
261
+ if (queue.length === 0) return Promise.resolve();
262
+ clearTimer();
263
+ let resolve;
264
+ let reject;
265
+ const task = new Promise((onResolve, onReject) => {
266
+ resolve = onResolve;
267
+ reject = onReject;
268
+ });
269
+ activeFlush = task;
270
+ (async () => {
271
+ try {
272
+ if (preferBeacon) flushBeaconQueue();
273
+ while (queue.length > 0) {
274
+ const batch = queue.splice(0, maxBatchSize);
275
+ try {
276
+ await sendFetchBatch(batch);
277
+ } catch (error) {
278
+ queue.unshift(...batch);
279
+ throw error;
280
+ }
281
+ }
282
+ resolve();
283
+ } catch (error) {
284
+ reject(error);
285
+ } finally {
286
+ activeFlush = void 0;
287
+ if (queue.length > 0) schedule();
288
+ }
289
+ })();
290
+ return task;
291
+ };
249
292
  const schedule = () => {
250
- if (timer || flushIntervalMs <= 0) return;
293
+ if (activeFlush || timer || flushIntervalMs <= 0) return;
251
294
  timer = setTimeout(() => {
295
+ timer = void 0;
252
296
  flush(false).catch((error) => reportInternalError(error, "flush"));
253
297
  }, flushIntervalMs);
254
298
  };
@@ -281,6 +325,7 @@ function browserHttpTransport(options) {
281
325
  if (dropped) reportDrop(dropped, "queue-full");
282
326
  }
283
327
  queue.push(event);
328
+ if (activeFlush) return;
284
329
  if (queue.length >= maxBatchSize) flush(false).catch((error) => reportInternalError(error, "flush"));
285
330
  else schedule();
286
331
  },
@@ -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 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 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 flushing = 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 let currentEvents: LogEvent[] = [];\n let currentPayload: string | Uint8Array | undefined;\n\n for (const event of batch) {\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) {\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 reportDrop(event, \"beacon-too-large\");\n }\n }\n\n if (currentEvents.length > 0 && currentPayload) {\n chunks.push({ events: currentEvents, payload: currentPayload });\n }\n\n return chunks;\n };\n\n const sendBeaconBatch = (batch: LogEvent[]): { ok: boolean; remaining: LogEvent[] } => {\n if (typeof navigator === \"undefined\" || !navigator.sendBeacon) {\n return { ok: false, 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 const ok = navigator.sendBeacon(\n options.url,\n payloadToBeaconBody(chunk.payload, beaconCodec.contentType),\n );\n if (!ok) return { ok: false, remaining: remainingEvents(chunks, index) };\n }\n\n return { ok: true, 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 const flush = async (preferBeacon = false) => {\n if (flushing || queue.length === 0) return;\n flushing = true;\n clearTimer();\n const batch = queue.splice(0, queue.length);\n let pendingBatch = batch;\n\n try {\n if (preferBeacon && !options.transformPayload) {\n const beaconResult = sendBeaconBatch(batch);\n if (beaconResult.ok || beaconResult.remaining.length === 0) return;\n pendingBatch = beaconResult.remaining;\n }\n\n await sendFetchBatch(pendingBatch);\n pendingBatch = [];\n } catch (error) {\n if (pendingBatch.length > 0) queue.unshift(...pendingBatch);\n throw error;\n } finally {\n flushing = false;\n if (queue.length > 0) schedule();\n }\n };\n\n const schedule = () => {\n if (timer || flushIntervalMs <= 0) return;\n timer = setTimeout(() => {\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 (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.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 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 +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,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,CAsSpF"}
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,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,CAiWpF"}
@@ -61,6 +61,7 @@ function browserHttpTransport(options) {
61
61
  const beaconCodec = options.beaconCodec ?? codec;
62
62
  const queue = [];
63
63
  const maxBatchSize = options.maxBatchSize ?? 50;
64
+ if (!Number.isSafeInteger(maxBatchSize) || maxBatchSize <= 0) throw new RangeError("maxBatchSize must be a positive safe integer");
64
65
  const flushIntervalMs = options.flushIntervalMs ?? 2e3;
65
66
  const maxQueueSize = options.maxQueueSize ?? 1e3;
66
67
  const dropPolicy = options.dropPolicy ?? "drop-oldest";
@@ -73,7 +74,8 @@ function browserHttpTransport(options) {
73
74
  const fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);
74
75
  let offlineEntrySeq = 0;
75
76
  let timer;
76
- let flushing = false;
77
+ let activeFlush;
78
+ let flushingBeacon = false;
77
79
  let replayingOffline = false;
78
80
  let lastContext;
79
81
  const headers = (payloadHeaders, contentType = codec.contentType) => ({
@@ -99,9 +101,18 @@ function browserHttpTransport(options) {
99
101
  };
100
102
  const createBeaconChunks = (batch) => {
101
103
  const chunks = [];
104
+ const oversized = [];
102
105
  let currentEvents = [];
103
106
  let currentPayload;
104
107
  for (const event of batch) {
108
+ if (currentEvents.length === maxBatchSize && currentPayload !== void 0) {
109
+ chunks.push({
110
+ events: currentEvents,
111
+ payload: currentPayload
112
+ });
113
+ currentEvents = [];
114
+ currentPayload = void 0;
115
+ }
105
116
  const candidateEvents = [...currentEvents, event];
106
117
  const candidatePayload = beaconCodec.encode(candidateEvents);
107
118
  if (payloadByteLength(candidatePayload) <= beaconMaxBytes) {
@@ -109,7 +120,7 @@ function browserHttpTransport(options) {
109
120
  currentPayload = candidatePayload;
110
121
  continue;
111
122
  }
112
- if (currentEvents.length > 0 && currentPayload) chunks.push({
123
+ if (currentEvents.length > 0 && currentPayload !== void 0) chunks.push({
113
124
  events: currentEvents,
114
125
  payload: currentPayload
115
126
  });
@@ -120,33 +131,32 @@ function browserHttpTransport(options) {
120
131
  } else {
121
132
  currentEvents = [];
122
133
  currentPayload = void 0;
123
- reportDrop(event, "beacon-too-large");
134
+ oversized.push(event);
124
135
  }
125
136
  }
126
- if (currentEvents.length > 0 && currentPayload) chunks.push({
137
+ if (currentEvents.length > 0 && currentPayload !== void 0) chunks.push({
127
138
  events: currentEvents,
128
139
  payload: currentPayload
129
140
  });
141
+ for (const event of oversized) reportDrop(event, "beacon-too-large");
130
142
  return chunks;
131
143
  };
132
144
  const sendBeaconBatch = (batch) => {
133
- if (typeof navigator === "undefined" || !navigator.sendBeacon) return {
134
- ok: false,
135
- remaining: batch
136
- };
145
+ if (typeof navigator === "undefined" || !navigator.sendBeacon) return { remaining: batch };
137
146
  const chunks = createBeaconChunks(batch);
138
147
  for (let index = 0; index < chunks.length; index++) {
139
148
  const chunk = chunks[index];
140
149
  if (!chunk) continue;
141
- if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) return {
142
- ok: false,
143
- remaining: remainingEvents(chunks, index)
144
- };
150
+ try {
151
+ if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) return { remaining: remainingEvents(chunks, index) };
152
+ } catch (error) {
153
+ return {
154
+ remaining: remainingEvents(chunks, index),
155
+ failure: { error }
156
+ };
157
+ }
145
158
  }
146
- return {
147
- ok: true,
148
- remaining: []
149
- };
159
+ return { remaining: [] };
150
160
  };
151
161
  const sendPayload = async (entry) => {
152
162
  if (!fetchFn) throw new Error("fetch is not available for browserHttpTransport");
@@ -223,31 +233,65 @@ function browserHttpTransport(options) {
223
233
  replayingOffline = false;
224
234
  }
225
235
  };
226
- const flush = async (preferBeacon = false) => {
227
- if (flushing || queue.length === 0) return;
228
- flushing = true;
229
- clearTimer();
230
- const batch = queue.splice(0, queue.length);
231
- let pendingBatch = batch;
236
+ const flushBeaconQueue = () => {
237
+ if (flushingBeacon || options.transformPayload || queue.length === 0) return;
238
+ flushingBeacon = true;
239
+ let pending = queue.splice(0, queue.length);
232
240
  try {
233
- if (preferBeacon && !options.transformPayload) {
234
- const beaconResult = sendBeaconBatch(batch);
235
- if (beaconResult.ok || beaconResult.remaining.length === 0) return;
236
- pendingBatch = beaconResult.remaining;
237
- }
238
- await sendFetchBatch(pendingBatch);
239
- pendingBatch = [];
240
- } catch (error) {
241
- if (pendingBatch.length > 0) queue.unshift(...pendingBatch);
242
- throw error;
241
+ const result = sendBeaconBatch(pending);
242
+ pending = result.remaining;
243
+ if (result.failure) throw result.failure.error;
243
244
  } finally {
244
- flushing = false;
245
- if (queue.length > 0) schedule();
245
+ if (pending.length > 0) queue.unshift(...pending);
246
+ flushingBeacon = false;
246
247
  }
247
248
  };
249
+ const flush = (preferBeacon = false) => {
250
+ if (activeFlush) {
251
+ if (preferBeacon) try {
252
+ flushBeaconQueue();
253
+ } catch (error) {
254
+ return activeFlush.then(() => {
255
+ throw error;
256
+ });
257
+ }
258
+ return activeFlush;
259
+ }
260
+ if (queue.length === 0) return Promise.resolve();
261
+ clearTimer();
262
+ let resolve;
263
+ let reject;
264
+ const task = new Promise((onResolve, onReject) => {
265
+ resolve = onResolve;
266
+ reject = onReject;
267
+ });
268
+ activeFlush = task;
269
+ (async () => {
270
+ try {
271
+ if (preferBeacon) flushBeaconQueue();
272
+ while (queue.length > 0) {
273
+ const batch = queue.splice(0, maxBatchSize);
274
+ try {
275
+ await sendFetchBatch(batch);
276
+ } catch (error) {
277
+ queue.unshift(...batch);
278
+ throw error;
279
+ }
280
+ }
281
+ resolve();
282
+ } catch (error) {
283
+ reject(error);
284
+ } finally {
285
+ activeFlush = void 0;
286
+ if (queue.length > 0) schedule();
287
+ }
288
+ })();
289
+ return task;
290
+ };
248
291
  const schedule = () => {
249
- if (timer || flushIntervalMs <= 0) return;
292
+ if (activeFlush || timer || flushIntervalMs <= 0) return;
250
293
  timer = setTimeout(() => {
294
+ timer = void 0;
251
295
  flush(false).catch((error) => reportInternalError(error, "flush"));
252
296
  }, flushIntervalMs);
253
297
  };
@@ -280,6 +324,7 @@ function browserHttpTransport(options) {
280
324
  if (dropped) reportDrop(dropped, "queue-full");
281
325
  }
282
326
  queue.push(event);
327
+ if (activeFlush) return;
283
328
  if (queue.length >= maxBatchSize) flush(false).catch((error) => reportInternalError(error, "flush"));
284
329
  else schedule();
285
330
  },
@@ -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 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 flushing = 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 let currentEvents: LogEvent[] = [];\n let currentPayload: string | Uint8Array | undefined;\n\n for (const event of batch) {\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) {\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 reportDrop(event, \"beacon-too-large\");\n }\n }\n\n if (currentEvents.length > 0 && currentPayload) {\n chunks.push({ events: currentEvents, payload: currentPayload });\n }\n\n return chunks;\n };\n\n const sendBeaconBatch = (batch: LogEvent[]): { ok: boolean; remaining: LogEvent[] } => {\n if (typeof navigator === \"undefined\" || !navigator.sendBeacon) {\n return { ok: false, 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 const ok = navigator.sendBeacon(\n options.url,\n payloadToBeaconBody(chunk.payload, beaconCodec.contentType),\n );\n if (!ok) return { ok: false, remaining: remainingEvents(chunks, index) };\n }\n\n return { ok: true, 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 const flush = async (preferBeacon = false) => {\n if (flushing || queue.length === 0) return;\n flushing = true;\n clearTimer();\n const batch = queue.splice(0, queue.length);\n let pendingBatch = batch;\n\n try {\n if (preferBeacon && !options.transformPayload) {\n const beaconResult = sendBeaconBatch(batch);\n if (beaconResult.ok || beaconResult.remaining.length === 0) return;\n pendingBatch = beaconResult.remaining;\n }\n\n await sendFetchBatch(pendingBatch);\n pendingBatch = [];\n } catch (error) {\n if (pendingBatch.length > 0) queue.unshift(...pendingBatch);\n throw error;\n } finally {\n flushing = false;\n if (queue.length > 0) schedule();\n }\n };\n\n const schedule = () => {\n if (timer || flushIntervalMs <= 0) return;\n timer = setTimeout(() => {\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 (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 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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"}