@shuffleio/shuffle-mcps 0.0.33 → 0.0.35

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.
@@ -2,7 +2,7 @@ import {
2
2
  fetchApps,
3
3
  fetchAppsViaApiConfig,
4
4
  invalidateAppsCache
5
- } from "./chunk-B4JFC2JU.mjs";
5
+ } from "./chunk-5B3Q2RHS.mjs";
6
6
  export {
7
7
  fetchApps,
8
8
  fetchAppsViaApiConfig,
@@ -1,7 +1,9 @@
1
1
  // fetchBreaker.ts
2
2
  var FAIL_THRESHOLD = 12;
3
3
  var ROLLING_WINDOW_MS = 5e3;
4
- var COOLDOWN_MS = 3e4;
4
+ var COOLDOWN_MS = 1e4;
5
+ var PROBE_AFTER_MS = 1500;
6
+ var MAX_PROBE_BACKOFF_MS = 5e3;
5
7
  var HARD_BURST_THRESHOLD = 30;
6
8
  var HARD_BURST_WINDOW_MS = 1e3;
7
9
  var state = /* @__PURE__ */ new Map();
@@ -10,11 +12,24 @@ var allowedOrigins = /* @__PURE__ */ new Set();
10
12
  var getEntry = (key) => {
11
13
  let entry = state.get(key);
12
14
  if (!entry) {
13
- entry = { failures: [], attempts: [], blockedUntil: 0, warned: false };
15
+ entry = { failures: [], attempts: [], blockedUntil: 0, probeAfter: 0, probing: false, warned: false };
14
16
  state.set(key, entry);
15
17
  }
16
18
  return entry;
17
19
  };
20
+ var trip = (entry, now, cooldownMs = COOLDOWN_MS) => {
21
+ entry.blockedUntil = now + cooldownMs;
22
+ entry.probeAfter = now + PROBE_AFTER_MS;
23
+ entry.probing = false;
24
+ };
25
+ var reset = (entry) => {
26
+ entry.failures.length = 0;
27
+ entry.attempts.length = 0;
28
+ entry.blockedUntil = 0;
29
+ entry.probeAfter = 0;
30
+ entry.probing = false;
31
+ entry.warned = false;
32
+ };
18
33
  var prune = (arr, now, windowMs) => {
19
34
  const cutoff = now - windowMs;
20
35
  while (arr.length && arr[0] < cutoff) arr.shift();
@@ -64,21 +79,35 @@ var installFetchBreaker = () => {
64
79
  if (!key) return original(input, init);
65
80
  const entry = getEntry(key);
66
81
  const now = Date.now();
82
+ let isProbe = false;
67
83
  if (entry.blockedUntil > now) {
68
- return synthetic503(key, entry.blockedUntil - now);
84
+ if (!entry.probing && now >= entry.probeAfter) {
85
+ entry.probing = true;
86
+ isProbe = true;
87
+ } else {
88
+ return synthetic503(key, entry.blockedUntil - now);
89
+ }
69
90
  }
70
- entry.attempts.push(now);
71
- prune(entry.attempts, now, HARD_BURST_WINDOW_MS);
72
- if (entry.attempts.length > HARD_BURST_THRESHOLD) {
73
- entry.blockedUntil = now + COOLDOWN_MS;
74
- if (!entry.warned) {
75
- entry.warned = true;
76
- console.error(
77
- `[fetchBreaker] Burst detected on ${key} (${entry.attempts.length} calls / ${HARD_BURST_WINDOW_MS}ms). Blocking for ${COOLDOWN_MS / 1e3}s. Fix the caller \u2014 this is almost always a missing useEffect dep or a render loop.`
78
- );
91
+ if (!isProbe) {
92
+ entry.attempts.push(now);
93
+ prune(entry.attempts, now, HARD_BURST_WINDOW_MS);
94
+ if (entry.attempts.length > HARD_BURST_THRESHOLD) {
95
+ trip(entry, now);
96
+ if (!entry.warned) {
97
+ entry.warned = true;
98
+ console.error(
99
+ `[fetchBreaker] Burst detected on ${key} (${entry.attempts.length} calls / ${HARD_BURST_WINDOW_MS}ms). Blocking for up to ${COOLDOWN_MS / 1e3}s (probing again in ${PROBE_AFTER_MS / 1e3}s). Fix the caller \u2014 this is almost always a missing useEffect dep or a render loop.`
100
+ );
101
+ }
102
+ return synthetic503(key, COOLDOWN_MS);
79
103
  }
80
- return synthetic503(key, COOLDOWN_MS);
81
104
  }
105
+ const onProbeFailure = (ts) => {
106
+ if (!isProbe) return;
107
+ entry.probing = false;
108
+ entry.probeAfter = ts + Math.min(MAX_PROBE_BACKOFF_MS, PROBE_AFTER_MS * 2);
109
+ entry.blockedUntil = Math.max(entry.blockedUntil, ts + PROBE_AFTER_MS);
110
+ };
82
111
  let response;
83
112
  try {
84
113
  response = await original(input, init);
@@ -86,12 +115,13 @@ var installFetchBreaker = () => {
86
115
  const ts = Date.now();
87
116
  entry.failures.push(ts);
88
117
  prune(entry.failures, ts, ROLLING_WINDOW_MS);
89
- if (entry.failures.length >= FAIL_THRESHOLD) {
90
- entry.blockedUntil = ts + COOLDOWN_MS;
118
+ onProbeFailure(ts);
119
+ if (!isProbe && entry.failures.length >= FAIL_THRESHOLD) {
120
+ trip(entry, ts);
91
121
  if (!entry.warned) {
92
122
  entry.warned = true;
93
123
  console.error(
94
- `[fetchBreaker] ${entry.failures.length} network errors on ${key} in ${ROLLING_WINDOW_MS / 1e3}s. Blocking for ${COOLDOWN_MS / 1e3}s.`
124
+ `[fetchBreaker] ${entry.failures.length} network errors on ${key} in ${ROLLING_WINDOW_MS / 1e3}s. Blocking for up to ${COOLDOWN_MS / 1e3}s.`
95
125
  );
96
126
  }
97
127
  }
@@ -101,18 +131,20 @@ var installFetchBreaker = () => {
101
131
  const ts = Date.now();
102
132
  entry.failures.push(ts);
103
133
  prune(entry.failures, ts, ROLLING_WINDOW_MS);
104
- if (entry.failures.length >= FAIL_THRESHOLD) {
105
- entry.blockedUntil = ts + COOLDOWN_MS;
134
+ onProbeFailure(ts);
135
+ if (!isProbe && entry.failures.length >= FAIL_THRESHOLD) {
136
+ trip(entry, ts);
106
137
  if (!entry.warned) {
107
138
  entry.warned = true;
108
139
  console.error(
109
- `[fetchBreaker] ${entry.failures.length} ${response.status}s on ${key} in ${ROLLING_WINDOW_MS / 1e3}s. Blocking for ${COOLDOWN_MS / 1e3}s.`
140
+ `[fetchBreaker] ${entry.failures.length} ${response.status}s on ${key} in ${ROLLING_WINDOW_MS / 1e3}s. Blocking for up to ${COOLDOWN_MS / 1e3}s.`
110
141
  );
111
142
  }
112
143
  }
113
144
  } else if (response.status < 400) {
114
- entry.failures.length = 0;
115
- entry.warned = false;
145
+ reset(entry);
146
+ } else {
147
+ if (isProbe) reset(entry);
116
148
  }
117
149
  return response;
118
150
  });
@@ -134,7 +166,7 @@ var getEnvVar = (key) => {
134
166
  var isDevEnvironment = () => {
135
167
  if (getEnvVar("VITE_SHUFFLE_API_URL")) return false;
136
168
  const hostname = typeof window !== "undefined" ? window.location.hostname : "";
137
- return hostname.includes("lovableproject.com") || hostname.includes("id-preview--");
169
+ return hostname.includes("lovableproject.com") || hostname.includes("id-preview--") || hostname.endsWith(".lovable.app") || hostname.endsWith(".lovable.dev");
138
170
  };
139
171
  var isCloudDomain = () => {
140
172
  const hostname = typeof window !== "undefined" ? window.location.hostname : "";
@@ -244,13 +276,6 @@ var API_CONFIG = {
244
276
  var getApiUrl = (endpoint) => {
245
277
  return `${API_CONFIG.baseUrl}${endpoint}`;
246
278
  };
247
- var getShuffleCoreFormUrl = (refUrl) => {
248
- if (!refUrl) return refUrl;
249
- if (/^https?:\/\//i.test(refUrl)) return refUrl;
250
- const path = refUrl.startsWith("/") ? refUrl : `/${refUrl}`;
251
- if (isCloud()) return `https://shuffler.io${path}`;
252
- return `${API_CONFIG.baseUrl}${path}`;
253
- };
254
279
  var shuffleFetch = (url, init) => {
255
280
  const { headers: extraHeaders, ...rest } = init || {};
256
281
  return fetch(url, {
@@ -340,7 +365,6 @@ export {
340
365
  getTrackedOrgId,
341
366
  API_CONFIG,
342
367
  getApiUrl,
343
- getShuffleCoreFormUrl,
344
368
  shuffleFetch,
345
369
  getAuthHeader,
346
370
  fetchApps,