@datum-cloud/activity-ui 0.6.0-dev.5b51180 → 0.6.0-dev.917556e
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/hooks/useActivityFeed.d.ts.map +1 -1
- package/dist/index.esm.js +71 -12
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +71 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useActivityFeed.d.ts","sourceRoot":"","sources":["../../src/hooks/useActivityFeed.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAyC,YAAY,EAAc,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvI,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"useActivityFeed.d.ts","sourceRoot":"","sources":["../../src/hooks/useActivityFeed.ts"],"names":[],"mappings":"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"}
|
package/dist/index.esm.js
CHANGED
|
@@ -9784,6 +9784,14 @@ function ActionToggle({ value, onChange, className = '', disabled = false, }) {
|
|
|
9784
9784
|
|
|
9785
9785
|
// Debounce delay for filter changes (ms)
|
|
9786
9786
|
const FILTER_DEBOUNCE_MS$1 = 300;
|
|
9787
|
+
// Backoff for watch stream reconnects. Without this, a persistently failing
|
|
9788
|
+
// upstream gets re-opened on every render with no delay — see
|
|
9789
|
+
// https://github.com/datum-cloud/infra/issues/5060, where a single browser
|
|
9790
|
+
// tab reconnected roughly 2.5 times a minute against a stream that kept
|
|
9791
|
+
// aborting, driving the proxy's memory up until the process was OOMKilled.
|
|
9792
|
+
const RECONNECT_BASE_DELAY_MS = 1000;
|
|
9793
|
+
const RECONNECT_MAX_DELAY_MS = 30000;
|
|
9794
|
+
const MAX_RECONNECT_ATTEMPTS = 8;
|
|
9787
9795
|
/**
|
|
9788
9796
|
* Escape a value for safe interpolation into a CEL string literal.
|
|
9789
9797
|
* Prevents injection via values containing backslashes or double quotes.
|
|
@@ -9878,6 +9886,12 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
9878
9886
|
const resourceVersionRef = useRef();
|
|
9879
9887
|
// Track the watch stop function
|
|
9880
9888
|
const watchStopRef = useRef(null);
|
|
9889
|
+
// Consecutive watch failures since the last successful event, used to back
|
|
9890
|
+
// off reconnect attempts. Reset on any real event (including BOOKMARK) and
|
|
9891
|
+
// on a manual startStreaming() call.
|
|
9892
|
+
const reconnectAttemptsRef = useRef(0);
|
|
9893
|
+
// Pending scheduled reconnect, if any.
|
|
9894
|
+
const reconnectTimerRef = useRef(null);
|
|
9881
9895
|
// Track whether streaming should restart after filter change
|
|
9882
9896
|
const shouldRestartStreamingRef = useRef(false);
|
|
9883
9897
|
// Track if we've done the initial load
|
|
@@ -9952,6 +9966,9 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
9952
9966
|
console.error('Watch error:', event.object);
|
|
9953
9967
|
return;
|
|
9954
9968
|
}
|
|
9969
|
+
// A real event (including a BOOKMARK keep-alive) means the connection is
|
|
9970
|
+
// healthy — reset backoff so a later failure starts from the base delay.
|
|
9971
|
+
reconnectAttemptsRef.current = 0;
|
|
9955
9972
|
if (event.type === 'BOOKMARK') {
|
|
9956
9973
|
// Update resource version for resume capability
|
|
9957
9974
|
if (event.object.metadata?.resourceVersion) {
|
|
@@ -10017,15 +10034,13 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
10017
10034
|
setActivities((prev) => prev.filter((a) => a.metadata?.name !== event.object.metadata?.name));
|
|
10018
10035
|
}
|
|
10019
10036
|
}, [filters]);
|
|
10020
|
-
//
|
|
10021
|
-
|
|
10037
|
+
// Open the watch connection. Does not touch userPaused/backoff state —
|
|
10038
|
+
// callers (startStreaming, scheduleReconnect) own that.
|
|
10039
|
+
const connectWatch = useCallback(() => {
|
|
10022
10040
|
if (watchStopRef.current) {
|
|
10023
10041
|
// Already watching
|
|
10024
10042
|
return;
|
|
10025
10043
|
}
|
|
10026
|
-
// Explicit start clears the user-paused flag so the auto-restart
|
|
10027
|
-
// effects can keep the stream alive after subsequent filter changes.
|
|
10028
|
-
setUserPaused(false);
|
|
10029
10044
|
// Clear any previous watch error when starting a new stream
|
|
10030
10045
|
setWatchError(null);
|
|
10031
10046
|
const params = buildWatchParams();
|
|
@@ -10039,6 +10054,7 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
10039
10054
|
setWatchError(streamError);
|
|
10040
10055
|
setIsStreaming(false);
|
|
10041
10056
|
watchStopRef.current = null;
|
|
10057
|
+
reconnectAttemptsRef.current += 1;
|
|
10042
10058
|
},
|
|
10043
10059
|
onClose: () => {
|
|
10044
10060
|
setIsStreaming(false);
|
|
@@ -10049,11 +10065,50 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
10049
10065
|
setIsStreaming(true);
|
|
10050
10066
|
setNewActivitiesCount(0);
|
|
10051
10067
|
}, [client, buildWatchParams, handleWatchEvent]);
|
|
10068
|
+
// Reconnect with exponential backoff. The first attempt (no prior
|
|
10069
|
+
// failures) connects immediately; each subsequent failure doubles the
|
|
10070
|
+
// delay up to RECONNECT_MAX_DELAY_MS. After MAX_RECONNECT_ATTEMPTS
|
|
10071
|
+
// consecutive failures, this stops retrying automatically — the watch
|
|
10072
|
+
// stays stopped (with watchError set) until the user calls
|
|
10073
|
+
// startStreaming() again, which resets the attempt count.
|
|
10074
|
+
const scheduleReconnect = useCallback(() => {
|
|
10075
|
+
if (reconnectTimerRef.current) {
|
|
10076
|
+
// A reconnect is already scheduled.
|
|
10077
|
+
return;
|
|
10078
|
+
}
|
|
10079
|
+
if (reconnectAttemptsRef.current >= MAX_RECONNECT_ATTEMPTS) {
|
|
10080
|
+
return;
|
|
10081
|
+
}
|
|
10082
|
+
const attempt = reconnectAttemptsRef.current;
|
|
10083
|
+
const delay = attempt === 0 ? 0 : Math.min(RECONNECT_MAX_DELAY_MS, RECONNECT_BASE_DELAY_MS * 2 ** (attempt - 1));
|
|
10084
|
+
reconnectTimerRef.current = setTimeout(() => {
|
|
10085
|
+
reconnectTimerRef.current = null;
|
|
10086
|
+
connectWatch();
|
|
10087
|
+
}, delay);
|
|
10088
|
+
}, [connectWatch]);
|
|
10089
|
+
// Start watching for real-time updates. This is the user/effect-facing
|
|
10090
|
+
// entry point: it resets backoff and any pending scheduled reconnect
|
|
10091
|
+
// before connecting, so a manual retry always starts from a clean slate.
|
|
10092
|
+
const startStreaming = useCallback(() => {
|
|
10093
|
+
// Explicit start clears the user-paused flag so the auto-restart
|
|
10094
|
+
// effects can keep the stream alive after subsequent filter changes.
|
|
10095
|
+
setUserPaused(false);
|
|
10096
|
+
reconnectAttemptsRef.current = 0;
|
|
10097
|
+
if (reconnectTimerRef.current) {
|
|
10098
|
+
clearTimeout(reconnectTimerRef.current);
|
|
10099
|
+
reconnectTimerRef.current = null;
|
|
10100
|
+
}
|
|
10101
|
+
connectWatch();
|
|
10102
|
+
}, [connectWatch]);
|
|
10052
10103
|
// Stop watching. Marks the user as paused so the auto-start effects
|
|
10053
10104
|
// below don't immediately re-open the stream this call just closed.
|
|
10054
10105
|
const stopStreaming = useCallback(() => {
|
|
10055
10106
|
setUserPaused(true);
|
|
10056
10107
|
setIsStreaming(false);
|
|
10108
|
+
if (reconnectTimerRef.current) {
|
|
10109
|
+
clearTimeout(reconnectTimerRef.current);
|
|
10110
|
+
reconnectTimerRef.current = null;
|
|
10111
|
+
}
|
|
10057
10112
|
if (watchStopRef.current) {
|
|
10058
10113
|
watchStopRef.current();
|
|
10059
10114
|
watchStopRef.current = null;
|
|
@@ -10216,9 +10271,10 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
10216
10271
|
}
|
|
10217
10272
|
};
|
|
10218
10273
|
}, [filters, timeRange]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
10219
|
-
// Auto-start streaming after initial load when enabled
|
|
10220
|
-
//
|
|
10221
|
-
//
|
|
10274
|
+
// Auto-start streaming after initial load when enabled, and reconnect
|
|
10275
|
+
// (with backoff — see scheduleReconnect) whenever a connection drops.
|
|
10276
|
+
// Skipped when the user has explicitly paused — otherwise clicking Pause
|
|
10277
|
+
// would be immediately undone by this effect on the next render.
|
|
10222
10278
|
useEffect(() => {
|
|
10223
10279
|
if (enableStreaming &&
|
|
10224
10280
|
autoStartStreaming &&
|
|
@@ -10226,9 +10282,9 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
10226
10282
|
activities.length > 0 &&
|
|
10227
10283
|
!isStreaming &&
|
|
10228
10284
|
!isLoading) {
|
|
10229
|
-
|
|
10285
|
+
scheduleReconnect();
|
|
10230
10286
|
}
|
|
10231
|
-
}, [enableStreaming, autoStartStreaming, userPaused, activities.length, isStreaming, isLoading,
|
|
10287
|
+
}, [enableStreaming, autoStartStreaming, userPaused, activities.length, isStreaming, isLoading, scheduleReconnect]);
|
|
10232
10288
|
// Restart streaming after filter change refresh completes. Also skipped
|
|
10233
10289
|
// when the user has paused — a filter change shouldn't silently resume
|
|
10234
10290
|
// a stream the user intentionally stopped.
|
|
@@ -10240,15 +10296,18 @@ function useActivityFeed({ client, initialFilters = {}, initialTimeRange = { sta
|
|
|
10240
10296
|
!isStreaming &&
|
|
10241
10297
|
!isLoading) {
|
|
10242
10298
|
shouldRestartStreamingRef.current = false;
|
|
10243
|
-
|
|
10299
|
+
scheduleReconnect();
|
|
10244
10300
|
}
|
|
10245
|
-
}, [enableStreaming, userPaused, activities.length, isStreaming, isLoading,
|
|
10301
|
+
}, [enableStreaming, userPaused, activities.length, isStreaming, isLoading, scheduleReconnect]);
|
|
10246
10302
|
// Cleanup on unmount
|
|
10247
10303
|
useEffect(() => {
|
|
10248
10304
|
return () => {
|
|
10249
10305
|
if (watchStopRef.current) {
|
|
10250
10306
|
watchStopRef.current();
|
|
10251
10307
|
}
|
|
10308
|
+
if (reconnectTimerRef.current) {
|
|
10309
|
+
clearTimeout(reconnectTimerRef.current);
|
|
10310
|
+
}
|
|
10252
10311
|
if (filterDebounceRef.current) {
|
|
10253
10312
|
clearTimeout(filterDebounceRef.current);
|
|
10254
10313
|
}
|