pi-kiosk-shared 2.1.86 → 2.1.87

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.
Files changed (88) hide show
  1. package/dist/analyticsEmitterManifest.js +1 -1
  2. package/dist/analyticsEmitterManifest.js.map +1 -1
  3. package/dist/analyticsEventLabels.d.ts +1 -1
  4. package/dist/analyticsEventLabels.d.ts.map +1 -1
  5. package/dist/analyticsEventLabels.js +83 -5
  6. package/dist/analyticsEventLabels.js.map +1 -1
  7. package/dist/catalog/createCoalescedRefetchScheduler.d.ts +12 -0
  8. package/dist/catalog/createCoalescedRefetchScheduler.d.ts.map +1 -0
  9. package/dist/catalog/createCoalescedRefetchScheduler.js +59 -0
  10. package/dist/catalog/createCoalescedRefetchScheduler.js.map +1 -0
  11. package/dist/checkout/resolvePickupHandoffModeForCheckout.d.ts +3 -0
  12. package/dist/checkout/resolvePickupHandoffModeForCheckout.d.ts.map +1 -0
  13. package/dist/checkout/resolvePickupHandoffModeForCheckout.js +18 -0
  14. package/dist/checkout/resolvePickupHandoffModeForCheckout.js.map +1 -0
  15. package/dist/errors/formatRateLimitMessage.d.ts +5 -0
  16. package/dist/errors/formatRateLimitMessage.d.ts.map +1 -0
  17. package/dist/errors/formatRateLimitMessage.js +8 -0
  18. package/dist/errors/formatRateLimitMessage.js.map +1 -0
  19. package/dist/getAnalyticsEventLabel.d.ts +1 -1
  20. package/dist/getAnalyticsEventLabel.d.ts.map +1 -1
  21. package/dist/getAnalyticsEventLabel.js +2 -1
  22. package/dist/getAnalyticsEventLabel.js.map +1 -1
  23. package/dist/hooks/databaseHealthCoordinator.d.ts +31 -0
  24. package/dist/hooks/databaseHealthCoordinator.d.ts.map +1 -0
  25. package/dist/hooks/databaseHealthCoordinator.js +371 -0
  26. package/dist/hooks/databaseHealthCoordinator.js.map +1 -0
  27. package/dist/hooks/useDatabaseHealth.d.ts +2 -6
  28. package/dist/hooks/useDatabaseHealth.d.ts.map +1 -1
  29. package/dist/hooks/useDatabaseHealth.js +22 -143
  30. package/dist/hooks/useDatabaseHealth.js.map +1 -1
  31. package/dist/hooks/useSubmitCooldown.d.ts +11 -0
  32. package/dist/hooks/useSubmitCooldown.d.ts.map +1 -0
  33. package/dist/hooks/useSubmitCooldown.js +46 -0
  34. package/dist/hooks/useSubmitCooldown.js.map +1 -0
  35. package/dist/http/rateLimitError.d.ts +9 -0
  36. package/dist/http/rateLimitError.d.ts.map +1 -0
  37. package/dist/http/rateLimitError.js +97 -0
  38. package/dist/http/rateLimitError.js.map +1 -0
  39. package/dist/index.d.ts +5 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +5 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/sentry/captureRateLimitBreadcrumb.d.ts +12 -0
  44. package/dist/sentry/captureRateLimitBreadcrumb.d.ts.map +1 -0
  45. package/dist/sentry/captureRateLimitBreadcrumb.js +23 -0
  46. package/dist/sentry/captureRateLimitBreadcrumb.js.map +1 -0
  47. package/dist/sentry/initSentry.d.ts +1 -1
  48. package/dist/sentry/initSentry.d.ts.map +1 -1
  49. package/dist/ui/Button/Button.d.ts +18 -0
  50. package/dist/ui/Button/Button.d.ts.map +1 -0
  51. package/dist/ui/Button/Button.js +151 -0
  52. package/dist/ui/Button/Button.js.map +1 -0
  53. package/dist/ui/Card/Card.d.ts +14 -0
  54. package/dist/ui/Card/Card.d.ts.map +1 -0
  55. package/dist/ui/Card/Card.js +56 -0
  56. package/dist/ui/Card/Card.js.map +1 -0
  57. package/dist/ui/FormField/FormField.d.ts +8 -0
  58. package/dist/ui/FormField/FormField.d.ts.map +1 -0
  59. package/dist/ui/FormField/FormField.js +43 -0
  60. package/dist/ui/FormField/FormField.js.map +1 -0
  61. package/dist/ui/LanguageToggle/LanguageToggle.d.ts +8 -0
  62. package/dist/ui/LanguageToggle/LanguageToggle.d.ts.map +1 -0
  63. package/dist/ui/LanguageToggle/LanguageToggle.js +89 -0
  64. package/dist/ui/LanguageToggle/LanguageToggle.js.map +1 -0
  65. package/dist/ui/Turnstile/TurnstileExecuteWidget.d.ts +11 -0
  66. package/dist/ui/Turnstile/TurnstileExecuteWidget.d.ts.map +1 -0
  67. package/dist/ui/Turnstile/TurnstileExecuteWidget.js +28 -0
  68. package/dist/ui/Turnstile/TurnstileExecuteWidget.js.map +1 -0
  69. package/dist/ui/Turnstile/TurnstileWidget.d.ts +9 -0
  70. package/dist/ui/Turnstile/TurnstileWidget.d.ts.map +1 -0
  71. package/dist/ui/Turnstile/TurnstileWidget.js +18 -0
  72. package/dist/ui/Turnstile/TurnstileWidget.js.map +1 -0
  73. package/dist/ui/Turnstile/useTurnstileAuth.d.ts +14 -0
  74. package/dist/ui/Turnstile/useTurnstileAuth.d.ts.map +1 -0
  75. package/dist/ui/Turnstile/useTurnstileAuth.js +35 -0
  76. package/dist/ui/Turnstile/useTurnstileAuth.js.map +1 -0
  77. package/dist/ui/Turnstile/useTurnstileExecute.d.ts +16 -0
  78. package/dist/ui/Turnstile/useTurnstileExecute.d.ts.map +1 -0
  79. package/dist/ui/Turnstile/useTurnstileExecute.js +73 -0
  80. package/dist/ui/Turnstile/useTurnstileExecute.js.map +1 -0
  81. package/dist/ui/index.d.ts +12 -0
  82. package/dist/ui/index.d.ts.map +1 -0
  83. package/dist/ui/index.js +12 -0
  84. package/dist/ui/index.js.map +1 -0
  85. package/package.json +168 -133
  86. package/src/tokens/__tests__/theme-exports.test.mjs +24 -0
  87. package/src/tokens/admin-theme.css +37 -0
  88. package/src/tokens/theme.css +49 -0
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Singleton /health poller — one schedule per endpoint per browser profile.
3
+ *
4
+ * Best practices:
5
+ * - Cross-tab leader via Web Locks (fallback: in-process mutex only)
6
+ * - Pause automatic polls while document is hidden
7
+ * - 429 = rate-limited (keep last known DB state; backoff via Retry-After)
8
+ * - Real outages (5xx / unhealthy JSON / network) use exponential backoff
9
+ * - No overlapping in-flight requests
10
+ */
11
+ import { createCrossTabBus } from '../crossTab/CrossTabBus.js';
12
+ const DEFAULT_HEALTHY_POLL_MS = 120_000;
13
+ const MIN_AUTO_INTERVAL_MS = 60_000;
14
+ const UNHEALTHY_BACKOFF_START_MS = 60_000;
15
+ const UNHEALTHY_BACKOFF_MAX_MS = 300_000;
16
+ const LOCK_STALE_MS = 15_000;
17
+ const endpoints = new Map();
18
+ const healthBus = createCrossTabBus({
19
+ channelName: 'rpapp-database-health',
20
+ });
21
+ healthBus.subscribe((message) => {
22
+ if (message.type === 'health-update') {
23
+ applyRemoteResult(message.endpoint, message.available, message.errorMessage, message.checkedAt);
24
+ }
25
+ });
26
+ function emptySnapshot() {
27
+ return {
28
+ isDatabaseAvailable: true,
29
+ isChecking: false,
30
+ retryCount: 0,
31
+ nextRetryDelay: 0,
32
+ error: null,
33
+ };
34
+ }
35
+ function getOrCreateState(endpoint) {
36
+ const existing = endpoints.get(endpoint);
37
+ if (existing) {
38
+ return existing;
39
+ }
40
+ const created = {
41
+ endpoint,
42
+ subscribers: new Set(),
43
+ snapshot: emptySnapshot(),
44
+ enabled: false,
45
+ pollIntervalMs: DEFAULT_HEALTHY_POLL_MS,
46
+ maxRetries: 5,
47
+ pollTimer: null,
48
+ leaderAbort: null,
49
+ inFlight: false,
50
+ lastCheckAt: 0,
51
+ unhealthyBackoffMs: UNHEALTHY_BACKOFF_START_MS,
52
+ retryCount: 0,
53
+ };
54
+ endpoints.set(endpoint, created);
55
+ return created;
56
+ }
57
+ function notify(state) {
58
+ for (const listener of state.subscribers) {
59
+ listener(state.snapshot);
60
+ }
61
+ }
62
+ function patchSnapshot(state, patch) {
63
+ state.snapshot = { ...state.snapshot, ...patch };
64
+ notify(state);
65
+ }
66
+ function applyRemoteResult(endpoint, available, errorMessage, checkedAt) {
67
+ const state = endpoints.get(endpoint);
68
+ if (!state) {
69
+ return;
70
+ }
71
+ state.lastCheckAt = checkedAt;
72
+ patchSnapshot(state, {
73
+ isDatabaseAvailable: available,
74
+ isChecking: false,
75
+ error: errorMessage ? new Error(errorMessage) : null,
76
+ retryCount: available ? 0 : state.retryCount,
77
+ nextRetryDelay: available ? 0 : state.snapshot.nextRetryDelay,
78
+ });
79
+ if (available) {
80
+ state.retryCount = 0;
81
+ state.unhealthyBackoffMs = UNHEALTHY_BACKOFF_START_MS;
82
+ }
83
+ }
84
+ function parseRetryAfterMs(response) {
85
+ const retryAfter = response.headers.get('retry-after');
86
+ if (retryAfter) {
87
+ const seconds = Number(retryAfter);
88
+ if (Number.isFinite(seconds) && seconds >= 0) {
89
+ return Math.max(seconds * 1000, MIN_AUTO_INTERVAL_MS);
90
+ }
91
+ const dateMs = Date.parse(retryAfter);
92
+ if (!Number.isNaN(dateMs)) {
93
+ return Math.max(dateMs - Date.now(), MIN_AUTO_INTERVAL_MS);
94
+ }
95
+ }
96
+ const reset = response.headers.get('ratelimit-reset');
97
+ if (reset) {
98
+ const resetSeconds = Number(reset);
99
+ if (Number.isFinite(resetSeconds) && resetSeconds >= 0) {
100
+ return Math.max(resetSeconds * 1000, MIN_AUTO_INTERVAL_MS);
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+ function isDocumentHidden() {
106
+ return typeof document !== 'undefined' && document.visibilityState === 'hidden';
107
+ }
108
+ function clearPollTimer(state) {
109
+ if (state.pollTimer !== null) {
110
+ clearTimeout(state.pollTimer);
111
+ state.pollTimer = null;
112
+ }
113
+ }
114
+ function scheduleNextPoll(state, delayMs) {
115
+ clearPollTimer(state);
116
+ if (!state.enabled || state.subscribers.size === 0) {
117
+ return;
118
+ }
119
+ const delay = Math.max(delayMs, MIN_AUTO_INTERVAL_MS);
120
+ patchSnapshot(state, { nextRetryDelay: delay });
121
+ state.pollTimer = setTimeout(() => {
122
+ void performHealthFetch(state);
123
+ }, delay);
124
+ }
125
+ async function performHealthFetch(state) {
126
+ if (state.inFlight) {
127
+ return;
128
+ }
129
+ const now = Date.now();
130
+ if (now - state.lastCheckAt < MIN_AUTO_INTERVAL_MS) {
131
+ return;
132
+ }
133
+ if (isDocumentHidden()) {
134
+ scheduleNextPoll(state, state.pollIntervalMs);
135
+ return;
136
+ }
137
+ state.inFlight = true;
138
+ patchSnapshot(state, { isChecking: true, error: null });
139
+ healthBus.publish({ type: 'health-checking', endpoint: state.endpoint });
140
+ try {
141
+ const response = await fetch(state.endpoint, {
142
+ method: 'GET',
143
+ headers: { Accept: 'application/json' },
144
+ cache: 'no-store',
145
+ });
146
+ if (response.status === 429) {
147
+ const retryMs = parseRetryAfterMs(response) ?? state.pollIntervalMs;
148
+ state.lastCheckAt = Date.now();
149
+ patchSnapshot(state, {
150
+ isChecking: false,
151
+ error: new Error('Health check rate-limited; backing off'),
152
+ });
153
+ scheduleNextPoll(state, retryMs);
154
+ return;
155
+ }
156
+ if (!response.ok) {
157
+ throw new Error(`Health check failed: ${response.status} ${response.statusText}`);
158
+ }
159
+ const contentType = response.headers.get('content-type');
160
+ if (!contentType?.includes('application/json')) {
161
+ throw new Error(`Invalid response: expected JSON, got ${contentType ?? 'unknown'}`);
162
+ }
163
+ const data = (await response.json());
164
+ if (data.success !== true) {
165
+ throw new Error('Database reported unhealthy status');
166
+ }
167
+ state.lastCheckAt = Date.now();
168
+ state.retryCount = 0;
169
+ state.unhealthyBackoffMs = UNHEALTHY_BACKOFF_START_MS;
170
+ patchSnapshot(state, {
171
+ isDatabaseAvailable: true,
172
+ isChecking: false,
173
+ retryCount: 0,
174
+ nextRetryDelay: 0,
175
+ error: null,
176
+ });
177
+ healthBus.publish({
178
+ type: 'health-update',
179
+ endpoint: state.endpoint,
180
+ available: true,
181
+ checkedAt: state.lastCheckAt,
182
+ errorMessage: null,
183
+ });
184
+ scheduleNextPoll(state, state.pollIntervalMs);
185
+ }
186
+ catch (err) {
187
+ const error = err instanceof Error ? err : new Error('Health check failed');
188
+ state.lastCheckAt = Date.now();
189
+ state.retryCount += 1;
190
+ const nextBackoff = Math.min(state.unhealthyBackoffMs * 2, UNHEALTHY_BACKOFF_MAX_MS);
191
+ state.unhealthyBackoffMs = nextBackoff;
192
+ const delayMs = state.retryCount >= state.maxRetries
193
+ ? Math.max(state.pollIntervalMs, nextBackoff)
194
+ : Math.max(nextBackoff, MIN_AUTO_INTERVAL_MS);
195
+ patchSnapshot(state, {
196
+ isDatabaseAvailable: false,
197
+ isChecking: false,
198
+ retryCount: state.retryCount,
199
+ nextRetryDelay: delayMs,
200
+ error,
201
+ });
202
+ healthBus.publish({
203
+ type: 'health-update',
204
+ endpoint: state.endpoint,
205
+ available: false,
206
+ checkedAt: state.lastCheckAt,
207
+ errorMessage: error.message,
208
+ });
209
+ scheduleNextPoll(state, delayMs);
210
+ }
211
+ finally {
212
+ state.inFlight = false;
213
+ }
214
+ }
215
+ async function leaderLoop(state, signal) {
216
+ while (!signal.aborted && state.enabled && state.subscribers.size > 0) {
217
+ if (!isDocumentHidden()) {
218
+ await performHealthFetch(state);
219
+ }
220
+ else {
221
+ scheduleNextPoll(state, state.pollIntervalMs);
222
+ }
223
+ await new Promise((resolve) => {
224
+ const onAbort = () => {
225
+ signal.removeEventListener('abort', onAbort);
226
+ resolve();
227
+ };
228
+ signal.addEventListener('abort', onAbort);
229
+ if (signal.aborted) {
230
+ onAbort();
231
+ }
232
+ });
233
+ if (state.pollTimer !== null) {
234
+ await new Promise((resolve) => {
235
+ const timer = state.pollTimer;
236
+ if (timer === null) {
237
+ resolve();
238
+ return;
239
+ }
240
+ const check = () => {
241
+ if (state.pollTimer !== timer) {
242
+ resolve();
243
+ }
244
+ };
245
+ const interval = setInterval(check, 250);
246
+ const timeout = setTimeout(() => {
247
+ clearInterval(interval);
248
+ resolve();
249
+ }, state.snapshot.nextRetryDelay + LOCK_STALE_MS);
250
+ void timeout;
251
+ });
252
+ }
253
+ }
254
+ }
255
+ function stopLeader(state) {
256
+ clearPollTimer(state);
257
+ state.leaderAbort?.abort();
258
+ state.leaderAbort = null;
259
+ }
260
+ function startLeader(state) {
261
+ if (state.leaderAbort) {
262
+ return;
263
+ }
264
+ const abort = new AbortController();
265
+ state.leaderAbort = abort;
266
+ const run = async () => {
267
+ if (typeof navigator !== 'undefined' && 'locks' in navigator && navigator.locks) {
268
+ try {
269
+ await navigator.locks.request(`rpapp-database-health:${state.endpoint}`, { signal: abort.signal }, async () => {
270
+ await leaderLoop(state, abort.signal);
271
+ });
272
+ }
273
+ catch {
274
+ if (!abort.signal.aborted) {
275
+ await leaderLoop(state, abort.signal);
276
+ }
277
+ }
278
+ }
279
+ else {
280
+ await leaderLoop(state, abort.signal);
281
+ }
282
+ state.leaderAbort = null;
283
+ };
284
+ void run();
285
+ }
286
+ function onVisibilityChange(endpoint) {
287
+ const state = endpoints.get(endpoint);
288
+ if (!state || !state.enabled) {
289
+ return;
290
+ }
291
+ if (!isDocumentHidden()) {
292
+ void performHealthFetch(state);
293
+ }
294
+ }
295
+ const visibilityHandlers = new Map();
296
+ function ensureVisibilityListener(endpoint) {
297
+ if (typeof document === 'undefined' || visibilityHandlers.has(endpoint)) {
298
+ return;
299
+ }
300
+ const handler = () => {
301
+ onVisibilityChange(endpoint);
302
+ };
303
+ visibilityHandlers.set(endpoint, handler);
304
+ document.addEventListener('visibilitychange', handler);
305
+ }
306
+ function removeVisibilityListener(endpoint) {
307
+ const handler = visibilityHandlers.get(endpoint);
308
+ if (!handler || typeof document === 'undefined') {
309
+ return;
310
+ }
311
+ document.removeEventListener('visibilitychange', handler);
312
+ visibilityHandlers.delete(endpoint);
313
+ }
314
+ export function subscribeDatabaseHealth(input, listener) {
315
+ const state = getOrCreateState(input.endpoint);
316
+ state.enabled = input.enabled ?? true;
317
+ state.pollIntervalMs = input.pollInterval ?? DEFAULT_HEALTHY_POLL_MS;
318
+ state.maxRetries = input.maxRetries ?? 5;
319
+ state.subscribers.add(listener);
320
+ listener(state.snapshot);
321
+ ensureVisibilityListener(input.endpoint);
322
+ if (state.subscribers.size === 1 && state.enabled) {
323
+ startLeader(state);
324
+ void performHealthFetch(state);
325
+ }
326
+ return () => {
327
+ state.subscribers.delete(listener);
328
+ if (state.subscribers.size === 0) {
329
+ state.enabled = false;
330
+ stopLeader(state);
331
+ endpoints.delete(input.endpoint);
332
+ removeVisibilityListener(input.endpoint);
333
+ }
334
+ };
335
+ }
336
+ export async function requestDatabaseHealthCheck(endpoint, options) {
337
+ const state = endpoints.get(endpoint);
338
+ if (!state) {
339
+ return;
340
+ }
341
+ if (!options?.force && Date.now() - state.lastCheckAt < MIN_AUTO_INTERVAL_MS) {
342
+ return;
343
+ }
344
+ await performHealthFetch(state);
345
+ }
346
+ export function resolveDatabaseHealthEndpoint() {
347
+ if (typeof window === 'undefined') {
348
+ return 'http://localhost:3015/health';
349
+ }
350
+ const windowConfig = window.__RUNTIME_CONFIG__;
351
+ const apiUrl = windowConfig?.apiUrl?.trim();
352
+ if (apiUrl) {
353
+ try {
354
+ return `${new URL(apiUrl).origin}/health`;
355
+ }
356
+ catch {
357
+ return `${apiUrl.replace(/\/+$/, '')}/health`;
358
+ }
359
+ }
360
+ return `${window.location.origin}/health`;
361
+ }
362
+ /** Test-only reset */
363
+ export function resetDatabaseHealthCoordinatorForTests() {
364
+ for (const state of endpoints.values()) {
365
+ stopLeader(state);
366
+ removeVisibilityListener(state.endpoint);
367
+ }
368
+ endpoints.clear();
369
+ healthBus.close();
370
+ }
371
+ //# sourceMappingURL=databaseHealthCoordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"databaseHealthCoordinator.js","sourceRoot":"","sources":["../../src/hooks/databaseHealthCoordinator.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,8 @@
1
1
  /**
2
2
  * Database Health Check Hook
3
3
  *
4
- * Polls backend health endpoint to determine database availability.
5
- * Features:
6
- * - At most one request in flight at a time (prevents duplicate/burst requests)
7
- * - Minimum 15s between automatic checks when unhealthy (prevents rapid retry storm)
8
- * - Poll interval when healthy: 60s default (1 request/min)
9
- * - User-triggered retry is not throttled (only automatic checks use the 15s minimum interval)
4
+ * Delegates to a singleton coordinator (one poll schedule per /health endpoint).
5
+ * See databaseHealthCoordinator.ts for cross-tab, visibility, and 429 handling.
10
6
  */
11
7
  interface UseDatabaseHealthOptions {
12
8
  pollInterval?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"useDatabaseHealth.d.ts","sourceRoot":"","sources":["../../src/hooks/useDatabaseHealth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH,UAAU,wBAAwB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,uBAAuB,CAwKzB"}
1
+ {"version":3,"file":"useDatabaseHealth.d.ts","sourceRoot":"","sources":["../../src/hooks/useDatabaseHealth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,UAAU,wBAAwB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,uBAAuB,CAoCzB"}
@@ -1,156 +1,35 @@
1
1
  /**
2
2
  * Database Health Check Hook
3
3
  *
4
- * Polls backend health endpoint to determine database availability.
5
- * Features:
6
- * - At most one request in flight at a time (prevents duplicate/burst requests)
7
- * - Minimum 15s between automatic checks when unhealthy (prevents rapid retry storm)
8
- * - Poll interval when healthy: 60s default (1 request/min)
9
- * - User-triggered retry is not throttled (only automatic checks use the 15s minimum interval)
4
+ * Delegates to a singleton coordinator (one poll schedule per /health endpoint).
5
+ * See databaseHealthCoordinator.ts for cross-tab, visibility, and 429 handling.
10
6
  */
11
7
  import { useState, useEffect, useCallback, useRef } from 'react';
12
- const MIN_AUTO_INTERVAL_MS = 15000; // Minimum 15s between automatic checks to avoid hammering /health
8
+ import { requestDatabaseHealthCheck, resolveDatabaseHealthEndpoint, subscribeDatabaseHealth, } from './databaseHealthCoordinator.js';
13
9
  export function useDatabaseHealth(options = {}) {
14
- const { pollInterval = 60000, maxRetries = 5, retryDelay = 1000, enabled = true, } = options;
15
- const [isDatabaseAvailable, setIsDatabaseAvailable] = useState(true);
16
- const [isChecking, setIsChecking] = useState(false);
17
- const [retryCount, setRetryCount] = useState(0);
18
- const [nextRetryDelay, setNextRetryDelay] = useState(0);
19
- const [error, setError] = useState(null);
20
- const [healthEndpoint] = useState(() => {
21
- if (typeof window === 'undefined') {
22
- return 'http://localhost:3015/health';
23
- }
24
- const pageOrigin = window.location.origin;
25
- const windowConfig = window.__RUNTIME_CONFIG__;
26
- const apiUrl = windowConfig?.apiUrl?.trim();
27
- if (apiUrl) {
28
- try {
29
- return `${new URL(apiUrl).origin}/health`;
30
- }
31
- catch {
32
- return `${apiUrl.replace(/\/+$/, '')}/health`;
33
- }
34
- }
35
- return `${pageOrigin}/health`;
10
+ const { pollInterval = 120_000, maxRetries = 5, enabled = true, } = options;
11
+ const endpointRef = useRef(resolveDatabaseHealthEndpoint());
12
+ const [snapshot, setSnapshot] = useState({
13
+ isDatabaseAvailable: true,
14
+ isChecking: false,
15
+ retryCount: 0,
16
+ nextRetryDelay: 0,
17
+ error: null,
36
18
  });
37
- const retryCountRef = useRef(0);
38
- const currentDelayRef = useRef(retryDelay);
39
- const pollTimeoutRef = useRef(null);
40
- const retryTimeoutRef = useRef(null);
41
- const checkHealthRef = useRef(null);
42
- const isCheckingRef = useRef(false);
43
- const lastCheckTimeRef = useRef(0);
44
- const checkHealth = useCallback(async () => {
45
- if (!enabled) {
46
- return;
47
- }
48
- // Prevent overlapping requests (e.g. Strict Mode double-mount or poll + retry at once)
49
- if (isCheckingRef.current) {
50
- return;
51
- }
52
- isCheckingRef.current = true;
53
- setIsChecking(true);
54
- setError(null);
55
- try {
56
- const response = await fetch(healthEndpoint, {
57
- method: 'GET',
58
- headers: { 'Content-Type': 'application/json' },
59
- cache: 'no-cache',
60
- });
61
- if (!response.ok) {
62
- throw new Error(`Health check failed: ${response.status} ${response.statusText}`);
63
- }
64
- const contentType = response.headers.get('content-type');
65
- if (!contentType?.includes('application/json')) {
66
- throw new Error(`Invalid response: expected JSON, got ${contentType}`);
67
- }
68
- const data = (await response.json());
69
- if (data.success === true) {
70
- setIsDatabaseAvailable(true);
71
- setRetryCount(0);
72
- setNextRetryDelay(0);
73
- retryCountRef.current = 0;
74
- currentDelayRef.current = retryDelay;
75
- }
76
- else {
77
- throw new Error('Database reported unhealthy status');
78
- }
79
- }
80
- catch (err) {
81
- const e = err instanceof Error ? err : new Error('Health check failed');
82
- setError(e);
83
- setIsDatabaseAvailable(false);
84
- const newRetryCount = retryCountRef.current + 1;
85
- retryCountRef.current = newRetryCount;
86
- setRetryCount(newRetryCount);
87
- const nextDelay = Math.min(currentDelayRef.current * 2, retryDelay * 30);
88
- currentDelayRef.current = nextDelay;
89
- const delayMs = Math.max(nextDelay, MIN_AUTO_INTERVAL_MS);
90
- setNextRetryDelay(delayMs);
91
- if (newRetryCount < maxRetries) {
92
- retryTimeoutRef.current = setTimeout(() => {
93
- void checkHealthRef.current?.();
94
- }, delayMs);
95
- }
96
- else {
97
- retryTimeoutRef.current = setTimeout(() => {
98
- void checkHealthRef.current?.();
99
- }, Math.max(pollInterval, MIN_AUTO_INTERVAL_MS));
100
- }
101
- }
102
- finally {
103
- lastCheckTimeRef.current = Date.now();
104
- isCheckingRef.current = false;
105
- setIsChecking(false);
106
- }
107
- }, [enabled, healthEndpoint, maxRetries, retryDelay, pollInterval]);
108
- checkHealthRef.current = checkHealth;
109
19
  useEffect(() => {
110
- if (!enabled) {
111
- return;
112
- }
113
- const doCheck = () => {
114
- const now = Date.now();
115
- if (now - lastCheckTimeRef.current < MIN_AUTO_INTERVAL_MS) {
116
- return;
117
- }
118
- if (checkHealthRef.current) {
119
- void checkHealthRef.current();
120
- }
121
- };
122
- const startPolling = () => {
123
- if (pollTimeoutRef.current) {
124
- clearTimeout(pollTimeoutRef.current);
125
- }
126
- pollTimeoutRef.current = setTimeout(() => {
127
- doCheck();
128
- startPolling();
129
- }, pollInterval);
130
- };
131
- doCheck();
132
- startPolling();
133
- return () => {
134
- if (pollTimeoutRef.current) {
135
- clearTimeout(pollTimeoutRef.current);
136
- pollTimeoutRef.current = null;
137
- }
138
- if (retryTimeoutRef.current) {
139
- clearTimeout(retryTimeoutRef.current);
140
- retryTimeoutRef.current = null;
141
- }
142
- };
143
- }, [enabled, healthEndpoint, pollInterval]);
144
- const checkHealthPublic = useCallback(() => {
145
- return checkHealthRef.current?.() ?? Promise.resolve();
20
+ const endpoint = endpointRef.current;
21
+ return subscribeDatabaseHealth({ endpoint, enabled, pollInterval, maxRetries }, setSnapshot);
22
+ }, [enabled, pollInterval, maxRetries]);
23
+ const checkHealth = useCallback(async () => {
24
+ await requestDatabaseHealthCheck(endpointRef.current, { force: true });
146
25
  }, []);
147
26
  return {
148
- isDatabaseAvailable,
149
- isChecking,
150
- retryCount,
151
- nextRetryDelay,
152
- error,
153
- checkHealth: checkHealthPublic,
27
+ isDatabaseAvailable: snapshot.isDatabaseAvailable,
28
+ isChecking: snapshot.isChecking,
29
+ retryCount: snapshot.retryCount,
30
+ nextRetryDelay: snapshot.nextRetryDelay,
31
+ error: snapshot.error,
32
+ checkHealth,
154
33
  };
155
34
  }
156
35
  //# sourceMappingURL=useDatabaseHealth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDatabaseHealth.js","sourceRoot":"","sources":["../../src/hooks/useDatabaseHealth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useDatabaseHealth.js","sourceRoot":"","sources":["../../src/hooks/useDatabaseHealth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAEL,0BAA0B,EAC1B,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,gCAAgC,CAAC;AAkBxC,MAAM,UAAU,iBAAiB,CAC/B,UAAoC,EAAE;IAEtC,MAAM,EACJ,YAAY,GAAG,OAAO,EACtB,UAAU,GAAG,CAAC,EACd,OAAO,GAAG,IAAI,GACf,GAAG,OAAO,CAAC;IAEZ,MAAM,WAAW,GAAG,MAAM,CAAC,6BAA6B,EAAE,CAAC,CAAC;IAC5D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAA4B;QAClE,mBAAmB,EAAE,IAAI;QACzB,UAAU,EAAE,KAAK;QACjB,UAAU,EAAE,CAAC;QACb,cAAc,EAAE,CAAC;QACjB,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC;QACrC,OAAO,uBAAuB,CAC5B,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,EAC/C,WAAW,CACZ,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;IAExC,MAAM,WAAW,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACxD,MAAM,0BAA0B,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACzE,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO;QACL,mBAAmB,EAAE,QAAQ,CAAC,mBAAmB;QACjD,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,WAAW;KACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface UseSubmitCooldownResult {
2
+ readonly isCoolingDown: boolean;
3
+ readonly remainingSeconds: number;
4
+ readonly startCooldown: (durationMs: number) => void;
5
+ readonly clearCooldown: () => void;
6
+ }
7
+ /**
8
+ * Prevents double-tab / rapid repeat submits after rate-limit or mutation errors.
9
+ */
10
+ export declare function useSubmitCooldown(): UseSubmitCooldownResult;
11
+ //# sourceMappingURL=useSubmitCooldown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSubmitCooldown.d.ts","sourceRoot":"","sources":["../../src/hooks/useSubmitCooldown.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,QAAQ,CAAC,aAAa,EAAE,MAAM,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,uBAAuB,CA8C3D"}