@mushi-mushi/core 1.10.0 → 1.12.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.cjs +106 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -2
- package/dist/index.d.ts +70 -2
- package/dist/index.js +102 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/payload-guard.ts
|
|
4
|
+
var MAX_REPORT_PAYLOAD_BYTES = 4 * 1024 * 1024;
|
|
5
|
+
var MAX_SCREENSHOT_DATA_URL_BYTES = 1.5 * 1024 * 1024;
|
|
6
|
+
function safeStringify(value) {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function estimateJsonBytes(value) {
|
|
14
|
+
const json = safeStringify(value);
|
|
15
|
+
return json === null ? Number.MAX_SAFE_INTEGER : new TextEncoder().encode(json).length;
|
|
16
|
+
}
|
|
17
|
+
function checkReportPayloadSize(payload, maxBytes = MAX_REPORT_PAYLOAD_BYTES) {
|
|
18
|
+
const json = safeStringify(payload);
|
|
19
|
+
if (json === null) {
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
bytes: 0,
|
|
23
|
+
maxBytes,
|
|
24
|
+
serializeFailed: true,
|
|
25
|
+
reason: "Report could not be serialized (circular reference in metadata?)"
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const bytes = new TextEncoder().encode(json).length;
|
|
29
|
+
if (bytes <= maxBytes) {
|
|
30
|
+
return { ok: true, bytes, maxBytes };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
ok: false,
|
|
34
|
+
bytes,
|
|
35
|
+
maxBytes,
|
|
36
|
+
reason: `Report payload ${formatBytes(bytes)} exceeds limit ${formatBytes(maxBytes)}`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function formatBytes(n) {
|
|
40
|
+
if (n < 1024) return `${n} B`;
|
|
41
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
42
|
+
return `${(n / (1024 * 1024)).toFixed(2)} MB`;
|
|
43
|
+
}
|
|
44
|
+
|
|
3
45
|
// src/api-client.ts
|
|
4
46
|
var DEFAULT_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
|
|
5
47
|
var MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
|
|
@@ -109,6 +151,16 @@ function createApiClient(options) {
|
|
|
109
151
|
}
|
|
110
152
|
return {
|
|
111
153
|
async submitReport(report) {
|
|
154
|
+
const guard = checkReportPayloadSize(report);
|
|
155
|
+
if (!guard.ok) {
|
|
156
|
+
return {
|
|
157
|
+
ok: false,
|
|
158
|
+
error: {
|
|
159
|
+
code: guard.serializeFailed ? "SERIALIZE_FAILED" : "PAYLOAD_TOO_LARGE",
|
|
160
|
+
message: guard.reason ?? "Report payload exceeds size limit"
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
112
164
|
return request("POST", "/v1/reports", report, maxRetries, "report-submit");
|
|
113
165
|
},
|
|
114
166
|
async getReportStatus(reportId) {
|
|
@@ -140,12 +192,54 @@ function createApiClient(options) {
|
|
|
140
192
|
reporterToken
|
|
141
193
|
);
|
|
142
194
|
},
|
|
143
|
-
async replyToReporterReport(reportId, reporterToken, body) {
|
|
195
|
+
async replyToReporterReport(reportId, reporterToken, body, feedbackSignal) {
|
|
144
196
|
return requestForReporter(
|
|
145
197
|
"POST",
|
|
146
198
|
`/v1/reporter/reports/${reportId}/reply`,
|
|
147
199
|
reporterToken,
|
|
148
|
-
{ body }
|
|
200
|
+
{ body, ...feedbackSignal ? { feedback_signal: feedbackSignal } : {} }
|
|
201
|
+
);
|
|
202
|
+
},
|
|
203
|
+
async reopenReporterReport(reportId, reporterToken, note) {
|
|
204
|
+
return requestForReporter(
|
|
205
|
+
"POST",
|
|
206
|
+
`/v1/reporter/reports/${reportId}/reopen`,
|
|
207
|
+
reporterToken,
|
|
208
|
+
{ note: note ?? "" }
|
|
209
|
+
);
|
|
210
|
+
},
|
|
211
|
+
async listNotifications(reporterToken, opts) {
|
|
212
|
+
const qs = new URLSearchParams();
|
|
213
|
+
if (opts?.since) qs.set("since", opts.since);
|
|
214
|
+
if (opts?.limit) qs.set("limit", String(opts.limit));
|
|
215
|
+
const suffix = qs.size ? `?${qs.toString()}` : "";
|
|
216
|
+
return requestForReporter(
|
|
217
|
+
"GET",
|
|
218
|
+
`/v1/notifications${suffix}`,
|
|
219
|
+
reporterToken
|
|
220
|
+
);
|
|
221
|
+
},
|
|
222
|
+
async markNotificationRead(notificationId, reporterToken) {
|
|
223
|
+
return requestForReporter(
|
|
224
|
+
"POST",
|
|
225
|
+
`/v1/notifications/${notificationId}/read`,
|
|
226
|
+
reporterToken,
|
|
227
|
+
{}
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
async listReporterFeatureBoard(reporterToken) {
|
|
231
|
+
return requestForReporter(
|
|
232
|
+
"GET",
|
|
233
|
+
"/v1/reporter/feature-board",
|
|
234
|
+
reporterToken
|
|
235
|
+
);
|
|
236
|
+
},
|
|
237
|
+
async voteReporterFeatureBoard(requestId, reporterToken) {
|
|
238
|
+
return requestForReporter(
|
|
239
|
+
"POST",
|
|
240
|
+
`/v1/reporter/feature-board/${requestId}/vote`,
|
|
241
|
+
reporterToken,
|
|
242
|
+
{}
|
|
149
243
|
);
|
|
150
244
|
},
|
|
151
245
|
// ─── Rewards program (P1) ──────────────────────────────────
|
|
@@ -790,7 +884,11 @@ function createOfflineQueue(config = {}) {
|
|
|
790
884
|
}
|
|
791
885
|
sent++;
|
|
792
886
|
} else {
|
|
793
|
-
const permanent = result.error?.code === "HTTP_400" || result.error?.code === "
|
|
887
|
+
const permanent = result.error?.code === "HTTP_400" || result.error?.code === "HTTP_413" || result.error?.code === "HTTP_422" || result.error?.code === "INGEST_ERROR" || result.error?.code === "VALIDATION_ERROR" || // A payload that exceeds the size guard will never shrink on its own;
|
|
888
|
+
// retrying re-serialises the multi-MB body every sync tick and wedges
|
|
889
|
+
// the queue (it matches neither permanent nor transient otherwise).
|
|
890
|
+
// SERIALIZE_FAILED (circular ref) is likewise unrecoverable on retry.
|
|
891
|
+
result.error?.code === "PAYLOAD_TOO_LARGE" || result.error?.code === "SERIALIZE_FAILED" || typeof result.error?.message === "string" && /invalid payload|description must be at least|validation/i.test(
|
|
794
892
|
result.error.message
|
|
795
893
|
);
|
|
796
894
|
const transient = !permanent && (result.error?.code === "NETWORK_ERROR" || result.error?.code === "HTTP_403" || result.error?.code === "HTTP_429" || result.error?.code === "HTTP_502" || result.error?.code === "HTTP_503" || result.error?.code === "HTTP_504" || typeof result.error?.code === "string" && result.error.code.startsWith("HTTP_5"));
|
|
@@ -1331,10 +1429,13 @@ function normaliseThrown(thrown) {
|
|
|
1331
1429
|
}
|
|
1332
1430
|
|
|
1333
1431
|
exports.DEFAULT_API_ENDPOINT = DEFAULT_API_ENDPOINT;
|
|
1432
|
+
exports.MAX_REPORT_PAYLOAD_BYTES = MAX_REPORT_PAYLOAD_BYTES;
|
|
1433
|
+
exports.MAX_SCREENSHOT_DATA_URL_BYTES = MAX_SCREENSHOT_DATA_URL_BYTES;
|
|
1334
1434
|
exports.MUSHI_INTERNAL_HEADER = MUSHI_INTERNAL_HEADER;
|
|
1335
1435
|
exports.MUSHI_INTERNAL_INIT_MARKER = MUSHI_INTERNAL_INIT_MARKER;
|
|
1336
1436
|
exports.REGION_ENDPOINTS = REGION_ENDPOINTS;
|
|
1337
1437
|
exports.captureEnvironment = captureEnvironment;
|
|
1438
|
+
exports.checkReportPayloadSize = checkReportPayloadSize;
|
|
1338
1439
|
exports.createApiClient = createApiClient;
|
|
1339
1440
|
exports.createBreadcrumbBuffer = createBreadcrumbBuffer;
|
|
1340
1441
|
exports.createLogger = createLogger;
|
|
@@ -1342,6 +1443,8 @@ exports.createOfflineQueue = createOfflineQueue;
|
|
|
1342
1443
|
exports.createPiiScrubber = createPiiScrubber;
|
|
1343
1444
|
exports.createPreFilter = createPreFilter;
|
|
1344
1445
|
exports.createRateLimiter = createRateLimiter;
|
|
1446
|
+
exports.estimateJsonBytes = estimateJsonBytes;
|
|
1447
|
+
exports.formatBytes = formatBytes;
|
|
1345
1448
|
exports.getDeviceFingerprintHash = getDeviceFingerprintHash;
|
|
1346
1449
|
exports.getReporterToken = getReporterToken;
|
|
1347
1450
|
exports.getSessionId = getSessionId;
|