@edraj/sauron-browser 1.5.0 → 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/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  All notable changes to `@edraj/sauron-browser` are documented here.
4
4
 
5
+ ## 1.6.0
6
+
7
+ ### Added
8
+
9
+ - **Navigation direction on history breadcrumbs.** SPA navigation breadcrumbs
10
+ now carry `operation` alongside `from` and `to`: `push` for
11
+ `history.pushState`, `replace` for `history.replaceState`, and `pop` for
12
+ `popstate`. A breadcrumb trail no longer reads the same whether the user
13
+ advanced through a flow or backed out of it.
14
+
15
+ The vocabulary is shared with the Flutter SDK's `SauronNavigatorObserver`
16
+ (`push` / `pop` / `replace` / `remove`), so a trail reads the same whichever
17
+ SDK sent it. `remove` has no web equivalent and is never emitted here.
18
+
19
+ **A forward navigation is recorded as `pop`.** `history.forward()` fires the
20
+ same `popstate` event as `history.back()` and carries nothing to separate
21
+ them, so `pop` means "moved through history" rather than specifically "went
22
+ back". Telling them apart would mean writing a counter into `history.state`,
23
+ which the host app's router also owns — not a trade this SDK makes to
24
+ improve a breadcrumb.
25
+
26
+ No API change: `operation` is added to `data` on breadcrumbs the SDK already
27
+ emitted, and the same-path guard still suppresses a `replaceState` to the
28
+ current URL before any direction is recorded.
29
+
5
30
  ## 1.5.0
6
31
 
7
32
  ### Added
package/README.md CHANGED
@@ -985,7 +985,7 @@ const appFrames = frames.filter((f) => isInAppFrame(f.filename));
985
985
 
986
986
  ```ts
987
987
  const SDK_NAME: string // 'sauron.javascript'
988
- const SDK_VERSION: string // '1.5.0'
988
+ const SDK_VERSION: string // '1.6.0'
989
989
  ```
990
990
 
991
991
  The SDK identity embedded in `header.sdk` of every envelope.
@@ -1027,7 +1027,7 @@ On by default:
1027
1027
  | `window.onunhandledrejection` | Error item from `event.reason`, mechanism `{ type: 'onunhandledrejection', handled: false }`, level `error`. |
1028
1028
  | `console.log/info/warn/error/debug` | Breadcrumb, category `console`, level mapped (`warn`→`warning`, `error`→`error`, `debug`→`debug`, else `info`), message = arguments joined and truncated to 512 chars, `data: { arguments: n }`. Output is untouched. |
1029
1029
  | `document` click listener (capture, passive) | Breadcrumb, category `ui.click`, message = a `tag#id.class` selector (up to 3 classes). Element text and attribute values are never serialized. |
1030
- | `history.pushState` / `replaceState` / `popstate` | Breadcrumb, type `navigation`, category `history`, `data: { from, to }` as paths. Same-path transitions are skipped. |
1030
+ | `history.pushState` / `replaceState` / `popstate` | Breadcrumb, type `navigation`, category `history`, `data: { from, to, operation }` paths plus the direction: `push`, `replace` or `pop`. Same-path transitions are skipped. |
1031
1031
  | `fetch` | Breadcrumb, category `fetch`, message `METHOD url`, `data: { method, url, status_code }`, level `warning` for status >= 400. |
1032
1032
  | `XMLHttpRequest.prototype.open` / `send` | Breadcrumb, category `xhr`, same shape as `fetch`. |
1033
1033
  | `document` `visibilitychange` + window `pagehide` | Beacon flush of the pending batch on unload. |
@@ -1040,6 +1040,16 @@ Opt-in:
1040
1040
  | `performance: true` | A `navigation` transaction for the initial page load (Navigation Timing, captured on `load`), an `http` transaction per instrumented `fetch` (`name` = `METHOD /path`, `status` `ok`/`error`), and a `navigation` transaction per SPA route change measured over one animation frame. No-op when `document` is undefined. |
1041
1041
  | `screenTracking: true` | Sets the screen to the new path on each SPA History navigation, which emits a `$screen` event on change. |
1042
1042
 
1043
+ `operation` uses the same vocabulary as the Flutter SDK's
1044
+ `SauronNavigatorObserver` (`push` / `pop` / `replace` / `remove`), so a trail
1045
+ reads the same whichever SDK sent it. `remove` has no web equivalent and is
1046
+ never emitted here. One limit worth knowing: **a forward navigation is recorded
1047
+ as `pop`.** `history.forward()` fires the same `popstate` event as
1048
+ `history.back()` and carries nothing to separate them, so `pop` means "moved
1049
+ through history", not specifically "went back". Distinguishing them would mean
1050
+ writing to `history.state`, which the host app's router also owns — not a
1051
+ trade the SDK makes.
1052
+
1043
1053
  Two guards keep the SDK from observing itself: a reentrancy flag held while SDK
1044
1054
  code runs, and a denylist on the DSN host. Requests the transport makes are
1045
1055
  therefore never turned into breadcrumbs or transactions. Wrappers are tagged, so
@@ -1116,7 +1126,7 @@ Sauron.track('upgraded', {}, { tags: { tier: 'trial' } });
1116
1126
 
1117
1127
  ```html
1118
1128
  <script type="module">
1119
- import { Sauron } from 'https://esm.sh/@edraj/sauron-browser@1.5.0';
1129
+ import { Sauron } from 'https://esm.sh/@edraj/sauron-browser@1.6.0';
1120
1130
  Sauron.init({ dsn: 'https://pk_test@ingest.example.com/42' });
1121
1131
  </script>
1122
1132
  ```
package/dist/index.cjs CHANGED
@@ -8,7 +8,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
8
8
 
9
9
  // src/utils.ts
10
10
  var SDK_NAME = "sauron.javascript";
11
- var SDK_VERSION = "1.5.0";
11
+ var SDK_VERSION = "1.6.0";
12
12
  function getGlobal() {
13
13
  return globalThis;
14
14
  }
@@ -843,12 +843,12 @@ function installHistory() {
843
843
  const loc = g2.location;
844
844
  if (!hist) return;
845
845
  let lastPath = toPath(loc?.href ?? null, loc?.href);
846
- const emit = (toHref) => {
846
+ const emit = (toHref, operation) => {
847
847
  const to = toPath(toHref, loc?.href);
848
848
  const from = lastPath;
849
849
  lastPath = to;
850
850
  if (from === to) return;
851
- withInternal(() => addNavigationBreadcrumb(from, to));
851
+ withInternal(() => addNavigationBreadcrumb(from, to, operation));
852
852
  if (to && navHandler) {
853
853
  try {
854
854
  navHandler(to);
@@ -859,11 +859,12 @@ function installHistory() {
859
859
  const wrap = (name) => {
860
860
  const original = hist[name];
861
861
  if (typeof original !== "function" || isWrapped(original)) return;
862
+ const operation = name === "pushState" ? "push" : "replace";
862
863
  hist[name] = markWrapped(function sauronHistory(...args) {
863
864
  const result = original.apply(this, args);
864
865
  if (!isInternal()) {
865
866
  const urlArg = args[2];
866
- emit(urlArg != null ? String(urlArg) : loc?.href ?? null);
867
+ emit(urlArg != null ? String(urlArg) : loc?.href ?? null, operation);
867
868
  }
868
869
  return result;
869
870
  });
@@ -875,7 +876,7 @@ function installHistory() {
875
876
  wrap("replaceState");
876
877
  if (typeof g2.addEventListener === "function") {
877
878
  const onPopState = () => {
878
- if (!isInternal()) emit(loc?.href ?? null);
879
+ if (!isInternal()) emit(loc?.href ?? null, "pop");
879
880
  };
880
881
  g2.addEventListener("popstate", onPopState);
881
882
  registerPatch("popstate", () => {
@@ -2226,13 +2227,13 @@ function addBreadcrumb(input, hint) {
2226
2227
  if (!client) return;
2227
2228
  client.addBreadcrumb(normalizeBreadcrumb(input), hint);
2228
2229
  }
2229
- function addNavigationBreadcrumb(from, to) {
2230
+ function addNavigationBreadcrumb(from, to, operation = "push") {
2230
2231
  addBreadcrumb({
2231
2232
  type: "navigation",
2232
2233
  category: "history",
2233
2234
  level: "info",
2234
2235
  message: null,
2235
- data: { from, to }
2236
+ data: { from, to, operation }
2236
2237
  });
2237
2238
  }
2238
2239