pi-kiosk-shared 2.1.86 → 2.1.88
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/analyticsCatalogVersion.d.ts +2 -1
- package/dist/analyticsCatalogVersion.d.ts.map +1 -1
- package/dist/analyticsCatalogVersion.js +1 -0
- package/dist/analyticsCatalogVersion.js.map +1 -1
- package/dist/analyticsEmitterManifest.js +1 -1
- package/dist/analyticsEmitterManifest.js.map +1 -1
- package/dist/analyticsEventLabels.d.ts +1 -1
- package/dist/analyticsEventLabels.d.ts.map +1 -1
- package/dist/analyticsEventLabels.js +83 -5
- package/dist/analyticsEventLabels.js.map +1 -1
- package/dist/analyticsIngestOutboxPolicy.d.ts +19 -0
- package/dist/analyticsIngestOutboxPolicy.d.ts.map +1 -0
- package/dist/analyticsIngestOutboxPolicy.js +78 -0
- package/dist/analyticsIngestOutboxPolicy.js.map +1 -0
- package/dist/auditEventCodes.d.ts +1 -1
- package/dist/auditEventCodes.d.ts.map +1 -1
- package/dist/auditEventCodes.js +1 -0
- package/dist/auditEventCodes.js.map +1 -1
- package/dist/auditEventDescriptions.d.ts.map +1 -1
- package/dist/auditEventDescriptions.js +4 -0
- package/dist/auditEventDescriptions.js.map +1 -1
- package/dist/auditEventLabels.d.ts.map +1 -1
- package/dist/auditEventLabels.js +4 -0
- package/dist/auditEventLabels.js.map +1 -1
- package/dist/catalog/createCoalescedRefetchScheduler.d.ts +12 -0
- package/dist/catalog/createCoalescedRefetchScheduler.d.ts.map +1 -0
- package/dist/catalog/createCoalescedRefetchScheduler.js +59 -0
- package/dist/catalog/createCoalescedRefetchScheduler.js.map +1 -0
- package/dist/checkout/resolvePickupHandoffModeForCheckout.d.ts +3 -0
- package/dist/checkout/resolvePickupHandoffModeForCheckout.d.ts.map +1 -0
- package/dist/checkout/resolvePickupHandoffModeForCheckout.js +18 -0
- package/dist/checkout/resolvePickupHandoffModeForCheckout.js.map +1 -0
- package/dist/errors/formatRateLimitMessage.d.ts +5 -0
- package/dist/errors/formatRateLimitMessage.d.ts.map +1 -0
- package/dist/errors/formatRateLimitMessage.js +8 -0
- package/dist/errors/formatRateLimitMessage.js.map +1 -0
- package/dist/getAnalyticsEventLabel.d.ts +1 -1
- package/dist/getAnalyticsEventLabel.d.ts.map +1 -1
- package/dist/getAnalyticsEventLabel.js +2 -1
- package/dist/getAnalyticsEventLabel.js.map +1 -1
- package/dist/hooks/databaseHealthCoordinator.d.ts +31 -0
- package/dist/hooks/databaseHealthCoordinator.d.ts.map +1 -0
- package/dist/hooks/databaseHealthCoordinator.js +371 -0
- package/dist/hooks/databaseHealthCoordinator.js.map +1 -0
- package/dist/hooks/useDatabaseHealth.d.ts +2 -6
- package/dist/hooks/useDatabaseHealth.d.ts.map +1 -1
- package/dist/hooks/useDatabaseHealth.js +22 -143
- package/dist/hooks/useDatabaseHealth.js.map +1 -1
- package/dist/hooks/useSubmitCooldown.d.ts +11 -0
- package/dist/hooks/useSubmitCooldown.d.ts.map +1 -0
- package/dist/hooks/useSubmitCooldown.js +46 -0
- package/dist/hooks/useSubmitCooldown.js.map +1 -0
- package/dist/http/rateLimitError.d.ts +9 -0
- package/dist/http/rateLimitError.d.ts.map +1 -0
- package/dist/http/rateLimitError.js +97 -0
- package/dist/http/rateLimitError.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/sales-point/salesPointPublicConfig.d.ts +20 -0
- package/dist/sales-point/salesPointPublicConfig.d.ts.map +1 -1
- package/dist/sales-point/salesPointPublicConfig.js +18 -0
- package/dist/sales-point/salesPointPublicConfig.js.map +1 -1
- package/dist/sentry/captureRateLimitBreadcrumb.d.ts +12 -0
- package/dist/sentry/captureRateLimitBreadcrumb.d.ts.map +1 -0
- package/dist/sentry/captureRateLimitBreadcrumb.js +23 -0
- package/dist/sentry/captureRateLimitBreadcrumb.js.map +1 -0
- package/dist/sentry/initSentry.d.ts +1 -1
- package/dist/sentry/initSentry.d.ts.map +1 -1
- package/dist/tenant-entitlements/catalog.d.ts +11 -0
- package/dist/tenant-entitlements/catalog.d.ts.map +1 -0
- package/dist/tenant-entitlements/catalog.js +347 -0
- package/dist/tenant-entitlements/catalog.js.map +1 -0
- package/dist/tenant-entitlements/evaluateNavEntitlement.d.ts +14 -0
- package/dist/tenant-entitlements/evaluateNavEntitlement.d.ts.map +1 -0
- package/dist/tenant-entitlements/evaluateNavEntitlement.js +19 -0
- package/dist/tenant-entitlements/evaluateNavEntitlement.js.map +1 -0
- package/dist/tenant-entitlements/evaluatePosture.d.ts +11 -0
- package/dist/tenant-entitlements/evaluatePosture.d.ts.map +1 -0
- package/dist/tenant-entitlements/evaluatePosture.js +21 -0
- package/dist/tenant-entitlements/evaluatePosture.js.map +1 -0
- package/dist/tenant-entitlements/index.d.ts +9 -0
- package/dist/tenant-entitlements/index.d.ts.map +1 -0
- package/dist/tenant-entitlements/index.js +6 -0
- package/dist/tenant-entitlements/index.js.map +1 -0
- package/dist/tenant-entitlements/types.d.ts +63 -0
- package/dist/tenant-entitlements/types.d.ts.map +1 -0
- package/dist/tenant-entitlements/types.js +114 -0
- package/dist/tenant-entitlements/types.js.map +1 -0
- package/dist/ui/Button/Button.d.ts +18 -0
- package/dist/ui/Button/Button.d.ts.map +1 -0
- package/dist/ui/Button/Button.js +151 -0
- package/dist/ui/Button/Button.js.map +1 -0
- package/dist/ui/Card/Card.d.ts +14 -0
- package/dist/ui/Card/Card.d.ts.map +1 -0
- package/dist/ui/Card/Card.js +56 -0
- package/dist/ui/Card/Card.js.map +1 -0
- package/dist/ui/FormField/FormField.d.ts +8 -0
- package/dist/ui/FormField/FormField.d.ts.map +1 -0
- package/dist/ui/FormField/FormField.js +43 -0
- package/dist/ui/FormField/FormField.js.map +1 -0
- package/dist/ui/LanguageToggle/LanguageToggle.d.ts +8 -0
- package/dist/ui/LanguageToggle/LanguageToggle.d.ts.map +1 -0
- package/dist/ui/LanguageToggle/LanguageToggle.js +89 -0
- package/dist/ui/LanguageToggle/LanguageToggle.js.map +1 -0
- package/dist/ui/Turnstile/TurnstileExecuteWidget.d.ts +11 -0
- package/dist/ui/Turnstile/TurnstileExecuteWidget.d.ts.map +1 -0
- package/dist/ui/Turnstile/TurnstileExecuteWidget.js +28 -0
- package/dist/ui/Turnstile/TurnstileExecuteWidget.js.map +1 -0
- package/dist/ui/Turnstile/TurnstileWidget.d.ts +9 -0
- package/dist/ui/Turnstile/TurnstileWidget.d.ts.map +1 -0
- package/dist/ui/Turnstile/TurnstileWidget.js +18 -0
- package/dist/ui/Turnstile/TurnstileWidget.js.map +1 -0
- package/dist/ui/Turnstile/useTurnstileAuth.d.ts +14 -0
- package/dist/ui/Turnstile/useTurnstileAuth.d.ts.map +1 -0
- package/dist/ui/Turnstile/useTurnstileAuth.js +35 -0
- package/dist/ui/Turnstile/useTurnstileAuth.js.map +1 -0
- package/dist/ui/Turnstile/useTurnstileExecute.d.ts +16 -0
- package/dist/ui/Turnstile/useTurnstileExecute.d.ts.map +1 -0
- package/dist/ui/Turnstile/useTurnstileExecute.js +73 -0
- package/dist/ui/Turnstile/useTurnstileExecute.js.map +1 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +12 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +178 -133
- package/src/tokens/__tests__/theme-exports.test.mjs +24 -0
- package/src/tokens/admin-theme.css +37 -0
- package/src/tokens/theme.css +49 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Localized rate-limit user message with optional retry countdown.
|
|
3
|
+
*/
|
|
4
|
+
export function formatRateLimitMessage(translate, retryAfterSeconds) {
|
|
5
|
+
const seconds = Math.max(retryAfterSeconds, 1);
|
|
6
|
+
return translate('auth.rateLimit.retryAfter', { seconds });
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=formatRateLimitMessage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatRateLimitMessage.js","sourceRoot":"","sources":["../../src/errors/formatRateLimitMessage.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAAqE,EACrE,iBAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC;IAC/C,OAAO,SAAS,CAAC,2BAA2B,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type LabelAudience, type LabelLocale } from './labels/localizedLabel.js';
|
|
2
2
|
export declare function getAnalyticsEventLabel(name: string, locale: LabelLocale, _audience?: LabelAudience): string;
|
|
3
3
|
//# sourceMappingURL=getAnalyticsEventLabel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getAnalyticsEventLabel.d.ts","sourceRoot":"","sources":["../src/getAnalyticsEventLabel.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"getAnalyticsEventLabel.d.ts","sourceRoot":"","sources":["../src/getAnalyticsEventLabel.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEpG,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,SAAS,GAAE,aAA0B,GACpC,MAAM,CAKR"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { isAnalyticsEventName } from './analyticsEvents.js';
|
|
2
2
|
import { ANALYTICS_EVENT_LABELS } from './analyticsEventLabels.js';
|
|
3
|
+
import { snakeCaseToLabel } from './labels/localizedLabel.js';
|
|
3
4
|
export function getAnalyticsEventLabel(name, locale, _audience = 'operator') {
|
|
4
5
|
if (!isAnalyticsEventName(name)) {
|
|
5
|
-
return name;
|
|
6
|
+
return snakeCaseToLabel(name)[locale];
|
|
6
7
|
}
|
|
7
8
|
return ANALYTICS_EVENT_LABELS[name][locale];
|
|
8
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getAnalyticsEventLabel.js","sourceRoot":"","sources":["../src/getAnalyticsEventLabel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAA2B,MAAM,sBAAsB,CAAC;AACrF,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"getAnalyticsEventLabel.js","sourceRoot":"","sources":["../src/getAnalyticsEventLabel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAA2B,MAAM,sBAAsB,CAAC;AACrF,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAwC,MAAM,4BAA4B,CAAC;AAEpG,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,MAAmB,EACnB,YAA2B,UAAU;IAErC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,sBAAsB,CAAC,IAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
export interface HealthCoordinatorSnapshot {
|
|
12
|
+
readonly isDatabaseAvailable: boolean;
|
|
13
|
+
readonly isChecking: boolean;
|
|
14
|
+
readonly retryCount: number;
|
|
15
|
+
readonly nextRetryDelay: number;
|
|
16
|
+
readonly error: Error | null;
|
|
17
|
+
}
|
|
18
|
+
export interface SubscribeDatabaseHealthInput {
|
|
19
|
+
readonly endpoint: string;
|
|
20
|
+
readonly enabled?: boolean;
|
|
21
|
+
readonly pollInterval?: number;
|
|
22
|
+
readonly maxRetries?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function subscribeDatabaseHealth(input: SubscribeDatabaseHealthInput, listener: (snapshot: HealthCoordinatorSnapshot) => void): () => void;
|
|
25
|
+
export declare function requestDatabaseHealthCheck(endpoint: string, options?: {
|
|
26
|
+
readonly force?: boolean;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
export declare function resolveDatabaseHealthEndpoint(): string;
|
|
29
|
+
/** Test-only reset */
|
|
30
|
+
export declare function resetDatabaseHealthCoordinatorForTests(): void;
|
|
31
|
+
//# sourceMappingURL=databaseHealthCoordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"databaseHealthCoordinator.d.ts","sourceRoot":"","sources":["../../src/hooks/databaseHealthCoordinator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC9B;AA+WD,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,4BAA4B,EACnC,QAAQ,EAAE,CAAC,QAAQ,EAAE,yBAAyB,KAAK,IAAI,GACtD,MAAM,IAAI,CAwBZ;AAED,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACrC,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CActD;AAED,sBAAsB;AACtB,wBAAgB,sCAAsC,IAAI,IAAI,CAO7D"}
|
|
@@ -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
|
-
*
|
|
5
|
-
*
|
|
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
|
|
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"}
|