@loggerjs/browser 0.5.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -14
- package/dist/http-transport.cjs +110 -47
- package/dist/http-transport.cjs.map +1 -1
- package/dist/http-transport.d.ts +1 -0
- package/dist/http-transport.d.ts.map +1 -1
- package/dist/http-transport.js +110 -47
- package/dist/http-transport.js.map +1 -1
- package/dist/index.cjs +111 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +111 -47
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-transport.cjs.map +1 -1
- package/dist/indexeddb-transport.js.map +1 -1
- package/dist/offline-first-transport.cjs +1 -0
- package/dist/offline-first-transport.cjs.map +1 -1
- package/dist/offline-first-transport.d.ts.map +1 -1
- package/dist/offline-first-transport.js +1 -0
- package/dist/offline-first-transport.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
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,8 +185,9 @@ function browserHttpTransport(options) {
|
|
|
184
185
|
const fetchFn = options.fetchFn ?? globalThis.fetch?.bind(globalThis);
|
|
185
186
|
let offlineEntrySeq = 0;
|
|
186
187
|
let timer;
|
|
187
|
-
let
|
|
188
|
-
let
|
|
188
|
+
let activeFlush;
|
|
189
|
+
let flushingBeacon = false;
|
|
190
|
+
let replayPromise;
|
|
189
191
|
let lastContext;
|
|
190
192
|
const headers = (payloadHeaders, contentType = codec.contentType) => ({
|
|
191
193
|
"content-type": 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
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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");
|
|
@@ -293,17 +303,18 @@ function browserHttpTransport(options) {
|
|
|
293
303
|
incrementLoggerMetaCounter("transport.offline.queued");
|
|
294
304
|
return true;
|
|
295
305
|
};
|
|
306
|
+
const isOnline = () => typeof navigator === "undefined" || navigator.onLine !== false;
|
|
296
307
|
const sendFetchBatch = async (batch) => {
|
|
297
|
-
if (batch.length === 0) return;
|
|
298
308
|
const transformed = await encodeTransformedPayload(batch);
|
|
299
|
-
if (offlineQueue &&
|
|
309
|
+
if (offlineQueue && !isOnline()) {
|
|
300
310
|
await enqueueOfflinePayload(transformed.payload, transformed.headers, transformed.contentType);
|
|
301
|
-
return;
|
|
311
|
+
return "queued";
|
|
302
312
|
}
|
|
303
313
|
try {
|
|
304
314
|
await sendPayload(createOfflineEntry(transformed.payload, transformed.headers, transformed.contentType));
|
|
315
|
+
return "sent";
|
|
305
316
|
} catch (error) {
|
|
306
|
-
if (await enqueueOfflinePayload(transformed.payload, transformed.headers, transformed.contentType)) return;
|
|
317
|
+
if (await enqueueOfflinePayload(transformed.payload, transformed.headers, transformed.contentType)) return "queued";
|
|
307
318
|
throw error;
|
|
308
319
|
}
|
|
309
320
|
};
|
|
@@ -325,40 +336,88 @@ function browserHttpTransport(options) {
|
|
|
325
336
|
await sleep(replayRetryDelay(attempt));
|
|
326
337
|
}
|
|
327
338
|
};
|
|
328
|
-
const replayOfflineQueue =
|
|
329
|
-
if (!offlineQueue
|
|
330
|
-
|
|
339
|
+
const replayOfflineQueue = () => {
|
|
340
|
+
if (!offlineQueue) return Promise.resolve();
|
|
341
|
+
replayPromise ??= (async () => {
|
|
342
|
+
try {
|
|
343
|
+
await offlineQueue.replay(sendOfflineEntryWithRetry);
|
|
344
|
+
} finally {
|
|
345
|
+
replayPromise = void 0;
|
|
346
|
+
}
|
|
347
|
+
})();
|
|
348
|
+
return replayPromise;
|
|
349
|
+
};
|
|
350
|
+
const replayOfflineQueueSafely = async (operation) => {
|
|
351
|
+
if (!offlineQueue || !isOnline()) return;
|
|
331
352
|
try {
|
|
332
|
-
await
|
|
333
|
-
}
|
|
334
|
-
|
|
353
|
+
await replayOfflineQueue();
|
|
354
|
+
} catch (error) {
|
|
355
|
+
reportInternalError(error, operation);
|
|
335
356
|
}
|
|
336
357
|
};
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const batch = queue.splice(0, queue.length);
|
|
342
|
-
let pendingBatch = batch;
|
|
358
|
+
const flushBeaconQueue = () => {
|
|
359
|
+
if (flushingBeacon || options.transformPayload || queue.length === 0) return;
|
|
360
|
+
flushingBeacon = true;
|
|
361
|
+
let pending = queue.splice(0, queue.length);
|
|
343
362
|
try {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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;
|
|
363
|
+
const result = sendBeaconBatch(pending);
|
|
364
|
+
pending = result.remaining;
|
|
365
|
+
if (result.failure) throw result.failure.error;
|
|
354
366
|
} finally {
|
|
355
|
-
|
|
356
|
-
|
|
367
|
+
if (pending.length > 0) queue.unshift(...pending);
|
|
368
|
+
flushingBeacon = false;
|
|
357
369
|
}
|
|
358
370
|
};
|
|
371
|
+
const flush = (preferBeacon = false) => {
|
|
372
|
+
if (activeFlush) {
|
|
373
|
+
if (preferBeacon) try {
|
|
374
|
+
flushBeaconQueue();
|
|
375
|
+
} catch (error) {
|
|
376
|
+
return activeFlush.then(() => {
|
|
377
|
+
throw error;
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
return activeFlush;
|
|
381
|
+
}
|
|
382
|
+
if (queue.length === 0) return preferBeacon ? Promise.resolve() : replayOfflineQueueSafely("replay");
|
|
383
|
+
clearTimer();
|
|
384
|
+
let resolve;
|
|
385
|
+
let reject;
|
|
386
|
+
const task = new Promise((onResolve, onReject) => {
|
|
387
|
+
resolve = onResolve;
|
|
388
|
+
reject = onReject;
|
|
389
|
+
});
|
|
390
|
+
activeFlush = task;
|
|
391
|
+
(async () => {
|
|
392
|
+
try {
|
|
393
|
+
if (preferBeacon) flushBeaconQueue();
|
|
394
|
+
let sent = false;
|
|
395
|
+
let queued = false;
|
|
396
|
+
while (queue.length > 0) {
|
|
397
|
+
const batch = queue.splice(0, maxBatchSize);
|
|
398
|
+
try {
|
|
399
|
+
if (await sendFetchBatch(batch) === "sent") sent = true;
|
|
400
|
+
else queued = true;
|
|
401
|
+
} catch (error) {
|
|
402
|
+
queue.unshift(...batch);
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (!preferBeacon && sent && !queued) await replayOfflineQueueSafely("replay");
|
|
407
|
+
resolve();
|
|
408
|
+
} catch (error) {
|
|
409
|
+
reject(error);
|
|
410
|
+
} finally {
|
|
411
|
+
activeFlush = void 0;
|
|
412
|
+
if (queue.length > 0) schedule();
|
|
413
|
+
}
|
|
414
|
+
})();
|
|
415
|
+
return task;
|
|
416
|
+
};
|
|
359
417
|
const schedule = () => {
|
|
360
|
-
if (timer || flushIntervalMs <= 0) return;
|
|
418
|
+
if (activeFlush || timer || flushIntervalMs <= 0) return;
|
|
361
419
|
timer = setTimeout(() => {
|
|
420
|
+
timer = void 0;
|
|
362
421
|
flush(false).catch((error) => reportInternalError(error, "flush"));
|
|
363
422
|
}, flushIntervalMs);
|
|
364
423
|
};
|
|
@@ -375,7 +434,10 @@ function browserHttpTransport(options) {
|
|
|
375
434
|
globalThis.addEventListener?.("pagehide", onPageHide);
|
|
376
435
|
globalThis.addEventListener?.("visibilitychange", onVisibilityChange);
|
|
377
436
|
}
|
|
378
|
-
if (offlineQueue)
|
|
437
|
+
if (offlineQueue) {
|
|
438
|
+
globalThis.addEventListener?.("online", onOnline);
|
|
439
|
+
if (options.offlineReplayOnStart ?? true) setTimeout(() => void replayOfflineQueueSafely("startup-replay"), 0);
|
|
440
|
+
}
|
|
379
441
|
return {
|
|
380
442
|
name: options.name ?? "browser-http",
|
|
381
443
|
minLevel: options.minLevel,
|
|
@@ -391,6 +453,7 @@ function browserHttpTransport(options) {
|
|
|
391
453
|
if (dropped) reportDrop(dropped, "queue-full");
|
|
392
454
|
}
|
|
393
455
|
queue.push(event);
|
|
456
|
+
if (activeFlush) return;
|
|
394
457
|
if (queue.length >= maxBatchSize) flush(false).catch((error) => reportInternalError(error, "flush"));
|
|
395
458
|
else schedule();
|
|
396
459
|
},
|
|
@@ -2028,6 +2091,7 @@ function offlineFirstTransport(remote, options = {}) {
|
|
|
2028
2091
|
name,
|
|
2029
2092
|
minLevel: options.minLevel ?? remote.minLevel,
|
|
2030
2093
|
queue,
|
|
2094
|
+
ready: remote.ready && (() => remote.ready?.()),
|
|
2031
2095
|
write(record, context) {
|
|
2032
2096
|
return sendOrQueue(context.toEvent(record), context);
|
|
2033
2097
|
},
|