@logbrew/sdk 0.1.30 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/core.cjs +45 -10
  2. package/package.json +1 -1
package/core.cjs CHANGED
@@ -97,6 +97,8 @@ const MAX_CLIENT_REPORT_COUNT = 1_000_000_000;
97
97
  const DELIVERY_HEALTH_SCHEMA_VERSION = 1;
98
98
  const EVENT_QUEUE_FACTORY = Symbol.for("@logbrew/sdk.eventQueueFactory");
99
99
  const CONTEXT_PROVIDER = Symbol.for("@logbrew/sdk.contextProvider");
100
+ // Runtime packages supply request-scoped breadcrumbs through this seam; core merges them into issues.
101
+ const BREADCRUMB_PROVIDER = Symbol.for("@logbrew/sdk.breadcrumbProvider");
100
102
  const MAX_ERROR_CAUSES = 5;
101
103
  const BUILTIN_ERROR_NAMES = new Set([
102
104
  "AggregateError",
@@ -216,6 +218,7 @@ class LogBrewClient {
216
218
  sdkVersion,
217
219
  context,
218
220
  [CONTEXT_PROVIDER]: contextProvider,
221
+ [BREADCRUMB_PROVIDER]: breadcrumbProvider,
219
222
  maxRetries = 2,
220
223
  eventFilter,
221
224
  maxQueueSize = DEFAULT_MAX_QUEUE_SIZE,
@@ -237,6 +240,9 @@ class LogBrewClient {
237
240
  if (contextProvider !== undefined && typeof contextProvider !== "function") {
238
241
  throw new SdkError("validation_error", "context provider must be a function");
239
242
  }
243
+ if (breadcrumbProvider !== undefined && typeof breadcrumbProvider !== "function") {
244
+ throw new SdkError("validation_error", "breadcrumb provider must be a function");
245
+ }
240
246
  requireNonNegativeInteger("maxRetries", maxRetries);
241
247
  if (eventFilter !== undefined && typeof eventFilter !== "function") {
242
248
  throw new SdkError("validation_error", "eventFilter must be a function");
@@ -286,6 +292,7 @@ class LogBrewClient {
286
292
  onEventDropped,
287
293
  context: clientContext,
288
294
  contextProvider,
295
+ breadcrumbProvider,
289
296
  sdk: {
290
297
  name: sdkName,
291
298
  language: "javascript",
@@ -308,6 +315,7 @@ class LogBrewClient {
308
315
  onEventDropped,
309
316
  context,
310
317
  contextProvider,
318
+ breadcrumbProvider,
311
319
  eventStore,
312
320
  eventQueueFactory,
313
321
  transport,
@@ -327,6 +335,7 @@ class LogBrewClient {
327
335
  this.onEventDropped = onEventDropped;
328
336
  this.context = cloneTelemetryContext(context);
329
337
  this.contextProvider = contextProvider;
338
+ this.breadcrumbProvider = breadcrumbProvider;
330
339
  this.transport = transport;
331
340
  this.sdk = sdk;
332
341
  this.maxRetries = maxRetries;
@@ -632,20 +641,46 @@ class LogBrewClient {
632
641
  }
633
642
 
634
643
  #pushIssue(id, timestamp, attributes) {
635
- const withBreadcrumbs = attributes.breadcrumbs !== undefined || this.issueBreadcrumbs.length === 0
644
+ if (attributes.breadcrumbs !== undefined) {
645
+ return this.#pushEvent("issue", id, timestamp, validateIssue(attributes));
646
+ }
647
+ const provided = this.#providedBreadcrumbs();
648
+ const merged = provided.breadcrumbs.length === 0
649
+ ? this.issueBreadcrumbs
650
+ : [...this.issueBreadcrumbs, ...provided.breadcrumbs]
651
+ .sort((left, right) => Date.parse(left.timestamp) - Date.parse(right.timestamp));
652
+ const breadcrumbs = merged.slice(-MAX_ISSUE_BREADCRUMBS);
653
+ const truncated = attributes.breadcrumbsTruncated === true || this.issueBreadcrumbsTruncated
654
+ || provided.truncated || merged.length > breadcrumbs.length;
655
+ const withBreadcrumbs = breadcrumbs.length === 0
636
656
  ? attributes
637
- : {
638
- ...attributes,
639
- breadcrumbs: this.issueBreadcrumbs,
640
- ...(
641
- attributes.breadcrumbsTruncated === true || this.issueBreadcrumbsTruncated
642
- ? { breadcrumbsTruncated: true }
643
- : {}
644
- )
645
- };
657
+ : { ...attributes, breadcrumbs, ...(truncated ? { breadcrumbsTruncated: true } : {}) };
646
658
  return this.#pushEvent("issue", id, timestamp, validateIssue(withBreadcrumbs));
647
659
  }
648
660
 
661
+ // Provider failures and invalid entries never cost the issue itself.
662
+ #providedBreadcrumbs() {
663
+ let provided;
664
+ try {
665
+ provided = this.breadcrumbProvider?.();
666
+ } catch {
667
+ return { breadcrumbs: [], truncated: false };
668
+ }
669
+ if (!provided || !Array.isArray(provided.breadcrumbs)) {
670
+ return { breadcrumbs: [], truncated: false };
671
+ }
672
+ const breadcrumbs = [];
673
+ let truncated = provided.truncated === true || provided.breadcrumbs.length > MAX_ISSUE_BREADCRUMBS;
674
+ for (const breadcrumb of provided.breadcrumbs.slice(-MAX_ISSUE_BREADCRUMBS)) {
675
+ try {
676
+ breadcrumbs.push(validateIssueBreadcrumb(breadcrumb));
677
+ } catch {
678
+ truncated = true;
679
+ }
680
+ }
681
+ return { breadcrumbs, truncated };
682
+ }
683
+
649
684
  #pushEvent(eventType, id, timestamp, attributes) {
650
685
  if (this.closed) {
651
686
  throw new SdkError("shutdown_error", "client is already shut down");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logbrew/sdk",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "Public LogBrew JavaScript SDK for building, validating, and flushing event batches.",
5
5
  "type": "module",
6
6
  "main": "./index.cjs",