@mushi-mushi/core 1.32.0 → 1.32.3

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
@@ -1,7 +1,7 @@
1
- import { M as MushiApiClient, a as MushiConfig, b as MushiPreFilterConfig, c as MushiReport, d as MushiOfflineConfig, e as MushiEnvironment, f as MushiAnalyticsConfig, g as MushiPropertyValue, h as MushiBreadcrumb } from './types-0j5BJ3aC.cjs';
2
- export { i as MushiActivityEvent, j as MushiApiCascadeConfig, k as MushiApiResponse, l as MushiAssistantConfig, m as MushiAssistantReply, n as MushiAssistantStep, o as MushiBannerConfig, p as MushiBannerLink, q as MushiBetaChangelogEntry, r as MushiBetaModeConfig, s as MushiCaptureConfig, t as MushiCaptureEventInput, u as MushiCaptureExceptionOptions, v as MushiConsoleEntry, w as MushiCooldownConfig, x as MushiCrossAppReport, y as MushiCustomCategory, z as MushiDiagnosticsResult, A as MushiDiscoverInventoryConfig, B as MushiDiscoveryEventPayload, C as MushiEventHandler, D as MushiEventType, E as MushiHallOfFameEntry, F as MushiIntegrationsConfig, G as MushiLeaderboardEntry, H as MushiNetworkEntry, I as MushiNotificationsConfig, J as MushiOnDeviceClassifier, K as MushiOnDeviceClassifierInput, L as MushiOnDeviceClassifierResult, N as MushiPageContext, O as MushiPerformanceMetrics, P as MushiPreset, Q as MushiPrivacyConfig, R as MushiProactiveConfig, S as MushiProductEventPayload, T as MushiPushSubscriptionJSON, U as MushiReportBuilder, V as MushiReportCategory, W as MushiReportStatus, X as MushiReporterChannelUnavailable, Y as MushiReporterComment, Z as MushiReporterNotificationPrefs, _ as MushiReporterPrefsUpdate, $ as MushiReporterReport, a0 as MushiReporterReportDetail, a1 as MushiReporterTimelineItem, a2 as MushiReporterUpdates, a3 as MushiReputationResult, a4 as MushiRewardsConfig, a5 as MushiRuntimeSdkConfig, a6 as MushiSDKInstance, a7 as MushiSdkVersionInfo, a8 as MushiSelectedElement, a9 as MushiSentryConfig, aa as MushiSentryContext, ab as MushiSessionEventPayload, ac as MushiTesterReputation, ad as MushiTierResult, ae as MushiTimelineEntry, af as MushiTimelineKind, ag as MushiTracePropagationConfig, ah as MushiUrlMatcher, ai as MushiWidgetAnchor, aj as MushiWidgetConfig } from './types-0j5BJ3aC.cjs';
1
+ import { M as MushiApiClient, a as MushiConfig, b as MushiPreFilterConfig, c as MushiReport, d as MushiOfflineConfig, e as MushiEnvironment, f as MushiAnalyticsConfig, g as MushiPropertyValue, h as MushiBreadcrumb } from './types-DhR6TWJZ.cjs';
2
+ export { i as MushiActivityEvent, j as MushiApiCascadeConfig, k as MushiApiResponse, l as MushiAssistantConfig, m as MushiAssistantReply, n as MushiAssistantStep, o as MushiBannerConfig, p as MushiBannerLink, q as MushiBetaChangelogEntry, r as MushiBetaModeConfig, s as MushiCaptureConfig, t as MushiCaptureEventInput, u as MushiCaptureExceptionOptions, v as MushiConsoleEntry, w as MushiCooldownConfig, x as MushiCrossAppReport, y as MushiCustomCategory, z as MushiDiagnosticsResult, A as MushiDiscoverInventoryConfig, B as MushiDiscoveryEventPayload, C as MushiEventHandler, D as MushiEventType, E as MushiHallOfFameEntry, F as MushiInitConfig, G as MushiIntegrationsConfig, H as MushiLeaderboardEntry, I as MushiNetworkEntry, J as MushiNotificationsConfig, K as MushiOnDeviceClassifier, L as MushiOnDeviceClassifierInput, N as MushiOnDeviceClassifierResult, O as MushiPageContext, P as MushiPerformanceMetrics, Q as MushiPreset, R as MushiPrivacyConfig, S as MushiProactiveConfig, T as MushiProductEventPayload, U as MushiPushSubscriptionJSON, V as MushiReportBuilder, W as MushiReportCategory, X as MushiReportStatus, Y as MushiReporterChannelUnavailable, Z as MushiReporterComment, _ as MushiReporterNotificationPrefs, $ as MushiReporterPrefsUpdate, a0 as MushiReporterReport, a1 as MushiReporterReportDetail, a2 as MushiReporterTimelineItem, a3 as MushiReporterUpdates, a4 as MushiReputationResult, a5 as MushiRewardsConfig, a6 as MushiRuntimeSdkConfig, a7 as MushiRuntimeWidgetConfig, a8 as MushiSDKInstance, a9 as MushiSdkVersionInfo, aa as MushiSelectedElement, ab as MushiSentryConfig, ac as MushiSentryContext, ad as MushiSessionEventPayload, ae as MushiTesterReputation, af as MushiTierResult, ag as MushiTimelineEntry, ah as MushiTimelineKind, ai as MushiTracePropagationConfig, aj as MushiUrlMatcher, ak as MushiWidgetAnchor, al as MushiWidgetConfig } from './types-DhR6TWJZ.cjs';
3
3
 
4
- /** Replay the last POST via keepalive/beacon during pagehide. */
4
+ /** Replay the in-flight POST via keepalive/beacon during pagehide. */
5
5
  declare function flushLastOutboundOnUnload(): boolean;
6
6
  /** Header carrying the signed end-user identity token (verified server-side). */
7
7
  declare const MUSHI_USER_TOKEN_HEADER = "X-Mushi-User-Token";
@@ -463,19 +463,21 @@ interface SessionTrackerOptions {
463
463
  * Initialise the session tracker. Call once at SDK init for web environments.
464
464
  * Subsequent calls on the same page are no-ops (idempotent).
465
465
  *
466
- * Emits, once the analytics gate allows it: session_start, heartbeats every
467
- * minute, session_end on visibilitychange/pagehide, and page_view on history
468
- * API navigation. Under `consent: 'required'` nothing is sent until
469
- * setConsent('granted').
466
+ * Emits, once the analytics gate allows it: session_start, a heartbeat after
467
+ * each minute without another request while the tab is visible, one
468
+ * session_end per hide (visibilitychange/pagehide), and page_view when a
469
+ * history API navigation changes the pathname. Under `consent: 'required'`
470
+ * nothing is sent until setConsent('granted').
470
471
  */
471
472
  declare function initSessionTracker(opts: SessionTrackerOptions): void;
472
473
  /**
473
474
  * Record a page view manually — call from framework router hooks where the
474
475
  * history patch may fire too early (e.g. React Router v7 loader transitions),
475
- * or from a host that initialised with `patchHistory: false`.
476
+ * or from a host that initialised with `patchHistory: false`. A call whose
477
+ * pathname matches the last page view is ignored: it is the same page.
476
478
  */
477
479
  declare function trackPageView(route?: string): void;
478
- /** Update the user identity after a Mushi.identify() call. */
480
+ /** Set the identity heartbeats carry: a SHA-256 hex of the host user id, never the raw id. */
479
481
  declare function updateSessionIdentity(userIdHash: string | null): void;
480
482
  /** Tear down timers (e.g. in tests or SSR environments). */
481
483
  declare function destroySessionTracker(): void;
@@ -1217,7 +1219,7 @@ declare function resolveWidgetAccent(mode: MushiThemeMode, accent?: string, acce
1217
1219
  * label / CTA wording (seen as inconsistent across the four host apps).
1218
1220
  */
1219
1221
  declare const MUSHI_COPY: {
1220
- readonly bannerLabel: "Beta";
1222
+ readonly bannerLabel: "Feedback";
1221
1223
  readonly bannerMessage: "Found a bug? Tap to report it.";
1222
1224
  readonly bugCta: "Report a bug";
1223
1225
  readonly featureCta: "Feature request";
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { M as MushiApiClient, a as MushiConfig, b as MushiPreFilterConfig, c as MushiReport, d as MushiOfflineConfig, e as MushiEnvironment, f as MushiAnalyticsConfig, g as MushiPropertyValue, h as MushiBreadcrumb } from './types-0j5BJ3aC.js';
2
- export { i as MushiActivityEvent, j as MushiApiCascadeConfig, k as MushiApiResponse, l as MushiAssistantConfig, m as MushiAssistantReply, n as MushiAssistantStep, o as MushiBannerConfig, p as MushiBannerLink, q as MushiBetaChangelogEntry, r as MushiBetaModeConfig, s as MushiCaptureConfig, t as MushiCaptureEventInput, u as MushiCaptureExceptionOptions, v as MushiConsoleEntry, w as MushiCooldownConfig, x as MushiCrossAppReport, y as MushiCustomCategory, z as MushiDiagnosticsResult, A as MushiDiscoverInventoryConfig, B as MushiDiscoveryEventPayload, C as MushiEventHandler, D as MushiEventType, E as MushiHallOfFameEntry, F as MushiIntegrationsConfig, G as MushiLeaderboardEntry, H as MushiNetworkEntry, I as MushiNotificationsConfig, J as MushiOnDeviceClassifier, K as MushiOnDeviceClassifierInput, L as MushiOnDeviceClassifierResult, N as MushiPageContext, O as MushiPerformanceMetrics, P as MushiPreset, Q as MushiPrivacyConfig, R as MushiProactiveConfig, S as MushiProductEventPayload, T as MushiPushSubscriptionJSON, U as MushiReportBuilder, V as MushiReportCategory, W as MushiReportStatus, X as MushiReporterChannelUnavailable, Y as MushiReporterComment, Z as MushiReporterNotificationPrefs, _ as MushiReporterPrefsUpdate, $ as MushiReporterReport, a0 as MushiReporterReportDetail, a1 as MushiReporterTimelineItem, a2 as MushiReporterUpdates, a3 as MushiReputationResult, a4 as MushiRewardsConfig, a5 as MushiRuntimeSdkConfig, a6 as MushiSDKInstance, a7 as MushiSdkVersionInfo, a8 as MushiSelectedElement, a9 as MushiSentryConfig, aa as MushiSentryContext, ab as MushiSessionEventPayload, ac as MushiTesterReputation, ad as MushiTierResult, ae as MushiTimelineEntry, af as MushiTimelineKind, ag as MushiTracePropagationConfig, ah as MushiUrlMatcher, ai as MushiWidgetAnchor, aj as MushiWidgetConfig } from './types-0j5BJ3aC.js';
1
+ import { M as MushiApiClient, a as MushiConfig, b as MushiPreFilterConfig, c as MushiReport, d as MushiOfflineConfig, e as MushiEnvironment, f as MushiAnalyticsConfig, g as MushiPropertyValue, h as MushiBreadcrumb } from './types-DhR6TWJZ.js';
2
+ export { i as MushiActivityEvent, j as MushiApiCascadeConfig, k as MushiApiResponse, l as MushiAssistantConfig, m as MushiAssistantReply, n as MushiAssistantStep, o as MushiBannerConfig, p as MushiBannerLink, q as MushiBetaChangelogEntry, r as MushiBetaModeConfig, s as MushiCaptureConfig, t as MushiCaptureEventInput, u as MushiCaptureExceptionOptions, v as MushiConsoleEntry, w as MushiCooldownConfig, x as MushiCrossAppReport, y as MushiCustomCategory, z as MushiDiagnosticsResult, A as MushiDiscoverInventoryConfig, B as MushiDiscoveryEventPayload, C as MushiEventHandler, D as MushiEventType, E as MushiHallOfFameEntry, F as MushiInitConfig, G as MushiIntegrationsConfig, H as MushiLeaderboardEntry, I as MushiNetworkEntry, J as MushiNotificationsConfig, K as MushiOnDeviceClassifier, L as MushiOnDeviceClassifierInput, N as MushiOnDeviceClassifierResult, O as MushiPageContext, P as MushiPerformanceMetrics, Q as MushiPreset, R as MushiPrivacyConfig, S as MushiProactiveConfig, T as MushiProductEventPayload, U as MushiPushSubscriptionJSON, V as MushiReportBuilder, W as MushiReportCategory, X as MushiReportStatus, Y as MushiReporterChannelUnavailable, Z as MushiReporterComment, _ as MushiReporterNotificationPrefs, $ as MushiReporterPrefsUpdate, a0 as MushiReporterReport, a1 as MushiReporterReportDetail, a2 as MushiReporterTimelineItem, a3 as MushiReporterUpdates, a4 as MushiReputationResult, a5 as MushiRewardsConfig, a6 as MushiRuntimeSdkConfig, a7 as MushiRuntimeWidgetConfig, a8 as MushiSDKInstance, a9 as MushiSdkVersionInfo, aa as MushiSelectedElement, ab as MushiSentryConfig, ac as MushiSentryContext, ad as MushiSessionEventPayload, ae as MushiTesterReputation, af as MushiTierResult, ag as MushiTimelineEntry, ah as MushiTimelineKind, ai as MushiTracePropagationConfig, aj as MushiUrlMatcher, ak as MushiWidgetAnchor, al as MushiWidgetConfig } from './types-DhR6TWJZ.js';
3
3
 
4
- /** Replay the last POST via keepalive/beacon during pagehide. */
4
+ /** Replay the in-flight POST via keepalive/beacon during pagehide. */
5
5
  declare function flushLastOutboundOnUnload(): boolean;
6
6
  /** Header carrying the signed end-user identity token (verified server-side). */
7
7
  declare const MUSHI_USER_TOKEN_HEADER = "X-Mushi-User-Token";
@@ -463,19 +463,21 @@ interface SessionTrackerOptions {
463
463
  * Initialise the session tracker. Call once at SDK init for web environments.
464
464
  * Subsequent calls on the same page are no-ops (idempotent).
465
465
  *
466
- * Emits, once the analytics gate allows it: session_start, heartbeats every
467
- * minute, session_end on visibilitychange/pagehide, and page_view on history
468
- * API navigation. Under `consent: 'required'` nothing is sent until
469
- * setConsent('granted').
466
+ * Emits, once the analytics gate allows it: session_start, a heartbeat after
467
+ * each minute without another request while the tab is visible, one
468
+ * session_end per hide (visibilitychange/pagehide), and page_view when a
469
+ * history API navigation changes the pathname. Under `consent: 'required'`
470
+ * nothing is sent until setConsent('granted').
470
471
  */
471
472
  declare function initSessionTracker(opts: SessionTrackerOptions): void;
472
473
  /**
473
474
  * Record a page view manually — call from framework router hooks where the
474
475
  * history patch may fire too early (e.g. React Router v7 loader transitions),
475
- * or from a host that initialised with `patchHistory: false`.
476
+ * or from a host that initialised with `patchHistory: false`. A call whose
477
+ * pathname matches the last page view is ignored: it is the same page.
476
478
  */
477
479
  declare function trackPageView(route?: string): void;
478
- /** Update the user identity after a Mushi.identify() call. */
480
+ /** Set the identity heartbeats carry: a SHA-256 hex of the host user id, never the raw id. */
479
481
  declare function updateSessionIdentity(userIdHash: string | null): void;
480
482
  /** Tear down timers (e.g. in tests or SSR environments). */
481
483
  declare function destroySessionTracker(): void;
@@ -1217,7 +1219,7 @@ declare function resolveWidgetAccent(mode: MushiThemeMode, accent?: string, acce
1217
1219
  * label / CTA wording (seen as inconsistent across the four host apps).
1218
1220
  */
1219
1221
  declare const MUSHI_COPY: {
1220
- readonly bannerLabel: "Beta";
1222
+ readonly bannerLabel: "Feedback";
1221
1223
  readonly bannerMessage: "Found a bug? Tap to report it.";
1222
1224
  readonly bugCta: "Report a bug";
1223
1225
  readonly featureCta: "Feature request";
package/dist/index.js CHANGED
@@ -167,8 +167,9 @@ function isPageUnloading() {
167
167
  // src/api-client.ts
168
168
  var lastOutbound = null;
169
169
  function flushLastOutboundOnUnload() {
170
- if (!lastOutbound) return false;
171
- return sendOnUnload(lastOutbound);
170
+ const outbound = lastOutbound;
171
+ lastOutbound = null;
172
+ return !!outbound && sendOnUnload(outbound);
172
173
  }
173
174
  var _credWarningEmitted = false;
174
175
  var MUSHI_USER_TOKEN_HEADER = "X-Mushi-User-Token";
@@ -250,8 +251,10 @@ function createApiClient(options) {
250
251
  ...extraHeaders
251
252
  };
252
253
  const serialized = body ? JSON.stringify(body) : void 0;
254
+ let outbound = null;
253
255
  if (serialized && method !== "GET" && internalKind !== "reporter-poll") {
254
- lastOutbound = { url, headers, body: serialized, path };
256
+ outbound = { url, headers, body: serialized, path };
257
+ lastOutbound = outbound;
255
258
  }
256
259
  try {
257
260
  const response = await fetch(url, {
@@ -321,6 +324,8 @@ function createApiClient(options) {
321
324
  message: error instanceof Error ? error.message : "Unknown network error"
322
325
  }
323
326
  };
327
+ } finally {
328
+ if (lastOutbound === outbound) lastOutbound = null;
324
329
  }
325
330
  }
326
331
  async function requestForReporter(method, path, reporterToken, body) {
@@ -618,6 +623,7 @@ function matchesErrorFilter(value, filters) {
618
623
  continue;
619
624
  }
620
625
  try {
626
+ filter.lastIndex = 0;
621
627
  if (filter.test(value)) return true;
622
628
  } catch {
623
629
  }
@@ -1588,14 +1594,10 @@ function createOfflineQueue(config = {}) {
1588
1594
  });
1589
1595
  break;
1590
1596
  }
1591
- const permanent = result.error?.code === "HTTP_400" || result.error?.code === "HTTP_413" || result.error?.code === "HTTP_422" || result.error?.code === "INGEST_ERROR" || result.error?.code === "VALIDATION_ERROR" || // A payload that exceeds the size guard will never shrink on its own;
1592
- // retrying re-serialises the multi-MB body every sync tick and wedges
1593
- // the queue (it matches neither permanent nor transient otherwise).
1594
- // SERIALIZE_FAILED (circular ref) is likewise unrecoverable on retry.
1595
- result.error?.code === "PAYLOAD_TOO_LARGE" || result.error?.code === "SERIALIZE_FAILED" || typeof result.error?.message === "string" && /invalid payload|description must be at least|validation/i.test(
1597
+ const permanent = result.error?.code === "HTTP_400" || result.error?.status === 401 || result.error?.status === 403 || result.error?.code === "HTTP_401" || result.error?.code === "HTTP_403" || result.error?.code === "HTTP_413" || result.error?.code === "HTTP_422" || result.error?.code === "INGEST_ERROR" || result.error?.code === "VALIDATION_ERROR" || result.error?.code === "PAYLOAD_TOO_LARGE" || result.error?.code === "SERIALIZE_FAILED" || typeof result.error?.message === "string" && /invalid payload|description must be at least|validation/i.test(
1596
1598
  result.error.message
1597
1599
  );
1598
- const transient = !permanent && (result.error?.code === "NETWORK_ERROR" || result.error?.code === "HTTP_403" || result.error?.code === "HTTP_429" || result.error?.code === "HTTP_502" || result.error?.code === "HTTP_503" || result.error?.code === "HTTP_504" || typeof result.error?.code === "string" && result.error.code.startsWith("HTTP_5"));
1600
+ const transient = !permanent && (result.error?.code === "NETWORK_ERROR" || result.error?.code === "HTTP_429" || result.error?.code === "HTTP_502" || result.error?.code === "HTTP_503" || result.error?.code === "HTTP_504" || typeof result.error?.code === "string" && result.error.code.startsWith("HTTP_5"));
1599
1601
  if (permanent) {
1600
1602
  await removeRow(backend, rowId);
1601
1603
  } else {
@@ -2040,7 +2042,7 @@ function randomSuffix() {
2040
2042
  }
2041
2043
  let n = 0;
2042
2044
  for (const b of bytes) n = n * 256 + b >>> 0;
2043
- return n.toString(36).padStart(6, "0").slice(0, 6);
2045
+ return (n % 36 ** 6).toString(36).padStart(6, "0");
2044
2046
  }
2045
2047
 
2046
2048
  // src/analytics-gate.ts
@@ -2112,7 +2114,9 @@ var _sdkVersion;
2112
2114
  var _userIdHash = null;
2113
2115
  var _reporterToken = null;
2114
2116
  var _pageViewCount = 0;
2115
- var _heartbeatTimer = null;
2117
+ var _heartbeatTimer;
2118
+ var _lastRoute = null;
2119
+ var _endSent = false;
2116
2120
  var _initialized = false;
2117
2121
  var _active = false;
2118
2122
  var _started = false;
@@ -2154,30 +2158,62 @@ function send(payload) {
2154
2158
  if (!_client || !_active) return;
2155
2159
  _client.postSessionEvent(payload).catch(() => {
2156
2160
  });
2161
+ scheduleHeartbeat();
2162
+ }
2163
+ function clearHeartbeat() {
2164
+ clearTimeout(_heartbeatTimer);
2165
+ }
2166
+ function scheduleHeartbeat() {
2167
+ clearHeartbeat();
2168
+ if (!_active || globalThis.document?.visibilityState === "hidden") return;
2169
+ _heartbeatTimer = setTimeout(() => {
2170
+ send(buildPayload("session_heartbeat", { route: currentRoute() }));
2171
+ }, HEARTBEAT_INTERVAL_MS);
2157
2172
  }
2158
2173
  function recordPageView(route) {
2159
2174
  if (!_active) return;
2175
+ if (route === _lastRoute) return;
2176
+ _lastRoute = route;
2160
2177
  _pageViewCount += 1;
2161
2178
  send(buildPayload("page_view", { route }));
2162
2179
  }
2180
+ function sendSessionEnd() {
2181
+ if (_endSent || !_active) return;
2182
+ _endSent = true;
2183
+ send(buildPayload("session_end", { route: currentRoute() }));
2184
+ clearHeartbeat();
2185
+ }
2163
2186
  function activate() {
2164
2187
  if (_active || !_initialized) return;
2165
2188
  _active = true;
2166
2189
  if (!_started) {
2167
2190
  _started = true;
2168
2191
  _pageViewCount = 1;
2169
- send(buildPayload("session_start", { route: currentRoute(), referrer: referrerOrigin() }));
2192
+ _lastRoute = currentRoute();
2193
+ send(buildPayload("session_start", { route: _lastRoute, referrer: referrerOrigin() }));
2194
+ } else {
2195
+ scheduleHeartbeat();
2170
2196
  }
2171
- _heartbeatTimer = setInterval(() => {
2172
- send(buildPayload("session_heartbeat", { route: currentRoute() }));
2173
- }, HEARTBEAT_INTERVAL_MS);
2197
+ }
2198
+ function onHide() {
2199
+ if (document.visibilityState === "hidden") {
2200
+ sendSessionEnd();
2201
+ } else {
2202
+ _endSent = false;
2203
+ scheduleHeartbeat();
2204
+ }
2205
+ }
2206
+ function onPagehide() {
2207
+ sendSessionEnd();
2208
+ }
2209
+ function onPageshow(event) {
2210
+ if (!event.persisted) return;
2211
+ _endSent = false;
2212
+ scheduleHeartbeat();
2174
2213
  }
2175
2214
  function deactivate() {
2176
2215
  _active = false;
2177
- if (_heartbeatTimer != null) {
2178
- clearInterval(_heartbeatTimer);
2179
- _heartbeatTimer = null;
2180
- }
2216
+ clearHeartbeat();
2181
2217
  }
2182
2218
  function initSessionTracker(opts) {
2183
2219
  if (_initialized || typeof window === "undefined") return;
@@ -2187,17 +2223,11 @@ function initSessionTracker(opts) {
2187
2223
  _sdkVersion = opts.sdkVersion;
2188
2224
  _reporterToken = opts.reporterToken ?? opts.reporterTokenHash ?? null;
2189
2225
  _userIdHash = opts.userIdHash ?? null;
2190
- const onHide = () => {
2191
- if (typeof document !== "undefined" && document.visibilityState === "hidden") {
2192
- send(buildPayload("session_end", { route: currentRoute() }));
2193
- }
2194
- };
2195
2226
  if (typeof document !== "undefined") {
2196
2227
  document.addEventListener("visibilitychange", onHide, { passive: true });
2197
2228
  }
2198
- window.addEventListener("pagehide", () => {
2199
- send(buildPayload("session_end", { route: currentRoute() }));
2200
- }, { passive: true });
2229
+ window.addEventListener("pagehide", onPagehide, { passive: true });
2230
+ window.addEventListener("pageshow", onPageshow, { passive: true });
2201
2231
  if (opts.patchHistory !== false) {
2202
2232
  patchHistoryForPageViews();
2203
2233
  }
@@ -2212,6 +2242,7 @@ function trackPageView(route) {
2212
2242
  recordPageView(route ?? currentRoute());
2213
2243
  }
2214
2244
  function updateSessionIdentity(userIdHash) {
2245
+ if (userIdHash && userIdHash !== _userIdHash) _lastRoute = null;
2215
2246
  _userIdHash = userIdHash;
2216
2247
  }
2217
2248
  function destroySessionTracker() {
@@ -2220,6 +2251,8 @@ function destroySessionTracker() {
2220
2251
  _unsubscribeConsent = null;
2221
2252
  _initialized = false;
2222
2253
  _started = false;
2254
+ _lastRoute = null;
2255
+ _endSent = false;
2223
2256
  _client = null;
2224
2257
  }
2225
2258
  var _historyPatched = false;
@@ -2541,10 +2574,10 @@ function initEventTracker(opts) {
2541
2574
  _flushTimer = setInterval(() => {
2542
2575
  void flushNow();
2543
2576
  }, interval);
2544
- const onHide = () => {
2577
+ const onHide2 = () => {
2545
2578
  if (typeof document !== "undefined" && document.visibilityState === "hidden") void flushNow();
2546
2579
  };
2547
- if (typeof document !== "undefined") document.addEventListener("visibilitychange", onHide, { passive: true });
2580
+ if (typeof document !== "undefined") document.addEventListener("visibilitychange", onHide2, { passive: true });
2548
2581
  window.addEventListener("pagehide", () => {
2549
2582
  void flushNow();
2550
2583
  }, { passive: true });
@@ -3008,7 +3041,7 @@ function resolveWidgetAccent(mode, accent = "", accentText = "") {
3008
3041
  return { accent: resolvedAccent, accentWash, accentInk };
3009
3042
  }
3010
3043
  var MUSHI_COPY = {
3011
- bannerLabel: "Beta",
3044
+ bannerLabel: "Feedback",
3012
3045
  bannerMessage: "Found a bug? Tap to report it.",
3013
3046
  bugCta: "Report a bug",
3014
3047
  featureCta: "Feature request",