@mushi-mushi/core 1.32.2 → 1.33.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.cjs CHANGED
@@ -1596,14 +1596,10 @@ function createOfflineQueue(config = {}) {
1596
1596
  });
1597
1597
  break;
1598
1598
  }
1599
- 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;
1600
- // retrying re-serialises the multi-MB body every sync tick and wedges
1601
- // the queue (it matches neither permanent nor transient otherwise).
1602
- // SERIALIZE_FAILED (circular ref) is likewise unrecoverable on retry.
1603
- 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(
1599
+ 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(
1604
1600
  result.error.message
1605
1601
  );
1606
- 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"));
1602
+ 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"));
1607
1603
  if (permanent) {
1608
1604
  await removeRow(backend, rowId);
1609
1605
  } else {
@@ -2048,7 +2044,7 @@ function randomSuffix() {
2048
2044
  }
2049
2045
  let n = 0;
2050
2046
  for (const b of bytes) n = n * 256 + b >>> 0;
2051
- return n.toString(36).padStart(6, "0").slice(0, 6);
2047
+ return (n % 36 ** 6).toString(36).padStart(6, "0");
2052
2048
  }
2053
2049
 
2054
2050
  // src/analytics-gate.ts
@@ -2120,7 +2116,9 @@ var _sdkVersion;
2120
2116
  var _userIdHash = null;
2121
2117
  var _reporterToken = null;
2122
2118
  var _pageViewCount = 0;
2123
- var _heartbeatTimer = null;
2119
+ var _heartbeatTimer;
2120
+ var _lastRoute = null;
2121
+ var _endSent = false;
2124
2122
  var _initialized = false;
2125
2123
  var _active = false;
2126
2124
  var _started = false;
@@ -2162,30 +2160,62 @@ function send(payload) {
2162
2160
  if (!_client || !_active) return;
2163
2161
  _client.postSessionEvent(payload).catch(() => {
2164
2162
  });
2163
+ scheduleHeartbeat();
2164
+ }
2165
+ function clearHeartbeat() {
2166
+ clearTimeout(_heartbeatTimer);
2167
+ }
2168
+ function scheduleHeartbeat() {
2169
+ clearHeartbeat();
2170
+ if (!_active || globalThis.document?.visibilityState === "hidden") return;
2171
+ _heartbeatTimer = setTimeout(() => {
2172
+ send(buildPayload("session_heartbeat", { route: currentRoute() }));
2173
+ }, HEARTBEAT_INTERVAL_MS);
2165
2174
  }
2166
2175
  function recordPageView(route) {
2167
2176
  if (!_active) return;
2177
+ if (route === _lastRoute) return;
2178
+ _lastRoute = route;
2168
2179
  _pageViewCount += 1;
2169
2180
  send(buildPayload("page_view", { route }));
2170
2181
  }
2182
+ function sendSessionEnd() {
2183
+ if (_endSent || !_active) return;
2184
+ _endSent = true;
2185
+ send(buildPayload("session_end", { route: currentRoute() }));
2186
+ clearHeartbeat();
2187
+ }
2171
2188
  function activate() {
2172
2189
  if (_active || !_initialized) return;
2173
2190
  _active = true;
2174
2191
  if (!_started) {
2175
2192
  _started = true;
2176
2193
  _pageViewCount = 1;
2177
- send(buildPayload("session_start", { route: currentRoute(), referrer: referrerOrigin() }));
2194
+ _lastRoute = currentRoute();
2195
+ send(buildPayload("session_start", { route: _lastRoute, referrer: referrerOrigin() }));
2196
+ } else {
2197
+ scheduleHeartbeat();
2178
2198
  }
2179
- _heartbeatTimer = setInterval(() => {
2180
- send(buildPayload("session_heartbeat", { route: currentRoute() }));
2181
- }, HEARTBEAT_INTERVAL_MS);
2199
+ }
2200
+ function onHide() {
2201
+ if (document.visibilityState === "hidden") {
2202
+ sendSessionEnd();
2203
+ } else {
2204
+ _endSent = false;
2205
+ scheduleHeartbeat();
2206
+ }
2207
+ }
2208
+ function onPagehide() {
2209
+ sendSessionEnd();
2210
+ }
2211
+ function onPageshow(event) {
2212
+ if (!event.persisted) return;
2213
+ _endSent = false;
2214
+ scheduleHeartbeat();
2182
2215
  }
2183
2216
  function deactivate() {
2184
2217
  _active = false;
2185
- if (_heartbeatTimer != null) {
2186
- clearInterval(_heartbeatTimer);
2187
- _heartbeatTimer = null;
2188
- }
2218
+ clearHeartbeat();
2189
2219
  }
2190
2220
  function initSessionTracker(opts) {
2191
2221
  if (_initialized || typeof window === "undefined") return;
@@ -2195,17 +2225,11 @@ function initSessionTracker(opts) {
2195
2225
  _sdkVersion = opts.sdkVersion;
2196
2226
  _reporterToken = opts.reporterToken ?? opts.reporterTokenHash ?? null;
2197
2227
  _userIdHash = opts.userIdHash ?? null;
2198
- const onHide = () => {
2199
- if (typeof document !== "undefined" && document.visibilityState === "hidden") {
2200
- send(buildPayload("session_end", { route: currentRoute() }));
2201
- }
2202
- };
2203
2228
  if (typeof document !== "undefined") {
2204
2229
  document.addEventListener("visibilitychange", onHide, { passive: true });
2205
2230
  }
2206
- window.addEventListener("pagehide", () => {
2207
- send(buildPayload("session_end", { route: currentRoute() }));
2208
- }, { passive: true });
2231
+ window.addEventListener("pagehide", onPagehide, { passive: true });
2232
+ window.addEventListener("pageshow", onPageshow, { passive: true });
2209
2233
  if (opts.patchHistory !== false) {
2210
2234
  patchHistoryForPageViews();
2211
2235
  }
@@ -2220,6 +2244,7 @@ function trackPageView(route) {
2220
2244
  recordPageView(route ?? currentRoute());
2221
2245
  }
2222
2246
  function updateSessionIdentity(userIdHash) {
2247
+ if (userIdHash && userIdHash !== _userIdHash) _lastRoute = null;
2223
2248
  _userIdHash = userIdHash;
2224
2249
  }
2225
2250
  function destroySessionTracker() {
@@ -2228,6 +2253,8 @@ function destroySessionTracker() {
2228
2253
  _unsubscribeConsent = null;
2229
2254
  _initialized = false;
2230
2255
  _started = false;
2256
+ _lastRoute = null;
2257
+ _endSent = false;
2231
2258
  _client = null;
2232
2259
  }
2233
2260
  var _historyPatched = false;
@@ -2549,10 +2576,10 @@ function initEventTracker(opts) {
2549
2576
  _flushTimer = setInterval(() => {
2550
2577
  void flushNow();
2551
2578
  }, interval);
2552
- const onHide = () => {
2579
+ const onHide2 = () => {
2553
2580
  if (typeof document !== "undefined" && document.visibilityState === "hidden") void flushNow();
2554
2581
  };
2555
- if (typeof document !== "undefined") document.addEventListener("visibilitychange", onHide, { passive: true });
2582
+ if (typeof document !== "undefined") document.addEventListener("visibilitychange", onHide2, { passive: true });
2556
2583
  window.addEventListener("pagehide", () => {
2557
2584
  void flushNow();
2558
2585
  }, { passive: true });
@@ -2992,8 +3019,8 @@ var MUSHI_LETTER_SPACING = {
2992
3019
  var MUSHI_GEOMETRY = {
2993
3020
  bannerHeight: 36,
2994
3021
  fabSize: 52,
2995
- /** edge-tab launcher width (px) */
2996
- edgeTabWidth: 32,
3022
+ /** edge-tab launcher drawn width (px) */
3023
+ edgeTabWidth: 22,
2997
3024
  /** default gutter from the screen edge (added on top of safe-area insets) */
2998
3025
  gutter: 24,
2999
3026
  /** Web panel width (px) — matches shipped Shadow DOM widget */
@@ -3016,7 +3043,7 @@ function resolveWidgetAccent(mode, accent = "", accentText = "") {
3016
3043
  return { accent: resolvedAccent, accentWash, accentInk };
3017
3044
  }
3018
3045
  var MUSHI_COPY = {
3019
- bannerLabel: "Beta",
3046
+ bannerLabel: "Feedback",
3020
3047
  bannerMessage: "Found a bug? Tap to report it.",
3021
3048
  bugCta: "Report a bug",
3022
3049
  featureCta: "Feature request",