@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.
package/dist/index.cjs CHANGED
@@ -172,6 +172,7 @@ function browserHttpTransport(options) {
172
172
  const beaconCodec = options.beaconCodec ?? codec;
173
173
  const queue = [];
174
174
  const maxBatchSize = options.maxBatchSize ?? 50;
175
+ if (!Number.isSafeInteger(maxBatchSize) || maxBatchSize <= 0) throw new RangeError("maxBatchSize must be a positive safe integer");
175
176
  const flushIntervalMs = options.flushIntervalMs ?? 2e3;
176
177
  const maxQueueSize = options.maxQueueSize ?? 1e3;
177
178
  const dropPolicy = options.dropPolicy ?? "drop-oldest";
@@ -184,7 +185,8 @@ function browserHttpTransport(options) {
184
185
  const fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);
185
186
  let offlineEntrySeq = 0;
186
187
  let timer;
187
- let flushing = false;
188
+ let activeFlush;
189
+ let flushingBeacon = false;
188
190
  let replayingOffline = false;
189
191
  let lastContext;
190
192
  const headers = (payloadHeaders, contentType = codec.contentType) => ({
@@ -210,9 +212,18 @@ function browserHttpTransport(options) {
210
212
  };
211
213
  const createBeaconChunks = (batch) => {
212
214
  const chunks = [];
215
+ const oversized = [];
213
216
  let currentEvents = [];
214
217
  let currentPayload;
215
218
  for (const event of batch) {
219
+ if (currentEvents.length === maxBatchSize && currentPayload !== void 0) {
220
+ chunks.push({
221
+ events: currentEvents,
222
+ payload: currentPayload
223
+ });
224
+ currentEvents = [];
225
+ currentPayload = void 0;
226
+ }
216
227
  const candidateEvents = [...currentEvents, event];
217
228
  const candidatePayload = beaconCodec.encode(candidateEvents);
218
229
  if (payloadByteLength$1(candidatePayload) <= beaconMaxBytes) {
@@ -220,7 +231,7 @@ function browserHttpTransport(options) {
220
231
  currentPayload = candidatePayload;
221
232
  continue;
222
233
  }
223
- if (currentEvents.length > 0 && currentPayload) chunks.push({
234
+ if (currentEvents.length > 0 && currentPayload !== void 0) chunks.push({
224
235
  events: currentEvents,
225
236
  payload: currentPayload
226
237
  });
@@ -231,33 +242,32 @@ function browserHttpTransport(options) {
231
242
  } else {
232
243
  currentEvents = [];
233
244
  currentPayload = void 0;
234
- reportDrop(event, "beacon-too-large");
245
+ oversized.push(event);
235
246
  }
236
247
  }
237
- if (currentEvents.length > 0 && currentPayload) chunks.push({
248
+ if (currentEvents.length > 0 && currentPayload !== void 0) chunks.push({
238
249
  events: currentEvents,
239
250
  payload: currentPayload
240
251
  });
252
+ for (const event of oversized) reportDrop(event, "beacon-too-large");
241
253
  return chunks;
242
254
  };
243
255
  const sendBeaconBatch = (batch) => {
244
- if (typeof navigator === "undefined" || !navigator.sendBeacon) return {
245
- ok: false,
246
- remaining: batch
247
- };
256
+ if (typeof navigator === "undefined" || !navigator.sendBeacon) return { remaining: batch };
248
257
  const chunks = createBeaconChunks(batch);
249
258
  for (let index = 0; index < chunks.length; index++) {
250
259
  const chunk = chunks[index];
251
260
  if (!chunk) continue;
252
- if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) return {
253
- ok: false,
254
- remaining: remainingEvents(chunks, index)
255
- };
261
+ try {
262
+ if (!navigator.sendBeacon(options.url, payloadToBeaconBody(chunk.payload, beaconCodec.contentType))) return { remaining: remainingEvents(chunks, index) };
263
+ } catch (error) {
264
+ return {
265
+ remaining: remainingEvents(chunks, index),
266
+ failure: { error }
267
+ };
268
+ }
256
269
  }
257
- return {
258
- ok: true,
259
- remaining: []
260
- };
270
+ return { remaining: [] };
261
271
  };
262
272
  const sendPayload = async (entry) => {
263
273
  if (!fetchFn) throw new Error("fetch is not available for browserHttpTransport");
@@ -334,31 +344,65 @@ function browserHttpTransport(options) {
334
344
  replayingOffline = false;
335
345
  }
336
346
  };
337
- const flush = async (preferBeacon = false) => {
338
- if (flushing || queue.length === 0) return;
339
- flushing = true;
340
- clearTimer();
341
- const batch = queue.splice(0, queue.length);
342
- let pendingBatch = batch;
347
+ const flushBeaconQueue = () => {
348
+ if (flushingBeacon || options.transformPayload || queue.length === 0) return;
349
+ flushingBeacon = true;
350
+ let pending = queue.splice(0, queue.length);
343
351
  try {
344
- if (preferBeacon && !options.transformPayload) {
345
- const beaconResult = sendBeaconBatch(batch);
346
- if (beaconResult.ok || beaconResult.remaining.length === 0) return;
347
- pendingBatch = beaconResult.remaining;
348
- }
349
- await sendFetchBatch(pendingBatch);
350
- pendingBatch = [];
351
- } catch (error) {
352
- if (pendingBatch.length > 0) queue.unshift(...pendingBatch);
353
- throw error;
352
+ const result = sendBeaconBatch(pending);
353
+ pending = result.remaining;
354
+ if (result.failure) throw result.failure.error;
354
355
  } finally {
355
- flushing = false;
356
- if (queue.length > 0) schedule();
356
+ if (pending.length > 0) queue.unshift(...pending);
357
+ flushingBeacon = false;
357
358
  }
358
359
  };
360
+ const flush = (preferBeacon = false) => {
361
+ if (activeFlush) {
362
+ if (preferBeacon) try {
363
+ flushBeaconQueue();
364
+ } catch (error) {
365
+ return activeFlush.then(() => {
366
+ throw error;
367
+ });
368
+ }
369
+ return activeFlush;
370
+ }
371
+ if (queue.length === 0) return Promise.resolve();
372
+ clearTimer();
373
+ let resolve;
374
+ let reject;
375
+ const task = new Promise((onResolve, onReject) => {
376
+ resolve = onResolve;
377
+ reject = onReject;
378
+ });
379
+ activeFlush = task;
380
+ (async () => {
381
+ try {
382
+ if (preferBeacon) flushBeaconQueue();
383
+ while (queue.length > 0) {
384
+ const batch = queue.splice(0, maxBatchSize);
385
+ try {
386
+ await sendFetchBatch(batch);
387
+ } catch (error) {
388
+ queue.unshift(...batch);
389
+ throw error;
390
+ }
391
+ }
392
+ resolve();
393
+ } catch (error) {
394
+ reject(error);
395
+ } finally {
396
+ activeFlush = void 0;
397
+ if (queue.length > 0) schedule();
398
+ }
399
+ })();
400
+ return task;
401
+ };
359
402
  const schedule = () => {
360
- if (timer || flushIntervalMs <= 0) return;
403
+ if (activeFlush || timer || flushIntervalMs <= 0) return;
361
404
  timer = setTimeout(() => {
405
+ timer = void 0;
362
406
  flush(false).catch((error) => reportInternalError(error, "flush"));
363
407
  }, flushIntervalMs);
364
408
  };
@@ -391,6 +435,7 @@ function browserHttpTransport(options) {
391
435
  if (dropped) reportDrop(dropped, "queue-full");
392
436
  }
393
437
  queue.push(event);
438
+ if (activeFlush) return;
394
439
  if (queue.length >= maxBatchSize) flush(false).catch((error) => reportInternalError(error, "flush"));
395
440
  else schedule();
396
441
  },