@mushi-mushi/core 1.20.2 → 1.22.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.js CHANGED
@@ -104,6 +104,8 @@ var MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
104
104
  var MUSHI_INTERNAL_INIT_MARKER = "__mushiInternal";
105
105
  var DEFAULT_TIMEOUT = 1e4;
106
106
  var DEFAULT_MAX_RETRIES = 2;
107
+ var DEFAULT_CIRCUIT_THRESHOLD = 4;
108
+ var DEFAULT_CIRCUIT_COOLDOWN_MS = 3e4;
107
109
  function createApiClient(options) {
108
110
  const {
109
111
  projectId,
@@ -113,10 +115,29 @@ function createApiClient(options) {
113
115
  maxRetries = DEFAULT_MAX_RETRIES,
114
116
  getUserToken,
115
117
  sdkPackage,
116
- sdkVersion
118
+ sdkVersion,
119
+ circuitBreaker
117
120
  } = options;
118
121
  let baseUrl = apiEndpoint.replace(/\/$/, "");
122
+ const cbEnabled = circuitBreaker?.enabled !== false;
123
+ const cbThreshold = Math.max(1, circuitBreaker?.threshold ?? DEFAULT_CIRCUIT_THRESHOLD);
124
+ const cbCooldownMs = Math.max(1e3, circuitBreaker?.cooldownMs ?? DEFAULT_CIRCUIT_COOLDOWN_MS);
125
+ let cbFailures = 0;
126
+ let cbOpenUntil = 0;
127
+ const cbIsOpen = () => cbEnabled && cbOpenUntil > Date.now();
128
+ const cbRecordReachable = () => {
129
+ cbFailures = 0;
130
+ cbOpenUntil = 0;
131
+ };
132
+ const cbRecordUnreachable = () => {
133
+ if (!cbEnabled) return;
134
+ cbFailures += 1;
135
+ if (cbFailures >= cbThreshold) cbOpenUntil = Date.now() + cbCooldownMs;
136
+ };
119
137
  async function request(method, path, body, retries = maxRetries, internalKind, extraHeaders) {
138
+ if (cbIsOpen()) {
139
+ return { ok: false, error: { code: "CIRCUIT_OPEN", message: "Endpoint temporarily unavailable; retrying later." } };
140
+ }
120
141
  const url = `${baseUrl}${path}`;
121
142
  const controller = new AbortController();
122
143
  const timer = setTimeout(() => controller.abort(), timeout);
@@ -163,6 +184,8 @@ function createApiClient(options) {
163
184
  await sleep(getBackoffDelay(maxRetries - retries));
164
185
  return request(method, path, body, retries - 1, internalKind, extraHeaders);
165
186
  }
187
+ if (response.status >= 500) cbRecordUnreachable();
188
+ else cbRecordReachable();
166
189
  return {
167
190
  ok: false,
168
191
  error: {
@@ -173,6 +196,7 @@ function createApiClient(options) {
173
196
  }
174
197
  const payload = await response.json();
175
198
  const data = payload && typeof payload === "object" && "ok" in payload && "data" in payload ? payload.data : payload;
199
+ cbRecordReachable();
176
200
  return { ok: true, data };
177
201
  } catch (error) {
178
202
  clearTimeout(timer);
@@ -180,6 +204,7 @@ function createApiClient(options) {
180
204
  await sleep(getBackoffDelay(maxRetries - retries));
181
205
  return request(method, path, body, retries - 1, internalKind);
182
206
  }
207
+ cbRecordUnreachable();
183
208
  return {
184
209
  ok: false,
185
210
  error: {
@@ -464,9 +489,9 @@ function isRetryable(error) {
464
489
 
465
490
  // src/region.ts
466
491
  var REGION_ENDPOINTS = {
467
- us: "https://api.us.mushimushi.dev/functions/v1/api",
468
- eu: "https://api.eu.mushimushi.dev/functions/v1/api",
469
- jp: "https://api.jp.mushimushi.dev/functions/v1/api"
492
+ us: "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api",
493
+ eu: "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api",
494
+ jp: "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api"
470
495
  };
471
496
  var ROUTING_CACHE_KEY = "mushi_region_v1";
472
497
  var ROUTING_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
@@ -1212,7 +1237,8 @@ function createOfflineQueue(config = {}) {
1212
1237
  if (!enabled || !syncOnReconnect || typeof window === "undefined") return;
1213
1238
  const tryFlush = () => {
1214
1239
  if (navigator.onLine) {
1215
- flush(client).catch(() => {
1240
+ flush(client).catch((err) => {
1241
+ queueLog.warn("Offline queue auto-flush failed", { err: String(err) });
1216
1242
  });
1217
1243
  }
1218
1244
  };
@@ -1245,6 +1271,15 @@ function captureEnvironment() {
1245
1271
  const scr = typeof screen !== "undefined" ? screen : void 0;
1246
1272
  void kickOffUserAgentData(nav);
1247
1273
  const connection = nav && "connection" in nav ? nav.connection : void 0;
1274
+ const w = win;
1275
+ const isCapacitor = !!w?.Capacitor;
1276
+ const isCordova = !!w?.cordova;
1277
+ const isReactNative = nav?.product === "ReactNative" || typeof globalThis.HermesInternal !== "undefined";
1278
+ const native = isCapacitor || isCordova || isReactNative ? {
1279
+ ...isCapacitor ? { capacitor: true } : {},
1280
+ ...isCordova ? { cordova: true } : {},
1281
+ ...isReactNative ? { reactNative: true } : {}
1282
+ } : void 0;
1248
1283
  return {
1249
1284
  userAgent: nav?.userAgent ?? "unknown",
1250
1285
  platform: nav?.platform ?? "unknown",
@@ -1277,7 +1312,8 @@ function captureEnvironment() {
1277
1312
  displayMode: matchDisplayMode(win),
1278
1313
  documentTitle: doc?.title?.slice(0, 200),
1279
1314
  buildId: readBuildIdMeta(doc),
1280
- pageLoadTiming: capturePageLoadTiming(win)
1315
+ pageLoadTiming: capturePageLoadTiming(win),
1316
+ ...native ? { native } : {}
1281
1317
  };
1282
1318
  }
1283
1319
  function findNearestTestidFromActive(doc) {
@@ -1405,20 +1441,9 @@ function capturePageLoadTiming(win) {
1405
1441
  }
1406
1442
 
1407
1443
  // src/reporter-token.ts
1408
- var STORAGE_KEY = "mushi_reporter_token";
1409
- function getReporterToken() {
1410
- if (typeof localStorage !== "undefined") {
1411
- const existing = localStorage.getItem(STORAGE_KEY);
1412
- if (existing) return existing;
1413
- }
1414
- const token = generateToken();
1415
- if (typeof localStorage !== "undefined") {
1416
- try {
1417
- localStorage.setItem(STORAGE_KEY, token);
1418
- } catch {
1419
- }
1420
- }
1421
- return token;
1444
+ var LEGACY_STORAGE_KEY = "mushi_reporter_token";
1445
+ function storageKey(projectId) {
1446
+ return `mushi:reporter-token:${projectId}`;
1422
1447
  }
1423
1448
  function generateToken() {
1424
1449
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
@@ -1435,6 +1460,38 @@ function generateToken() {
1435
1460
  const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1436
1461
  return `mushi_${hex}`;
1437
1462
  }
1463
+ function readStoredToken(key) {
1464
+ if (typeof localStorage === "undefined") return null;
1465
+ try {
1466
+ const existing = localStorage.getItem(key);
1467
+ return existing && existing.length > 0 ? existing : null;
1468
+ } catch {
1469
+ return null;
1470
+ }
1471
+ }
1472
+ function writeStoredToken(key, token) {
1473
+ if (typeof localStorage === "undefined") return;
1474
+ try {
1475
+ localStorage.setItem(key, token);
1476
+ } catch {
1477
+ }
1478
+ }
1479
+ function getReporterToken(projectId) {
1480
+ if (!projectId) {
1481
+ const legacy2 = readStoredToken(LEGACY_STORAGE_KEY);
1482
+ if (legacy2) return legacy2;
1483
+ const token2 = generateToken();
1484
+ writeStoredToken(LEGACY_STORAGE_KEY, token2);
1485
+ return token2;
1486
+ }
1487
+ const key = storageKey(projectId);
1488
+ const scoped = readStoredToken(key);
1489
+ if (scoped) return scoped;
1490
+ const legacy = readStoredToken(LEGACY_STORAGE_KEY);
1491
+ const token = legacy ?? generateToken();
1492
+ writeStoredToken(key, token);
1493
+ return token;
1494
+ }
1438
1495
 
1439
1496
  // src/uuid.ts
1440
1497
  function newUuid() {
@@ -1730,6 +1787,42 @@ var MUSHI_COLORS_DARK = {
1730
1787
  ok: "#4ADE80",
1731
1788
  danger: "#F87171"
1732
1789
  };
1790
+ var MUSHI_TIER_COLORS = {
1791
+ free: MUSHI_COLORS_LIGHT.inkMuted,
1792
+ explorer: "#2563EB",
1793
+ contributor: "#7C3AED",
1794
+ champion: "#D97706",
1795
+ default: MUSHI_COLORS_LIGHT.accent
1796
+ };
1797
+ var MUSHI_BANNER_NEON = {
1798
+ bg: "#0FFF50",
1799
+ fg: "#0a1a0a",
1800
+ border: "#00C43A"
1801
+ };
1802
+ var MUSHI_ON_ACCENT = "#FAF7F0";
1803
+ var MUSHI_INVERSE = "#ffffff";
1804
+ var MUSHI_ACCENT_SHADOW = {
1805
+ light: "#9A2A1E",
1806
+ dark: "#7A1F15"
1807
+ };
1808
+ var MUSHI_BANNER_BRAND_BORDER = {
1809
+ light: "#B52F1F",
1810
+ dark: "#C4321E"
1811
+ };
1812
+ var MUSHI_REPORTER_STATUS = {
1813
+ light: {
1814
+ sent: { fg: "#1E4A8C", bg: "rgba(30,74,140,0.08)", border: "rgba(30,74,140,0.16)" },
1815
+ review: { fg: "#8A5A00", bg: "rgba(180,120,0,0.10)", border: "rgba(180,120,0,0.18)" },
1816
+ fixing: { fg: "#9A3D12", bg: "rgba(224,60,44,0.10)", border: "rgba(224,60,44,0.18)" },
1817
+ fixed: { fg: "#1F6B3A", bg: "rgba(31,107,58,0.10)", border: "rgba(31,107,58,0.18)" }
1818
+ },
1819
+ dark: {
1820
+ sent: { fg: "#A8C4FF", bg: "rgba(120,160,255,0.12)", border: "rgba(120,160,255,0.22)" },
1821
+ review: { fg: "#FFD27A", bg: "rgba(255,190,90,0.12)", border: "rgba(255,190,90,0.22)" },
1822
+ fixing: { fg: "#FFB899", bg: "rgba(255,120,60,0.12)", border: "rgba(255,120,60,0.24)" },
1823
+ fixed: { fg: "#8FE3B0", bg: "rgba(80,200,130,0.12)", border: "rgba(80,200,130,0.22)" }
1824
+ }
1825
+ };
1733
1826
  function mushiPalette(mode) {
1734
1827
  return mode === "dark" ? MUSHI_COLORS_DARK : MUSHI_COLORS_LIGHT;
1735
1828
  }
@@ -1776,16 +1869,32 @@ var MUSHI_MOTION = {
1776
1869
  var MUSHI_GEOMETRY = {
1777
1870
  bannerHeight: 36,
1778
1871
  fabSize: 52,
1779
- edgeTabWidth: 28,
1872
+ /** edge-tab launcher width (px) */
1873
+ edgeTabWidth: 32,
1780
1874
  /** default gutter from the screen edge (added on top of safe-area insets) */
1781
1875
  gutter: 24,
1782
- panelWidth: 384,
1876
+ /** Web panel width (px) — matches shipped Shadow DOM widget */
1877
+ panelWidth: 360,
1878
+ /** max panel height before keyboard inset (px) */
1879
+ panelMaxHeight: 480,
1783
1880
  /** below this viewport width the panel becomes a full-width bottom sheet */
1784
1881
  panelSheetBreakpoint: 480
1785
1882
  };
1883
+ function safeWidgetHex(v) {
1884
+ return /^#[0-9a-fA-F]{3,8}$/.test(v) ? v : "";
1885
+ }
1886
+ function resolveWidgetAccent(mode, accent = "", accentText = "") {
1887
+ const pal = mushiPalette(mode);
1888
+ const safeAccent = safeWidgetHex(accent);
1889
+ const safeAccentText = safeWidgetHex(accentText);
1890
+ const resolvedAccent = safeAccent || pal.accent;
1891
+ const accentWash = safeAccent ? mode === "dark" ? `${resolvedAccent}1F` : `${resolvedAccent}14` : pal.accentWash;
1892
+ const accentInk = safeAccentText || pal.accentInk;
1893
+ return { accent: resolvedAccent, accentWash, accentInk };
1894
+ }
1786
1895
  var MUSHI_COPY = {
1787
1896
  bannerLabel: "Beta",
1788
- bannerMessage: "Help us improve \u2014 your feedback shapes what we ship.",
1897
+ bannerMessage: "Found a bug? Tap to report it.",
1789
1898
  bugCta: "Report a bug",
1790
1899
  featureCta: "Feature request",
1791
1900
  triggerText: "\u{1F41B}",
@@ -1858,7 +1967,7 @@ var SLUG_DOMAIN_HINTS = {
1858
1967
  "glot-it": "glot.it",
1859
1968
  glotit: "glot.it",
1860
1969
  "solo-boss-cloud": "sbc-front.vercel.app",
1861
- "mushi-mushi": "mushimushi.dev"
1970
+ "mushi-mushi": "kensaur.us"
1862
1971
  };
1863
1972
  var SLUG_ICON_URL_HINTS = {
1864
1973
  "mushi-mushi": "https://kensaur.us/mushi-mushi/admin/favicon.svg",
@@ -2036,6 +2145,6 @@ function isSafeFaviconHref(href) {
2036
2145
  }
2037
2146
  }
2038
2147
 
2039
- export { DEFAULT_API_ENDPOINT, MAX_REPORT_PAYLOAD_BYTES, MAX_SCREENSHOT_DATA_URL_BYTES, MUSHI_COLORS_DARK, MUSHI_COLORS_LIGHT, MUSHI_COPY, MUSHI_GEOMETRY, MUSHI_IDENTITY_TOKEN_PREFIX, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, MUSHI_MOTION, MUSHI_PROJECT_HEADER, MUSHI_RADIUS, MUSHI_SDK_PACKAGE_HEADER, MUSHI_SDK_VERSION_HEADER, MUSHI_SPACING, MUSHI_TYPE, MUSHI_USER_TOKEN_HEADER, MUSHI_Z, REGION_ENDPOINTS, buildIdentityClaims, buildSdkIngestHeaders, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, faviconUrlCandidates, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, githubRepoDomainHint, isLikelyGenericFavicon, isUntrustedFaviconUrl, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, originToDomain, parseIdentityToken, projectFaviconUrlCandidates, projectInitials, projectInitialsThemeIndex, readPageFaviconHref, resolveEnvConfig, resolveProjectDomain, resolveProjectFaviconDomains, resolveRegionEndpoint, scrubPii };
2148
+ export { DEFAULT_API_ENDPOINT, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT, MAX_REPORT_PAYLOAD_BYTES, MAX_SCREENSHOT_DATA_URL_BYTES, MUSHI_ACCENT_SHADOW, MUSHI_BANNER_BRAND_BORDER, MUSHI_BANNER_NEON, MUSHI_COLORS_DARK, MUSHI_COLORS_LIGHT, MUSHI_COPY, MUSHI_GEOMETRY, MUSHI_IDENTITY_TOKEN_PREFIX, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, MUSHI_INVERSE, MUSHI_MOTION, MUSHI_ON_ACCENT, MUSHI_PROJECT_HEADER, MUSHI_RADIUS, MUSHI_REPORTER_STATUS, MUSHI_SDK_PACKAGE_HEADER, MUSHI_SDK_VERSION_HEADER, MUSHI_SPACING, MUSHI_TIER_COLORS, MUSHI_TYPE, MUSHI_USER_TOKEN_HEADER, MUSHI_Z, REGION_ENDPOINTS, buildIdentityClaims, buildSdkIngestHeaders, captureEnvironment, checkReportPayloadSize, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, estimateJsonBytes, faviconUrlCandidates, formatBytes, getDeviceFingerprintHash, getReporterToken, getSessionId, githubRepoDomainHint, hmacSha256Hex, isLikelyGenericFavicon, isUntrustedFaviconUrl, mushiPalette, mushiTokens, newUuid, noopLogger, normaliseThrown, originToDomain, parseIdentityToken, projectFaviconUrlCandidates, projectInitials, projectInitialsThemeIndex, readPageFaviconHref, resolveEnvConfig, resolveProjectDomain, resolveProjectFaviconDomains, resolveRegionEndpoint, resolveWidgetAccent, safeWidgetHex, scrubPii, sha256Hex };
2040
2149
  //# sourceMappingURL=index.js.map
2041
2150
  //# sourceMappingURL=index.js.map