@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/README.md +1 -1
- package/dist/index.cjs +19 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -5
- package/dist/index.d.ts +42 -5
- package/dist/index.js +19 -36
- package/dist/index.js.map +1 -1
- package/dist/reporter-ui.cjs +475 -0
- package/dist/reporter-ui.cjs.map +1 -0
- package/dist/reporter-ui.d.cts +131 -0
- package/dist/reporter-ui.d.ts +131 -0
- package/dist/reporter-ui.js +462 -0
- package/dist/reporter-ui.js.map +1 -0
- package/package.json +18 -1
package/dist/index.d.cts
CHANGED
|
@@ -230,7 +230,13 @@ interface MushiWidgetConfig {
|
|
|
230
230
|
* 3. Default `false`.
|
|
231
231
|
*/
|
|
232
232
|
brandFooter?: boolean;
|
|
233
|
-
/**
|
|
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
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
293
|
-
|
|
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
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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":
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
|
|
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) ─────────────────────
|