@mushi-mushi/core 0.9.0 → 1.1.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/CONTRIBUTING.md +27 -0
- package/README.md +7 -1
- package/SECURITY.md +167 -4
- package/dist/index.cjs +244 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +497 -2
- package/dist/index.d.ts +497 -2
- package/dist/index.js +243 -2
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -66,6 +66,38 @@ interface MushiWidgetConfig {
|
|
|
66
66
|
brandFooter?: boolean;
|
|
67
67
|
/** How the widget should surface SDK freshness warnings. Defaults to auto. */
|
|
68
68
|
outdatedBanner?: 'auto' | 'banner' | 'console-only' | 'off';
|
|
69
|
+
/**
|
|
70
|
+
* Beta mode: injects discreet "early access" messaging into the widget panel.
|
|
71
|
+
* Shows a beta strip on the category step and a contact footer on the success
|
|
72
|
+
* step. Designed to reduce user frustration with in-progress apps while
|
|
73
|
+
* actively inviting feedback.
|
|
74
|
+
*/
|
|
75
|
+
betaMode?: MushiBetaModeConfig;
|
|
76
|
+
}
|
|
77
|
+
interface MushiBetaModeConfig {
|
|
78
|
+
enabled?: boolean;
|
|
79
|
+
/** Display name of the app shown in the beta strip. Defaults to 'This app'. */
|
|
80
|
+
appName?: string;
|
|
81
|
+
/** Contact email shown on success step for direct founder/dev reach. */
|
|
82
|
+
contactEmail?: string;
|
|
83
|
+
/** Override the default beta strip message. */
|
|
84
|
+
message?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Optional perks shown to the user for opting in as a beta tester.
|
|
87
|
+
* e.g. ['Early access to new lessons', 'Priority bug fix queue'].
|
|
88
|
+
*/
|
|
89
|
+
perks?: string[];
|
|
90
|
+
/**
|
|
91
|
+
* Version-tied release notes rendered as a collapsible "What's new in this build"
|
|
92
|
+
* row in the widget. Closes the feedback loop — "did you fix what I reported?"
|
|
93
|
+
* Only the first entry (latest) is shown by default.
|
|
94
|
+
*/
|
|
95
|
+
changelogItems?: MushiBetaChangelogEntry[];
|
|
96
|
+
}
|
|
97
|
+
interface MushiBetaChangelogEntry {
|
|
98
|
+
version: string;
|
|
99
|
+
date?: string;
|
|
100
|
+
items: string[];
|
|
69
101
|
}
|
|
70
102
|
interface MushiWidgetInset {
|
|
71
103
|
top?: number | 'auto';
|
|
@@ -256,10 +288,62 @@ interface MushiOfflineConfig {
|
|
|
256
288
|
}
|
|
257
289
|
interface MushiRewardsConfig {
|
|
258
290
|
enabled?: boolean;
|
|
259
|
-
|
|
291
|
+
/**
|
|
292
|
+
* P1: when true the SDK auto-tracks route navigations, [data-testid] clicks,
|
|
293
|
+
* and session dwell time and flushes them to POST /v1/sdk/activity.
|
|
294
|
+
* Default false. Requires opted_in_to_rewards = true from the end user.
|
|
295
|
+
*/
|
|
296
|
+
trackActivity?: boolean;
|
|
297
|
+
/**
|
|
298
|
+
* Activity batch flush interval in ms. Default 300_000 (5 min).
|
|
299
|
+
* Minimum 30_000 (30s) — smaller values are clamped.
|
|
300
|
+
*/
|
|
301
|
+
flushIntervalMs?: number;
|
|
302
|
+
/**
|
|
303
|
+
* Show a tier + points footer in the SDK widget.
|
|
304
|
+
* Default false.
|
|
305
|
+
*/
|
|
306
|
+
showInWidget?: boolean;
|
|
307
|
+
/**
|
|
308
|
+
* Whether to show a "+X pts" toast notification on each award.
|
|
309
|
+
* Default true when enabled.
|
|
310
|
+
*/
|
|
260
311
|
showNotifications?: boolean;
|
|
312
|
+
/**
|
|
313
|
+
* 'auto' — immediately opt the user in when identify() is called.
|
|
314
|
+
* 'explicit' — SDK surfaces a one-time consent prompt. Default 'explicit'.
|
|
315
|
+
*/
|
|
316
|
+
consentMode?: 'auto' | 'explicit';
|
|
317
|
+
/**
|
|
318
|
+
* P2: callback that returns a host-app JWT for server-side identity
|
|
319
|
+
* verification before monetary rewards are issued. Return null to skip
|
|
320
|
+
* verification for non-monetary actions.
|
|
321
|
+
*/
|
|
322
|
+
verifyUserToken?: () => Promise<string | null>;
|
|
323
|
+
/**
|
|
324
|
+
* @deprecated Superseded by server-side reward_webhooks table.
|
|
325
|
+
* Kept for backwards compatibility; has no effect from v0.10.0.
|
|
326
|
+
*/
|
|
261
327
|
webhookOnReward?: string;
|
|
262
328
|
}
|
|
329
|
+
interface MushiReputationResult {
|
|
330
|
+
totalPoints: number;
|
|
331
|
+
points30d: number;
|
|
332
|
+
reputation: number;
|
|
333
|
+
confirmedBugs: number;
|
|
334
|
+
totalReports: number;
|
|
335
|
+
}
|
|
336
|
+
interface MushiTierResult {
|
|
337
|
+
id: string;
|
|
338
|
+
slug: string;
|
|
339
|
+
displayName: string;
|
|
340
|
+
pointsThreshold: number;
|
|
341
|
+
perks: Record<string, unknown>;
|
|
342
|
+
}
|
|
343
|
+
interface MushiActivityEvent {
|
|
344
|
+
action: string;
|
|
345
|
+
metadata?: Record<string, unknown>;
|
|
346
|
+
}
|
|
263
347
|
type MushiReportCategory = 'bug' | 'slow' | 'visual' | 'confusing' | 'other';
|
|
264
348
|
type MushiReportStatus = 'pending' | 'submitted' | 'classified' | 'grouped' | 'fixing' | 'fixed' | 'dismissed';
|
|
265
349
|
interface MushiReport {
|
|
@@ -290,11 +374,136 @@ interface MushiReport {
|
|
|
290
374
|
/** npm package version that submitted the report, e.g. `0.8.0`. */
|
|
291
375
|
sdkVersion?: string;
|
|
292
376
|
proactiveTrigger?: string;
|
|
377
|
+
/**
|
|
378
|
+
* Mushi-side breadcrumbs maintained by the SDK in a 50-entry ring buffer.
|
|
379
|
+
* Captured automatically (SDK lifecycle events, console errors, route
|
|
380
|
+
* changes, `[data-testid]` clicks) and via `Mushi.addBreadcrumb()`.
|
|
381
|
+
* Independent of Sentry's breadcrumbs (those land in
|
|
382
|
+
* `sentryContext.breadcrumbs`) so a host using Mushi without Sentry
|
|
383
|
+
* still gets a timeline.
|
|
384
|
+
*/
|
|
385
|
+
breadcrumbs?: MushiBreadcrumb[];
|
|
386
|
+
/**
|
|
387
|
+
* Sticky tags set via `Mushi.setTag()` / `Mushi.setTags()`. Mirrors
|
|
388
|
+
* the Sentry / DataDog "tag" vocabulary — short string keys with
|
|
389
|
+
* scalar values. The Triage LLM treats these as high-signal hints
|
|
390
|
+
* ("checkout-flow=redesign-v2" tells the LLM which feature flag the
|
|
391
|
+
* report came from). Plumbed through to `reports.metadata.tags`
|
|
392
|
+
* server-side until a dedicated `tags` column is added.
|
|
393
|
+
*/
|
|
394
|
+
tags?: Record<string, string | number | boolean>;
|
|
395
|
+
/**
|
|
396
|
+
* Rich Sentry context captured at report time. Replaces the legacy
|
|
397
|
+
* `sentryEventId`/`sentryReplayId` pair (those are kept for
|
|
398
|
+
* back-compat — server unifies them). When the host has Sentry
|
|
399
|
+
* installed (v7, v8, or v9), every Mushi report carries enough trace
|
|
400
|
+
* data for the admin to pivot into Sentry's MCP / web UI without
|
|
401
|
+
* a manual paste.
|
|
402
|
+
*/
|
|
403
|
+
sentryContext?: MushiSentryContext;
|
|
404
|
+
/** @deprecated use `sentryContext.eventId` — kept for back-compat. */
|
|
293
405
|
sentryEventId?: string;
|
|
406
|
+
/** @deprecated use `sentryContext.replayId` — kept for back-compat. */
|
|
294
407
|
sentryReplayId?: string;
|
|
295
408
|
queuedAt?: string;
|
|
296
409
|
createdAt: string;
|
|
297
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Single breadcrumb entry. Shape follows the Sentry breadcrumb schema
|
|
413
|
+
* 1:1 so an admin tooling layer (or the Triage LLM) can treat Mushi
|
|
414
|
+
* breadcrumbs and Sentry breadcrumbs interchangeably without a
|
|
415
|
+
* normalisation pass.
|
|
416
|
+
*/
|
|
417
|
+
interface MushiBreadcrumb {
|
|
418
|
+
/** Unix epoch ms when the breadcrumb fired. */
|
|
419
|
+
timestamp: number;
|
|
420
|
+
/**
|
|
421
|
+
* Coarse bucket for filtering / coloring in the report drawer.
|
|
422
|
+
* - `navigation` — route or url change
|
|
423
|
+
* - `ui.click` — user clicked a `[data-testid]` element (web SDK)
|
|
424
|
+
* - `ui.tap` — user tapped a `UIView` / Android `View` (native SDKs)
|
|
425
|
+
* - `console` — `console.error` / `console.warn` callsite
|
|
426
|
+
* - `xhr` / `fetch` — network request that errored or 4xx/5xx (web SDK)
|
|
427
|
+
* - `network` — network failure on iOS / Android (native SDKs)
|
|
428
|
+
* - `lifecycle` — Mushi SDK init / open / submit / queue
|
|
429
|
+
* - `custom` — host called `Mushi.addBreadcrumb()`
|
|
430
|
+
*
|
|
431
|
+
* Admin tooling that filters by interaction should treat
|
|
432
|
+
* `ui.click` ∪ `ui.tap` as one bucket and `xhr` ∪ `fetch` ∪ `network`
|
|
433
|
+
* as one bucket. Native enums are platform-idiomatic (`tap` on touch
|
|
434
|
+
* devices, `network` because iOS/Android don't expose `XHR` / `fetch`
|
|
435
|
+
* separately) — every wire string is documented in the native READMEs.
|
|
436
|
+
*/
|
|
437
|
+
category: 'navigation' | 'ui.click' | 'ui.tap' | 'console' | 'xhr' | 'fetch' | 'network' | 'lifecycle' | 'custom';
|
|
438
|
+
/**
|
|
439
|
+
* Severity — `info` is the default. `warning` / `error` map to a
|
|
440
|
+
* coloured pill in the drawer; the Triage LLM uses these to decide
|
|
441
|
+
* which breadcrumbs to feature in the "what happened" summary.
|
|
442
|
+
*/
|
|
443
|
+
level: 'debug' | 'info' | 'warning' | 'error';
|
|
444
|
+
/** Free-form short summary, capped at 500 chars at submit time. */
|
|
445
|
+
message: string;
|
|
446
|
+
/** Optional structured payload — kept small to keep ingest cheap. */
|
|
447
|
+
data?: Record<string, unknown>;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Snapshot of Sentry's current scope at report submission. Captured by
|
|
451
|
+
* `captureSentryContext()` in the web SDK; designed to be cheap to
|
|
452
|
+
* serialise and exhaustively useful when the admin pivots into Sentry
|
|
453
|
+
* MCP via `find_organizations` → `search_issues` → `get_event_attachment`.
|
|
454
|
+
*
|
|
455
|
+
* Every field is optional so a host without Sentry installed (and a
|
|
456
|
+
* Sentry SDK that exposes only a subset of these globals) still
|
|
457
|
+
* produces a partial — but useful — payload.
|
|
458
|
+
*/
|
|
459
|
+
interface MushiSentryContext {
|
|
460
|
+
/** Sentry SDK version family detected at capture time. */
|
|
461
|
+
sdk?: 'v7' | 'v8' | 'v9' | 'unknown';
|
|
462
|
+
/** `Sentry.lastEventId()` (v8+) / `getLastEventId()` (v7). */
|
|
463
|
+
eventId?: string;
|
|
464
|
+
/** Replay session id from `Sentry.getReplay()?.getReplayId()`. */
|
|
465
|
+
replayId?: string;
|
|
466
|
+
/** Distributed-tracing trace id (32 hex). */
|
|
467
|
+
traceId?: string;
|
|
468
|
+
/** Active span id at capture time (16 hex). */
|
|
469
|
+
spanId?: string;
|
|
470
|
+
/** `transaction` field from the active scope, e.g. `GET /checkout`. */
|
|
471
|
+
transactionName?: string;
|
|
472
|
+
/** Build identifier set via `Sentry.init({ release })`. */
|
|
473
|
+
release?: string;
|
|
474
|
+
/** Logical environment, e.g. `production`. */
|
|
475
|
+
environment?: string;
|
|
476
|
+
/** Session id when Sentry session-tracking is enabled. */
|
|
477
|
+
sessionId?: string;
|
|
478
|
+
/** User context from Sentry's scope (id/email/username/ip). */
|
|
479
|
+
user?: {
|
|
480
|
+
id?: string;
|
|
481
|
+
email?: string;
|
|
482
|
+
username?: string;
|
|
483
|
+
ip_address?: string;
|
|
484
|
+
};
|
|
485
|
+
/** Tags attached to Sentry's current scope. */
|
|
486
|
+
tags?: Record<string, string | number | boolean>;
|
|
487
|
+
/**
|
|
488
|
+
* Last N Sentry breadcrumbs (default cap = 30). Already-formatted
|
|
489
|
+
* Sentry breadcrumbs, not Mushi's — the two are surfaced side by
|
|
490
|
+
* side in the admin drawer so users can correlate.
|
|
491
|
+
*/
|
|
492
|
+
breadcrumbs?: Array<{
|
|
493
|
+
timestamp?: number;
|
|
494
|
+
category?: string;
|
|
495
|
+
level?: string;
|
|
496
|
+
message?: string;
|
|
497
|
+
type?: string;
|
|
498
|
+
data?: Record<string, unknown>;
|
|
499
|
+
}>;
|
|
500
|
+
/**
|
|
501
|
+
* Issue url (deeplink) when the SDK can derive it from the event id
|
|
502
|
+
* + DSN. Lets the admin jump straight to the Sentry issue page
|
|
503
|
+
* without the user pasting a link.
|
|
504
|
+
*/
|
|
505
|
+
issueUrl?: string;
|
|
506
|
+
}
|
|
298
507
|
interface MushiReportBuilder {
|
|
299
508
|
addMetadata(key: string, value: unknown): void;
|
|
300
509
|
setCategory(category: MushiReportCategory): void;
|
|
@@ -336,6 +545,101 @@ interface MushiEnvironment {
|
|
|
336
545
|
*/
|
|
337
546
|
route?: string;
|
|
338
547
|
nearestTestid?: string;
|
|
548
|
+
/**
|
|
549
|
+
* SDK boost (2026-05-07): richer per-report context so the Triage LLM
|
|
550
|
+
* and the admin /reports detail drawer have something to reason about
|
|
551
|
+
* beyond `userAgent`. Every field is optional so legacy SDKs and
|
|
552
|
+
* non-browser callers (CLI, server-to-server) keep validating.
|
|
553
|
+
*/
|
|
554
|
+
/**
|
|
555
|
+
* Parsed UA-CH (User-Agent Client Hints) high-entropy values when the
|
|
556
|
+
* browser supports `navigator.userAgentData.getHighEntropyValues`.
|
|
557
|
+
* This is the modern, reliable way to identify Chromium browsers — UA
|
|
558
|
+
* sniffing is unreliable post-Chrome 100 because the UA string was
|
|
559
|
+
* frozen for privacy. Safari + Firefox don't expose UA-CH so we still
|
|
560
|
+
* fall back to UA parsing for those (handled server-side).
|
|
561
|
+
*/
|
|
562
|
+
userAgentData?: {
|
|
563
|
+
/** Best-effort browser brand (e.g. "Chrome", "Edge", "Brave"). */
|
|
564
|
+
browser?: string;
|
|
565
|
+
/** Browser version (full semver where available, e.g. "131.0.6778.86"). */
|
|
566
|
+
browserVersion?: string;
|
|
567
|
+
/** OS family (e.g. "macOS", "Windows", "Android", "iOS"). */
|
|
568
|
+
os?: string;
|
|
569
|
+
/** OS version when the browser exposes it (e.g. "14.5.0"). */
|
|
570
|
+
osVersion?: string;
|
|
571
|
+
/** Whether the device self-identifies as mobile (UA-CH `mobile`). */
|
|
572
|
+
mobile?: boolean;
|
|
573
|
+
/** Device model when the OS exposes it (Android only, e.g. "Pixel 8"). */
|
|
574
|
+
model?: string;
|
|
575
|
+
/** CPU architecture (e.g. "x86", "arm"). */
|
|
576
|
+
architecture?: string;
|
|
577
|
+
/** CPU bitness ("32" or "64"). */
|
|
578
|
+
bitness?: string;
|
|
579
|
+
};
|
|
580
|
+
/**
|
|
581
|
+
* Physical device pixels behind the viewport. `viewport` already
|
|
582
|
+
* captures the CSS pixel box; this lets us tell a 1080p MacBook from a
|
|
583
|
+
* Retina iPhone and explain "looks fine on my screen, broken on
|
|
584
|
+
* theirs" by surfacing devicePixelRatio mismatches.
|
|
585
|
+
*/
|
|
586
|
+
screen?: {
|
|
587
|
+
/** `screen.width` — outer device width in CSS px. */
|
|
588
|
+
width?: number;
|
|
589
|
+
/** `screen.height` — outer device height in CSS px. */
|
|
590
|
+
height?: number;
|
|
591
|
+
/** `window.devicePixelRatio` — physical / CSS px ratio. */
|
|
592
|
+
devicePixelRatio?: number;
|
|
593
|
+
/** `screen.colorDepth` — bits per pixel (typically 24/30). */
|
|
594
|
+
colorDepth?: number;
|
|
595
|
+
/** Active orientation type from `screen.orientation`. */
|
|
596
|
+
orientation?: 'portrait-primary' | 'portrait-secondary' | 'landscape-primary' | 'landscape-secondary' | string;
|
|
597
|
+
};
|
|
598
|
+
/**
|
|
599
|
+
* Accessibility / display preferences resolved via media queries.
|
|
600
|
+
* These are reproduction hints — a bug that only repros under
|
|
601
|
+
* `prefers-reduced-motion: reduce` or `forced-colors: active` is one
|
|
602
|
+
* the developer would otherwise spend hours hunting for.
|
|
603
|
+
*/
|
|
604
|
+
prefersColorScheme?: 'dark' | 'light' | 'no-preference';
|
|
605
|
+
prefersReducedMotion?: boolean;
|
|
606
|
+
prefersReducedData?: boolean;
|
|
607
|
+
prefersContrast?: 'more' | 'less' | 'no-preference' | 'custom';
|
|
608
|
+
forcedColors?: boolean;
|
|
609
|
+
/** `navigator.onLine` at capture time. False = the report was filed offline. */
|
|
610
|
+
online?: boolean;
|
|
611
|
+
/**
|
|
612
|
+
* `(display-mode: standalone | minimal-ui | fullscreen | browser)`
|
|
613
|
+
* resolved via media queries. Tells us whether the user filed the
|
|
614
|
+
* report from a regular browser tab, an installed PWA, or a TWA — a
|
|
615
|
+
* different code path on iOS Safari for each.
|
|
616
|
+
*/
|
|
617
|
+
displayMode?: 'browser' | 'minimal-ui' | 'standalone' | 'fullscreen';
|
|
618
|
+
/** `document.title` at capture time. Surface for "what page were they on?". */
|
|
619
|
+
documentTitle?: string;
|
|
620
|
+
/**
|
|
621
|
+
* Optional opt-in build identifier from `<meta name="mushi:build" content="...">`.
|
|
622
|
+
* Hosts that already expose a git SHA / build number to their HTML
|
|
623
|
+
* (e.g. `<meta name="mushi:build" content="abc123def">`) get it
|
|
624
|
+
* threaded through automatically — no SDK config required. Pairs with
|
|
625
|
+
* `appVersion` to pin reports to a specific deploy.
|
|
626
|
+
*/
|
|
627
|
+
buildId?: string;
|
|
628
|
+
/**
|
|
629
|
+
* Snapshot of the Navigation Timing entry. Reports that come in
|
|
630
|
+
* during a slow page load look very different from steady-state
|
|
631
|
+
* reports, and the LLM can't tell the difference from a stack alone.
|
|
632
|
+
*/
|
|
633
|
+
pageLoadTiming?: {
|
|
634
|
+
/** `domContentLoadedEventEnd - startTime` in ms. */
|
|
635
|
+
domContentLoadedMs?: number;
|
|
636
|
+
/** `loadEventEnd - startTime` in ms. */
|
|
637
|
+
loadCompleteMs?: number;
|
|
638
|
+
/** `responseStart - startTime` in ms (TTFB). */
|
|
639
|
+
timeToFirstByteMs?: number;
|
|
640
|
+
/** Navigation type (`navigate`, `reload`, `back_forward`, `prerender`). */
|
|
641
|
+
navigationType?: string;
|
|
642
|
+
};
|
|
339
643
|
}
|
|
340
644
|
interface MushiConsoleEntry {
|
|
341
645
|
level: 'log' | 'warn' | 'error' | 'info' | 'debug';
|
|
@@ -445,6 +749,20 @@ interface MushiSDKInstance {
|
|
|
445
749
|
* Returns the server-assigned report id when the submit succeeds.
|
|
446
750
|
*/
|
|
447
751
|
captureEvent(event: MushiCaptureEventInput): Promise<string | null>;
|
|
752
|
+
/**
|
|
753
|
+
* 2026-05-07 — `try/catch`-friendly sugar over `captureEvent`. Pass
|
|
754
|
+
* a thrown value directly and Mushi normalises it (any exotic
|
|
755
|
+
* throw shape: `Error`, string, plain object, `null`) into a report
|
|
756
|
+
* with category `bug`, severity `high` (overridable), and the
|
|
757
|
+
* stack trace folded into both `description` (compact) and
|
|
758
|
+
* `metadata.error.stack` (full).
|
|
759
|
+
*
|
|
760
|
+
* Pairs with Sentry: when `config.sentry` is present, the same
|
|
761
|
+
* call-site can flush to Sentry (`Sentry.captureException(err)`)
|
|
762
|
+
* and Mushi (`Mushi.captureException(err)`) — the two reports get
|
|
763
|
+
* cross-linked via `sentryContext.eventId` automatically.
|
|
764
|
+
*/
|
|
765
|
+
captureException(error: unknown, options?: MushiCaptureExceptionOptions): Promise<string | null>;
|
|
448
766
|
/**
|
|
449
767
|
* Wave G4 — sugar alias for `setUser()`. Name mirrors the
|
|
450
768
|
* identify/track/capture vocabulary that PostHog/Segment/Mixpanel
|
|
@@ -455,6 +773,65 @@ interface MushiSDKInstance {
|
|
|
455
773
|
name?: string;
|
|
456
774
|
[k: string]: unknown;
|
|
457
775
|
}): void;
|
|
776
|
+
/**
|
|
777
|
+
* Sentry-grade observability surface (2026-05-07).
|
|
778
|
+
*
|
|
779
|
+
* Hosts can drop a breadcrumb on every meaningful state change in
|
|
780
|
+
* their app — feature-flag toggles, route transitions, optimistic
|
|
781
|
+
* UI commits, server reconciliation — and the buffer's last 50
|
|
782
|
+
* entries automatically attach to every report. Useful even when
|
|
783
|
+
* Sentry isn't installed; when it is, Mushi reports also carry
|
|
784
|
+
* Sentry's breadcrumbs alongside Mushi's own.
|
|
785
|
+
*/
|
|
786
|
+
addBreadcrumb(crumb: Omit<MushiBreadcrumb, 'timestamp'> & {
|
|
787
|
+
timestamp?: number;
|
|
788
|
+
}): void;
|
|
789
|
+
/** Snapshot of the current breadcrumb ring buffer (oldest first). */
|
|
790
|
+
getBreadcrumbs(): MushiBreadcrumb[];
|
|
791
|
+
/**
|
|
792
|
+
* Set a sticky tag that lands on every subsequent report. Numeric
|
|
793
|
+
* and boolean values are accepted; they're coerced to strings on
|
|
794
|
+
* the wire so the Triage LLM can read them without type juggling.
|
|
795
|
+
*/
|
|
796
|
+
setTag(key: string, value: string | number | boolean): void;
|
|
797
|
+
/** Bulk variant of `setTag`. Replaces existing values for shared keys. */
|
|
798
|
+
setTags(tags: Record<string, string | number | boolean>): void;
|
|
799
|
+
/** Remove a single tag, or all tags when called with no argument. */
|
|
800
|
+
clearTag(key?: string): void;
|
|
801
|
+
/**
|
|
802
|
+
* Returns the current user's reputation + legacy point totals from the
|
|
803
|
+
* reporter_reputation table. Available even when the rewards program is
|
|
804
|
+
* disabled (legacy surface).
|
|
805
|
+
*/
|
|
806
|
+
getReputation(): Promise<MushiReputationResult | null>;
|
|
807
|
+
/**
|
|
808
|
+
* Returns the current user's tier from the rewards program.
|
|
809
|
+
* Returns null when the user has not yet been identified or the project
|
|
810
|
+
* has rewards_enabled = false.
|
|
811
|
+
*/
|
|
812
|
+
getTier(): Promise<MushiTierResult | null>;
|
|
813
|
+
/**
|
|
814
|
+
* Manually record a host-defined activity event (e.g. 'lesson_completed').
|
|
815
|
+
* The SDK batches these and flushes to POST /v1/sdk/activity.
|
|
816
|
+
* No-op when rewards are disabled or the user has not opted in.
|
|
817
|
+
*/
|
|
818
|
+
recordActivity(action: string, metadata?: Record<string, unknown>): void;
|
|
819
|
+
}
|
|
820
|
+
interface MushiCaptureExceptionOptions {
|
|
821
|
+
/** Override the default `'bug'` category (e.g. `'slow'` for timeouts). */
|
|
822
|
+
category?: MushiReportCategory;
|
|
823
|
+
/** Default `'high'`. Use `'critical'` for boot-time errors, `'low'` for known recoverables. */
|
|
824
|
+
severity?: 'critical' | 'high' | 'medium' | 'low';
|
|
825
|
+
/** Affected component / page area, surfaces in the admin reports list. */
|
|
826
|
+
component?: string;
|
|
827
|
+
/** Optional human-readable summary that overrides the auto-derived one. */
|
|
828
|
+
description?: string;
|
|
829
|
+
/** Per-call tags merged with sticky tags. */
|
|
830
|
+
tags?: Record<string, string | number | boolean>;
|
|
831
|
+
/** Free-form metadata folded into `reports.metadata`. */
|
|
832
|
+
metadata?: Record<string, unknown>;
|
|
833
|
+
/** Source label — defaults to `'captureException'`. */
|
|
834
|
+
source?: string;
|
|
458
835
|
}
|
|
459
836
|
interface MushiCaptureEventInput {
|
|
460
837
|
/** Human-readable summary; becomes `reports.description`. */
|
|
@@ -502,6 +879,42 @@ interface MushiApiClient {
|
|
|
502
879
|
replyToReporterReport(reportId: string, reporterToken: string, body: string): Promise<MushiApiResponse<{
|
|
503
880
|
comment: MushiReporterComment;
|
|
504
881
|
}>>;
|
|
882
|
+
/**
|
|
883
|
+
* Submit a batch of activity events for the current user.
|
|
884
|
+
* Requires rewards to be enabled on the project.
|
|
885
|
+
*/
|
|
886
|
+
submitActivity(userId: string, events: MushiActivityEvent[], opts?: {
|
|
887
|
+
userTraits?: {
|
|
888
|
+
email?: string;
|
|
889
|
+
name?: string;
|
|
890
|
+
provider?: string;
|
|
891
|
+
};
|
|
892
|
+
reporterTokenHash?: string;
|
|
893
|
+
optedIn?: boolean;
|
|
894
|
+
/** P2: host-app JWT; when present the server attempts verifyHostJwt
|
|
895
|
+
* and updates end_users.jwt_verified_at if valid. Required for
|
|
896
|
+
* monetary payout eligibility. */
|
|
897
|
+
hostJwt?: string;
|
|
898
|
+
}): Promise<MushiApiResponse<{
|
|
899
|
+
accepted: number;
|
|
900
|
+
total: number;
|
|
901
|
+
}>>;
|
|
902
|
+
/** Fetch the current user's point totals (requires userId). */
|
|
903
|
+
getMyPoints(userId: string): Promise<MushiApiResponse<{
|
|
904
|
+
total_points: number;
|
|
905
|
+
points_30d: number;
|
|
906
|
+
points_lifetime: number;
|
|
907
|
+
tier: MushiTierResult | null;
|
|
908
|
+
}>>;
|
|
909
|
+
/** Fetch the current user's tier (requires userId). */
|
|
910
|
+
getMyTier(userId: string): Promise<MushiApiResponse<MushiTierResult | null>>;
|
|
911
|
+
/** Fetch the current user's activity history (requires userId). */
|
|
912
|
+
getMyHistory(userId: string, opts?: {
|
|
913
|
+
limit?: number;
|
|
914
|
+
}): Promise<MushiApiResponse<{
|
|
915
|
+
items: unknown[];
|
|
916
|
+
total: number;
|
|
917
|
+
}>>;
|
|
505
918
|
}
|
|
506
919
|
/**
|
|
507
920
|
* Wire shape of a single discovery event sent by the SDK to
|
|
@@ -637,6 +1050,21 @@ interface OfflineQueue {
|
|
|
637
1050
|
}
|
|
638
1051
|
declare function createOfflineQueue(config?: MushiOfflineConfig): OfflineQueue;
|
|
639
1052
|
|
|
1053
|
+
/**
|
|
1054
|
+
* Capture a snapshot of the runtime environment. Called once per
|
|
1055
|
+
* report submission, so every read here must be:
|
|
1056
|
+
*
|
|
1057
|
+
* 1. Synchronous (UA-Client Hints `getHighEntropyValues()` is the
|
|
1058
|
+
* sole exception — see `kickOffUserAgentData` below; the result
|
|
1059
|
+
* is memoised and folded back in on the next capture).
|
|
1060
|
+
* 2. SSR-safe (every `window`/`document`/`navigator` access must be
|
|
1061
|
+
* gated on `typeof X !== 'undefined'` because the SDK is
|
|
1062
|
+
* imported by Next.js / Remix server bundles).
|
|
1063
|
+
* 3. Tolerant of missing APIs (Safari doesn't have `Network
|
|
1064
|
+
* Information`, Firefox doesn't have UA-CH, screen.orientation is
|
|
1065
|
+
* flaky on iOS) — every individual field is optional so a
|
|
1066
|
+
* partially-supported browser still produces a useful payload.
|
|
1067
|
+
*/
|
|
640
1068
|
declare function captureEnvironment(): MushiEnvironment;
|
|
641
1069
|
|
|
642
1070
|
declare function getReporterToken(): string;
|
|
@@ -706,6 +1134,73 @@ declare function createPiiScrubber(config?: PiiScrubberConfig): {
|
|
|
706
1134
|
};
|
|
707
1135
|
declare function scrubPii(text: string, config?: PiiScrubberConfig): string;
|
|
708
1136
|
|
|
1137
|
+
/**
|
|
1138
|
+
* Ring-buffer breadcrumb store. Capped at `max` entries (default 50);
|
|
1139
|
+
* once full, every new add evicts the oldest. We deliberately don't
|
|
1140
|
+
* use a Map — order matters and is the only thing we ever read, so a
|
|
1141
|
+
* plain array we slice-copy on snapshot is the right shape.
|
|
1142
|
+
*
|
|
1143
|
+
* Design notes:
|
|
1144
|
+
* - `add()` is O(1) on the steady-state hot path (just `push`). When
|
|
1145
|
+
* we hit `max` we evict the oldest with `shift()` — that's O(n)
|
|
1146
|
+
* in the worst case, but with `max=50` the constant is tiny and
|
|
1147
|
+
* it only fires once per insert past the cap. We deliberately
|
|
1148
|
+
* append-then-shift (rather than head-insert) so `getAll()` can
|
|
1149
|
+
* return the underlying array in oldest-first order without an
|
|
1150
|
+
* extra reverse copy on every snapshot.
|
|
1151
|
+
* - `getAll()` returns a *copy* so callers can't mutate the buffer
|
|
1152
|
+
* after a report is composed. Reports send the snapshot, not the
|
|
1153
|
+
* live ring.
|
|
1154
|
+
* - Long messages are truncated at 500 chars *at insert time*
|
|
1155
|
+
* because a runaway log line shouldn't push useful breadcrumbs
|
|
1156
|
+
* out of the buffer just by virtue of taking the whole entry.
|
|
1157
|
+
* - PII concerns: this buffer is intentionally *not* scrubbed at
|
|
1158
|
+
* insert time — `getBreadcrumbs()` should return the host's own
|
|
1159
|
+
* values verbatim so they're useful for in-app debugging. The
|
|
1160
|
+
* scrubbing pass runs at *report-snapshot time* in
|
|
1161
|
+
* `packages/web/src/mushi.ts`, applying the same `createPiiScrubber()`
|
|
1162
|
+
* used on `description` to every breadcrumb message + every string
|
|
1163
|
+
* field in `data`. That way emails / Stripe keys / JWTs in a
|
|
1164
|
+
* breadcrumb never leave the SDK process.
|
|
1165
|
+
*/
|
|
1166
|
+
interface BreadcrumbBuffer {
|
|
1167
|
+
/** Append a single breadcrumb; auto-fills `timestamp` when omitted. */
|
|
1168
|
+
add(crumb: Omit<MushiBreadcrumb, 'timestamp'> & {
|
|
1169
|
+
timestamp?: number;
|
|
1170
|
+
}): void;
|
|
1171
|
+
/** Return a copy of every retained breadcrumb, oldest first. */
|
|
1172
|
+
getAll(): MushiBreadcrumb[];
|
|
1173
|
+
/** Drop every entry. Useful for tests and `Mushi.destroy()`. */
|
|
1174
|
+
clear(): void;
|
|
1175
|
+
/** Number of entries currently held. */
|
|
1176
|
+
size(): number;
|
|
1177
|
+
}
|
|
1178
|
+
interface BreadcrumbBufferOptions {
|
|
1179
|
+
/** Hard cap on retained entries. Default 50. */
|
|
1180
|
+
max?: number;
|
|
1181
|
+
/** Hard cap on `message` length, in chars. Default 500. */
|
|
1182
|
+
maxMessageLength?: number;
|
|
1183
|
+
}
|
|
1184
|
+
declare function createBreadcrumbBuffer(options?: BreadcrumbBufferOptions): BreadcrumbBuffer;
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Normalise *anything* a host might throw — `Error`, string, plain
|
|
1188
|
+
* object, `null`, frozen DOMException — into the shape Mushi reports
|
|
1189
|
+
* use. Mirrors Sentry's own internal normaliser; lets `captureException`
|
|
1190
|
+
* be a thin sugar layer over `captureEvent`.
|
|
1191
|
+
*
|
|
1192
|
+
* Truncates the stack at 8 KB so a runaway long stack (recursive React
|
|
1193
|
+
* render error during a hot reload, for example) doesn't blow the
|
|
1194
|
+
* description budget when callers fall back to the stack as the body.
|
|
1195
|
+
*/
|
|
1196
|
+
interface NormalisedException {
|
|
1197
|
+
name: string;
|
|
1198
|
+
message: string;
|
|
1199
|
+
stack?: string;
|
|
1200
|
+
cause?: unknown;
|
|
1201
|
+
}
|
|
1202
|
+
declare function normaliseThrown(thrown: unknown): NormalisedException;
|
|
1203
|
+
|
|
709
1204
|
/**
|
|
710
1205
|
* FILE: logger.ts
|
|
711
1206
|
* PURPOSE: Zero-dependency structured logger for the mushi-mushi SDK ecosystem.
|
|
@@ -772,4 +1267,4 @@ declare function createLogger(options: LoggerOptions): Logger;
|
|
|
772
1267
|
*/
|
|
773
1268
|
declare const noopLogger: Logger;
|
|
774
1269
|
|
|
775
|
-
export { type ApiClientOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, type MushiPerformanceMetrics, type MushiPreFilterConfig, type MushiPreset, type MushiPrivacyConfig, type MushiProactiveConfig, type MushiRegion, type MushiReport, type MushiReportBuilder, type MushiReportCategory, type MushiReportStatus, type MushiReporterComment, type MushiReporterReport, type MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, resolveRegionEndpoint, scrubPii };
|
|
1270
|
+
export { type ApiClientOptions, type BreadcrumbBuffer, type BreadcrumbBufferOptions, DEFAULT_API_ENDPOINT, type LogEntry, type LogFormat, type LogLevel, type Logger, type LoggerOptions, MUSHI_INTERNAL_HEADER, MUSHI_INTERNAL_INIT_MARKER, type MushiActivityEvent, type MushiApiCascadeConfig, type MushiApiClient, type MushiApiResponse, type MushiBetaChangelogEntry, type MushiBetaModeConfig, type MushiBreadcrumb, type MushiCaptureConfig, type MushiCaptureEventInput, type MushiCaptureExceptionOptions, type MushiConfig, type MushiConsoleEntry, type MushiCooldownConfig, type MushiDiagnosticsResult, type MushiDiscoverInventoryConfig, type MushiDiscoveryEventPayload, type MushiEnvironment, type MushiEventHandler, type MushiEventType, type MushiIntegrationsConfig, type MushiInternalRequestKind, type MushiNetworkEntry, type MushiOfflineConfig, type MushiOnDeviceClassifier, type MushiOnDeviceClassifierInput, type MushiOnDeviceClassifierResult, type MushiPerformanceMetrics, type MushiPreFilterConfig, type MushiPreset, type MushiPrivacyConfig, type MushiProactiveConfig, type MushiRegion, type MushiReport, type MushiReportBuilder, type MushiReportCategory, type MushiReportStatus, type MushiReporterComment, type MushiReporterReport, type MushiReputationResult, type MushiRewardsConfig, type MushiRuntimeSdkConfig, type MushiSDKInstance, type MushiSdkVersionInfo, type MushiSelectedElement, type MushiSentryConfig, type MushiSentryContext, type MushiTierResult, type MushiTimelineEntry, type MushiTimelineKind, type MushiUrlMatcher, type MushiWidgetAnchor, type MushiWidgetConfig, type NormalisedException, type OfflineQueue, type PiiScrubberConfig, type PreFilterResult, REGION_ENDPOINTS, type RateLimiter, type RateLimiterConfig, captureEnvironment, createApiClient, createBreadcrumbBuffer, createLogger, createOfflineQueue, createPiiScrubber, createPreFilter, createRateLimiter, getDeviceFingerprintHash, getReporterToken, getSessionId, noopLogger, normaliseThrown, resolveRegionEndpoint, scrubPii };
|