@logbrew/sdk 0.1.13 → 0.1.15

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 CHANGED
@@ -125,6 +125,13 @@ try {
125
125
  service: "checkout-web",
126
126
  runtime: "browser",
127
127
  fingerprint: "checkout-runtime-error",
128
+ evidence: {
129
+ likelyRootCause: "The payment provider exhausted its retry budget.",
130
+ likelyFixArea: { file: "src/payments/gateway.js", function: "chargeOrder", line: 42 },
131
+ impact: { failedAction: "checkout.submit", userVisibleOutcome: "The order was not confirmed." },
132
+ capturedFields: ["provider.status", "retry.count"],
133
+ redactedFields: ["provider.message"]
134
+ },
128
135
  debugIdMap: {
129
136
  "https://cdn.example/assets/app.js": "11111111-2222-4333-8444-555555555555"
130
137
  },
@@ -136,6 +143,8 @@ try {
136
143
 
137
144
  The helper records the error name/message, a typed exception with capture mechanism and handled state, and up to 32 ordered generated `stackFrames`, with query strings, hashes, and local absolute prefixes removed. Each frame carries filename, positive line/column, a conservatively parsed function name when the runtime provides one, and an optional matched Debug ID. Applications that create frames directly may also provide bounded `function`, `module`, and `inApp` identity. Existing first-frame metadata remains available for compatible grouping and tooling. The helper also emits an `issueGroupingKey` based on source, error type, and the sanitized first frame, plus an optional app-owned `issueFingerprint` when you pass a stable, safe, low-cardinality `fingerprint`. Nested `Error.cause` chains and `AggregateError.errors` are summarized as bounded cause counts, types, and sources without copying nested messages or stacks. Raw stack text is included only with `includeErrorStack: true`.
138
145
 
146
+ `evidence` is explicit application knowledge, not an SDK inference. LogBrew labels `likelyRootCause` as a reported hypothesis and keeps `likelyFixArea` separate from observed frames. Field-state arrays make missing, redacted, and truncated evidence visible to API, CLI, dashboard, and agent consumers. Keep identities low-cardinality, use repository-relative file paths, and never put credentials, request bodies, personal data, or raw user input in these fields.
147
+
139
148
  Use `addBreadcrumb()` for explicit navigation, state, action, or network steps that should appear on later issues. The client keeps only the most recent 64 entries and marks an issue with `breadcrumbsTruncated: true` after older entries are evicted. `clearBreadcrumbs()` removes the current history.
140
149
 
141
150
  ```js
package/core.cjs CHANGED
@@ -75,6 +75,7 @@ const PINO_SENSITIVE_CONTEXT_FIELDS = new Set([
75
75
  const TRACEPARENT_PATTERN = /^([0-9a-fA-F]{2})-([0-9a-fA-F]{32})-([0-9a-fA-F]{16})-([0-9a-fA-F]{2})$/u;
76
76
  const ZERO_TRACE_ID = "00000000000000000000000000000000";
77
77
  const ZERO_SPAN_ID = "0000000000000000";
78
+ const RFC3339_TIMESTAMP_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d{1,9})?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/u;
78
79
  const DEFAULT_MAX_QUEUE_SIZE = 1000;
79
80
  const DEFAULT_MAX_QUEUE_BYTES = 4 * 1024 * 1024;
80
81
  const DEFAULT_MAX_BATCH_EVENTS = 100;
@@ -138,7 +139,8 @@ const {
138
139
  cloneIssueDiagnostics,
139
140
  createIssueException,
140
141
  validateIssueBreadcrumb,
141
- validateIssueDiagnostics
142
+ validateIssueDiagnostics,
143
+ validateIssueEvidence
142
144
  } = buildIssueDiagnosticsHelpers({ SdkError, requireTimestamp, validateIssueStackFrames });
143
145
  const {
144
146
  cloneTelemetryContext,
@@ -1410,6 +1412,7 @@ function createIssueAttributesFromError(error, options = {}) {
1410
1412
  exception,
1411
1413
  exceptionChain,
1412
1414
  ...(stackFrames.length > 0 ? { stackFrames } : {}),
1415
+ ...(options.evidence === undefined ? {} : { evidence: validateIssueEvidence(options.evidence) }),
1413
1416
  metadata: compactMetadata(metadata)
1414
1417
  };
1415
1418
  }
@@ -2197,17 +2200,25 @@ function requireTraceFlags(traceFlags) {
2197
2200
 
2198
2201
  function requireTimestamp(timestamp) {
2199
2202
  requireNonEmpty("timestamp", timestamp);
2200
- if (timestamp.endsWith("Z")) {
2201
- return;
2202
- }
2203
2203
  const timePortion = timestamp.split("T")[1];
2204
- if (timePortion && (timePortion.includes("+") || /.+-.+/.test(timePortion))) {
2205
- return;
2204
+ if (!timestamp.endsWith("Z")
2205
+ && !(timePortion && (timePortion.includes("+") || /.+-.+/.test(timePortion)))) {
2206
+ throw new SdkError(
2207
+ "validation_error",
2208
+ `timestamp must include a timezone offset: ${timestamp}`
2209
+ );
2210
+ }
2211
+ const match = timestamp.match(RFC3339_TIMESTAMP_PATTERN);
2212
+ const calendar = new Date(0);
2213
+ calendar.setUTCHours(0, 0, 0, 0);
2214
+ calendar.setUTCFullYear(Number(match?.[1]), Number(match?.[2]) - 1, Number(match?.[3]));
2215
+ if (!match
2216
+ || Number(match[1]) === 0
2217
+ || calendar.getUTCFullYear() !== Number(match[1])
2218
+ || calendar.getUTCMonth() !== Number(match[2]) - 1
2219
+ || calendar.getUTCDate() !== Number(match[3])) {
2220
+ throw new SdkError("validation_error", `invalid timestamp: ${timestamp}`);
2206
2221
  }
2207
- throw new SdkError(
2208
- "validation_error",
2209
- `timestamp must include a timezone offset: ${timestamp}`
2210
- );
2211
2222
  }
2212
2223
 
2213
2224
  function cloneMetadata(metadata) {