@spidy092/auth-client 3.0.8 โ 3.1.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/api.cjs +87 -4
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +87 -4
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +465 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +452 -33
- package/dist/index.js.map +1 -1
- package/dist/preferences.cjs +657 -0
- package/dist/preferences.cjs.map +1 -0
- package/dist/preferences.js +608 -0
- package/dist/preferences.js.map +1 -0
- package/dist/react/AuthProvider.cjs +143 -30
- package/dist/react/AuthProvider.cjs.map +1 -1
- package/dist/react/AuthProvider.js +143 -30
- package/dist/react/AuthProvider.js.map +1 -1
- package/dist/react/PreferencesProvider.cjs +621 -0
- package/dist/react/PreferencesProvider.cjs.map +1 -0
- package/dist/react/PreferencesProvider.js +586 -0
- package/dist/react/PreferencesProvider.js.map +1 -0
- package/dist/react/useAuth.cjs +17 -0
- package/dist/react/useAuth.cjs.map +1 -1
- package/dist/react/useAuth.js +17 -0
- package/dist/react/useAuth.js.map +1 -1
- package/dist/react/useSessionMonitor.cjs +381 -31
- package/dist/react/useSessionMonitor.cjs.map +1 -1
- package/dist/react/useSessionMonitor.js +380 -30
- package/dist/react/useSessionMonitor.js.map +1 -1
- package/dist/utils/jwt.cjs +16 -0
- package/dist/utils/jwt.cjs.map +1 -1
- package/dist/utils/jwt.js +16 -0
- package/dist/utils/jwt.js.map +1 -1
- package/package.json +10 -2
package/dist/index.cjs
CHANGED
|
@@ -30,8 +30,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
var index_exports = {};
|
|
31
31
|
__export(index_exports, {
|
|
32
32
|
AuthProvider: () => AuthProvider,
|
|
33
|
+
PREFERENCE_DEFAULTS: () => PREFERENCE_DEFAULTS,
|
|
34
|
+
PreferencesProvider: () => PreferencesProvider,
|
|
35
|
+
applyPreferences: () => applyPreferences,
|
|
33
36
|
auth: () => auth,
|
|
37
|
+
formatUserDate: () => formatUserDate,
|
|
38
|
+
formatUserNumber: () => formatUserNumber,
|
|
39
|
+
formatUserRelativeTime: () => formatUserRelativeTime,
|
|
40
|
+
getPreferences: () => getPreferences,
|
|
41
|
+
normalizePreferences: () => normalizePreferences,
|
|
42
|
+
resolveTheme: () => resolveTheme,
|
|
43
|
+
startPreferenceSync: () => startPreferenceSync,
|
|
44
|
+
subscribePreferences: () => subscribePreferences,
|
|
45
|
+
updatePreferences: () => updatePreferences,
|
|
34
46
|
useAuth: () => useAuth,
|
|
47
|
+
usePreferences: () => usePreferences,
|
|
35
48
|
useSessionMonitor: () => useSessionMonitor
|
|
36
49
|
});
|
|
37
50
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -207,6 +220,21 @@ function removeTokenListener(listener) {
|
|
|
207
220
|
function getListenerCount() {
|
|
208
221
|
return listeners.size;
|
|
209
222
|
}
|
|
223
|
+
if (typeof window !== "undefined" && window.addEventListener) {
|
|
224
|
+
window.addEventListener("storage", (event) => {
|
|
225
|
+
if (event.key !== "authToken") return;
|
|
226
|
+
const previousToken = accessToken;
|
|
227
|
+
accessToken = event.newValue || null;
|
|
228
|
+
if (previousToken === accessToken) return;
|
|
229
|
+
listeners.forEach((listener) => {
|
|
230
|
+
try {
|
|
231
|
+
listener(accessToken, previousToken);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.warn("Token listener error:", err);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
210
238
|
|
|
211
239
|
// config.js
|
|
212
240
|
var config = {
|
|
@@ -251,8 +279,8 @@ function setConfig(customConfig = {}) {
|
|
|
251
279
|
// โ
Auto-detect router mode
|
|
252
280
|
isRouter: customConfig.isRouter || customConfig.clientKey === "account-ui"
|
|
253
281
|
};
|
|
282
|
+
enableRefreshTokenPersistence(config.persistRefreshToken);
|
|
254
283
|
if (config.persistRefreshToken) {
|
|
255
|
-
enableRefreshTokenPersistence(true);
|
|
256
284
|
console.log("\u{1F4E6} Refresh token persistence ENABLED (localStorage on HTTPS)");
|
|
257
285
|
}
|
|
258
286
|
console.log(`\u{1F527} Auth Client Mode: ${config.isRouter ? "ROUTER" : "CLIENT"}`, {
|
|
@@ -268,6 +296,82 @@ function isRouterMode() {
|
|
|
268
296
|
return config.isRouter;
|
|
269
297
|
}
|
|
270
298
|
|
|
299
|
+
// diagnostics.js
|
|
300
|
+
var STORAGE_KEY = "auth_diagnostic_context";
|
|
301
|
+
var LOGIN_LOCK_KEY = "auth_login_lock";
|
|
302
|
+
var randomId = () => {
|
|
303
|
+
var _a;
|
|
304
|
+
if ((_a = globalThis.crypto) == null ? void 0 : _a.randomUUID) return globalThis.crypto.randomUUID();
|
|
305
|
+
return `auth-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
306
|
+
};
|
|
307
|
+
var fingerprint = async (value) => {
|
|
308
|
+
var _a;
|
|
309
|
+
if (!value || !((_a = globalThis.crypto) == null ? void 0 : _a.subtle)) return null;
|
|
310
|
+
const bytes = new TextEncoder().encode(String(value));
|
|
311
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
312
|
+
return [...new Uint8Array(digest)].slice(0, 6).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
313
|
+
};
|
|
314
|
+
function getDiagnosticContext() {
|
|
315
|
+
try {
|
|
316
|
+
const existing = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || "null");
|
|
317
|
+
if (existing == null ? void 0 : existing.correlationId) return existing;
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
const context = { correlationId: randomId(), startedAt: Date.now() };
|
|
321
|
+
try {
|
|
322
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(context));
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
return context;
|
|
326
|
+
}
|
|
327
|
+
function resetDiagnosticContext() {
|
|
328
|
+
const context = { correlationId: randomId(), startedAt: Date.now() };
|
|
329
|
+
try {
|
|
330
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(context));
|
|
331
|
+
} catch {
|
|
332
|
+
}
|
|
333
|
+
return context;
|
|
334
|
+
}
|
|
335
|
+
function diagnosticHeaders() {
|
|
336
|
+
const { correlationId } = getDiagnosticContext();
|
|
337
|
+
return { "X-Correlation-ID": correlationId, "X-Request-ID": correlationId };
|
|
338
|
+
}
|
|
339
|
+
function acquireLoginLock(clientKey, redirectUri, ttlMs = 5e3) {
|
|
340
|
+
const now = Date.now();
|
|
341
|
+
const signature = `${clientKey}|${redirectUri}`;
|
|
342
|
+
try {
|
|
343
|
+
const current = JSON.parse(sessionStorage.getItem(LOGIN_LOCK_KEY) || "null");
|
|
344
|
+
if ((current == null ? void 0 : current.signature) === signature && now - current.createdAt < ttlMs) return false;
|
|
345
|
+
sessionStorage.setItem(LOGIN_LOCK_KEY, JSON.stringify({ signature, createdAt: now }));
|
|
346
|
+
} catch {
|
|
347
|
+
}
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
function clearLoginLock() {
|
|
351
|
+
try {
|
|
352
|
+
sessionStorage.removeItem(LOGIN_LOCK_KEY);
|
|
353
|
+
} catch {
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
357
|
+
const context = getDiagnosticContext();
|
|
358
|
+
const safe = {
|
|
359
|
+
eventType: "auth_client_diagnostic",
|
|
360
|
+
event,
|
|
361
|
+
outcome,
|
|
362
|
+
reasonCode,
|
|
363
|
+
correlationId: context.correlationId,
|
|
364
|
+
elapsedMs: Date.now() - context.startedAt,
|
|
365
|
+
clientKey: details.clientKey || null,
|
|
366
|
+
status: details.status || null,
|
|
367
|
+
stateFingerprint: await fingerprint(details.state),
|
|
368
|
+
online: typeof navigator === "undefined" ? null : navigator.onLine
|
|
369
|
+
};
|
|
370
|
+
const method = outcome === "FAILURE" ? "error" : outcome === "WARNING" ? "warn" : "info";
|
|
371
|
+
console[method]("[auth-client]", safe);
|
|
372
|
+
return safe;
|
|
373
|
+
}
|
|
374
|
+
|
|
271
375
|
// core.js
|
|
272
376
|
var callbackProcessed = false;
|
|
273
377
|
function login(clientKeyArg, redirectUriArg) {
|
|
@@ -280,14 +384,16 @@ function login(clientKeyArg, redirectUriArg) {
|
|
|
280
384
|
} = getConfig();
|
|
281
385
|
const clientKey = clientKeyArg || defaultClientKey;
|
|
282
386
|
const redirectUri = redirectUriArg || defaultRedirectUri;
|
|
283
|
-
console.log("\u{1F504} Smart Login initiated:", {
|
|
284
|
-
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
285
|
-
clientKey,
|
|
286
|
-
redirectUri
|
|
287
|
-
});
|
|
288
387
|
if (!clientKey || !redirectUri) {
|
|
388
|
+
emitAuthDiagnostic("LOGIN_REJECTED", "FAILURE", "CLIENT_CONFIG_MISSING", { clientKey });
|
|
289
389
|
throw new Error("Missing clientKey or redirectUri");
|
|
290
390
|
}
|
|
391
|
+
if (!acquireLoginLock(clientKey, redirectUri)) {
|
|
392
|
+
emitAuthDiagnostic("LOGIN_DUPLICATE_SUPPRESSED", "WARNING", "LOGIN_ALREADY_IN_PROGRESS", { clientKey });
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
resetDiagnosticContext();
|
|
396
|
+
emitAuthDiagnostic("LOGIN_INITIATED", "PENDING", "NONE", { clientKey });
|
|
291
397
|
sessionStorage.setItem("originalApp", clientKey);
|
|
292
398
|
sessionStorage.setItem("returnUrl", redirectUri);
|
|
293
399
|
if (isRouterMode()) {
|
|
@@ -302,13 +408,9 @@ function routerLogin(clientKey, redirectUri) {
|
|
|
302
408
|
if (redirectUri) {
|
|
303
409
|
params.append("redirect_uri", redirectUri);
|
|
304
410
|
}
|
|
411
|
+
params.append("correlation_id", getDiagnosticContext().correlationId);
|
|
305
412
|
const query = params.toString();
|
|
306
413
|
const backendLoginUrl = `${authBaseUrl}/login/${clientKey}${query ? `?${query}` : ""}`;
|
|
307
|
-
console.log("\u{1F3ED} Router Login: Direct backend authentication", {
|
|
308
|
-
clientKey,
|
|
309
|
-
redirectUri,
|
|
310
|
-
backendUrl: backendLoginUrl
|
|
311
|
-
});
|
|
312
414
|
window.location.href = backendLoginUrl;
|
|
313
415
|
}
|
|
314
416
|
function clientLogin(clientKey, redirectUri) {
|
|
@@ -320,19 +422,19 @@ function clientLogin(clientKey, redirectUri) {
|
|
|
320
422
|
params.append("redirect_uri", redirectUri);
|
|
321
423
|
}
|
|
322
424
|
const centralizedLoginUrl = `${accountUiUrl}/login?${params.toString()}`;
|
|
323
|
-
console.log("\u{1F504} Client Login: Redirecting to centralized login", {
|
|
324
|
-
clientKey,
|
|
325
|
-
redirectUri,
|
|
326
|
-
centralizedUrl: centralizedLoginUrl
|
|
327
|
-
});
|
|
328
425
|
window.location.href = centralizedLoginUrl;
|
|
329
426
|
}
|
|
330
|
-
async function logout() {
|
|
427
|
+
async function logout(options = {}) {
|
|
331
428
|
resetCallbackState();
|
|
332
429
|
const { clientKey, authBaseUrl, accountUiUrl } = getConfig();
|
|
430
|
+
const scope = options.scope === "client" ? "client" : "sso";
|
|
333
431
|
const token = getToken();
|
|
334
432
|
const refreshToken2 = getRefreshToken();
|
|
335
|
-
console.log("\u{1F6AA} Smart Logout initiated", {
|
|
433
|
+
console.log("\u{1F6AA} Smart Logout initiated", {
|
|
434
|
+
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
435
|
+
clientKey,
|
|
436
|
+
scope
|
|
437
|
+
});
|
|
336
438
|
clearToken();
|
|
337
439
|
clearRefreshToken();
|
|
338
440
|
sessionStorage.removeItem("originalApp");
|
|
@@ -342,21 +444,33 @@ async function logout() {
|
|
|
342
444
|
method: "POST",
|
|
343
445
|
credentials: "include",
|
|
344
446
|
headers: {
|
|
447
|
+
...diagnosticHeaders(),
|
|
345
448
|
"Authorization": token ? `Bearer ${token}` : "",
|
|
346
449
|
"Content-Type": "application/json"
|
|
347
450
|
},
|
|
348
|
-
body: JSON.stringify({ refreshToken: refreshToken2 })
|
|
451
|
+
body: JSON.stringify({ refreshToken: refreshToken2, scope })
|
|
349
452
|
});
|
|
453
|
+
if (!response.ok) {
|
|
454
|
+
throw new Error(`Logout failed: ${response.status}`);
|
|
455
|
+
}
|
|
350
456
|
const data = await response.json();
|
|
351
457
|
console.log("\u2705 Logout response:", data);
|
|
458
|
+
if (data == null ? void 0 : data.logoutRedirectUrl) {
|
|
459
|
+
window.location.replace(data.logoutRedirectUrl);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
352
462
|
if (data == null ? void 0 : data.keycloakLogoutUrl) {
|
|
353
|
-
window.location.
|
|
463
|
+
window.location.replace(data.keycloakLogoutUrl);
|
|
354
464
|
return;
|
|
355
465
|
}
|
|
356
466
|
} catch (error) {
|
|
357
467
|
console.warn("\u26A0\uFE0F Logout backend call failed:", error);
|
|
358
468
|
}
|
|
359
|
-
|
|
469
|
+
const fallbackUrl = isRouterMode() ? new URL("/login", window.location.origin) : new URL("/login", accountUiUrl);
|
|
470
|
+
fallbackUrl.searchParams.set("logged_out", "true");
|
|
471
|
+
fallbackUrl.searchParams.set("client", clientKey);
|
|
472
|
+
fallbackUrl.searchParams.set("scope", scope);
|
|
473
|
+
window.location.replace(fallbackUrl.toString());
|
|
360
474
|
}
|
|
361
475
|
function handleCallback() {
|
|
362
476
|
var _a;
|
|
@@ -376,11 +490,19 @@ function handleCallback() {
|
|
|
376
490
|
callbackProcessed = false;
|
|
377
491
|
}
|
|
378
492
|
callbackProcessed = true;
|
|
493
|
+
clearLoginLock();
|
|
379
494
|
sessionStorage.removeItem("originalApp");
|
|
380
495
|
sessionStorage.removeItem("returnUrl");
|
|
381
496
|
if (error) {
|
|
382
497
|
const errorDescription = params.get("error_description") || error;
|
|
383
|
-
|
|
498
|
+
const authError = new Error(`Authentication failed: ${errorDescription}`);
|
|
499
|
+
authError.code = error;
|
|
500
|
+
authError.correlationId = getDiagnosticContext().correlationId;
|
|
501
|
+
emitAuthDiagnostic("CALLBACK_REJECTED", "FAILURE", error.toUpperCase(), {
|
|
502
|
+
clientKey: getConfig().clientKey,
|
|
503
|
+
state: params.get("state")
|
|
504
|
+
});
|
|
505
|
+
throw authError;
|
|
384
506
|
}
|
|
385
507
|
if (accessToken2) {
|
|
386
508
|
setToken(accessToken2);
|
|
@@ -403,9 +525,19 @@ function handleCallback() {
|
|
|
403
525
|
url.searchParams.delete("error_description");
|
|
404
526
|
window.history.replaceState({}, "", url);
|
|
405
527
|
console.log("\u2705 Callback processed successfully, token stored");
|
|
528
|
+
emitAuthDiagnostic("CALLBACK_COMPLETED", "SUCCESS", "NONE", {
|
|
529
|
+
clientKey: getConfig().clientKey,
|
|
530
|
+
state: params.get("state")
|
|
531
|
+
});
|
|
406
532
|
return accessToken2;
|
|
407
533
|
}
|
|
408
|
-
|
|
534
|
+
emitAuthDiagnostic("CALLBACK_REJECTED", "FAILURE", "ACCESS_TOKEN_MISSING", {
|
|
535
|
+
clientKey: getConfig().clientKey,
|
|
536
|
+
state: params.get("state")
|
|
537
|
+
});
|
|
538
|
+
const missingTokenError = new Error("No access token found in callback URL");
|
|
539
|
+
missingTokenError.code = "ACCESS_TOKEN_MISSING";
|
|
540
|
+
throw missingTokenError;
|
|
409
541
|
}
|
|
410
542
|
function resetCallbackState() {
|
|
411
543
|
callbackProcessed = false;
|
|
@@ -420,7 +552,7 @@ async function refreshToken() {
|
|
|
420
552
|
}
|
|
421
553
|
refreshInProgress = true;
|
|
422
554
|
refreshPromise = (async () => {
|
|
423
|
-
var _a, _b, _c, _d;
|
|
555
|
+
var _a, _b, _c, _d, _e, _f;
|
|
424
556
|
try {
|
|
425
557
|
const storedRefreshToken = getRefreshToken();
|
|
426
558
|
console.log("\u{1F504} Refreshing token:", {
|
|
@@ -433,6 +565,7 @@ async function refreshToken() {
|
|
|
433
565
|
credentials: "include",
|
|
434
566
|
// โ
Include httpOnly cookies (for HTTPS)
|
|
435
567
|
headers: {
|
|
568
|
+
...diagnosticHeaders(),
|
|
436
569
|
"Content-Type": "application/json"
|
|
437
570
|
}
|
|
438
571
|
};
|
|
@@ -443,8 +576,19 @@ async function refreshToken() {
|
|
|
443
576
|
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
444
577
|
if (!response.ok) {
|
|
445
578
|
const errorText = await response.text();
|
|
446
|
-
|
|
447
|
-
|
|
579
|
+
let serverCode = null;
|
|
580
|
+
try {
|
|
581
|
+
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
582
|
+
} catch {
|
|
583
|
+
}
|
|
584
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
585
|
+
clientKey,
|
|
586
|
+
status: response.status
|
|
587
|
+
});
|
|
588
|
+
const refreshError = new Error(`Refresh failed: ${response.status}`);
|
|
589
|
+
refreshError.code = serverCode || `HTTP_${response.status}`;
|
|
590
|
+
refreshError.status = response.status;
|
|
591
|
+
throw refreshError;
|
|
448
592
|
}
|
|
449
593
|
const data = await response.json();
|
|
450
594
|
const { access_token, refresh_token: new_refresh_token } = data;
|
|
@@ -457,10 +601,11 @@ async function refreshToken() {
|
|
|
457
601
|
console.log("\u{1F504} New refresh token stored from rotation");
|
|
458
602
|
}
|
|
459
603
|
console.log("\u2705 Token refresh successful, listeners notified");
|
|
604
|
+
emitAuthDiagnostic("TOKEN_REFRESH_COMPLETED", "SUCCESS", "NONE", { clientKey });
|
|
460
605
|
return access_token;
|
|
461
606
|
} catch (err) {
|
|
462
607
|
console.error("\u274C Token refresh error:", err);
|
|
463
|
-
const isAuthRejection = ((
|
|
608
|
+
const isAuthRejection = ((_c = err.message) == null ? void 0 : _c.includes("401")) || ((_d = err.message) == null ? void 0 : _d.includes("403")) || ((_e = err.message) == null ? void 0 : _e.includes("invalid_grant")) || ((_f = err.message) == null ? void 0 : _f.includes("Refresh failed: 4"));
|
|
464
609
|
if (isAuthRejection) {
|
|
465
610
|
clearToken();
|
|
466
611
|
clearRefreshToken();
|
|
@@ -483,6 +628,7 @@ async function validateCurrentSession() {
|
|
|
483
628
|
const response = await fetch(`${authBaseUrl}/account/validate-session`, {
|
|
484
629
|
method: "GET",
|
|
485
630
|
headers: {
|
|
631
|
+
...diagnosticHeaders(),
|
|
486
632
|
"Authorization": `Bearer ${token}`,
|
|
487
633
|
"Content-Type": "application/json"
|
|
488
634
|
},
|
|
@@ -735,6 +881,7 @@ api.interceptors.request.use((config2) => {
|
|
|
735
881
|
if ((runtimeConfig == null ? void 0 : runtimeConfig.clientKey) && !config2.headers["X-Client-Key"]) {
|
|
736
882
|
config2.headers["X-Client-Key"] = runtimeConfig.clientKey;
|
|
737
883
|
}
|
|
884
|
+
Object.assign(config2.headers, diagnosticHeaders());
|
|
738
885
|
const token = getToken();
|
|
739
886
|
if (token) {
|
|
740
887
|
config2.headers.Authorization = `Bearer ${token}`;
|
|
@@ -753,6 +900,10 @@ api.interceptors.response.use(
|
|
|
753
900
|
return Promise.reject(error);
|
|
754
901
|
}
|
|
755
902
|
config2._retry = true;
|
|
903
|
+
emitAuthDiagnostic("API_401_REFRESH_STARTED", "PENDING", "HTTP_401", {
|
|
904
|
+
clientKey: getConfig().clientKey,
|
|
905
|
+
status: 401
|
|
906
|
+
});
|
|
756
907
|
if (!refreshPromise2) {
|
|
757
908
|
refreshPromise2 = refreshToken().then((newToken) => {
|
|
758
909
|
refreshPromise2 = null;
|
|
@@ -812,6 +963,208 @@ function isAuthenticated() {
|
|
|
812
963
|
return !!token && !isTokenExpired(token, 10);
|
|
813
964
|
}
|
|
814
965
|
|
|
966
|
+
// preferences.js
|
|
967
|
+
var PREFERENCE_DEFAULTS = Object.freeze({
|
|
968
|
+
theme: "system",
|
|
969
|
+
colorScheme: "blue",
|
|
970
|
+
density: "comfortable",
|
|
971
|
+
reducedMotion: false,
|
|
972
|
+
language: "en",
|
|
973
|
+
timezone: "Etc/UTC",
|
|
974
|
+
dateFormat: "MM/DD/YYYY",
|
|
975
|
+
timeFormat: "12h",
|
|
976
|
+
highContrast: false,
|
|
977
|
+
largeText: false
|
|
978
|
+
});
|
|
979
|
+
var ALLOWED = {
|
|
980
|
+
theme: /* @__PURE__ */ new Set(["light", "dark", "system"]),
|
|
981
|
+
density: /* @__PURE__ */ new Set(["compact", "comfortable", "spacious"]),
|
|
982
|
+
timeFormat: /* @__PURE__ */ new Set(["12h", "24h"])
|
|
983
|
+
};
|
|
984
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
985
|
+
var cachedPreferences = null;
|
|
986
|
+
var pendingRequest = null;
|
|
987
|
+
var syncCleanup = null;
|
|
988
|
+
function extractResponseData(response) {
|
|
989
|
+
var _a;
|
|
990
|
+
return ((_a = response == null ? void 0 : response.data) == null ? void 0 : _a.data) ?? (response == null ? void 0 : response.data) ?? response ?? {};
|
|
991
|
+
}
|
|
992
|
+
function normalizePreferences(value = {}) {
|
|
993
|
+
const next = { ...PREFERENCE_DEFAULTS };
|
|
994
|
+
for (const key of Object.keys(PREFERENCE_DEFAULTS)) {
|
|
995
|
+
if (!Object.hasOwn(value, key)) continue;
|
|
996
|
+
const candidate = value[key];
|
|
997
|
+
if (ALLOWED[key] && !ALLOWED[key].has(candidate)) continue;
|
|
998
|
+
if (typeof PREFERENCE_DEFAULTS[key] !== typeof candidate) continue;
|
|
999
|
+
next[key] = candidate;
|
|
1000
|
+
}
|
|
1001
|
+
return next;
|
|
1002
|
+
}
|
|
1003
|
+
function resolveTheme(theme, matchMedia2 = globalThis.matchMedia) {
|
|
1004
|
+
if (theme === "dark" || theme === "light") return theme;
|
|
1005
|
+
return typeof matchMedia2 === "function" && matchMedia2("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1006
|
+
}
|
|
1007
|
+
function announce(preferences2) {
|
|
1008
|
+
cachedPreferences = normalizePreferences(preferences2);
|
|
1009
|
+
listeners2.forEach((listener) => listener(cachedPreferences));
|
|
1010
|
+
return cachedPreferences;
|
|
1011
|
+
}
|
|
1012
|
+
async function getPreferences({ force = false } = {}) {
|
|
1013
|
+
if (cachedPreferences && !force) return cachedPreferences;
|
|
1014
|
+
if (pendingRequest) return pendingRequest;
|
|
1015
|
+
pendingRequest = api_default.get("/account/preferences").then((response) => announce(extractResponseData(response))).finally(() => {
|
|
1016
|
+
pendingRequest = null;
|
|
1017
|
+
});
|
|
1018
|
+
return pendingRequest;
|
|
1019
|
+
}
|
|
1020
|
+
async function updatePreferences(patch) {
|
|
1021
|
+
const allowedPatch = {};
|
|
1022
|
+
for (const key of Object.keys(PREFERENCE_DEFAULTS)) {
|
|
1023
|
+
if (Object.hasOwn(patch, key)) allowedPatch[key] = patch[key];
|
|
1024
|
+
}
|
|
1025
|
+
const candidate = normalizePreferences({ ...cachedPreferences || PREFERENCE_DEFAULTS, ...allowedPatch });
|
|
1026
|
+
const response = await api_default.put("/account/preferences", allowedPatch);
|
|
1027
|
+
const saved = extractResponseData(response);
|
|
1028
|
+
return announce(Object.keys(saved).length ? saved : candidate);
|
|
1029
|
+
}
|
|
1030
|
+
function subscribePreferences(listener) {
|
|
1031
|
+
listeners2.add(listener);
|
|
1032
|
+
if (cachedPreferences) listener(cachedPreferences);
|
|
1033
|
+
return () => listeners2.delete(listener);
|
|
1034
|
+
}
|
|
1035
|
+
function clearPreferenceCache() {
|
|
1036
|
+
cachedPreferences = null;
|
|
1037
|
+
pendingRequest = null;
|
|
1038
|
+
}
|
|
1039
|
+
function applyPreferences(preferences2, documentRef = globalThis.document) {
|
|
1040
|
+
var _a, _b, _c;
|
|
1041
|
+
if (!(documentRef == null ? void 0 : documentRef.documentElement)) return;
|
|
1042
|
+
const value = normalizePreferences(preferences2);
|
|
1043
|
+
const root = documentRef.documentElement;
|
|
1044
|
+
const resolvedTheme = resolveTheme(value.theme, (_b = (_a = documentRef.defaultView) == null ? void 0 : _a.matchMedia) == null ? void 0 : _b.bind(documentRef.defaultView));
|
|
1045
|
+
root.dataset.theme = resolvedTheme;
|
|
1046
|
+
root.dataset.themePreference = value.theme;
|
|
1047
|
+
root.dataset.colorScheme = value.colorScheme;
|
|
1048
|
+
root.dataset.density = value.density;
|
|
1049
|
+
root.lang = value.language;
|
|
1050
|
+
root.style.colorScheme = resolvedTheme;
|
|
1051
|
+
root.style.setProperty("--user-accent-color", {
|
|
1052
|
+
blue: "#1976d2",
|
|
1053
|
+
green: "#2e7d32",
|
|
1054
|
+
purple: "#7b1fa2",
|
|
1055
|
+
orange: "#c65d00",
|
|
1056
|
+
red: "#c62828"
|
|
1057
|
+
}[value.colorScheme] || "#1976d2");
|
|
1058
|
+
root.classList.toggle("user-reduced-motion", value.reducedMotion);
|
|
1059
|
+
root.classList.toggle("user-high-contrast", value.highContrast);
|
|
1060
|
+
root.classList.toggle("user-large-text", value.largeText);
|
|
1061
|
+
const styleId = "sso-user-preference-styles";
|
|
1062
|
+
if (!documentRef.getElementById(styleId)) {
|
|
1063
|
+
const style = documentRef.createElement("style");
|
|
1064
|
+
style.id = styleId;
|
|
1065
|
+
style.textContent = `
|
|
1066
|
+
html.user-large-text { font-size: 112.5%; }
|
|
1067
|
+
html.user-high-contrast { filter: contrast(1.15); }
|
|
1068
|
+
html[data-density="compact"] { --user-density-padding: 6px; }
|
|
1069
|
+
html[data-density="comfortable"] { --user-density-padding: 10px; }
|
|
1070
|
+
html[data-density="spacious"] { --user-density-padding: 14px; }
|
|
1071
|
+
html[data-density] .MuiListItem-root { padding-top: var(--user-density-padding); padding-bottom: var(--user-density-padding); }
|
|
1072
|
+
html.user-reduced-motion *, html.user-reduced-motion *::before, html.user-reduced-motion *::after {
|
|
1073
|
+
animation-duration: 0.01ms !important;
|
|
1074
|
+
animation-iteration-count: 1 !important;
|
|
1075
|
+
scroll-behavior: auto !important;
|
|
1076
|
+
transition-duration: 0.01ms !important;
|
|
1077
|
+
}
|
|
1078
|
+
`;
|
|
1079
|
+
(_c = documentRef.head) == null ? void 0 : _c.appendChild(style);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
function startPreferenceSync({ refreshOnFocus = true } = {}) {
|
|
1083
|
+
var _a, _b;
|
|
1084
|
+
if (syncCleanup || typeof globalThis.addEventListener !== "function") return syncCleanup || (() => {
|
|
1085
|
+
});
|
|
1086
|
+
const refresh = () => {
|
|
1087
|
+
if (!globalThis.document || globalThis.document.visibilityState === "visible") {
|
|
1088
|
+
getPreferences({ force: true }).catch(() => {
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
const onVisibility = () => refresh();
|
|
1093
|
+
(_b = (_a = globalThis.document) == null ? void 0 : _a.addEventListener) == null ? void 0 : _b.call(_a, "visibilitychange", onVisibility);
|
|
1094
|
+
if (refreshOnFocus) globalThis.addEventListener("focus", refresh);
|
|
1095
|
+
syncCleanup = () => {
|
|
1096
|
+
var _a2, _b2;
|
|
1097
|
+
(_b2 = (_a2 = globalThis.document) == null ? void 0 : _a2.removeEventListener) == null ? void 0 : _b2.call(_a2, "visibilitychange", onVisibility);
|
|
1098
|
+
if (refreshOnFocus) globalThis.removeEventListener("focus", refresh);
|
|
1099
|
+
syncCleanup = null;
|
|
1100
|
+
};
|
|
1101
|
+
return syncCleanup;
|
|
1102
|
+
}
|
|
1103
|
+
function localeFor(language) {
|
|
1104
|
+
const supported = Intl.DateTimeFormat.supportedLocalesOf([language]);
|
|
1105
|
+
return supported[0] || "en";
|
|
1106
|
+
}
|
|
1107
|
+
function fixedOffset(value) {
|
|
1108
|
+
const match = /^UTC([+-])(\d{2}):(\d{2})$/.exec(value || "");
|
|
1109
|
+
if (!match) return null;
|
|
1110
|
+
const minutes = Number(match[2]) * 60 + Number(match[3]);
|
|
1111
|
+
return (match[1] === "-" ? -1 : 1) * minutes;
|
|
1112
|
+
}
|
|
1113
|
+
function formatUserDate(input, preferences2 = cachedPreferences || PREFERENCE_DEFAULTS, options = {}) {
|
|
1114
|
+
const value = normalizePreferences(preferences2);
|
|
1115
|
+
const date = input instanceof Date ? input : new Date(input);
|
|
1116
|
+
const offset = fixedOffset(value.timezone);
|
|
1117
|
+
const adjusted = offset === null ? date : new Date(date.getTime() + offset * 6e4);
|
|
1118
|
+
const timeZone = offset === null ? value.timezone : "UTC";
|
|
1119
|
+
const hasExplicitParts = ["year", "month", "day", "weekday", "hour", "minute", "second", "dateStyle", "timeStyle"].some((key) => options[key] !== void 0);
|
|
1120
|
+
const dateOptions = hasExplicitParts ? options : { year: "numeric", month: "2-digit", day: "2-digit" };
|
|
1121
|
+
const formatter = new Intl.DateTimeFormat(localeFor(value.language), {
|
|
1122
|
+
...dateOptions,
|
|
1123
|
+
timeZone,
|
|
1124
|
+
...dateOptions.hour || dateOptions.timeStyle ? { hour12: value.timeFormat === "12h" } : {}
|
|
1125
|
+
});
|
|
1126
|
+
if (!hasExplicitParts) {
|
|
1127
|
+
const parts = Object.fromEntries(formatter.formatToParts(adjusted).map((part) => [part.type, part.value]));
|
|
1128
|
+
if (value.dateFormat === "DD/MM/YYYY") return `${parts.day}/${parts.month}/${parts.year}`;
|
|
1129
|
+
if (value.dateFormat === "YYYY-MM-DD") return `${parts.year}-${parts.month}-${parts.day}`;
|
|
1130
|
+
return `${parts.month}/${parts.day}/${parts.year}`;
|
|
1131
|
+
}
|
|
1132
|
+
return formatter.format(adjusted);
|
|
1133
|
+
}
|
|
1134
|
+
function formatUserNumber(input, preferences2 = cachedPreferences || PREFERENCE_DEFAULTS, options = {}) {
|
|
1135
|
+
const value = normalizePreferences(preferences2);
|
|
1136
|
+
return new Intl.NumberFormat(localeFor(value.language), options).format(input);
|
|
1137
|
+
}
|
|
1138
|
+
function formatUserRelativeTime(input, preferences2 = cachedPreferences || PREFERENCE_DEFAULTS, now = /* @__PURE__ */ new Date()) {
|
|
1139
|
+
const value = normalizePreferences(preferences2);
|
|
1140
|
+
const deltaSeconds = (new Date(input).getTime() - now.getTime()) / 1e3;
|
|
1141
|
+
const units = [
|
|
1142
|
+
["year", 31536e3],
|
|
1143
|
+
["month", 2592e3],
|
|
1144
|
+
["week", 604800],
|
|
1145
|
+
["day", 86400],
|
|
1146
|
+
["hour", 3600],
|
|
1147
|
+
["minute", 60],
|
|
1148
|
+
["second", 1]
|
|
1149
|
+
];
|
|
1150
|
+
const [unit, seconds] = units.find(([, size]) => Math.abs(deltaSeconds) >= size) || units.at(-1);
|
|
1151
|
+
return new Intl.RelativeTimeFormat(localeFor(value.language), { numeric: "auto" }).format(Math.round(deltaSeconds / seconds), unit);
|
|
1152
|
+
}
|
|
1153
|
+
var preferences = {
|
|
1154
|
+
defaults: PREFERENCE_DEFAULTS,
|
|
1155
|
+
get: getPreferences,
|
|
1156
|
+
update: updatePreferences,
|
|
1157
|
+
subscribe: subscribePreferences,
|
|
1158
|
+
clearCache: clearPreferenceCache,
|
|
1159
|
+
apply: applyPreferences,
|
|
1160
|
+
startSync: startPreferenceSync,
|
|
1161
|
+
formatDate: formatUserDate,
|
|
1162
|
+
formatNumber: formatUserNumber,
|
|
1163
|
+
formatRelativeTime: formatUserRelativeTime,
|
|
1164
|
+
normalize: normalizePreferences,
|
|
1165
|
+
resolveTheme
|
|
1166
|
+
};
|
|
1167
|
+
|
|
815
1168
|
// react/AuthProvider.jsx
|
|
816
1169
|
var import_react = __toESM(require("react"), 1);
|
|
817
1170
|
var AuthContext = (0, import_react.createContext)();
|
|
@@ -875,7 +1228,8 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
875
1228
|
console.log("\u{1F4E5} Profile response status:", res.status);
|
|
876
1229
|
if (!res.ok) throw new Error("Failed to fetch user");
|
|
877
1230
|
return res.json();
|
|
878
|
-
}).then((
|
|
1231
|
+
}).then((responseBody) => {
|
|
1232
|
+
const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
|
|
879
1233
|
console.log("\u2705 Profile fetched successfully:", userData.email);
|
|
880
1234
|
setUser(userData);
|
|
881
1235
|
setSessionValid(true);
|
|
@@ -891,13 +1245,16 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
891
1245
|
const login2 = (clientKey, redirectUri, state) => {
|
|
892
1246
|
login(clientKey, redirectUri, state);
|
|
893
1247
|
};
|
|
894
|
-
const logout2 = () => {
|
|
1248
|
+
const logout2 = async (options) => {
|
|
895
1249
|
stopSessionSecurity();
|
|
896
1250
|
sessionSecurityRef.current = null;
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1251
|
+
try {
|
|
1252
|
+
await logout(options);
|
|
1253
|
+
} finally {
|
|
1254
|
+
setUser(null);
|
|
1255
|
+
setTokenState(null);
|
|
1256
|
+
setSessionValid(true);
|
|
1257
|
+
}
|
|
901
1258
|
};
|
|
902
1259
|
const value = {
|
|
903
1260
|
token,
|
|
@@ -1019,6 +1376,62 @@ var useSessionMonitor = (options = {}) => {
|
|
|
1019
1376
|
};
|
|
1020
1377
|
};
|
|
1021
1378
|
|
|
1379
|
+
// react/PreferencesProvider.jsx
|
|
1380
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
1381
|
+
var PreferencesContext = (0, import_react4.createContext)(null);
|
|
1382
|
+
function PreferencesProvider({ children, initialPreferences, applyToDocument = true }) {
|
|
1383
|
+
const [preferences2, setPreferences] = (0, import_react4.useState)(() => ({
|
|
1384
|
+
...PREFERENCE_DEFAULTS,
|
|
1385
|
+
...initialPreferences
|
|
1386
|
+
}));
|
|
1387
|
+
const [loading, setLoading] = (0, import_react4.useState)(true);
|
|
1388
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
1389
|
+
(0, import_react4.useEffect)(() => {
|
|
1390
|
+
const unsubscribe = subscribePreferences((next) => {
|
|
1391
|
+
setPreferences(next);
|
|
1392
|
+
setLoading(false);
|
|
1393
|
+
setError(null);
|
|
1394
|
+
});
|
|
1395
|
+
const stopSync = startPreferenceSync();
|
|
1396
|
+
getPreferences().catch((nextError) => {
|
|
1397
|
+
setError(nextError);
|
|
1398
|
+
setLoading(false);
|
|
1399
|
+
});
|
|
1400
|
+
return () => {
|
|
1401
|
+
unsubscribe();
|
|
1402
|
+
stopSync();
|
|
1403
|
+
};
|
|
1404
|
+
}, []);
|
|
1405
|
+
(0, import_react4.useEffect)(() => {
|
|
1406
|
+
if (applyToDocument) applyPreferences(preferences2);
|
|
1407
|
+
}, [applyToDocument, preferences2]);
|
|
1408
|
+
(0, import_react4.useEffect)(() => {
|
|
1409
|
+
var _a;
|
|
1410
|
+
if (preferences2.theme !== "system" || typeof matchMedia !== "function") return void 0;
|
|
1411
|
+
const media = matchMedia("(prefers-color-scheme: dark)");
|
|
1412
|
+
const applySystemTheme = () => applyPreferences(preferences2);
|
|
1413
|
+
(_a = media.addEventListener) == null ? void 0 : _a.call(media, "change", applySystemTheme);
|
|
1414
|
+
return () => {
|
|
1415
|
+
var _a2;
|
|
1416
|
+
return (_a2 = media.removeEventListener) == null ? void 0 : _a2.call(media, "change", applySystemTheme);
|
|
1417
|
+
};
|
|
1418
|
+
}, [preferences2]);
|
|
1419
|
+
const value = (0, import_react4.useMemo)(() => ({
|
|
1420
|
+
preferences: preferences2,
|
|
1421
|
+
resolvedTheme: resolveTheme(preferences2.theme),
|
|
1422
|
+
loading,
|
|
1423
|
+
error,
|
|
1424
|
+
refresh: () => getPreferences({ force: true }),
|
|
1425
|
+
update: updatePreferences
|
|
1426
|
+
}), [error, loading, preferences2]);
|
|
1427
|
+
return /* @__PURE__ */ import_react4.default.createElement(PreferencesContext.Provider, { value }, children);
|
|
1428
|
+
}
|
|
1429
|
+
function usePreferences() {
|
|
1430
|
+
const context = (0, import_react4.useContext)(PreferencesContext);
|
|
1431
|
+
if (!context) throw new Error("usePreferences must be used within a PreferencesProvider");
|
|
1432
|
+
return context;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1022
1435
|
// index.js
|
|
1023
1436
|
var auth = {
|
|
1024
1437
|
// ๐ง Config
|
|
@@ -1028,6 +1441,8 @@ var auth = {
|
|
|
1028
1441
|
// ๐ Core flows
|
|
1029
1442
|
login,
|
|
1030
1443
|
logout,
|
|
1444
|
+
logoutClient: () => logout({ scope: "client" }),
|
|
1445
|
+
logoutSso: () => logout({ scope: "sso" }),
|
|
1031
1446
|
handleCallback,
|
|
1032
1447
|
refreshToken,
|
|
1033
1448
|
resetCallbackState,
|
|
@@ -1047,6 +1462,10 @@ var auth = {
|
|
|
1047
1462
|
// โ
Debug function
|
|
1048
1463
|
// ๐ Authenticated API client
|
|
1049
1464
|
api: api_default,
|
|
1465
|
+
preferences,
|
|
1466
|
+
// ๐ Safe authentication diagnostics
|
|
1467
|
+
getDiagnosticContext,
|
|
1468
|
+
emitAuthDiagnostic,
|
|
1050
1469
|
// ๐งช Utilities
|
|
1051
1470
|
decodeToken,
|
|
1052
1471
|
isTokenExpired,
|
|
@@ -1094,8 +1513,21 @@ var auth = {
|
|
|
1094
1513
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1095
1514
|
0 && (module.exports = {
|
|
1096
1515
|
AuthProvider,
|
|
1516
|
+
PREFERENCE_DEFAULTS,
|
|
1517
|
+
PreferencesProvider,
|
|
1518
|
+
applyPreferences,
|
|
1097
1519
|
auth,
|
|
1520
|
+
formatUserDate,
|
|
1521
|
+
formatUserNumber,
|
|
1522
|
+
formatUserRelativeTime,
|
|
1523
|
+
getPreferences,
|
|
1524
|
+
normalizePreferences,
|
|
1525
|
+
resolveTheme,
|
|
1526
|
+
startPreferenceSync,
|
|
1527
|
+
subscribePreferences,
|
|
1528
|
+
updatePreferences,
|
|
1098
1529
|
useAuth,
|
|
1530
|
+
usePreferences,
|
|
1099
1531
|
useSessionMonitor
|
|
1100
1532
|
});
|
|
1101
1533
|
//# sourceMappingURL=index.cjs.map
|