@mushi-mushi/web 0.2.0 → 0.3.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
@@ -2,6 +2,10 @@
2
2
 
3
3
  Browser SDK for Mushi Mushi — embeddable bug reporting widget with Shadow DOM isolation.
4
4
 
5
+ > **One-command setup:** `npx mushi-mushi` installs this package for vanilla-JS apps (or alongside the framework SDK for Vue, Svelte, Angular).
6
+ >
7
+ > **Framework SDKs:** [`@mushi-mushi/react`](https://npmjs.com/package/@mushi-mushi/react) (Next.js / React) · [`@mushi-mushi/vue`](https://npmjs.com/package/@mushi-mushi/vue) (Nuxt / Vue) · [`@mushi-mushi/svelte`](https://npmjs.com/package/@mushi-mushi/svelte) (SvelteKit / 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)
8
+
5
9
  ## Features
6
10
 
7
11
  - Shadow DOM widget with full CSS isolation from host page
package/dist/index.cjs CHANGED
@@ -1545,6 +1545,7 @@ function createInstance(config) {
1545
1545
  }
1546
1546
  const scrubbedDescription = piiScrubber.scrub(preFilter.truncate(description));
1547
1547
  const sentryCtx = config.sentry ? captureSentryContext(config.sentry) : void 0;
1548
+ const fingerprintHash = await core.getDeviceFingerprintHash().catch(() => null);
1548
1549
  const report = {
1549
1550
  id: crypto.randomUUID?.() ?? `mushi_${Date.now()}_${Math.random().toString(36).slice(2)}`,
1550
1551
  projectId: config.projectId,
@@ -1564,6 +1565,7 @@ function createInstance(config) {
1564
1565
  },
1565
1566
  sessionId: core.getSessionId(),
1566
1567
  reporterToken: core.getReporterToken(),
1568
+ ...fingerprintHash ? { fingerprintHash } : {},
1567
1569
  appVersion: config.integrations?.vercel?.analyticsId,
1568
1570
  proactiveTrigger: pendingProactiveTrigger ?? void 0,
1569
1571
  sentryEventId: sentryCtx?.eventId,
@@ -1640,6 +1642,58 @@ function createInstance(config) {
1640
1642
  listeners.clear();
1641
1643
  instance = null;
1642
1644
  log.debug("Destroyed");
1645
+ },
1646
+ // Wave G4 — unified `captureEvent` API for programmatic/adapter-driven
1647
+ // reports. Skips the widget, runs the same PII scrub + rate limit +
1648
+ // offline-queue path as `submit()`, and returns the server report id.
1649
+ async captureEvent(input) {
1650
+ if (!rateLimiter.tryConsume()) {
1651
+ log.warn("captureEvent throttled \u2014 rate limit exceeded");
1652
+ return null;
1653
+ }
1654
+ const description = piiScrubber.scrub(preFilter.truncate(input.description));
1655
+ const category = input.category ?? "bug";
1656
+ const report = {
1657
+ id: crypto.randomUUID?.() ?? `mushi_${Date.now()}_${Math.random().toString(36).slice(2)}`,
1658
+ projectId: config.projectId,
1659
+ category,
1660
+ description,
1661
+ environment: core.captureEnvironment(),
1662
+ metadata: {
1663
+ ...input.metadata ?? {},
1664
+ ...userInfo ? { user: userInfo } : {},
1665
+ ...input.tags ? { tags: input.tags } : {},
1666
+ ...input.error ? { error: input.error } : {},
1667
+ ...input.severity ? { severity: input.severity } : {},
1668
+ ...input.component ? { component: input.component } : {},
1669
+ ...input.source ? { source: input.source } : { source: "captureEvent" }
1670
+ },
1671
+ sessionId: core.getSessionId(),
1672
+ reporterToken: core.getReporterToken(),
1673
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1674
+ };
1675
+ emit("report:submitted", { reportId: report.id });
1676
+ if (typeof navigator !== "undefined" && !navigator.onLine) {
1677
+ await offlineQueue.enqueue(report);
1678
+ emit("report:queued", { reportId: report.id });
1679
+ return null;
1680
+ }
1681
+ const res = await apiClient.submitReport(report);
1682
+ if (res.ok) {
1683
+ emit("report:sent", { reportId: res.data?.reportId });
1684
+ return res.data?.reportId ?? null;
1685
+ }
1686
+ await offlineQueue.enqueue(report);
1687
+ emit("report:failed", { reportId: report.id, error: res.error });
1688
+ return null;
1689
+ },
1690
+ identify(userId, traits) {
1691
+ userInfo = { id: userId, ...traits?.email ? { email: traits.email } : {}, ...traits?.name ? { name: traits.name } : {} };
1692
+ if (traits) {
1693
+ for (const [k, v] of Object.entries(traits)) {
1694
+ if (k !== "email" && k !== "name") customMetadata[`user.${k}`] = v;
1695
+ }
1696
+ }
1643
1697
  }
1644
1698
  };
1645
1699
  return sdk;
@@ -1661,6 +1715,9 @@ function createNoopInstance() {
1661
1715
  },
1662
1716
  destroy: () => {
1663
1717
  instance = null;
1718
+ },
1719
+ captureEvent: async () => null,
1720
+ identify: () => {
1664
1721
  }
1665
1722
  };
1666
1723
  }