@mushi-mushi/core 1.1.0 → 1.5.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 CHANGED
@@ -101,6 +101,17 @@ Releases are fully automated. Maintainers don't run `npm publish` by hand.
101
101
 
102
102
  If GitHub's anti-loop protection suppresses the auto re-fire (the squash merge can be attributed to `github-actions[bot]`), trigger the workflow manually: **Actions → release → Run workflow → master**.
103
103
 
104
+ ### Known CI/CD quirks and their automatic safeguards
105
+
106
+ A handful of GitHub-Actions × Changesets edge cases have caused release-pipeline stalls in the past. Each is now caught automatically — keep these in mind when you see the symptom:
107
+
108
+ | Symptom | Root cause | Automatic safeguard |
109
+ | --- | --- | --- |
110
+ | The `Build & Test` required check never registers on the `chore: version packages` PR — the PR stays "Required check missing" forever | `changeset-release/master` is pushed by `github-actions[bot]`. GitHub silently drops the downstream `pull_request` event to prevent bot loops (observed on PR #45, #102, #124). | `ci.yml` now also triggers on `push` to `changeset-release/master`, so `Build & Test` reports against the head commit directly. No empty-commit nudge needed. |
111
+ | Release workflow fails with `No commits between master and changeset-release/master` after merging a PR with a new changeset. | A `.changeset/*.md` file whose YAML frontmatter only targets packages listed in `.changeset/config.json#ignore` (e.g. `@mushi-mushi/server`, `@mushi-mushi/admin`). `changeset version` produces no bumps, the version PR is empty, the next push errors (PR #102 / #121, 2026-05-19). | `pnpm check:changeset-orphans` runs in both `ci.yml` and `release.yml`. PR CI fails with an actionable message naming the orphan file *before* it can reach master. If you legitimately need to record an internal-only change, omit the changeset entirely — the diff lives in git history. |
112
+ | Release workflow's `Audit signatures of installed dependencies` step fails with `npm ETARGET / No matching version found for @mushi-mushi/<pkg>@<ver>` seconds after the publish step succeeded. | npm's registry CDN can take up to ~30s to propagate a freshly-published manifest. The audit step shells out to `npm install` against the just-published version and races the CDN (observed 2026-05-20, run 26149167393). | The audit step retries with exponential backoff (1, 2, 4, 8, 16, 32s — 63s total) before failing. Sigstore signatures are written at publish time, so a one-off audit failure never indicates a corrupted package — `pnpm view <pkg> version` is the ground truth. |
113
+ | Push to `master` after merging a PR doesn't fire the `Release` workflow. | Same anti-loop protection: when a squash merge is attributed to `github-actions[bot]`, GitHub may suppress the downstream `push` trigger. Sporadic — observed twice in the last 60 days. | `release.yml` keeps `workflow_dispatch` as the manual fallback. Recovery: **Actions → Release → Run workflow → master**. The `Build & Verify` job re-runs identically to the auto-fired path. |
114
+
104
115
  ### Adding a brand-new publishable package
105
116
 
106
117
  Trusted Publisher bindings are configured **per package** on `npmjs.com` and require the package to already exist on the registry. New packages therefore need a one-time bootstrap before OIDC can take over.
package/dist/index.d.cts CHANGED
@@ -19,6 +19,41 @@ interface MushiConfig {
19
19
  integrations?: MushiIntegrationsConfig;
20
20
  offline?: MushiOfflineConfig;
21
21
  rewards?: MushiRewardsConfig;
22
+ /**
23
+ * Sentry-spec-1.0 hook fired AFTER preFilter / on-device classifier /
24
+ * rate-limit gates pass and BEFORE the report is sent or queued.
25
+ * Return:
26
+ * - the report (possibly modified) → submit as-is
27
+ * - a Promise<MushiReport> → await, then submit
28
+ * - `null` → drop the report silently (no `report:sent`/`:failed` event)
29
+ *
30
+ * Use this for app-level redaction of sensitive metadata, hard-coded
31
+ * tag overrides, or last-mile category routing. Errors thrown inside
32
+ * the hook are caught and logged; the report still ships unchanged so
33
+ * a buggy hook never silently swallows feedback.
34
+ *
35
+ * Mirrors Sentry's [SDK feedback spec §4](https://develop.sentry.dev/sdk/telemetry/feedbacks/)
36
+ * `beforeSendFeedback` callback.
37
+ */
38
+ beforeSendFeedback?: (report: MushiReport) => MushiReport | Promise<MushiReport | null> | null;
39
+ /**
40
+ * Sentry-spec-1.0 callback fired exactly once on `init()` after the
41
+ * SDK detects that the previous browser session ended in a crash
42
+ * (uncaught exception, unhandled rejection, or hard navigate during
43
+ * unfinished submit). Use it to surface a "Tell us what went wrong?"
44
+ * prompt to the user — the SDK does NOT auto-open the widget so the
45
+ * host app can decide on copy and timing.
46
+ *
47
+ * The promise resolves with `true` when a crash was detected, `false`
48
+ * when the previous run ended cleanly, and `null` when the SDK can't
49
+ * tell (typically: localStorage unavailable, or first-ever load).
50
+ *
51
+ * Mirrors Sentry's [SDK feedback spec §6](https://develop.sentry.dev/sdk/telemetry/feedbacks/)
52
+ * `onCrashedLastRun` hook.
53
+ */
54
+ onCrashedLastRun?: (info: {
55
+ crashed: boolean | null;
56
+ }) => void;
22
57
  debug?: boolean;
23
58
  enabled?: boolean;
24
59
  }
@@ -73,6 +108,13 @@ interface MushiWidgetConfig {
73
108
  * actively inviting feedback.
74
109
  */
75
110
  betaMode?: MushiBetaModeConfig;
111
+ /**
112
+ * Minimum description length (characters) required before the user can submit.
113
+ * Overrides the SDK default of 20. The widget halves this automatically for
114
+ * CJK locales (ja/zh/ko) where a short string carries more semantic content
115
+ * than in English. Forwarded from `preFilter.minDescriptionLength` if unset.
116
+ */
117
+ minDescriptionLength?: number;
76
118
  }
77
119
  interface MushiBetaModeConfig {
78
120
  enabled?: boolean;
@@ -662,7 +704,30 @@ interface MushiPerformanceMetrics {
662
704
  lcp?: number;
663
705
  cls?: number;
664
706
  fid?: number;
707
+ /**
708
+ * Interaction to Next Paint — the worst-observed user interaction
709
+ * latency (ms) since SDK init. Replaces FID as a Core Web Vital
710
+ * since March 2024. Captured via `PerformanceObserver({ type: 'event',
711
+ * durationThreshold: 40 })` per the [web-vitals INP spec](https://web.dev/articles/inp).
712
+ */
665
713
  inp?: number;
714
+ /**
715
+ * Optional INP attribution — captured from the worst-observed
716
+ * interaction. Lets the triage UI surface "the slow click was on
717
+ * <button.checkout>" rather than just "1200 ms INP".
718
+ */
719
+ inpAttribution?: {
720
+ /** PerformanceEventTiming.name (`pointerdown`, `keydown`, …). */
721
+ eventType?: string;
722
+ /** Tag + id + first class of the element that triggered the slow event. */
723
+ targetSelector?: string;
724
+ /** Time between the user input and the start of event processing. */
725
+ inputDelay?: number;
726
+ /** Time spent running the event handler. */
727
+ processingDuration?: number;
728
+ /** Time between handler end and the next paint. */
729
+ presentationDelay?: number;
730
+ };
666
731
  ttfb?: number;
667
732
  longTasks?: number;
668
733
  }
@@ -697,7 +762,14 @@ interface MushiTimelineEntry {
697
762
  kind: MushiTimelineKind;
698
763
  payload: Record<string, unknown>;
699
764
  }
700
- type MushiEventType = 'report:submitted' | 'report:queued' | 'report:sent' | 'report:failed' | 'widget:opened' | 'widget:closed' | 'proactive:triggered' | 'proactive:dismissed';
765
+ type MushiEventType = 'report:submitted' | 'report:queued' | 'report:sent' | 'report:failed'
766
+ /**
767
+ * Fired when the submitted report has been picked up by a Cursor Cloud
768
+ * Agent and an automated fix is in progress. `data.agentId` is the
769
+ * Cursor agent run ID (bc-…); `data.fixId` is the mushi fix_attempt UUID.
770
+ * Useful for showing a toast: "A Cursor agent is working on your report".
771
+ */
772
+ | 'report:dispatched' | 'widget:opened' | 'widget:closed' | 'proactive:triggered' | 'proactive:dismissed';
701
773
  type MushiEventHandler = (event: {
702
774
  type: MushiEventType;
703
775
  data?: unknown;
package/dist/index.d.ts CHANGED
@@ -19,6 +19,41 @@ interface MushiConfig {
19
19
  integrations?: MushiIntegrationsConfig;
20
20
  offline?: MushiOfflineConfig;
21
21
  rewards?: MushiRewardsConfig;
22
+ /**
23
+ * Sentry-spec-1.0 hook fired AFTER preFilter / on-device classifier /
24
+ * rate-limit gates pass and BEFORE the report is sent or queued.
25
+ * Return:
26
+ * - the report (possibly modified) → submit as-is
27
+ * - a Promise<MushiReport> → await, then submit
28
+ * - `null` → drop the report silently (no `report:sent`/`:failed` event)
29
+ *
30
+ * Use this for app-level redaction of sensitive metadata, hard-coded
31
+ * tag overrides, or last-mile category routing. Errors thrown inside
32
+ * the hook are caught and logged; the report still ships unchanged so
33
+ * a buggy hook never silently swallows feedback.
34
+ *
35
+ * Mirrors Sentry's [SDK feedback spec §4](https://develop.sentry.dev/sdk/telemetry/feedbacks/)
36
+ * `beforeSendFeedback` callback.
37
+ */
38
+ beforeSendFeedback?: (report: MushiReport) => MushiReport | Promise<MushiReport | null> | null;
39
+ /**
40
+ * Sentry-spec-1.0 callback fired exactly once on `init()` after the
41
+ * SDK detects that the previous browser session ended in a crash
42
+ * (uncaught exception, unhandled rejection, or hard navigate during
43
+ * unfinished submit). Use it to surface a "Tell us what went wrong?"
44
+ * prompt to the user — the SDK does NOT auto-open the widget so the
45
+ * host app can decide on copy and timing.
46
+ *
47
+ * The promise resolves with `true` when a crash was detected, `false`
48
+ * when the previous run ended cleanly, and `null` when the SDK can't
49
+ * tell (typically: localStorage unavailable, or first-ever load).
50
+ *
51
+ * Mirrors Sentry's [SDK feedback spec §6](https://develop.sentry.dev/sdk/telemetry/feedbacks/)
52
+ * `onCrashedLastRun` hook.
53
+ */
54
+ onCrashedLastRun?: (info: {
55
+ crashed: boolean | null;
56
+ }) => void;
22
57
  debug?: boolean;
23
58
  enabled?: boolean;
24
59
  }
@@ -73,6 +108,13 @@ interface MushiWidgetConfig {
73
108
  * actively inviting feedback.
74
109
  */
75
110
  betaMode?: MushiBetaModeConfig;
111
+ /**
112
+ * Minimum description length (characters) required before the user can submit.
113
+ * Overrides the SDK default of 20. The widget halves this automatically for
114
+ * CJK locales (ja/zh/ko) where a short string carries more semantic content
115
+ * than in English. Forwarded from `preFilter.minDescriptionLength` if unset.
116
+ */
117
+ minDescriptionLength?: number;
76
118
  }
77
119
  interface MushiBetaModeConfig {
78
120
  enabled?: boolean;
@@ -662,7 +704,30 @@ interface MushiPerformanceMetrics {
662
704
  lcp?: number;
663
705
  cls?: number;
664
706
  fid?: number;
707
+ /**
708
+ * Interaction to Next Paint — the worst-observed user interaction
709
+ * latency (ms) since SDK init. Replaces FID as a Core Web Vital
710
+ * since March 2024. Captured via `PerformanceObserver({ type: 'event',
711
+ * durationThreshold: 40 })` per the [web-vitals INP spec](https://web.dev/articles/inp).
712
+ */
665
713
  inp?: number;
714
+ /**
715
+ * Optional INP attribution — captured from the worst-observed
716
+ * interaction. Lets the triage UI surface "the slow click was on
717
+ * <button.checkout>" rather than just "1200 ms INP".
718
+ */
719
+ inpAttribution?: {
720
+ /** PerformanceEventTiming.name (`pointerdown`, `keydown`, …). */
721
+ eventType?: string;
722
+ /** Tag + id + first class of the element that triggered the slow event. */
723
+ targetSelector?: string;
724
+ /** Time between the user input and the start of event processing. */
725
+ inputDelay?: number;
726
+ /** Time spent running the event handler. */
727
+ processingDuration?: number;
728
+ /** Time between handler end and the next paint. */
729
+ presentationDelay?: number;
730
+ };
666
731
  ttfb?: number;
667
732
  longTasks?: number;
668
733
  }
@@ -697,7 +762,14 @@ interface MushiTimelineEntry {
697
762
  kind: MushiTimelineKind;
698
763
  payload: Record<string, unknown>;
699
764
  }
700
- type MushiEventType = 'report:submitted' | 'report:queued' | 'report:sent' | 'report:failed' | 'widget:opened' | 'widget:closed' | 'proactive:triggered' | 'proactive:dismissed';
765
+ type MushiEventType = 'report:submitted' | 'report:queued' | 'report:sent' | 'report:failed'
766
+ /**
767
+ * Fired when the submitted report has been picked up by a Cursor Cloud
768
+ * Agent and an automated fix is in progress. `data.agentId` is the
769
+ * Cursor agent run ID (bc-…); `data.fixId` is the mushi fix_attempt UUID.
770
+ * Useful for showing a toast: "A Cursor agent is working on your report".
771
+ */
772
+ | 'report:dispatched' | 'widget:opened' | 'widget:closed' | 'proactive:triggered' | 'proactive:dismissed';
701
773
  type MushiEventHandler = (event: {
702
774
  type: MushiEventType;
703
775
  data?: unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mushi-mushi/core",
3
- "version": "1.1.0",
3
+ "version": "1.5.0",
4
4
  "description": "Core types, API client, and pre-filter for Mushi Mushi SDK",
5
5
  "license": "MIT",
6
6
  "type": "module",