@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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @mushi-mushi/core
2
2
 
3
- Core types, API client, and utilities for the Mushi Mushi SDK.
3
+ > **Your AI wrote it. Mushi tells you why it broke.**
4
+
5
+ Core types, API client, and shared utilities for every Mushi SDK package.
4
6
 
5
7
  > **You almost certainly don't need to install this directly.** Run `npx mushi-mushi` and the wizard will pick the right framework SDK ([`@mushi-mushi/react`](https://npmjs.com/package/@mushi-mushi/react), [`@mushi-mushi/vue`](https://npmjs.com/package/@mushi-mushi/vue), [`@mushi-mushi/svelte`](https://npmjs.com/package/@mushi-mushi/svelte), [`@mushi-mushi/angular`](https://npmjs.com/package/@mushi-mushi/angular), [`@mushi-mushi/react-native`](https://npmjs.com/package/@mushi-mushi/react-native), [`@mushi-mushi/capacitor`](https://npmjs.com/package/@mushi-mushi/capacitor), or [`@mushi-mushi/web`](https://npmjs.com/package/@mushi-mushi/web)) which depends on this package.
6
8
 
@@ -70,4 +72,4 @@ MIT
70
72
  <!-- mushi-readme-stats-footer -->
71
73
  ---
72
74
 
73
- <sub>Monorepo scale (June 2026): 47 edge functions · 256 SQL migrations · 13 outbound plugins · 11 inbound adapters · 18 pipeline agents. Canonical counts: <a href="https://github.com/kensaurus/mushi-mushi/blob/master/docs/stats.md">docs/stats.md</a> · <code>pnpm docs-stats</code></sub>
75
+ <sub>Monorepo scale (June 2026): 51 edge functions · 298 SQL migrations · 13 outbound plugins · 11 inbound adapters · 19 pipeline agents. Canonical counts: <a href="https://github.com/kensaurus/mushi-mushi/blob/master/docs/stats.md">docs/stats.md</a> · <code>pnpm docs-stats</code></sub>
package/dist/index.cjs CHANGED
@@ -106,6 +106,8 @@ var MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
106
106
  var MUSHI_INTERNAL_INIT_MARKER = "__mushiInternal";
107
107
  var DEFAULT_TIMEOUT = 1e4;
108
108
  var DEFAULT_MAX_RETRIES = 2;
109
+ var DEFAULT_CIRCUIT_THRESHOLD = 4;
110
+ var DEFAULT_CIRCUIT_COOLDOWN_MS = 3e4;
109
111
  function createApiClient(options) {
110
112
  const {
111
113
  projectId,
@@ -115,10 +117,29 @@ function createApiClient(options) {
115
117
  maxRetries = DEFAULT_MAX_RETRIES,
116
118
  getUserToken,
117
119
  sdkPackage,
118
- sdkVersion
120
+ sdkVersion,
121
+ circuitBreaker
119
122
  } = options;
120
123
  let baseUrl = apiEndpoint.replace(/\/$/, "");
124
+ const cbEnabled = circuitBreaker?.enabled !== false;
125
+ const cbThreshold = Math.max(1, circuitBreaker?.threshold ?? DEFAULT_CIRCUIT_THRESHOLD);
126
+ const cbCooldownMs = Math.max(1e3, circuitBreaker?.cooldownMs ?? DEFAULT_CIRCUIT_COOLDOWN_MS);
127
+ let cbFailures = 0;
128
+ let cbOpenUntil = 0;
129
+ const cbIsOpen = () => cbEnabled && cbOpenUntil > Date.now();
130
+ const cbRecordReachable = () => {
131
+ cbFailures = 0;
132
+ cbOpenUntil = 0;
133
+ };
134
+ const cbRecordUnreachable = () => {
135
+ if (!cbEnabled) return;
136
+ cbFailures += 1;
137
+ if (cbFailures >= cbThreshold) cbOpenUntil = Date.now() + cbCooldownMs;
138
+ };
121
139
  async function request(method, path, body, retries = maxRetries, internalKind, extraHeaders) {
140
+ if (cbIsOpen()) {
141
+ return { ok: false, error: { code: "CIRCUIT_OPEN", message: "Endpoint temporarily unavailable; retrying later." } };
142
+ }
122
143
  const url = `${baseUrl}${path}`;
123
144
  const controller = new AbortController();
124
145
  const timer = setTimeout(() => controller.abort(), timeout);
@@ -165,6 +186,8 @@ function createApiClient(options) {
165
186
  await sleep(getBackoffDelay(maxRetries - retries));
166
187
  return request(method, path, body, retries - 1, internalKind, extraHeaders);
167
188
  }
189
+ if (response.status >= 500) cbRecordUnreachable();
190
+ else cbRecordReachable();
168
191
  return {
169
192
  ok: false,
170
193
  error: {
@@ -175,6 +198,7 @@ function createApiClient(options) {
175
198
  }
176
199
  const payload = await response.json();
177
200
  const data = payload && typeof payload === "object" && "ok" in payload && "data" in payload ? payload.data : payload;
201
+ cbRecordReachable();
178
202
  return { ok: true, data };
179
203
  } catch (error) {
180
204
  clearTimeout(timer);
@@ -182,6 +206,7 @@ function createApiClient(options) {
182
206
  await sleep(getBackoffDelay(maxRetries - retries));
183
207
  return request(method, path, body, retries - 1, internalKind);
184
208
  }
209
+ cbRecordUnreachable();
185
210
  return {
186
211
  ok: false,
187
212
  error: {
@@ -466,9 +491,9 @@ function isRetryable(error) {
466
491
 
467
492
  // src/region.ts
468
493
  var REGION_ENDPOINTS = {
469
- us: "https://api.us.mushimushi.dev/functions/v1/api",
470
- eu: "https://api.eu.mushimushi.dev/functions/v1/api",
471
- jp: "https://api.jp.mushimushi.dev/functions/v1/api"
494
+ us: "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api",
495
+ eu: "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api",
496
+ jp: "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api"
472
497
  };
473
498
  var ROUTING_CACHE_KEY = "mushi_region_v1";
474
499
  var ROUTING_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
@@ -1214,7 +1239,8 @@ function createOfflineQueue(config = {}) {
1214
1239
  if (!enabled || !syncOnReconnect || typeof window === "undefined") return;
1215
1240
  const tryFlush = () => {
1216
1241
  if (navigator.onLine) {
1217
- flush(client).catch(() => {
1242
+ flush(client).catch((err) => {
1243
+ queueLog.warn("Offline queue auto-flush failed", { err: String(err) });
1218
1244
  });
1219
1245
  }
1220
1246
  };
@@ -1247,6 +1273,15 @@ function captureEnvironment() {
1247
1273
  const scr = typeof screen !== "undefined" ? screen : void 0;
1248
1274
  void kickOffUserAgentData(nav);
1249
1275
  const connection = nav && "connection" in nav ? nav.connection : void 0;
1276
+ const w = win;
1277
+ const isCapacitor = !!w?.Capacitor;
1278
+ const isCordova = !!w?.cordova;
1279
+ const isReactNative = nav?.product === "ReactNative" || typeof globalThis.HermesInternal !== "undefined";
1280
+ const native = isCapacitor || isCordova || isReactNative ? {
1281
+ ...isCapacitor ? { capacitor: true } : {},
1282
+ ...isCordova ? { cordova: true } : {},
1283
+ ...isReactNative ? { reactNative: true } : {}
1284
+ } : void 0;
1250
1285
  return {
1251
1286
  userAgent: nav?.userAgent ?? "unknown",
1252
1287
  platform: nav?.platform ?? "unknown",
@@ -1279,7 +1314,8 @@ function captureEnvironment() {
1279
1314
  displayMode: matchDisplayMode(win),
1280
1315
  documentTitle: doc?.title?.slice(0, 200),
1281
1316
  buildId: readBuildIdMeta(doc),
1282
- pageLoadTiming: capturePageLoadTiming(win)
1317
+ pageLoadTiming: capturePageLoadTiming(win),
1318
+ ...native ? { native } : {}
1283
1319
  };
1284
1320
  }
1285
1321
  function findNearestTestidFromActive(doc) {
@@ -1407,20 +1443,9 @@ function capturePageLoadTiming(win) {
1407
1443
  }
1408
1444
 
1409
1445
  // src/reporter-token.ts
1410
- var STORAGE_KEY = "mushi_reporter_token";
1411
- function getReporterToken() {
1412
- if (typeof localStorage !== "undefined") {
1413
- const existing = localStorage.getItem(STORAGE_KEY);
1414
- if (existing) return existing;
1415
- }
1416
- const token = generateToken();
1417
- if (typeof localStorage !== "undefined") {
1418
- try {
1419
- localStorage.setItem(STORAGE_KEY, token);
1420
- } catch {
1421
- }
1422
- }
1423
- return token;
1446
+ var LEGACY_STORAGE_KEY = "mushi_reporter_token";
1447
+ function storageKey(projectId) {
1448
+ return `mushi:reporter-token:${projectId}`;
1424
1449
  }
1425
1450
  function generateToken() {
1426
1451
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
@@ -1437,6 +1462,38 @@ function generateToken() {
1437
1462
  const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1438
1463
  return `mushi_${hex}`;
1439
1464
  }
1465
+ function readStoredToken(key) {
1466
+ if (typeof localStorage === "undefined") return null;
1467
+ try {
1468
+ const existing = localStorage.getItem(key);
1469
+ return existing && existing.length > 0 ? existing : null;
1470
+ } catch {
1471
+ return null;
1472
+ }
1473
+ }
1474
+ function writeStoredToken(key, token) {
1475
+ if (typeof localStorage === "undefined") return;
1476
+ try {
1477
+ localStorage.setItem(key, token);
1478
+ } catch {
1479
+ }
1480
+ }
1481
+ function getReporterToken(projectId) {
1482
+ if (!projectId) {
1483
+ const legacy2 = readStoredToken(LEGACY_STORAGE_KEY);
1484
+ if (legacy2) return legacy2;
1485
+ const token2 = generateToken();
1486
+ writeStoredToken(LEGACY_STORAGE_KEY, token2);
1487
+ return token2;
1488
+ }
1489
+ const key = storageKey(projectId);
1490
+ const scoped = readStoredToken(key);
1491
+ if (scoped) return scoped;
1492
+ const legacy = readStoredToken(LEGACY_STORAGE_KEY);
1493
+ const token = legacy ?? generateToken();
1494
+ writeStoredToken(key, token);
1495
+ return token;
1496
+ }
1440
1497
 
1441
1498
  // src/uuid.ts
1442
1499
  function newUuid() {
@@ -1732,6 +1789,42 @@ var MUSHI_COLORS_DARK = {
1732
1789
  ok: "#4ADE80",
1733
1790
  danger: "#F87171"
1734
1791
  };
1792
+ var MUSHI_TIER_COLORS = {
1793
+ free: MUSHI_COLORS_LIGHT.inkMuted,
1794
+ explorer: "#2563EB",
1795
+ contributor: "#7C3AED",
1796
+ champion: "#D97706",
1797
+ default: MUSHI_COLORS_LIGHT.accent
1798
+ };
1799
+ var MUSHI_BANNER_NEON = {
1800
+ bg: "#0FFF50",
1801
+ fg: "#0a1a0a",
1802
+ border: "#00C43A"
1803
+ };
1804
+ var MUSHI_ON_ACCENT = "#FAF7F0";
1805
+ var MUSHI_INVERSE = "#ffffff";
1806
+ var MUSHI_ACCENT_SHADOW = {
1807
+ light: "#9A2A1E",
1808
+ dark: "#7A1F15"
1809
+ };
1810
+ var MUSHI_BANNER_BRAND_BORDER = {
1811
+ light: "#B52F1F",
1812
+ dark: "#C4321E"
1813
+ };
1814
+ var MUSHI_REPORTER_STATUS = {
1815
+ light: {
1816
+ sent: { fg: "#1E4A8C", bg: "rgba(30,74,140,0.08)", border: "rgba(30,74,140,0.16)" },
1817
+ review: { fg: "#8A5A00", bg: "rgba(180,120,0,0.10)", border: "rgba(180,120,0,0.18)" },
1818
+ fixing: { fg: "#9A3D12", bg: "rgba(224,60,44,0.10)", border: "rgba(224,60,44,0.18)" },
1819
+ fixed: { fg: "#1F6B3A", bg: "rgba(31,107,58,0.10)", border: "rgba(31,107,58,0.18)" }
1820
+ },
1821
+ dark: {
1822
+ sent: { fg: "#A8C4FF", bg: "rgba(120,160,255,0.12)", border: "rgba(120,160,255,0.22)" },
1823
+ review: { fg: "#FFD27A", bg: "rgba(255,190,90,0.12)", border: "rgba(255,190,90,0.22)" },
1824
+ fixing: { fg: "#FFB899", bg: "rgba(255,120,60,0.12)", border: "rgba(255,120,60,0.24)" },
1825
+ fixed: { fg: "#8FE3B0", bg: "rgba(80,200,130,0.12)", border: "rgba(80,200,130,0.22)" }
1826
+ }
1827
+ };
1735
1828
  function mushiPalette(mode) {
1736
1829
  return mode === "dark" ? MUSHI_COLORS_DARK : MUSHI_COLORS_LIGHT;
1737
1830
  }
@@ -1778,16 +1871,32 @@ var MUSHI_MOTION = {
1778
1871
  var MUSHI_GEOMETRY = {
1779
1872
  bannerHeight: 36,
1780
1873
  fabSize: 52,
1781
- edgeTabWidth: 28,
1874
+ /** edge-tab launcher width (px) */
1875
+ edgeTabWidth: 32,
1782
1876
  /** default gutter from the screen edge (added on top of safe-area insets) */
1783
1877
  gutter: 24,
1784
- panelWidth: 384,
1878
+ /** Web panel width (px) — matches shipped Shadow DOM widget */
1879
+ panelWidth: 360,
1880
+ /** max panel height before keyboard inset (px) */
1881
+ panelMaxHeight: 480,
1785
1882
  /** below this viewport width the panel becomes a full-width bottom sheet */
1786
1883
  panelSheetBreakpoint: 480
1787
1884
  };
1885
+ function safeWidgetHex(v) {
1886
+ return /^#[0-9a-fA-F]{3,8}$/.test(v) ? v : "";
1887
+ }
1888
+ function resolveWidgetAccent(mode, accent = "", accentText = "") {
1889
+ const pal = mushiPalette(mode);
1890
+ const safeAccent = safeWidgetHex(accent);
1891
+ const safeAccentText = safeWidgetHex(accentText);
1892
+ const resolvedAccent = safeAccent || pal.accent;
1893
+ const accentWash = safeAccent ? mode === "dark" ? `${resolvedAccent}1F` : `${resolvedAccent}14` : pal.accentWash;
1894
+ const accentInk = safeAccentText || pal.accentInk;
1895
+ return { accent: resolvedAccent, accentWash, accentInk };
1896
+ }
1788
1897
  var MUSHI_COPY = {
1789
1898
  bannerLabel: "Beta",
1790
- bannerMessage: "Help us improve \u2014 your feedback shapes what we ship.",
1899
+ bannerMessage: "Found a bug? Tap to report it.",
1791
1900
  bugCta: "Report a bug",
1792
1901
  featureCta: "Feature request",
1793
1902
  triggerText: "\u{1F41B}",
@@ -1860,7 +1969,7 @@ var SLUG_DOMAIN_HINTS = {
1860
1969
  "glot-it": "glot.it",
1861
1970
  glotit: "glot.it",
1862
1971
  "solo-boss-cloud": "sbc-front.vercel.app",
1863
- "mushi-mushi": "mushimushi.dev"
1972
+ "mushi-mushi": "kensaur.us"
1864
1973
  };
1865
1974
  var SLUG_ICON_URL_HINTS = {
1866
1975
  "mushi-mushi": "https://kensaur.us/mushi-mushi/admin/favicon.svg",
@@ -2039,8 +2148,13 @@ function isSafeFaviconHref(href) {
2039
2148
  }
2040
2149
 
2041
2150
  exports.DEFAULT_API_ENDPOINT = DEFAULT_API_ENDPOINT;
2151
+ exports.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES;
2152
+ exports.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
2042
2153
  exports.MAX_REPORT_PAYLOAD_BYTES = MAX_REPORT_PAYLOAD_BYTES;
2043
2154
  exports.MAX_SCREENSHOT_DATA_URL_BYTES = MAX_SCREENSHOT_DATA_URL_BYTES;
2155
+ exports.MUSHI_ACCENT_SHADOW = MUSHI_ACCENT_SHADOW;
2156
+ exports.MUSHI_BANNER_BRAND_BORDER = MUSHI_BANNER_BRAND_BORDER;
2157
+ exports.MUSHI_BANNER_NEON = MUSHI_BANNER_NEON;
2044
2158
  exports.MUSHI_COLORS_DARK = MUSHI_COLORS_DARK;
2045
2159
  exports.MUSHI_COLORS_LIGHT = MUSHI_COLORS_LIGHT;
2046
2160
  exports.MUSHI_COPY = MUSHI_COPY;
@@ -2048,12 +2162,16 @@ exports.MUSHI_GEOMETRY = MUSHI_GEOMETRY;
2048
2162
  exports.MUSHI_IDENTITY_TOKEN_PREFIX = MUSHI_IDENTITY_TOKEN_PREFIX;
2049
2163
  exports.MUSHI_INTERNAL_HEADER = MUSHI_INTERNAL_HEADER;
2050
2164
  exports.MUSHI_INTERNAL_INIT_MARKER = MUSHI_INTERNAL_INIT_MARKER;
2165
+ exports.MUSHI_INVERSE = MUSHI_INVERSE;
2051
2166
  exports.MUSHI_MOTION = MUSHI_MOTION;
2167
+ exports.MUSHI_ON_ACCENT = MUSHI_ON_ACCENT;
2052
2168
  exports.MUSHI_PROJECT_HEADER = MUSHI_PROJECT_HEADER;
2053
2169
  exports.MUSHI_RADIUS = MUSHI_RADIUS;
2170
+ exports.MUSHI_REPORTER_STATUS = MUSHI_REPORTER_STATUS;
2054
2171
  exports.MUSHI_SDK_PACKAGE_HEADER = MUSHI_SDK_PACKAGE_HEADER;
2055
2172
  exports.MUSHI_SDK_VERSION_HEADER = MUSHI_SDK_VERSION_HEADER;
2056
2173
  exports.MUSHI_SPACING = MUSHI_SPACING;
2174
+ exports.MUSHI_TIER_COLORS = MUSHI_TIER_COLORS;
2057
2175
  exports.MUSHI_TYPE = MUSHI_TYPE;
2058
2176
  exports.MUSHI_USER_TOKEN_HEADER = MUSHI_USER_TOKEN_HEADER;
2059
2177
  exports.MUSHI_Z = MUSHI_Z;
@@ -2076,6 +2194,7 @@ exports.getDeviceFingerprintHash = getDeviceFingerprintHash;
2076
2194
  exports.getReporterToken = getReporterToken;
2077
2195
  exports.getSessionId = getSessionId;
2078
2196
  exports.githubRepoDomainHint = githubRepoDomainHint;
2197
+ exports.hmacSha256Hex = hmacSha256Hex;
2079
2198
  exports.isLikelyGenericFavicon = isLikelyGenericFavicon;
2080
2199
  exports.isUntrustedFaviconUrl = isUntrustedFaviconUrl;
2081
2200
  exports.mushiPalette = mushiPalette;
@@ -2093,6 +2212,9 @@ exports.resolveEnvConfig = resolveEnvConfig;
2093
2212
  exports.resolveProjectDomain = resolveProjectDomain;
2094
2213
  exports.resolveProjectFaviconDomains = resolveProjectFaviconDomains;
2095
2214
  exports.resolveRegionEndpoint = resolveRegionEndpoint;
2215
+ exports.resolveWidgetAccent = resolveWidgetAccent;
2216
+ exports.safeWidgetHex = safeWidgetHex;
2096
2217
  exports.scrubPii = scrubPii;
2218
+ exports.sha256Hex = sha256Hex;
2097
2219
  //# sourceMappingURL=index.cjs.map
2098
2220
  //# sourceMappingURL=index.cjs.map