@mushi-mushi/core 1.29.0 → 1.30.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.d.cts CHANGED
@@ -230,7 +230,13 @@ interface MushiWidgetConfig {
230
230
  * 3. Default `false`.
231
231
  */
232
232
  brandFooter?: boolean;
233
- /** How the widget should surface SDK freshness warnings. Defaults to auto. */
233
+ /**
234
+ * How the widget surfaces the "update @mushi-mushi/web" notice. It is a
235
+ * developer instruction, so `'auto'` (default) shows it in the widget only on
236
+ * a dev host (localhost, loopback, `*.localhost`, `*.local`, `file:`) or with
237
+ * `debug: true` — never to an app's end users. `'banner'` always shows it;
238
+ * `'console-only'` and `'off'` never render it in the widget.
239
+ */
234
240
  outdatedBanner?: 'auto' | 'banner' | 'console-only' | 'off';
235
241
  /**
236
242
  * Privacy nudge shown beside an attached screenshot preview, reminding the
@@ -594,9 +600,10 @@ interface MushiPrivacyConfig {
594
600
  * sensitive fields that should never appear in any form — passwords, PII,
595
601
  * financial data. Applied in addition to `maskSelectors`.
596
602
  *
597
- * Default: `['input[type="password"]', '[data-mushi-redact]']`
598
- *
599
- * To disable the default redaction, pass an empty array.
603
+ * Always redacted, whatever this is set to: `input[type="password"]`,
604
+ * `input[autocomplete^="cc-"]`, `[data-private]`, `[data-mushi-mask]`.
605
+ * This list is added on top; it defaults to `['[data-mushi-redact]']`,
606
+ * and an empty array drops only that default.
600
607
  */
601
608
  redactSelectors?: string[];
602
609
  /** Let reporters remove an attached screenshot before submitting. Defaults to true. */
@@ -1791,8 +1798,30 @@ interface MushiSdkVersionInfo {
1791
1798
  interface MushiReporterReport {
1792
1799
  id: string;
1793
1800
  status: string;
1794
- category: string;
1801
+ /** @deprecated No longer sent to reporters (Plan 018); internal triage only. */
1802
+ category?: string;
1803
+ /** @deprecated No longer sent to reporters (Plan 018); internal triage only. */
1795
1804
  severity?: string | null;
1805
+ /** Reporter-safe title (the summary, or the first line of the description). */
1806
+ title?: string | null;
1807
+ /** The type the reporter picked (`user_category`), if any. */
1808
+ user_category?: string | null;
1809
+ /** Page path the report was filed on. */
1810
+ page?: string | null;
1811
+ app_version?: string | null;
1812
+ /** Signed, short-lived thumbnail URL; null without a screenshot. */
1813
+ screenshot_thumb_url?: string | null;
1814
+ /** Bucketed duplicate count: never a raw number. */
1815
+ group_bucket?: 'none' | 'few' | 'many';
1816
+ closed_reason?: string | null;
1817
+ fixed_in_version?: string | null;
1818
+ /** The developer asked a question and is waiting on the reporter. */
1819
+ awaiting_reporter?: boolean;
1820
+ /** True when this row is a report the reporter follows (theirs was a duplicate of it). */
1821
+ followed?: boolean;
1822
+ last_event_at?: string | null;
1823
+ /** Template-rendered (or verbatim developer) text of the latest event. */
1824
+ last_event_preview?: string | null;
1796
1825
  summary?: string | null;
1797
1826
  description?: string | null;
1798
1827
  created_at: string;
@@ -2433,6 +2462,10 @@ declare const MUSHI_EVENTS: {
2433
2462
  readonly surface: "console";
2434
2463
  readonly required: readonly ["project_id"];
2435
2464
  };
2465
+ readonly diagnosis_viewed: {
2466
+ readonly surface: "console";
2467
+ readonly required: readonly ["report_id", "project_id", "surface"];
2468
+ };
2436
2469
  readonly project_created: {
2437
2470
  readonly surface: "server";
2438
2471
  readonly required: readonly ["project_id"];
@@ -2445,6 +2478,10 @@ declare const MUSHI_EVENTS: {
2445
2478
  readonly surface: "server";
2446
2479
  readonly required: readonly ["project_id"];
2447
2480
  };
2481
+ readonly first_diagnosis_ready: {
2482
+ readonly surface: "server";
2483
+ readonly required: readonly ["project_id"];
2484
+ };
2448
2485
  readonly fix_merged: {
2449
2486
  readonly surface: "server";
2450
2487
  readonly required: readonly ["project_id"];
package/dist/index.d.ts CHANGED
@@ -230,7 +230,13 @@ interface MushiWidgetConfig {
230
230
  * 3. Default `false`.
231
231
  */
232
232
  brandFooter?: boolean;
233
- /** How the widget should surface SDK freshness warnings. Defaults to auto. */
233
+ /**
234
+ * How the widget surfaces the "update @mushi-mushi/web" notice. It is a
235
+ * developer instruction, so `'auto'` (default) shows it in the widget only on
236
+ * a dev host (localhost, loopback, `*.localhost`, `*.local`, `file:`) or with
237
+ * `debug: true` — never to an app's end users. `'banner'` always shows it;
238
+ * `'console-only'` and `'off'` never render it in the widget.
239
+ */
234
240
  outdatedBanner?: 'auto' | 'banner' | 'console-only' | 'off';
235
241
  /**
236
242
  * Privacy nudge shown beside an attached screenshot preview, reminding the
@@ -594,9 +600,10 @@ interface MushiPrivacyConfig {
594
600
  * sensitive fields that should never appear in any form — passwords, PII,
595
601
  * financial data. Applied in addition to `maskSelectors`.
596
602
  *
597
- * Default: `['input[type="password"]', '[data-mushi-redact]']`
598
- *
599
- * To disable the default redaction, pass an empty array.
603
+ * Always redacted, whatever this is set to: `input[type="password"]`,
604
+ * `input[autocomplete^="cc-"]`, `[data-private]`, `[data-mushi-mask]`.
605
+ * This list is added on top; it defaults to `['[data-mushi-redact]']`,
606
+ * and an empty array drops only that default.
600
607
  */
601
608
  redactSelectors?: string[];
602
609
  /** Let reporters remove an attached screenshot before submitting. Defaults to true. */
@@ -1791,8 +1798,30 @@ interface MushiSdkVersionInfo {
1791
1798
  interface MushiReporterReport {
1792
1799
  id: string;
1793
1800
  status: string;
1794
- category: string;
1801
+ /** @deprecated No longer sent to reporters (Plan 018); internal triage only. */
1802
+ category?: string;
1803
+ /** @deprecated No longer sent to reporters (Plan 018); internal triage only. */
1795
1804
  severity?: string | null;
1805
+ /** Reporter-safe title (the summary, or the first line of the description). */
1806
+ title?: string | null;
1807
+ /** The type the reporter picked (`user_category`), if any. */
1808
+ user_category?: string | null;
1809
+ /** Page path the report was filed on. */
1810
+ page?: string | null;
1811
+ app_version?: string | null;
1812
+ /** Signed, short-lived thumbnail URL; null without a screenshot. */
1813
+ screenshot_thumb_url?: string | null;
1814
+ /** Bucketed duplicate count: never a raw number. */
1815
+ group_bucket?: 'none' | 'few' | 'many';
1816
+ closed_reason?: string | null;
1817
+ fixed_in_version?: string | null;
1818
+ /** The developer asked a question and is waiting on the reporter. */
1819
+ awaiting_reporter?: boolean;
1820
+ /** True when this row is a report the reporter follows (theirs was a duplicate of it). */
1821
+ followed?: boolean;
1822
+ last_event_at?: string | null;
1823
+ /** Template-rendered (or verbatim developer) text of the latest event. */
1824
+ last_event_preview?: string | null;
1796
1825
  summary?: string | null;
1797
1826
  description?: string | null;
1798
1827
  created_at: string;
@@ -2433,6 +2462,10 @@ declare const MUSHI_EVENTS: {
2433
2462
  readonly surface: "console";
2434
2463
  readonly required: readonly ["project_id"];
2435
2464
  };
2465
+ readonly diagnosis_viewed: {
2466
+ readonly surface: "console";
2467
+ readonly required: readonly ["report_id", "project_id", "surface"];
2468
+ };
2436
2469
  readonly project_created: {
2437
2470
  readonly surface: "server";
2438
2471
  readonly required: readonly ["project_id"];
@@ -2445,6 +2478,10 @@ declare const MUSHI_EVENTS: {
2445
2478
  readonly surface: "server";
2446
2479
  readonly required: readonly ["project_id"];
2447
2480
  };
2481
+ readonly first_diagnosis_ready: {
2482
+ readonly surface: "server";
2483
+ readonly required: readonly ["project_id"];
2484
+ };
2448
2485
  readonly fix_merged: {
2449
2486
  readonly surface: "server";
2450
2487
  readonly required: readonly ["project_id"];
package/dist/index.js CHANGED
@@ -230,7 +230,8 @@ function createApiClient(options) {
230
230
  if (cbFailures >= cbThreshold) cbOpenUntil = Date.now() + cbCooldownMs;
231
231
  };
232
232
  async function request(method, path, body, retries = maxRetries, internalKind, extraHeaders) {
233
- if (cbIsOpen()) {
233
+ const tracked = internalKind !== "reporter-poll";
234
+ if (tracked && cbIsOpen()) {
234
235
  return { ok: false, error: { code: "CIRCUIT_OPEN", message: "Endpoint temporarily unavailable; retrying later." } };
235
236
  }
236
237
  const url = `${baseUrl}${path}`;
@@ -249,7 +250,7 @@ function createApiClient(options) {
249
250
  ...extraHeaders
250
251
  };
251
252
  const serialized = body ? JSON.stringify(body) : void 0;
252
- if (serialized && method !== "GET") {
253
+ if (serialized && method !== "GET" && internalKind !== "reporter-poll") {
253
254
  lastOutbound = { url, headers, body: serialized, path };
254
255
  }
255
256
  try {
@@ -289,8 +290,10 @@ function createApiClient(options) {
289
290
  await sleep(retryAfterMs ?? getBackoffDelay(maxRetries - retries));
290
291
  return request(method, path, body, retries - 1, internalKind, extraHeaders);
291
292
  }
292
- if (unusable) cbRecordUnreachable();
293
- else cbRecordReachable();
293
+ if (tracked) {
294
+ if (unusable) cbRecordUnreachable();
295
+ else cbRecordReachable();
296
+ }
294
297
  return {
295
298
  ok: false,
296
299
  error: {
@@ -302,7 +305,7 @@ function createApiClient(options) {
302
305
  }
303
306
  const payload = await response.json();
304
307
  const data = payload && typeof payload === "object" && "ok" in payload && "data" in payload ? payload.data : payload;
305
- cbRecordReachable();
308
+ if (tracked) cbRecordReachable();
306
309
  return { ok: true, data };
307
310
  } catch (error) {
308
311
  clearTimeout(timer);
@@ -310,7 +313,7 @@ function createApiClient(options) {
310
313
  await sleep(getBackoffDelay(maxRetries - retries));
311
314
  return request(method, path, body, retries - 1, internalKind, extraHeaders);
312
315
  }
313
- cbRecordUnreachable();
316
+ if (tracked) cbRecordUnreachable();
314
317
  return {
315
318
  ok: false,
316
319
  error: {
@@ -321,39 +324,17 @@ function createApiClient(options) {
321
324
  }
322
325
  }
323
326
  async function requestForReporter(method, path, reporterToken, body) {
324
- const tokenHash = await sha256Hex(reporterToken);
325
- const ts = String(Date.now());
326
- const hmac = await hmacSha256Hex(apiKey, `${projectId}.${ts}.${tokenHash}`);
327
- const url = `${baseUrl}${path}`;
328
- const response = await fetch(url, {
329
- method,
330
- headers: {
331
- "Content-Type": "application/json",
332
- "X-Mushi-Api-Key": apiKey,
333
- "X-Mushi-Project": projectId,
334
- ...sdkPackage ? { [MUSHI_SDK_PACKAGE_HEADER]: sdkPackage } : {},
335
- ...sdkVersion ? { [MUSHI_SDK_VERSION_HEADER]: sdkVersion } : {},
336
- [MUSHI_INTERNAL_HEADER]: "reporter-poll",
327
+ try {
328
+ const tokenHash = await sha256Hex(reporterToken);
329
+ const ts = String(Date.now());
330
+ return await request(method, path, body, maxRetries, "reporter-poll", {
337
331
  "X-Reporter-Token-Hash": tokenHash,
338
332
  "X-Reporter-Ts": ts,
339
- "X-Reporter-Hmac": hmac
340
- },
341
- body: body ? JSON.stringify(body) : void 0,
342
- keepalive: isPageUnloading(),
343
- [MUSHI_INTERNAL_INIT_MARKER]: "reporter-poll"
344
- });
345
- if (!response.ok) {
346
- const errorBody = await response.json().catch(() => ({}));
347
- return {
348
- ok: false,
349
- error: {
350
- code: `HTTP_${response.status}`,
351
- message: errorBody.error?.message ?? errorBody.message ?? `HTTP ${response.status} error`
352
- }
353
- };
333
+ "X-Reporter-Hmac": await hmacSha256Hex(apiKey, `${projectId}.${ts}.${tokenHash}`)
334
+ });
335
+ } catch (error) {
336
+ return { ok: false, error: { code: "NETWORK_ERROR", message: error instanceof Error ? error.message : "Unknown error" } };
354
337
  }
355
- const payload = await response.json();
356
- return { ok: true, data: payload.data ?? payload };
357
338
  }
358
339
  return {
359
340
  async submitReport(report) {
@@ -2294,10 +2275,12 @@ var MUSHI_EVENTS = {
2294
2275
  invite_sent: { surface: "console", required: [] },
2295
2276
  upgrade_clicked: { surface: "console", required: ["plan"] },
2296
2277
  test_report_sent: { surface: "console", required: ["project_id"] },
2278
+ diagnosis_viewed: { surface: "console", required: ["report_id", "project_id", "surface"] },
2297
2279
  // ── Server-emitted (edge functions) ──────────────────────────────────────
2298
2280
  project_created: { surface: "server", required: ["project_id"] },
2299
2281
  key_minted: { surface: "server", required: ["project_id"] },
2300
2282
  first_report_received: { surface: "server", required: ["project_id"] },
2283
+ first_diagnosis_ready: { surface: "server", required: ["project_id"] },
2301
2284
  fix_merged: { surface: "server", required: ["project_id"] },
2302
2285
  upgrade_completed: { surface: "server", required: ["project_id", "plan"] },
2303
2286
  // ── Growth loop (widget "Bug reports by Mushi" mark) ─────────────────────