@edraj/sauron-browser 1.4.1 → 1.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/dist/index.d.cts CHANGED
@@ -144,6 +144,19 @@ interface TransactionItem {
144
144
  workflow_id?: string;
145
145
  workflow_name?: string;
146
146
  timestamp: string;
147
+ /**
148
+ * Developer-supplied flat string tags for THIS transaction.
149
+ *
150
+ * Unlike {@link EventItem} and the error item, this is NOT merged with the
151
+ * scope — see {@link TransactionInput.tags}. Omitted entirely when empty.
152
+ */
153
+ tags?: Record<string, string>;
154
+ /**
155
+ * Developer-supplied freeform JSON for THIS transaction — the request body,
156
+ * the response body, a retry count. Capped and replaced with a truncation
157
+ * marker past {@link MAX_TRANSACTION_EXTRA_BYTES}. Omitted when empty.
158
+ */
159
+ extra?: Record<string, unknown>;
147
160
  }
148
161
  /** An identity association (PostHog-style `identify`). */
149
162
  interface IdentifyItem {
@@ -330,6 +343,29 @@ interface TransactionInput {
330
343
  httpMethod?: string | null;
331
344
  httpStatus?: number | null;
332
345
  url?: string | null;
346
+ /**
347
+ * Flat string tags for this transaction.
348
+ *
349
+ * **Per-call only — the scope is NOT merged in**, which is the one place
350
+ * transactions differ from `track()` and `captureException()`. Those two
351
+ * merge `setTag`/`setExtra` defaults; a transaction carries only what its own
352
+ * call site attached. Transactions are the highest-volume signal (one per
353
+ * navigation and per HTTP call), so inheriting a global blob would write it
354
+ * onto every row.
355
+ */
356
+ tags?: Record<string, string>;
357
+ /**
358
+ * Freeform JSON for this transaction — the request body, the response body,
359
+ * an order id, a retry count.
360
+ *
361
+ * Per-call only, for the reason on {@link TransactionInput.tags}. Serialized
362
+ * and capped at {@link MAX_TRANSACTION_EXTRA_BYTES}; past that the whole map
363
+ * is replaced with `{ _truncated: true, _bytes: N }` so one large body cannot
364
+ * take a batched envelope over the ingest limit and drop every span in it.
365
+ *
366
+ * Nothing here is scrubbed. `beforeSend` is the redaction seam.
367
+ */
368
+ extra?: Record<string, unknown>;
333
369
  }
334
370
 
335
371
  /**
@@ -561,7 +597,34 @@ declare function parseError(err: unknown): Frame[];
561
597
  /** Small dependency-free helpers shared across the SDK. */
562
598
  /** SDK identity, embedded in every envelope header. */
563
599
  declare const SDK_NAME = "sauron.javascript";
564
- declare const SDK_VERSION = "1.4.1";
600
+ declare const SDK_VERSION = "1.6.0";
601
+ /**
602
+ * Largest serialized `extra` a single transaction may carry, in bytes.
603
+ *
604
+ * Transactions are the highest-volume signal and they ship in BATCHED
605
+ * envelopes, so one oversized payload does not fail alone — ingest rejects the
606
+ * whole envelope past `INGEST_MAX_BODY_BYTES` (1 MiB by default) and every
607
+ * unrelated span batched with it is lost. Since the motivating use of
608
+ * transaction `extra` is request and response bodies, that is not a remote
609
+ * hazard.
610
+ */
611
+ declare const MAX_TRANSACTION_EXTRA_BYTES: number;
612
+ /**
613
+ * Cap a transaction's `extra`, substituting a marker when it is too large.
614
+ *
615
+ * Replaces the WHOLE map rather than trimming keys: a half-written JSON value
616
+ * is worse than an honest marker, and per-key trimming would make the result
617
+ * depend on key iteration order, which differs across the five SDKs. The
618
+ * marker is deliberately readable on the dashboard — `_truncated` says data
619
+ * was dropped rather than silently serving a short object that looks complete.
620
+ *
621
+ * Returns the input unchanged when it fits. A value that cannot be serialized
622
+ * at all (a cycle, a BigInt) is replaced by the same marker with `_bytes: -1`,
623
+ * because the alternative is throwing from inside `trackTransaction` — and an
624
+ * SDK that crashes the app it is measuring is worse than one that drops a
625
+ * payload.
626
+ */
627
+ declare function capTransactionExtra(extra: Record<string, unknown>, maxBytes?: number): Record<string, unknown>;
565
628
 
566
629
  /**
567
630
  * `@edraj/sauron-browser` — public API surface.
@@ -673,4 +736,4 @@ declare const Sauron: {
673
736
  getClient: typeof getClient;
674
737
  };
675
738
 
676
- export { type ActiveWorkflow, type AppContext, type BeforeBreadcrumb, type BeforeSend, type Breadcrumb, type BreadcrumbBatchItem, type BreadcrumbInput, type CaptureOptions, type Context, type DeviceContext, type Dsn, DsnError, type Envelope, type EnvelopeHeader, type EnvelopeItem, type ErrorItem, type EventItem, type ExceptionValue, type Frame, type Hint, type IdentifyItem, type InitOptions, type ItemType, type Level, type Mechanism, type OsContext, type ResolvedOptions, type RuntimeContext, SDK_NAME, SDK_VERSION, Sauron, SauronClient, type SdkInfo, type TrackOptions, type TransactionInput, type TransactionItem, type TransactionOp, type TransportOptions, type UserContext, type UserInput, type WorkflowResult, type WorkflowStatus, addBreadcrumb, buildEnvelope, cancelWorkflow, captureException, captureMessage, close, Sauron as default, endWorkflow, flush, getClient, getScreen, getWorkflow, identify, init, isInAppFrame, parseDsn, parseError, parseStackString, reset, setContext, setExtra, setScreen, setTag, setTags, setUser, startWorkflow, track, trackTransaction };
739
+ export { type ActiveWorkflow, type AppContext, type BeforeBreadcrumb, type BeforeSend, type Breadcrumb, type BreadcrumbBatchItem, type BreadcrumbInput, type CaptureOptions, type Context, type DeviceContext, type Dsn, DsnError, type Envelope, type EnvelopeHeader, type EnvelopeItem, type ErrorItem, type EventItem, type ExceptionValue, type Frame, type Hint, type IdentifyItem, type InitOptions, type ItemType, type Level, MAX_TRANSACTION_EXTRA_BYTES, type Mechanism, type OsContext, type ResolvedOptions, type RuntimeContext, SDK_NAME, SDK_VERSION, Sauron, SauronClient, type SdkInfo, type TrackOptions, type TransactionInput, type TransactionItem, type TransactionOp, type TransportOptions, type UserContext, type UserInput, type WorkflowResult, type WorkflowStatus, addBreadcrumb, buildEnvelope, cancelWorkflow, capTransactionExtra, captureException, captureMessage, close, Sauron as default, endWorkflow, flush, getClient, getScreen, getWorkflow, identify, init, isInAppFrame, parseDsn, parseError, parseStackString, reset, setContext, setExtra, setScreen, setTag, setTags, setUser, startWorkflow, track, trackTransaction };
package/dist/index.d.ts CHANGED
@@ -144,6 +144,19 @@ interface TransactionItem {
144
144
  workflow_id?: string;
145
145
  workflow_name?: string;
146
146
  timestamp: string;
147
+ /**
148
+ * Developer-supplied flat string tags for THIS transaction.
149
+ *
150
+ * Unlike {@link EventItem} and the error item, this is NOT merged with the
151
+ * scope — see {@link TransactionInput.tags}. Omitted entirely when empty.
152
+ */
153
+ tags?: Record<string, string>;
154
+ /**
155
+ * Developer-supplied freeform JSON for THIS transaction — the request body,
156
+ * the response body, a retry count. Capped and replaced with a truncation
157
+ * marker past {@link MAX_TRANSACTION_EXTRA_BYTES}. Omitted when empty.
158
+ */
159
+ extra?: Record<string, unknown>;
147
160
  }
148
161
  /** An identity association (PostHog-style `identify`). */
149
162
  interface IdentifyItem {
@@ -330,6 +343,29 @@ interface TransactionInput {
330
343
  httpMethod?: string | null;
331
344
  httpStatus?: number | null;
332
345
  url?: string | null;
346
+ /**
347
+ * Flat string tags for this transaction.
348
+ *
349
+ * **Per-call only — the scope is NOT merged in**, which is the one place
350
+ * transactions differ from `track()` and `captureException()`. Those two
351
+ * merge `setTag`/`setExtra` defaults; a transaction carries only what its own
352
+ * call site attached. Transactions are the highest-volume signal (one per
353
+ * navigation and per HTTP call), so inheriting a global blob would write it
354
+ * onto every row.
355
+ */
356
+ tags?: Record<string, string>;
357
+ /**
358
+ * Freeform JSON for this transaction — the request body, the response body,
359
+ * an order id, a retry count.
360
+ *
361
+ * Per-call only, for the reason on {@link TransactionInput.tags}. Serialized
362
+ * and capped at {@link MAX_TRANSACTION_EXTRA_BYTES}; past that the whole map
363
+ * is replaced with `{ _truncated: true, _bytes: N }` so one large body cannot
364
+ * take a batched envelope over the ingest limit and drop every span in it.
365
+ *
366
+ * Nothing here is scrubbed. `beforeSend` is the redaction seam.
367
+ */
368
+ extra?: Record<string, unknown>;
333
369
  }
334
370
 
335
371
  /**
@@ -561,7 +597,34 @@ declare function parseError(err: unknown): Frame[];
561
597
  /** Small dependency-free helpers shared across the SDK. */
562
598
  /** SDK identity, embedded in every envelope header. */
563
599
  declare const SDK_NAME = "sauron.javascript";
564
- declare const SDK_VERSION = "1.4.1";
600
+ declare const SDK_VERSION = "1.6.0";
601
+ /**
602
+ * Largest serialized `extra` a single transaction may carry, in bytes.
603
+ *
604
+ * Transactions are the highest-volume signal and they ship in BATCHED
605
+ * envelopes, so one oversized payload does not fail alone — ingest rejects the
606
+ * whole envelope past `INGEST_MAX_BODY_BYTES` (1 MiB by default) and every
607
+ * unrelated span batched with it is lost. Since the motivating use of
608
+ * transaction `extra` is request and response bodies, that is not a remote
609
+ * hazard.
610
+ */
611
+ declare const MAX_TRANSACTION_EXTRA_BYTES: number;
612
+ /**
613
+ * Cap a transaction's `extra`, substituting a marker when it is too large.
614
+ *
615
+ * Replaces the WHOLE map rather than trimming keys: a half-written JSON value
616
+ * is worse than an honest marker, and per-key trimming would make the result
617
+ * depend on key iteration order, which differs across the five SDKs. The
618
+ * marker is deliberately readable on the dashboard — `_truncated` says data
619
+ * was dropped rather than silently serving a short object that looks complete.
620
+ *
621
+ * Returns the input unchanged when it fits. A value that cannot be serialized
622
+ * at all (a cycle, a BigInt) is replaced by the same marker with `_bytes: -1`,
623
+ * because the alternative is throwing from inside `trackTransaction` — and an
624
+ * SDK that crashes the app it is measuring is worse than one that drops a
625
+ * payload.
626
+ */
627
+ declare function capTransactionExtra(extra: Record<string, unknown>, maxBytes?: number): Record<string, unknown>;
565
628
 
566
629
  /**
567
630
  * `@edraj/sauron-browser` — public API surface.
@@ -673,4 +736,4 @@ declare const Sauron: {
673
736
  getClient: typeof getClient;
674
737
  };
675
738
 
676
- export { type ActiveWorkflow, type AppContext, type BeforeBreadcrumb, type BeforeSend, type Breadcrumb, type BreadcrumbBatchItem, type BreadcrumbInput, type CaptureOptions, type Context, type DeviceContext, type Dsn, DsnError, type Envelope, type EnvelopeHeader, type EnvelopeItem, type ErrorItem, type EventItem, type ExceptionValue, type Frame, type Hint, type IdentifyItem, type InitOptions, type ItemType, type Level, type Mechanism, type OsContext, type ResolvedOptions, type RuntimeContext, SDK_NAME, SDK_VERSION, Sauron, SauronClient, type SdkInfo, type TrackOptions, type TransactionInput, type TransactionItem, type TransactionOp, type TransportOptions, type UserContext, type UserInput, type WorkflowResult, type WorkflowStatus, addBreadcrumb, buildEnvelope, cancelWorkflow, captureException, captureMessage, close, Sauron as default, endWorkflow, flush, getClient, getScreen, getWorkflow, identify, init, isInAppFrame, parseDsn, parseError, parseStackString, reset, setContext, setExtra, setScreen, setTag, setTags, setUser, startWorkflow, track, trackTransaction };
739
+ export { type ActiveWorkflow, type AppContext, type BeforeBreadcrumb, type BeforeSend, type Breadcrumb, type BreadcrumbBatchItem, type BreadcrumbInput, type CaptureOptions, type Context, type DeviceContext, type Dsn, DsnError, type Envelope, type EnvelopeHeader, type EnvelopeItem, type ErrorItem, type EventItem, type ExceptionValue, type Frame, type Hint, type IdentifyItem, type InitOptions, type ItemType, type Level, MAX_TRANSACTION_EXTRA_BYTES, type Mechanism, type OsContext, type ResolvedOptions, type RuntimeContext, SDK_NAME, SDK_VERSION, Sauron, SauronClient, type SdkInfo, type TrackOptions, type TransactionInput, type TransactionItem, type TransactionOp, type TransportOptions, type UserContext, type UserInput, type WorkflowResult, type WorkflowStatus, addBreadcrumb, buildEnvelope, cancelWorkflow, capTransactionExtra, captureException, captureMessage, close, Sauron as default, endWorkflow, flush, getClient, getScreen, getWorkflow, identify, init, isInAppFrame, parseDsn, parseError, parseStackString, reset, setContext, setExtra, setScreen, setTag, setTags, setUser, startWorkflow, track, trackTransaction };
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4
4
 
5
5
  // src/utils.ts
6
6
  var SDK_NAME = "sauron.javascript";
7
- var SDK_VERSION = "1.4.1";
7
+ var SDK_VERSION = "1.6.0";
8
8
  function getGlobal() {
9
9
  return globalThis;
10
10
  }
@@ -83,6 +83,32 @@ function makeLogger(debug) {
83
83
  warn: (...args) => console.warn("[sauron]", ...args)
84
84
  };
85
85
  }
86
+ var MAX_TRANSACTION_EXTRA_BYTES = 16 * 1024;
87
+ function capTransactionExtra(extra, maxBytes = MAX_TRANSACTION_EXTRA_BYTES) {
88
+ let bytes;
89
+ try {
90
+ const json = JSON.stringify(extra);
91
+ if (json === void 0) return { _truncated: true, _bytes: -1 };
92
+ bytes = utf8Length(json);
93
+ } catch {
94
+ return { _truncated: true, _bytes: -1 };
95
+ }
96
+ if (bytes <= maxBytes) return extra;
97
+ return { _truncated: true, _bytes: bytes };
98
+ }
99
+ function utf8Length(s) {
100
+ let n = 0;
101
+ for (let i = 0; i < s.length; i++) {
102
+ const c = s.charCodeAt(i);
103
+ if (c < 128) n += 1;
104
+ else if (c < 2048) n += 2;
105
+ else if (c >= 55296 && c <= 56319) {
106
+ n += 4;
107
+ i++;
108
+ } else n += 3;
109
+ }
110
+ return n;
111
+ }
86
112
 
87
113
  // src/identity.ts
88
114
  var DEVICE_ID_KEY = "sauron.device_id";
@@ -813,12 +839,12 @@ function installHistory() {
813
839
  const loc = g2.location;
814
840
  if (!hist) return;
815
841
  let lastPath = toPath(loc?.href ?? null, loc?.href);
816
- const emit = (toHref) => {
842
+ const emit = (toHref, operation) => {
817
843
  const to = toPath(toHref, loc?.href);
818
844
  const from = lastPath;
819
845
  lastPath = to;
820
846
  if (from === to) return;
821
- withInternal(() => addNavigationBreadcrumb(from, to));
847
+ withInternal(() => addNavigationBreadcrumb(from, to, operation));
822
848
  if (to && navHandler) {
823
849
  try {
824
850
  navHandler(to);
@@ -829,11 +855,12 @@ function installHistory() {
829
855
  const wrap = (name) => {
830
856
  const original = hist[name];
831
857
  if (typeof original !== "function" || isWrapped(original)) return;
858
+ const operation = name === "pushState" ? "push" : "replace";
832
859
  hist[name] = markWrapped(function sauronHistory(...args) {
833
860
  const result = original.apply(this, args);
834
861
  if (!isInternal()) {
835
862
  const urlArg = args[2];
836
- emit(urlArg != null ? String(urlArg) : loc?.href ?? null);
863
+ emit(urlArg != null ? String(urlArg) : loc?.href ?? null, operation);
837
864
  }
838
865
  return result;
839
866
  });
@@ -845,7 +872,7 @@ function installHistory() {
845
872
  wrap("replaceState");
846
873
  if (typeof g2.addEventListener === "function") {
847
874
  const onPopState = () => {
848
- if (!isInternal()) emit(loc?.href ?? null);
875
+ if (!isInternal()) emit(loc?.href ?? null, "pop");
849
876
  };
850
877
  g2.addEventListener("popstate", onPopState);
851
878
  registerPatch("popstate", () => {
@@ -1022,7 +1049,7 @@ function normalizeOp(op) {
1022
1049
  return op && TRANSACTION_OPS.includes(op) ? op : "custom";
1023
1050
  }
1024
1051
  function buildTransactionItem(input, distinctId, sessionId2) {
1025
- return {
1052
+ const item = {
1026
1053
  type: "transaction",
1027
1054
  name: input.name,
1028
1055
  op: normalizeOp(input.op),
@@ -1035,6 +1062,11 @@ function buildTransactionItem(input, distinctId, sessionId2) {
1035
1062
  session_id: sessionId2,
1036
1063
  timestamp: nowIso()
1037
1064
  };
1065
+ if (input.tags && Object.keys(input.tags).length > 0) item.tags = { ...input.tags };
1066
+ if (input.extra && Object.keys(input.extra).length > 0) {
1067
+ item.extra = capTransactionExtra({ ...input.extra });
1068
+ }
1069
+ return item;
1038
1070
  }
1039
1071
  function trackTransaction(input) {
1040
1072
  const client = getClient();
@@ -2191,13 +2223,13 @@ function addBreadcrumb(input, hint) {
2191
2223
  if (!client) return;
2192
2224
  client.addBreadcrumb(normalizeBreadcrumb(input), hint);
2193
2225
  }
2194
- function addNavigationBreadcrumb(from, to) {
2226
+ function addNavigationBreadcrumb(from, to, operation = "push") {
2195
2227
  addBreadcrumb({
2196
2228
  type: "navigation",
2197
2229
  category: "history",
2198
2230
  level: "info",
2199
2231
  message: null,
2200
- data: { from, to }
2232
+ data: { from, to, operation }
2201
2233
  });
2202
2234
  }
2203
2235
 
@@ -2297,6 +2329,6 @@ var Sauron = {
2297
2329
  };
2298
2330
  var index_default = Sauron;
2299
2331
 
2300
- export { DsnError, SDK_NAME, SDK_VERSION, Sauron, SauronClient, addBreadcrumb2 as addBreadcrumb, buildEnvelope, cancelWorkflow2 as cancelWorkflow, captureException2 as captureException, captureMessage2 as captureMessage, close, index_default as default, endWorkflow2 as endWorkflow, flush, getClient, getScreen2 as getScreen, getWorkflow2 as getWorkflow, identify2 as identify, init2 as init, isInAppFrame, parseDsn, parseError, parseStackString, reset, setContext, setExtra, setScreen2 as setScreen, setTag, setTags, setUser, startWorkflow2 as startWorkflow, track2 as track, trackTransaction2 as trackTransaction };
2332
+ export { DsnError, MAX_TRANSACTION_EXTRA_BYTES, SDK_NAME, SDK_VERSION, Sauron, SauronClient, addBreadcrumb2 as addBreadcrumb, buildEnvelope, cancelWorkflow2 as cancelWorkflow, capTransactionExtra, captureException2 as captureException, captureMessage2 as captureMessage, close, index_default as default, endWorkflow2 as endWorkflow, flush, getClient, getScreen2 as getScreen, getWorkflow2 as getWorkflow, identify2 as identify, init2 as init, isInAppFrame, parseDsn, parseError, parseStackString, reset, setContext, setExtra, setScreen2 as setScreen, setTag, setTags, setUser, startWorkflow2 as startWorkflow, track2 as track, trackTransaction2 as trackTransaction };
2301
2333
  //# sourceMappingURL=index.js.map
2302
2334
  //# sourceMappingURL=index.js.map