@mushi-mushi/core 1.9.0 → 1.11.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 CHANGED
@@ -140,12 +140,20 @@ function createApiClient(options) {
140
140
  reporterToken
141
141
  );
142
142
  },
143
- async replyToReporterReport(reportId, reporterToken, body) {
143
+ async replyToReporterReport(reportId, reporterToken, body, feedbackSignal) {
144
144
  return requestForReporter(
145
145
  "POST",
146
146
  `/v1/reporter/reports/${reportId}/reply`,
147
147
  reporterToken,
148
- { body }
148
+ { body, ...feedbackSignal ? { feedback_signal: feedbackSignal } : {} }
149
+ );
150
+ },
151
+ async reopenReporterReport(reportId, reporterToken, note) {
152
+ return requestForReporter(
153
+ "POST",
154
+ `/v1/reporter/reports/${reportId}/reopen`,
155
+ reporterToken,
156
+ { note: note ?? "" }
149
157
  );
150
158
  },
151
159
  // ─── Rewards program (P1) ──────────────────────────────────
@@ -194,6 +202,15 @@ function createApiClient(options) {
194
202
  1,
195
203
  "reporter-poll"
196
204
  );
205
+ },
206
+ async getHallOfFame(limit = 10) {
207
+ return request(
208
+ "GET",
209
+ `/v1/sdk/hall-of-fame?limit=${limit}`,
210
+ void 0,
211
+ 1,
212
+ "reporter-poll"
213
+ );
197
214
  }
198
215
  };
199
216
  }
@@ -292,8 +309,10 @@ var SPAM_PATTERNS = [
292
309
  // numbers only
293
310
  /^[^a-zA-Z\u00C0-\u024F\u4E00-\u9FFF\u3040-\u309F\u30A0-\u30FF]{10,}$/,
294
311
  // no real letters
295
- /\b(test|asdf|qwerty|lorem ipsum)\b/i
296
- // common test strings
312
+ // "test" removed — users legitimately write "I was testing…" in real reports.
313
+ // Keep clearly-nonsense patterns that are never real sentences.
314
+ /^(asdf|qwerty|lorem ipsum)[.,!?\s]*$/i
315
+ // standalone keyboard-mash or placeholder
297
316
  ];
298
317
  var GIBBERISH_PATTERN = /^[bcdfghjklmnpqrstvwxz]{6,}/i;
299
318
  function createPreFilter(config = {}) {
@@ -583,9 +602,11 @@ var DB_VERSION = 1;
583
602
  var LS_KEY = "mushi_offline_queue";
584
603
  var BATCH_SIZE = 10;
585
604
  var MAX_BACKOFF_MS = 6e4;
605
+ var AUTO_FLUSH_INTERVAL_MS = 3e4;
586
606
  function createOfflineQueue(config = {}) {
587
607
  const { enabled = true, maxQueueSize = 50, syncOnReconnect = true, encryptAtRest = true } = config;
588
608
  let syncCleanup = null;
609
+ let flushInterval = null;
589
610
  let backendType = null;
590
611
  async function wrapForStorage(report) {
591
612
  const queuedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -780,6 +801,7 @@ function createOfflineQueue(config = {}) {
780
801
  const permanent = result.error?.code === "HTTP_400" || result.error?.code === "HTTP_422" || result.error?.code === "INGEST_ERROR" || result.error?.code === "VALIDATION_ERROR" || typeof result.error?.message === "string" && /invalid payload|description must be at least|validation/i.test(
781
802
  result.error.message
782
803
  );
804
+ 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"));
783
805
  if (permanent) {
784
806
  try {
785
807
  if (backend === "indexeddb") await idbDelete(rowId);
@@ -787,6 +809,11 @@ function createOfflineQueue(config = {}) {
787
809
  } catch {
788
810
  lsDelete(rowId);
789
811
  }
812
+ } else if (transient) {
813
+ queueLog.debug("Offline queue: transient failure, will retry", {
814
+ id: rowId,
815
+ code: result.error?.code
816
+ });
790
817
  }
791
818
  failed++;
792
819
  if (i < batch.length - 1) {
@@ -822,14 +849,25 @@ function createOfflineQueue(config = {}) {
822
849
  }
823
850
  function startAutoSync(client) {
824
851
  if (!enabled || !syncOnReconnect || typeof window === "undefined") return;
825
- const handler = () => {
852
+ const tryFlush = () => {
826
853
  if (navigator.onLine) {
827
854
  flush(client).catch(() => {
828
855
  });
829
856
  }
830
857
  };
831
- window.addEventListener("online", handler);
832
- syncCleanup = () => window.removeEventListener("online", handler);
858
+ window.addEventListener("online", tryFlush);
859
+ flushInterval = setInterval(() => {
860
+ void size().then((n) => {
861
+ if (n > 0) tryFlush();
862
+ });
863
+ }, AUTO_FLUSH_INTERVAL_MS);
864
+ syncCleanup = () => {
865
+ window.removeEventListener("online", tryFlush);
866
+ if (flushInterval) {
867
+ clearInterval(flushInterval);
868
+ flushInterval = null;
869
+ }
870
+ };
833
871
  }
834
872
  function stopAutoSync() {
835
873
  syncCleanup?.();