@mushi-mushi/core 1.16.0 → 1.18.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 +83 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +83 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1115,6 +1115,8 @@ interface MushiSDKInstance {
|
|
|
1115
1115
|
hide(): void;
|
|
1116
1116
|
attachTo(selectorOrElement: string | Element, options?: MushiWidgetConfig): () => void;
|
|
1117
1117
|
setTrigger(trigger: NonNullable<MushiWidgetConfig['trigger']>): void;
|
|
1118
|
+
/** Open the widget directly to the reporter's "My reports" history view. */
|
|
1119
|
+
openReporter(): void;
|
|
1118
1120
|
close(): void;
|
|
1119
1121
|
destroy(): void;
|
|
1120
1122
|
updateConfig(config: MushiRuntimeSdkConfig): void;
|
|
@@ -1551,6 +1553,9 @@ interface ApiClientOptions {
|
|
|
1551
1553
|
apiEndpoint?: string;
|
|
1552
1554
|
timeout?: number;
|
|
1553
1555
|
maxRetries?: number;
|
|
1556
|
+
/** When set, sent on every request so heartbeats record the running SDK version. */
|
|
1557
|
+
sdkPackage?: string;
|
|
1558
|
+
sdkVersion?: string;
|
|
1554
1559
|
/**
|
|
1555
1560
|
* Returns the current signed end-user identity JWT, or null when the user is
|
|
1556
1561
|
* anonymous. When present it is sent on the X-Mushi-User-Token header so the
|
package/dist/index.d.ts
CHANGED
|
@@ -1115,6 +1115,8 @@ interface MushiSDKInstance {
|
|
|
1115
1115
|
hide(): void;
|
|
1116
1116
|
attachTo(selectorOrElement: string | Element, options?: MushiWidgetConfig): () => void;
|
|
1117
1117
|
setTrigger(trigger: NonNullable<MushiWidgetConfig['trigger']>): void;
|
|
1118
|
+
/** Open the widget directly to the reporter's "My reports" history view. */
|
|
1119
|
+
openReporter(): void;
|
|
1118
1120
|
close(): void;
|
|
1119
1121
|
destroy(): void;
|
|
1120
1122
|
updateConfig(config: MushiRuntimeSdkConfig): void;
|
|
@@ -1551,6 +1553,9 @@ interface ApiClientOptions {
|
|
|
1551
1553
|
apiEndpoint?: string;
|
|
1552
1554
|
timeout?: number;
|
|
1553
1555
|
maxRetries?: number;
|
|
1556
|
+
/** When set, sent on every request so heartbeats record the running SDK version. */
|
|
1557
|
+
sdkPackage?: string;
|
|
1558
|
+
sdkVersion?: string;
|
|
1554
1559
|
/**
|
|
1555
1560
|
* Returns the current signed end-user identity JWT, or null when the user is
|
|
1556
1561
|
* anonymous. When present it is sent on the X-Mushi-User-Token header so the
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,8 @@ function formatBytes(n) {
|
|
|
42
42
|
|
|
43
43
|
// src/api-client.ts
|
|
44
44
|
var MUSHI_USER_TOKEN_HEADER = "X-Mushi-User-Token";
|
|
45
|
+
var MUSHI_SDK_PACKAGE_HEADER = "X-Mushi-SDK-Package";
|
|
46
|
+
var MUSHI_SDK_VERSION_HEADER = "X-Mushi-SDK-Version";
|
|
45
47
|
var DEFAULT_API_ENDPOINT = "https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api";
|
|
46
48
|
var MUSHI_INTERNAL_HEADER = "X-Mushi-Internal";
|
|
47
49
|
var MUSHI_INTERNAL_INIT_MARKER = "__mushiInternal";
|
|
@@ -54,7 +56,9 @@ function createApiClient(options) {
|
|
|
54
56
|
apiEndpoint = DEFAULT_API_ENDPOINT,
|
|
55
57
|
timeout = DEFAULT_TIMEOUT,
|
|
56
58
|
maxRetries = DEFAULT_MAX_RETRIES,
|
|
57
|
-
getUserToken
|
|
59
|
+
getUserToken,
|
|
60
|
+
sdkPackage,
|
|
61
|
+
sdkVersion
|
|
58
62
|
} = options;
|
|
59
63
|
let baseUrl = apiEndpoint.replace(/\/$/, "");
|
|
60
64
|
async function request(method, path, body, retries = maxRetries, internalKind, extraHeaders) {
|
|
@@ -69,6 +73,8 @@ function createApiClient(options) {
|
|
|
69
73
|
"Content-Type": "application/json",
|
|
70
74
|
"X-Mushi-Api-Key": apiKey,
|
|
71
75
|
"X-Mushi-Project": projectId,
|
|
76
|
+
...sdkPackage ? { [MUSHI_SDK_PACKAGE_HEADER]: sdkPackage } : {},
|
|
77
|
+
...sdkVersion ? { [MUSHI_SDK_VERSION_HEADER]: sdkVersion } : {},
|
|
72
78
|
...userToken ? { [MUSHI_USER_TOKEN_HEADER]: userToken } : {},
|
|
73
79
|
...internalKind ? { [MUSHI_INTERNAL_HEADER]: internalKind } : {},
|
|
74
80
|
...extraHeaders
|
|
@@ -131,6 +137,8 @@ function createApiClient(options) {
|
|
|
131
137
|
"Content-Type": "application/json",
|
|
132
138
|
"X-Mushi-Api-Key": apiKey,
|
|
133
139
|
"X-Mushi-Project": projectId,
|
|
140
|
+
...sdkPackage ? { [MUSHI_SDK_PACKAGE_HEADER]: sdkPackage } : {},
|
|
141
|
+
...sdkVersion ? { [MUSHI_SDK_VERSION_HEADER]: sdkVersion } : {},
|
|
134
142
|
[MUSHI_INTERNAL_HEADER]: "reporter-poll",
|
|
135
143
|
"X-Reporter-Token-Hash": tokenHash,
|
|
136
144
|
"X-Reporter-Ts": ts,
|
|
@@ -832,6 +840,8 @@ var LS_KEY = "mushi_offline_queue";
|
|
|
832
840
|
var BATCH_SIZE = 10;
|
|
833
841
|
var MAX_BACKOFF_MS = 6e4;
|
|
834
842
|
var AUTO_FLUSH_INTERVAL_MS = 3e4;
|
|
843
|
+
var MAX_DELIVERY_ATTEMPTS = 8;
|
|
844
|
+
var MAX_QUEUE_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
835
845
|
function createOfflineQueue(config = {}) {
|
|
836
846
|
const { enabled = true, maxQueueSize = 50, syncOnReconnect = true, encryptAtRest = true } = config;
|
|
837
847
|
let syncCleanup = null;
|
|
@@ -864,6 +874,17 @@ function createOfflineQueue(config = {}) {
|
|
|
864
874
|
function isEncryptedRecord(row) {
|
|
865
875
|
return !!row.payload && isEncryptedPayload(row.payload);
|
|
866
876
|
}
|
|
877
|
+
function rowAttempts(row) {
|
|
878
|
+
const n = row.attempts;
|
|
879
|
+
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
880
|
+
}
|
|
881
|
+
function isExpired(row, now) {
|
|
882
|
+
const queuedAt = row.queuedAt;
|
|
883
|
+
if (!queuedAt) return false;
|
|
884
|
+
const ts = Date.parse(queuedAt);
|
|
885
|
+
if (Number.isNaN(ts)) return false;
|
|
886
|
+
return now - ts > MAX_QUEUE_AGE_MS;
|
|
887
|
+
}
|
|
867
888
|
function detectBackend() {
|
|
868
889
|
if (backendType) return backendType;
|
|
869
890
|
if (typeof indexedDB !== "undefined") {
|
|
@@ -919,6 +940,15 @@ function createOfflineQueue(config = {}) {
|
|
|
919
940
|
tx.onerror = () => reject(tx.error);
|
|
920
941
|
});
|
|
921
942
|
}
|
|
943
|
+
async function idbPutRow(row) {
|
|
944
|
+
const db = await openDb();
|
|
945
|
+
return new Promise((resolve, reject) => {
|
|
946
|
+
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
947
|
+
tx.objectStore(STORE_NAME).put(row);
|
|
948
|
+
tx.oncomplete = () => resolve();
|
|
949
|
+
tx.onerror = () => reject(tx.error);
|
|
950
|
+
});
|
|
951
|
+
}
|
|
922
952
|
async function idbSize() {
|
|
923
953
|
const db = await openDb();
|
|
924
954
|
return new Promise((resolve, reject) => {
|
|
@@ -960,6 +990,14 @@ function createOfflineQueue(config = {}) {
|
|
|
960
990
|
const rows = lsRead().filter((r) => r.id !== id);
|
|
961
991
|
lsWrite(rows);
|
|
962
992
|
}
|
|
993
|
+
function lsUpdateRow(row) {
|
|
994
|
+
const rows = lsRead();
|
|
995
|
+
const idx = rows.findIndex((r) => r.id === row.id);
|
|
996
|
+
if (idx >= 0) {
|
|
997
|
+
rows[idx] = row;
|
|
998
|
+
lsWrite(rows);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
963
1001
|
async function enqueue(report) {
|
|
964
1002
|
if (!enabled) return;
|
|
965
1003
|
const currentSize = await size();
|
|
@@ -1000,6 +1038,23 @@ function createOfflineQueue(config = {}) {
|
|
|
1000
1038
|
} else {
|
|
1001
1039
|
rows = lsRead();
|
|
1002
1040
|
}
|
|
1041
|
+
const now = Date.now();
|
|
1042
|
+
const fresh = [];
|
|
1043
|
+
for (const row of rows) {
|
|
1044
|
+
if (isExpired(row, now)) {
|
|
1045
|
+
const rowId = row.id;
|
|
1046
|
+
try {
|
|
1047
|
+
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1048
|
+
else lsDelete(rowId);
|
|
1049
|
+
} catch {
|
|
1050
|
+
lsDelete(rowId);
|
|
1051
|
+
}
|
|
1052
|
+
queueLog.debug("Offline queue: evicting stale report", { id: rowId });
|
|
1053
|
+
} else {
|
|
1054
|
+
fresh.push(row);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
rows = fresh;
|
|
1003
1058
|
const batch = rows.slice(0, BATCH_SIZE);
|
|
1004
1059
|
let sent = 0;
|
|
1005
1060
|
let failed = 0;
|
|
@@ -1043,10 +1098,33 @@ function createOfflineQueue(config = {}) {
|
|
|
1043
1098
|
lsDelete(rowId);
|
|
1044
1099
|
}
|
|
1045
1100
|
} else if (transient) {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1101
|
+
const nextAttempts = rowAttempts(row) + 1;
|
|
1102
|
+
if (nextAttempts >= MAX_DELIVERY_ATTEMPTS) {
|
|
1103
|
+
try {
|
|
1104
|
+
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1105
|
+
else lsDelete(rowId);
|
|
1106
|
+
} catch {
|
|
1107
|
+
lsDelete(rowId);
|
|
1108
|
+
}
|
|
1109
|
+
queueLog.warn("Offline queue: giving up on report after repeated failures", {
|
|
1110
|
+
id: rowId,
|
|
1111
|
+
attempts: nextAttempts,
|
|
1112
|
+
code: result.error?.code
|
|
1113
|
+
});
|
|
1114
|
+
} else {
|
|
1115
|
+
row.attempts = nextAttempts;
|
|
1116
|
+
try {
|
|
1117
|
+
if (backend === "indexeddb") await idbPutRow(row);
|
|
1118
|
+
else lsUpdateRow(row);
|
|
1119
|
+
} catch {
|
|
1120
|
+
lsUpdateRow(row);
|
|
1121
|
+
}
|
|
1122
|
+
queueLog.debug("Offline queue: transient failure, will retry", {
|
|
1123
|
+
id: rowId,
|
|
1124
|
+
attempts: nextAttempts,
|
|
1125
|
+
code: result.error?.code
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1050
1128
|
}
|
|
1051
1129
|
failed++;
|
|
1052
1130
|
if (i < batch.length - 1) {
|