@mushi-mushi/core 1.11.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 +96 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +92 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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) {
|
|
@@ -156,6 +208,40 @@ function createApiClient(options) {
|
|
|
156
208
|
{ note: note ?? "" }
|
|
157
209
|
);
|
|
158
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
|
+
{}
|
|
243
|
+
);
|
|
244
|
+
},
|
|
159
245
|
// ─── Rewards program (P1) ──────────────────────────────────
|
|
160
246
|
async submitActivity(userId, events, opts) {
|
|
161
247
|
return request(
|
|
@@ -798,7 +884,11 @@ function createOfflineQueue(config = {}) {
|
|
|
798
884
|
}
|
|
799
885
|
sent++;
|
|
800
886
|
} else {
|
|
801
|
-
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(
|
|
802
892
|
result.error.message
|
|
803
893
|
);
|
|
804
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"));
|
|
@@ -1339,10 +1429,13 @@ function normaliseThrown(thrown) {
|
|
|
1339
1429
|
}
|
|
1340
1430
|
|
|
1341
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;
|
|
1342
1434
|
exports.MUSHI_INTERNAL_HEADER = MUSHI_INTERNAL_HEADER;
|
|
1343
1435
|
exports.MUSHI_INTERNAL_INIT_MARKER = MUSHI_INTERNAL_INIT_MARKER;
|
|
1344
1436
|
exports.REGION_ENDPOINTS = REGION_ENDPOINTS;
|
|
1345
1437
|
exports.captureEnvironment = captureEnvironment;
|
|
1438
|
+
exports.checkReportPayloadSize = checkReportPayloadSize;
|
|
1346
1439
|
exports.createApiClient = createApiClient;
|
|
1347
1440
|
exports.createBreadcrumbBuffer = createBreadcrumbBuffer;
|
|
1348
1441
|
exports.createLogger = createLogger;
|
|
@@ -1350,6 +1443,8 @@ exports.createOfflineQueue = createOfflineQueue;
|
|
|
1350
1443
|
exports.createPiiScrubber = createPiiScrubber;
|
|
1351
1444
|
exports.createPreFilter = createPreFilter;
|
|
1352
1445
|
exports.createRateLimiter = createRateLimiter;
|
|
1446
|
+
exports.estimateJsonBytes = estimateJsonBytes;
|
|
1447
|
+
exports.formatBytes = formatBytes;
|
|
1353
1448
|
exports.getDeviceFingerprintHash = getDeviceFingerprintHash;
|
|
1354
1449
|
exports.getReporterToken = getReporterToken;
|
|
1355
1450
|
exports.getSessionId = getSessionId;
|