@mushi-mushi/core 1.17.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 +74 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +74 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -842,6 +842,8 @@ var LS_KEY = "mushi_offline_queue";
|
|
|
842
842
|
var BATCH_SIZE = 10;
|
|
843
843
|
var MAX_BACKOFF_MS = 6e4;
|
|
844
844
|
var AUTO_FLUSH_INTERVAL_MS = 3e4;
|
|
845
|
+
var MAX_DELIVERY_ATTEMPTS = 8;
|
|
846
|
+
var MAX_QUEUE_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
845
847
|
function createOfflineQueue(config = {}) {
|
|
846
848
|
const { enabled = true, maxQueueSize = 50, syncOnReconnect = true, encryptAtRest = true } = config;
|
|
847
849
|
let syncCleanup = null;
|
|
@@ -874,6 +876,17 @@ function createOfflineQueue(config = {}) {
|
|
|
874
876
|
function isEncryptedRecord(row) {
|
|
875
877
|
return !!row.payload && isEncryptedPayload(row.payload);
|
|
876
878
|
}
|
|
879
|
+
function rowAttempts(row) {
|
|
880
|
+
const n = row.attempts;
|
|
881
|
+
return typeof n === "number" && Number.isFinite(n) ? n : 0;
|
|
882
|
+
}
|
|
883
|
+
function isExpired(row, now) {
|
|
884
|
+
const queuedAt = row.queuedAt;
|
|
885
|
+
if (!queuedAt) return false;
|
|
886
|
+
const ts = Date.parse(queuedAt);
|
|
887
|
+
if (Number.isNaN(ts)) return false;
|
|
888
|
+
return now - ts > MAX_QUEUE_AGE_MS;
|
|
889
|
+
}
|
|
877
890
|
function detectBackend() {
|
|
878
891
|
if (backendType) return backendType;
|
|
879
892
|
if (typeof indexedDB !== "undefined") {
|
|
@@ -929,6 +942,15 @@ function createOfflineQueue(config = {}) {
|
|
|
929
942
|
tx.onerror = () => reject(tx.error);
|
|
930
943
|
});
|
|
931
944
|
}
|
|
945
|
+
async function idbPutRow(row) {
|
|
946
|
+
const db = await openDb();
|
|
947
|
+
return new Promise((resolve, reject) => {
|
|
948
|
+
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
949
|
+
tx.objectStore(STORE_NAME).put(row);
|
|
950
|
+
tx.oncomplete = () => resolve();
|
|
951
|
+
tx.onerror = () => reject(tx.error);
|
|
952
|
+
});
|
|
953
|
+
}
|
|
932
954
|
async function idbSize() {
|
|
933
955
|
const db = await openDb();
|
|
934
956
|
return new Promise((resolve, reject) => {
|
|
@@ -970,6 +992,14 @@ function createOfflineQueue(config = {}) {
|
|
|
970
992
|
const rows = lsRead().filter((r) => r.id !== id);
|
|
971
993
|
lsWrite(rows);
|
|
972
994
|
}
|
|
995
|
+
function lsUpdateRow(row) {
|
|
996
|
+
const rows = lsRead();
|
|
997
|
+
const idx = rows.findIndex((r) => r.id === row.id);
|
|
998
|
+
if (idx >= 0) {
|
|
999
|
+
rows[idx] = row;
|
|
1000
|
+
lsWrite(rows);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
973
1003
|
async function enqueue(report) {
|
|
974
1004
|
if (!enabled) return;
|
|
975
1005
|
const currentSize = await size();
|
|
@@ -1010,6 +1040,23 @@ function createOfflineQueue(config = {}) {
|
|
|
1010
1040
|
} else {
|
|
1011
1041
|
rows = lsRead();
|
|
1012
1042
|
}
|
|
1043
|
+
const now = Date.now();
|
|
1044
|
+
const fresh = [];
|
|
1045
|
+
for (const row of rows) {
|
|
1046
|
+
if (isExpired(row, now)) {
|
|
1047
|
+
const rowId = row.id;
|
|
1048
|
+
try {
|
|
1049
|
+
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1050
|
+
else lsDelete(rowId);
|
|
1051
|
+
} catch {
|
|
1052
|
+
lsDelete(rowId);
|
|
1053
|
+
}
|
|
1054
|
+
queueLog.debug("Offline queue: evicting stale report", { id: rowId });
|
|
1055
|
+
} else {
|
|
1056
|
+
fresh.push(row);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
rows = fresh;
|
|
1013
1060
|
const batch = rows.slice(0, BATCH_SIZE);
|
|
1014
1061
|
let sent = 0;
|
|
1015
1062
|
let failed = 0;
|
|
@@ -1053,10 +1100,33 @@ function createOfflineQueue(config = {}) {
|
|
|
1053
1100
|
lsDelete(rowId);
|
|
1054
1101
|
}
|
|
1055
1102
|
} else if (transient) {
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1103
|
+
const nextAttempts = rowAttempts(row) + 1;
|
|
1104
|
+
if (nextAttempts >= MAX_DELIVERY_ATTEMPTS) {
|
|
1105
|
+
try {
|
|
1106
|
+
if (backend === "indexeddb") await idbDelete(rowId);
|
|
1107
|
+
else lsDelete(rowId);
|
|
1108
|
+
} catch {
|
|
1109
|
+
lsDelete(rowId);
|
|
1110
|
+
}
|
|
1111
|
+
queueLog.warn("Offline queue: giving up on report after repeated failures", {
|
|
1112
|
+
id: rowId,
|
|
1113
|
+
attempts: nextAttempts,
|
|
1114
|
+
code: result.error?.code
|
|
1115
|
+
});
|
|
1116
|
+
} else {
|
|
1117
|
+
row.attempts = nextAttempts;
|
|
1118
|
+
try {
|
|
1119
|
+
if (backend === "indexeddb") await idbPutRow(row);
|
|
1120
|
+
else lsUpdateRow(row);
|
|
1121
|
+
} catch {
|
|
1122
|
+
lsUpdateRow(row);
|
|
1123
|
+
}
|
|
1124
|
+
queueLog.debug("Offline queue: transient failure, will retry", {
|
|
1125
|
+
id: rowId,
|
|
1126
|
+
attempts: nextAttempts,
|
|
1127
|
+
code: result.error?.code
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1060
1130
|
}
|
|
1061
1131
|
failed++;
|
|
1062
1132
|
if (i < batch.length - 1) {
|