@payloadcms/ui 4.0.0-canary.20 → 4.0.0-canary.21

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 (86) hide show
  1. package/dist/elements/CopyToClipboard/index.d.ts +1 -0
  2. package/dist/elements/CopyToClipboard/index.d.ts.map +1 -1
  3. package/dist/elements/CopyToClipboard/index.js +10 -8
  4. package/dist/elements/CopyToClipboard/index.js.map +1 -1
  5. package/dist/elements/FileManager/FileToolbar/index.d.ts.map +1 -1
  6. package/dist/elements/FileManager/FileToolbar/index.js +3 -0
  7. package/dist/elements/FileManager/FileToolbar/index.js.map +1 -1
  8. package/dist/exports/client/index.d.ts +2 -1
  9. package/dist/exports/client/index.d.ts.map +1 -1
  10. package/dist/exports/client/index.js +1 -1
  11. package/dist/exports/client/index.js.map +3 -3
  12. package/dist/forms/Form/index.d.ts.map +1 -1
  13. package/dist/forms/Form/index.js +4 -1
  14. package/dist/forms/Form/index.js.map +1 -1
  15. package/dist/providers/Auth/AuthSessionDebug/index.css +48 -0
  16. package/dist/providers/Auth/AuthSessionDebug/index.d.ts +13 -0
  17. package/dist/providers/Auth/AuthSessionDebug/index.d.ts.map +1 -0
  18. package/dist/providers/Auth/AuthSessionDebug/index.js +93 -0
  19. package/dist/providers/Auth/AuthSessionDebug/index.js.map +1 -0
  20. package/dist/providers/Auth/AuthSessionDebug/utilities.d.ts +21 -0
  21. package/dist/providers/Auth/AuthSessionDebug/utilities.d.ts.map +1 -0
  22. package/dist/providers/Auth/AuthSessionDebug/utilities.js +53 -0
  23. package/dist/providers/Auth/AuthSessionDebug/utilities.js.map +1 -0
  24. package/dist/providers/Auth/authSessionRequests.d.ts +27 -0
  25. package/dist/providers/Auth/authSessionRequests.d.ts.map +1 -0
  26. package/dist/providers/Auth/authSessionRequests.js +119 -0
  27. package/dist/providers/Auth/authSessionRequests.js.map +1 -0
  28. package/dist/providers/Auth/authSessionRequests.spec.js +121 -0
  29. package/dist/providers/Auth/authSessionRequests.spec.js.map +1 -0
  30. package/dist/providers/Auth/index.d.ts +2 -56
  31. package/dist/providers/Auth/index.d.ts.map +1 -1
  32. package/dist/providers/Auth/index.js +283 -135
  33. package/dist/providers/Auth/index.js.map +1 -1
  34. package/dist/providers/Auth/sessionActivity.d.ts +23 -0
  35. package/dist/providers/Auth/sessionActivity.d.ts.map +1 -0
  36. package/dist/providers/Auth/sessionActivity.js +49 -0
  37. package/dist/providers/Auth/sessionActivity.js.map +1 -0
  38. package/dist/providers/Auth/sessionActivity.spec.js +44 -0
  39. package/dist/providers/Auth/sessionActivity.spec.js.map +1 -0
  40. package/dist/providers/Auth/sessionTiming.d.ts +11 -0
  41. package/dist/providers/Auth/sessionTiming.d.ts.map +1 -0
  42. package/dist/providers/Auth/sessionTiming.js +18 -0
  43. package/dist/providers/Auth/sessionTiming.js.map +1 -0
  44. package/dist/providers/Auth/tabSessionSync/index.d.ts +17 -0
  45. package/dist/providers/Auth/tabSessionSync/index.d.ts.map +1 -0
  46. package/dist/providers/Auth/tabSessionSync/index.js +263 -0
  47. package/dist/providers/Auth/tabSessionSync/index.js.map +1 -0
  48. package/dist/providers/Auth/tabSessionSync/index.spec.js +463 -0
  49. package/dist/providers/Auth/tabSessionSync/index.spec.js.map +1 -0
  50. package/dist/providers/Auth/tabSessionSync/types.d.ts +107 -0
  51. package/dist/providers/Auth/tabSessionSync/types.d.ts.map +1 -0
  52. package/dist/providers/Auth/tabSessionSync/types.js +6 -0
  53. package/dist/providers/Auth/tabSessionSync/types.js.map +1 -0
  54. package/dist/providers/Auth/tabSessionSync/useTabSessionSync.d.ts +11 -0
  55. package/dist/providers/Auth/tabSessionSync/useTabSessionSync.d.ts.map +1 -0
  56. package/dist/providers/Auth/tabSessionSync/useTabSessionSync.js +90 -0
  57. package/dist/providers/Auth/tabSessionSync/useTabSessionSync.js.map +1 -0
  58. package/dist/providers/Auth/tabSessionSync/utilities.d.ts +26 -0
  59. package/dist/providers/Auth/tabSessionSync/utilities.d.ts.map +1 -0
  60. package/dist/providers/Auth/tabSessionSync/utilities.js +159 -0
  61. package/dist/providers/Auth/tabSessionSync/utilities.js.map +1 -0
  62. package/dist/providers/Auth/types.d.ts +66 -0
  63. package/dist/providers/Auth/types.d.ts.map +1 -0
  64. package/dist/providers/Auth/types.js +2 -0
  65. package/dist/providers/Auth/types.js.map +1 -0
  66. package/dist/providers/Auth/useAuthSessionTimers.d.ts +26 -0
  67. package/dist/providers/Auth/useAuthSessionTimers.d.ts.map +1 -0
  68. package/dist/providers/Auth/useAuthSessionTimers.js +203 -0
  69. package/dist/providers/Auth/useAuthSessionTimers.js.map +1 -0
  70. package/dist/providers/Auth/useAuthSessionTimers.spec.js +311 -0
  71. package/dist/providers/Auth/useAuthSessionTimers.spec.js.map +1 -0
  72. package/dist/providers/FormErrorHandler/index.d.ts +5 -1
  73. package/dist/providers/FormErrorHandler/index.d.ts.map +1 -1
  74. package/dist/providers/FormErrorHandler/index.js.map +1 -1
  75. package/dist/providers/Root/index.js +4 -4
  76. package/dist/providers/Root/index.js.map +1 -1
  77. package/dist/providers/RouteTransition/ProgressBar/index.js +4 -1
  78. package/dist/providers/RouteTransition/ProgressBar/index.js.map +1 -1
  79. package/dist/providers/RouteTransition/ProgressBar/index.spec.js +44 -0
  80. package/dist/providers/RouteTransition/ProgressBar/index.spec.js.map +1 -0
  81. package/dist/providers/RouteTransition/index.d.ts +2 -0
  82. package/dist/providers/RouteTransition/index.d.ts.map +1 -1
  83. package/dist/providers/RouteTransition/index.js +42 -14
  84. package/dist/providers/RouteTransition/index.js.map +1 -1
  85. package/dist/styles.css +1 -1
  86. package/package.json +5 -5
@@ -0,0 +1,23 @@
1
+ export type SessionActivitySource = 'focus' | 'keydown' | 'mousemove' | 'route';
2
+ export type MarkSessionActivity = (source: SessionActivitySource) => boolean;
3
+ export type SessionActivityTracker = {
4
+ record: MarkSessionActivity;
5
+ reset: () => void;
6
+ };
7
+ export declare const sessionActivityThrottleMs = 5000;
8
+ /**
9
+ * Coalesces browser and route activity into periodic session activity.
10
+ *
11
+ * The tracker reports whether the activity was accepted. Activity inside the throttle
12
+ * interval is ignored and does not call `onActivity`.
13
+ */
14
+ export declare function createSessionActivityTracker({ now, onActivity, throttleMs, }: {
15
+ now?: () => number;
16
+ onActivity: (source: SessionActivitySource, occurredAt: number) => void;
17
+ throttleMs?: number;
18
+ }): SessionActivityTracker;
19
+ export declare function registerSessionActivityListeners({ markActivity, window, }: {
20
+ markActivity: MarkSessionActivity;
21
+ window: Window;
22
+ }): () => void;
23
+ //# sourceMappingURL=sessionActivity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionActivity.d.ts","sourceRoot":"","sources":["../../../src/providers/Auth/sessionActivity.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAA;AAE/E,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,qBAAqB,KAAK,OAAO,CAAA;AAE5E,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,mBAAmB,CAAA;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAAA;AAED,eAAO,MAAM,yBAAyB,OAAQ,CAAA;AAE9C;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,GAAc,EACd,UAAU,EACV,UAAsC,GACvC,EAAE;IACD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAA;IACvE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG,sBAAsB,CAoBzB;AAED,wBAAgB,gCAAgC,CAAC,EAC/C,YAAY,EACZ,MAAM,GACP,EAAE;IACD,YAAY,EAAE,mBAAmB,CAAA;IACjC,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,MAAM,IAAI,CAeb"}
@@ -0,0 +1,49 @@
1
+ export const sessionActivityThrottleMs = 5_000;
2
+ /**
3
+ * Coalesces browser and route activity into periodic session activity.
4
+ *
5
+ * The tracker reports whether the activity was accepted. Activity inside the throttle
6
+ * interval is ignored and does not call `onActivity`.
7
+ */
8
+ export function createSessionActivityTracker({
9
+ now = Date.now,
10
+ onActivity,
11
+ throttleMs = sessionActivityThrottleMs
12
+ }) {
13
+ let lastActivityAt;
14
+ return {
15
+ record: source => {
16
+ const occurredAt = now();
17
+ if (lastActivityAt !== undefined && occurredAt - lastActivityAt < throttleMs) {
18
+ return false;
19
+ }
20
+ lastActivityAt = occurredAt;
21
+ onActivity(source, occurredAt);
22
+ return true;
23
+ },
24
+ reset: () => {
25
+ lastActivityAt = undefined;
26
+ }
27
+ };
28
+ }
29
+ export function registerSessionActivityListeners({
30
+ markActivity,
31
+ window
32
+ }) {
33
+ const focusListener = () => markActivity('focus');
34
+ const keydownListener = () => markActivity('keydown');
35
+ const mousemoveListener = () => markActivity('mousemove');
36
+ const mousemoveListenerOptions = {
37
+ capture: true,
38
+ passive: true
39
+ };
40
+ window.addEventListener('focus', focusListener, true);
41
+ window.addEventListener('keydown', keydownListener, true);
42
+ window.addEventListener('mousemove', mousemoveListener, mousemoveListenerOptions);
43
+ return () => {
44
+ window.removeEventListener('focus', focusListener, true);
45
+ window.removeEventListener('keydown', keydownListener, true);
46
+ window.removeEventListener('mousemove', mousemoveListener, mousemoveListenerOptions);
47
+ };
48
+ }
49
+ //# sourceMappingURL=sessionActivity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionActivity.js","names":["sessionActivityThrottleMs","createSessionActivityTracker","now","Date","onActivity","throttleMs","lastActivityAt","record","source","occurredAt","undefined","reset","registerSessionActivityListeners","markActivity","window","focusListener","keydownListener","mousemoveListener","mousemoveListenerOptions","capture","passive","addEventListener","removeEventListener"],"sources":["../../../src/providers/Auth/sessionActivity.ts"],"sourcesContent":["export type SessionActivitySource = 'focus' | 'keydown' | 'mousemove' | 'route'\n\nexport type MarkSessionActivity = (source: SessionActivitySource) => boolean\n\nexport type SessionActivityTracker = {\n record: MarkSessionActivity\n reset: () => void\n}\n\nexport const sessionActivityThrottleMs = 5_000\n\n/**\n * Coalesces browser and route activity into periodic session activity.\n *\n * The tracker reports whether the activity was accepted. Activity inside the throttle\n * interval is ignored and does not call `onActivity`.\n */\nexport function createSessionActivityTracker({\n now = Date.now,\n onActivity,\n throttleMs = sessionActivityThrottleMs,\n}: {\n now?: () => number\n onActivity: (source: SessionActivitySource, occurredAt: number) => void\n throttleMs?: number\n}): SessionActivityTracker {\n let lastActivityAt: number | undefined\n\n return {\n record: (source) => {\n const occurredAt = now()\n\n if (lastActivityAt !== undefined && occurredAt - lastActivityAt < throttleMs) {\n return false\n }\n\n lastActivityAt = occurredAt\n onActivity(source, occurredAt)\n\n return true\n },\n reset: () => {\n lastActivityAt = undefined\n },\n }\n}\n\nexport function registerSessionActivityListeners({\n markActivity,\n window,\n}: {\n markActivity: MarkSessionActivity\n window: Window\n}): () => void {\n const focusListener = () => markActivity('focus')\n const keydownListener = () => markActivity('keydown')\n const mousemoveListener = () => markActivity('mousemove')\n const mousemoveListenerOptions = { capture: true, passive: true }\n\n window.addEventListener('focus', focusListener, true)\n window.addEventListener('keydown', keydownListener, true)\n window.addEventListener('mousemove', mousemoveListener, mousemoveListenerOptions)\n\n return () => {\n window.removeEventListener('focus', focusListener, true)\n window.removeEventListener('keydown', keydownListener, true)\n window.removeEventListener('mousemove', mousemoveListener, mousemoveListenerOptions)\n }\n}\n"],"mappings":"AASA,OAAO,MAAMA,yBAAA,GAA4B;AAEzC;;;;;;AAMA,OAAO,SAASC,6BAA6B;EAC3CC,GAAA,GAAMC,IAAA,CAAKD,GAAG;EACdE,UAAU;EACVC,UAAA,GAAaL;AAAyB,CAKvC;EACC,IAAIM,cAAA;EAEJ,OAAO;IACLC,MAAA,EAASC,MAAA;MACP,MAAMC,UAAA,GAAaP,GAAA;MAEnB,IAAII,cAAA,KAAmBI,SAAA,IAAaD,UAAA,GAAaH,cAAA,GAAiBD,UAAA,EAAY;QAC5E,OAAO;MACT;MAEAC,cAAA,GAAiBG,UAAA;MACjBL,UAAA,CAAWI,MAAA,EAAQC,UAAA;MAEnB,OAAO;IACT;IACAE,KAAA,EAAOA,CAAA;MACLL,cAAA,GAAiBI,SAAA;IACnB;EACF;AACF;AAEA,OAAO,SAASE,iCAAiC;EAC/CC,YAAY;EACZC;AAAM,CAIP;EACC,MAAMC,aAAA,GAAgBA,CAAA,KAAMF,YAAA,CAAa;EACzC,MAAMG,eAAA,GAAkBA,CAAA,KAAMH,YAAA,CAAa;EAC3C,MAAMI,iBAAA,GAAoBA,CAAA,KAAMJ,YAAA,CAAa;EAC7C,MAAMK,wBAAA,GAA2B;IAAEC,OAAA,EAAS;IAAMC,OAAA,EAAS;EAAK;EAEhEN,MAAA,CAAOO,gBAAgB,CAAC,SAASN,aAAA,EAAe;EAChDD,MAAA,CAAOO,gBAAgB,CAAC,WAAWL,eAAA,EAAiB;EACpDF,MAAA,CAAOO,gBAAgB,CAAC,aAAaJ,iBAAA,EAAmBC,wBAAA;EAExD,OAAO;IACLJ,MAAA,CAAOQ,mBAAmB,CAAC,SAASP,aAAA,EAAe;IACnDD,MAAA,CAAOQ,mBAAmB,CAAC,WAAWN,eAAA,EAAiB;IACvDF,MAAA,CAAOQ,mBAAmB,CAAC,aAAaL,iBAAA,EAAmBC,wBAAA;EAC7D;AACF","ignoreList":[]}
@@ -0,0 +1,44 @@
1
+ // @vitest-environment jsdom
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import { createSessionActivityTracker, registerSessionActivityListeners, sessionActivityThrottleMs } from './sessionActivity.js';
4
+ describe('createSessionActivityTracker', () => {
5
+ it('should throttle activity for five seconds', () => {
6
+ expect(sessionActivityThrottleMs).toBe(5_000);
7
+ let now = 100;
8
+ const onActivity = vi.fn();
9
+ const activityTracker = createSessionActivityTracker({
10
+ now: () => now,
11
+ onActivity
12
+ });
13
+ expect(activityTracker.record('mousemove')).toBe(true);
14
+ expect(onActivity).toHaveBeenCalledWith('mousemove', 100);
15
+ now += sessionActivityThrottleMs - 1;
16
+ expect(activityTracker.record('focus')).toBe(false);
17
+ expect(onActivity).toHaveBeenCalledTimes(1);
18
+ activityTracker.reset();
19
+ expect(activityTracker.record('focus')).toBe(true);
20
+ expect(onActivity).toHaveBeenCalledTimes(2);
21
+ now += 1;
22
+ expect(activityTracker.record('focus')).toBe(false);
23
+ });
24
+ });
25
+ describe('registerSessionActivityListeners', () => {
26
+ it('should register and remove focus, keyboard, and mouse movement listeners', () => {
27
+ const markActivity = vi.fn();
28
+ const cleanup = registerSessionActivityListeners({
29
+ markActivity,
30
+ window
31
+ });
32
+ window.dispatchEvent(new Event('focus'));
33
+ window.dispatchEvent(new KeyboardEvent('keydown'));
34
+ window.dispatchEvent(new MouseEvent('mousemove'));
35
+ expect(markActivity.mock.calls).toEqual([['focus'], ['keydown'], ['mousemove']]);
36
+ cleanup();
37
+ markActivity.mockClear();
38
+ window.dispatchEvent(new Event('focus'));
39
+ window.dispatchEvent(new KeyboardEvent('keydown'));
40
+ window.dispatchEvent(new MouseEvent('mousemove'));
41
+ expect(markActivity).not.toHaveBeenCalled();
42
+ });
43
+ });
44
+ //# sourceMappingURL=sessionActivity.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionActivity.spec.js","names":["describe","expect","it","vi","createSessionActivityTracker","registerSessionActivityListeners","sessionActivityThrottleMs","toBe","now","onActivity","fn","activityTracker","record","toHaveBeenCalledWith","toHaveBeenCalledTimes","reset","markActivity","cleanup","window","dispatchEvent","Event","KeyboardEvent","MouseEvent","mock","calls","toEqual","mockClear","not","toHaveBeenCalled"],"sources":["../../../src/providers/Auth/sessionActivity.spec.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, expect, it, vi } from 'vitest'\n\nimport {\n createSessionActivityTracker,\n registerSessionActivityListeners,\n sessionActivityThrottleMs,\n} from './sessionActivity.js'\n\ndescribe('createSessionActivityTracker', () => {\n it('should throttle activity for five seconds', () => {\n expect(sessionActivityThrottleMs).toBe(5_000)\n\n let now = 100\n const onActivity = vi.fn()\n const activityTracker = createSessionActivityTracker({\n now: () => now,\n onActivity,\n })\n\n expect(activityTracker.record('mousemove')).toBe(true)\n expect(onActivity).toHaveBeenCalledWith('mousemove', 100)\n\n now += sessionActivityThrottleMs - 1\n\n expect(activityTracker.record('focus')).toBe(false)\n expect(onActivity).toHaveBeenCalledTimes(1)\n\n activityTracker.reset()\n\n expect(activityTracker.record('focus')).toBe(true)\n expect(onActivity).toHaveBeenCalledTimes(2)\n\n now += 1\n\n expect(activityTracker.record('focus')).toBe(false)\n })\n})\n\ndescribe('registerSessionActivityListeners', () => {\n it('should register and remove focus, keyboard, and mouse movement listeners', () => {\n const markActivity = vi.fn()\n\n const cleanup = registerSessionActivityListeners({\n markActivity,\n window,\n })\n\n window.dispatchEvent(new Event('focus'))\n window.dispatchEvent(new KeyboardEvent('keydown'))\n window.dispatchEvent(new MouseEvent('mousemove'))\n\n expect(markActivity.mock.calls).toEqual([['focus'], ['keydown'], ['mousemove']])\n\n cleanup()\n markActivity.mockClear()\n\n window.dispatchEvent(new Event('focus'))\n window.dispatchEvent(new KeyboardEvent('keydown'))\n window.dispatchEvent(new MouseEvent('mousemove'))\n\n expect(markActivity).not.toHaveBeenCalled()\n })\n})\n"],"mappings":"AAAA;AACA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ;AAEzC,SACEC,4BAA4B,EAC5BC,gCAAgC,EAChCC,yBAAyB,QACpB;AAEPN,QAAA,CAAS,gCAAgC;EACvCE,EAAA,CAAG,6CAA6C;IAC9CD,MAAA,CAAOK,yBAAA,EAA2BC,IAAI,CAAC;IAEvC,IAAIC,GAAA,GAAM;IACV,MAAMC,UAAA,GAAaN,EAAA,CAAGO,EAAE;IACxB,MAAMC,eAAA,GAAkBP,4BAAA,CAA6B;MACnDI,GAAA,EAAKA,CAAA,KAAMA,GAAA;MACXC;IACF;IAEAR,MAAA,CAAOU,eAAA,CAAgBC,MAAM,CAAC,cAAcL,IAAI,CAAC;IACjDN,MAAA,CAAOQ,UAAA,EAAYI,oBAAoB,CAAC,aAAa;IAErDL,GAAA,IAAOF,yBAAA,GAA4B;IAEnCL,MAAA,CAAOU,eAAA,CAAgBC,MAAM,CAAC,UAAUL,IAAI,CAAC;IAC7CN,MAAA,CAAOQ,UAAA,EAAYK,qBAAqB,CAAC;IAEzCH,eAAA,CAAgBI,KAAK;IAErBd,MAAA,CAAOU,eAAA,CAAgBC,MAAM,CAAC,UAAUL,IAAI,CAAC;IAC7CN,MAAA,CAAOQ,UAAA,EAAYK,qBAAqB,CAAC;IAEzCN,GAAA,IAAO;IAEPP,MAAA,CAAOU,eAAA,CAAgBC,MAAM,CAAC,UAAUL,IAAI,CAAC;EAC/C;AACF;AAEAP,QAAA,CAAS,oCAAoC;EAC3CE,EAAA,CAAG,4EAA4E;IAC7E,MAAMc,YAAA,GAAeb,EAAA,CAAGO,EAAE;IAE1B,MAAMO,OAAA,GAAUZ,gCAAA,CAAiC;MAC/CW,YAAA;MACAE;IACF;IAEAA,MAAA,CAAOC,aAAa,CAAC,IAAIC,KAAA,CAAM;IAC/BF,MAAA,CAAOC,aAAa,CAAC,IAAIE,aAAA,CAAc;IACvCH,MAAA,CAAOC,aAAa,CAAC,IAAIG,UAAA,CAAW;IAEpCrB,MAAA,CAAOe,YAAA,CAAaO,IAAI,CAACC,KAAK,EAAEC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,CAAC,YAAY,CAAC;IAE/ER,OAAA;IACAD,YAAA,CAAaU,SAAS;IAEtBR,MAAA,CAAOC,aAAa,CAAC,IAAIC,KAAA,CAAM;IAC/BF,MAAA,CAAOC,aAAa,CAAC,IAAIE,aAAA,CAAc;IACvCH,MAAA,CAAOC,aAAa,CAAC,IAAIG,UAAA,CAAW;IAEpCrB,MAAA,CAAOe,YAAA,EAAcW,GAAG,CAACC,gBAAgB;EAC3C;AACF","ignoreList":[]}
@@ -0,0 +1,11 @@
1
+ import type { AuthSession } from './types.js';
2
+ export type AuthSessionTiming = Omit<AuthSession, 'activityRecorded'>;
3
+ export type GetAuthSessionTimingArgs = {
4
+ expirationMs: number;
5
+ nowMs?: number;
6
+ };
7
+ /**
8
+ * Returns the fixed lifecycle boundaries for a token expiration.
9
+ */
10
+ export declare function getAuthSessionTiming({ expirationMs, nowMs, }: GetAuthSessionTimingArgs): AuthSessionTiming;
11
+ //# sourceMappingURL=sessionTiming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionTiming.d.ts","sourceRoot":"","sources":["../../../src/providers/Auth/sessionTiming.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAA;AAErE,MAAM,MAAM,wBAAwB,GAAG;IACrC,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EACZ,KAAkB,GACnB,EAAE,wBAAwB,GAAG,iBAAiB,CAY9C"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Returns the fixed lifecycle boundaries for a token expiration.
3
+ */export function getAuthSessionTiming({
4
+ expirationMs,
5
+ nowMs = Date.now()
6
+ }) {
7
+ const expiresInMs = Math.max(0, expirationMs - nowMs);
8
+ const reminderWindowMs = Math.min(60_000, expiresInMs / 2);
9
+ const refreshWindowMs = reminderWindowMs * 2;
10
+ const activityTrackingWindowMs = refreshWindowMs * 2;
11
+ return Object.freeze({
12
+ activityTrackingStartsAt: Math.max(expirationMs - activityTrackingWindowMs, nowMs),
13
+ expiresAt: expirationMs,
14
+ refreshStartsAt: Math.max(expirationMs - refreshWindowMs, nowMs),
15
+ reminderStartsAt: Math.max(expirationMs - reminderWindowMs, nowMs)
16
+ });
17
+ }
18
+ //# sourceMappingURL=sessionTiming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionTiming.js","names":["getAuthSessionTiming","expirationMs","nowMs","Date","now","expiresInMs","Math","max","reminderWindowMs","min","refreshWindowMs","activityTrackingWindowMs","Object","freeze","activityTrackingStartsAt","expiresAt","refreshStartsAt","reminderStartsAt"],"sources":["../../../src/providers/Auth/sessionTiming.ts"],"sourcesContent":["import type { AuthSession } from './types.js'\n\nexport type AuthSessionTiming = Omit<AuthSession, 'activityRecorded'>\n\nexport type GetAuthSessionTimingArgs = {\n expirationMs: number\n nowMs?: number\n}\n\n/**\n * Returns the fixed lifecycle boundaries for a token expiration.\n */\nexport function getAuthSessionTiming({\n expirationMs,\n nowMs = Date.now(),\n}: GetAuthSessionTimingArgs): AuthSessionTiming {\n const expiresInMs = Math.max(0, expirationMs - nowMs)\n const reminderWindowMs = Math.min(60_000, expiresInMs / 2)\n const refreshWindowMs = reminderWindowMs * 2\n const activityTrackingWindowMs = refreshWindowMs * 2\n\n return Object.freeze({\n activityTrackingStartsAt: Math.max(expirationMs - activityTrackingWindowMs, nowMs),\n expiresAt: expirationMs,\n refreshStartsAt: Math.max(expirationMs - refreshWindowMs, nowMs),\n reminderStartsAt: Math.max(expirationMs - reminderWindowMs, nowMs),\n })\n}\n"],"mappings":"AASA;;GAGA,OAAO,SAASA,qBAAqB;EACnCC,YAAY;EACZC,KAAA,GAAQC,IAAA,CAAKC,GAAG;AAAE,CACO;EACzB,MAAMC,WAAA,GAAcC,IAAA,CAAKC,GAAG,CAAC,GAAGN,YAAA,GAAeC,KAAA;EAC/C,MAAMM,gBAAA,GAAmBF,IAAA,CAAKG,GAAG,CAAC,QAAQJ,WAAA,GAAc;EACxD,MAAMK,eAAA,GAAkBF,gBAAA,GAAmB;EAC3C,MAAMG,wBAAA,GAA2BD,eAAA,GAAkB;EAEnD,OAAOE,MAAA,CAAOC,MAAM,CAAC;IACnBC,wBAAA,EAA0BR,IAAA,CAAKC,GAAG,CAACN,YAAA,GAAeU,wBAAA,EAA0BT,KAAA;IAC5Ea,SAAA,EAAWd,YAAA;IACXe,eAAA,EAAiBV,IAAA,CAAKC,GAAG,CAACN,YAAA,GAAeS,eAAA,EAAiBR,KAAA;IAC1De,gBAAA,EAAkBX,IAAA,CAAKC,GAAG,CAACN,YAAA,GAAeO,gBAAA,EAAkBN,KAAA;EAC9D;AACF","ignoreList":[]}
@@ -0,0 +1,17 @@
1
+ import type { CreateTabSessionSyncArgs, TabSessionEvent, TabSessionLogoutPublication, TabSessionPublication } from './types.js';
2
+ export type { CreateTabSessionSyncArgs, TabSessionEvent, TabSessionEventType, TabSessionLogoutPublication, TabSessionMessage, TabSessionPublication, TabSessionReconciliationOptions, TabSessionReconciliationResult, } from './types.js';
3
+ export { TAB_SESSION_EVENT_TYPES } from './types.js';
4
+ /**
5
+ * Coordinates authentication session state across browser tabs.
6
+ *
7
+ * BroadcastChannel carries complete session events when available. Transient localStorage
8
+ * notifications carry lifecycle metadata only and allow tabs to reconcile through
9
+ * `reconcileSession` when BroadcastChannel is unavailable. Lifecycle ordering prevents older async
10
+ * work from replacing a newer session state.
11
+ */
12
+ export declare function createTabSessionSync({ getTokenExpirationMs, now, onSessionExpired, onSessionLoggedOut, onSessionRefreshed, onTabSessionUnauthenticated, reconcileSession, sourceTabID, }: CreateTabSessionSyncArgs): {
13
+ broadcast: (event: TabSessionEvent) => TabSessionPublication;
14
+ broadcastLogoutSettlement: (publication: TabSessionLogoutPublication) => void;
15
+ cleanup: () => void;
16
+ };
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/providers/Auth/tabSessionSync/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EAKxB,eAAe,EAEf,2BAA2B,EAC3B,qBAAqB,EAGtB,MAAM,YAAY,CAAA;AAanB,YAAY,EACV,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,2BAA2B,EAC3B,iBAAiB,EACjB,qBAAqB,EACrB,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAEpD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,oBAAoB,EACpB,GAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,EAC3B,gBAAgB,EAChB,WAAW,GACZ,EAAE,wBAAwB,GAAG;IAC5B,SAAS,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,qBAAqB,CAAA;IAC5D,yBAAyB,EAAE,CAAC,WAAW,EAAE,2BAA2B,KAAK,IAAI,CAAA;IAC7E,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CA8PA"}
@@ -0,0 +1,263 @@
1
+ import { TAB_SESSION_EVENT_TYPES } from './types.js';
2
+ import { compareLifecycleOrders, createTabSessionMessage, getLifecycleOrder, isTabSessionMessage, parseTabSessionStorageNotification, tabSessionSyncChannelName, tabSessionSyncStorageKey } from './utilities.js';
3
+ export { TAB_SESSION_EVENT_TYPES } from './types.js';
4
+ /**
5
+ * Coordinates authentication session state across browser tabs.
6
+ *
7
+ * BroadcastChannel carries complete session events when available. Transient localStorage
8
+ * notifications carry lifecycle metadata only and allow tabs to reconcile through
9
+ * `reconcileSession` when BroadcastChannel is unavailable. Lifecycle ordering prevents older async
10
+ * work from replacing a newer session state.
11
+ */
12
+ export function createTabSessionSync({
13
+ getTokenExpirationMs,
14
+ now = Date.now,
15
+ onSessionExpired,
16
+ onSessionLoggedOut,
17
+ onSessionRefreshed,
18
+ onTabSessionUnauthenticated,
19
+ reconcileSession,
20
+ sourceTabID
21
+ }) {
22
+ let channel;
23
+ let isActive = true;
24
+ let isStorageListenerInstalled = false;
25
+ let latestLifecycleOrder;
26
+ let pendingStorageLifecycleOrder;
27
+ const onExpiredSession = ({
28
+ lifecycleOrder,
29
+ message
30
+ }) => {
31
+ const localExpirationMs = getTokenExpirationMs();
32
+ if (localExpirationMs === undefined || localExpirationMs <= message.expiredTokenAt) {
33
+ pendingStorageLifecycleOrder = undefined;
34
+ latestLifecycleOrder = lifecycleOrder;
35
+ onSessionExpired(message.expiredTokenAt);
36
+ }
37
+ };
38
+ const onLoggedOutSession = ({
39
+ lifecycleOrder
40
+ }) => {
41
+ pendingStorageLifecycleOrder = undefined;
42
+ latestLifecycleOrder = lifecycleOrder;
43
+ onSessionLoggedOut();
44
+ };
45
+ const onRefreshedSession = ({
46
+ lifecycleOrder,
47
+ message
48
+ }) => {
49
+ pendingStorageLifecycleOrder = undefined;
50
+ latestLifecycleOrder = lifecycleOrder;
51
+ onSessionRefreshed(message.session);
52
+ };
53
+ const handleChannelMessage = ({
54
+ data
55
+ }) => {
56
+ if (!isTabSessionMessage(data) || data.sourceTabID === sourceTabID) {
57
+ return;
58
+ }
59
+ const lifecycleOrder = getLifecycleOrder(data);
60
+ const lifecycleComparison = latestLifecycleOrder ? compareLifecycleOrders({
61
+ first: lifecycleOrder,
62
+ second: latestLifecycleOrder
63
+ }) : undefined;
64
+ const resolvesPendingStorageBarrier = lifecycleComparison === 0 && pendingStorageLifecycleOrder === latestLifecycleOrder;
65
+ if (lifecycleComparison !== undefined && (lifecycleComparison < 0 || lifecycleComparison === 0 && !resolvesPendingStorageBarrier)) {
66
+ return;
67
+ }
68
+ switch (data.type) {
69
+ case TAB_SESSION_EVENT_TYPES.EXPIRED:
70
+ onExpiredSession({
71
+ lifecycleOrder,
72
+ message: data
73
+ });
74
+ break;
75
+ case TAB_SESSION_EVENT_TYPES.LOGGED_OUT:
76
+ onLoggedOutSession({
77
+ lifecycleOrder
78
+ });
79
+ break;
80
+ case TAB_SESSION_EVENT_TYPES.REFRESHED:
81
+ onRefreshedSession({
82
+ lifecycleOrder,
83
+ message: data
84
+ });
85
+ break;
86
+ }
87
+ };
88
+ const reconcileStorageSession = ({
89
+ notification
90
+ }) => {
91
+ const isTabSessionEventStale = () => !isActive || latestLifecycleOrder !== notification;
92
+ void reconcileSession({
93
+ isTabSessionEventStale
94
+ }).then(result => {
95
+ if (!isTabSessionEventStale()) {
96
+ if (result.status === 'authenticated') {
97
+ pendingStorageLifecycleOrder = undefined;
98
+ latestLifecycleOrder = {
99
+ type: TAB_SESSION_EVENT_TYPES.REFRESHED,
100
+ affectedExpirationMs: result.expirationMs,
101
+ refreshStartedAt: notification.type === TAB_SESSION_EVENT_TYPES.REFRESHED ? notification.refreshStartedAt : notification.sentAt,
102
+ sentAt: notification.sentAt,
103
+ sourceTabID: notification.sourceTabID
104
+ };
105
+ } else if (result.status === 'unauthenticated') {
106
+ pendingStorageLifecycleOrder = undefined;
107
+ latestLifecycleOrder = {
108
+ type: TAB_SESSION_EVENT_TYPES.LOGGED_OUT,
109
+ affectedExpirationMs: 0,
110
+ sentAt: notification.sentAt,
111
+ sourceTabID: notification.sourceTabID
112
+ };
113
+ onTabSessionUnauthenticated();
114
+ }
115
+ }
116
+ }).catch(() => undefined);
117
+ };
118
+ const handleStorageNotification = event => {
119
+ const notification = parseTabSessionStorageNotification(event);
120
+ if (!notification || notification.sourceTabID === sourceTabID) {
121
+ return;
122
+ }
123
+ if (channel && !notification.isBroadcastChannelFallback) {
124
+ return;
125
+ }
126
+ if (latestLifecycleOrder && compareLifecycleOrders({
127
+ first: notification,
128
+ second: latestLifecycleOrder
129
+ }) <= 0) {
130
+ return;
131
+ }
132
+ pendingStorageLifecycleOrder = notification;
133
+ latestLifecycleOrder = notification;
134
+ if (notification.type === TAB_SESSION_EVENT_TYPES.LOGGED_OUT) {
135
+ onLoggedOutSession({
136
+ lifecycleOrder: notification
137
+ });
138
+ } else {
139
+ reconcileStorageSession({
140
+ notification
141
+ });
142
+ }
143
+ };
144
+ function closeChannel(channelToClose) {
145
+ if (!channelToClose) {
146
+ return;
147
+ }
148
+ try {
149
+ channelToClose.removeEventListener('message', handleChannelMessage);
150
+ } catch {
151
+ // Cross-tab synchronization cleanup is best-effort.
152
+ }
153
+ try {
154
+ channelToClose.close();
155
+ } catch {
156
+ // Cross-tab synchronization cleanup is best-effort.
157
+ }
158
+ }
159
+ function attachStorageListener() {
160
+ if (isStorageListenerInstalled) {
161
+ return;
162
+ }
163
+ try {
164
+ window.addEventListener('storage', handleStorageNotification);
165
+ isStorageListenerInstalled = true;
166
+ } catch {
167
+ // Local authentication must continue when storage events are unavailable.
168
+ }
169
+ }
170
+ function downgradeToStorage() {
171
+ closeChannel(channel);
172
+ channel = undefined;
173
+ attachStorageListener();
174
+ }
175
+ function getNextLifecycleTimestamp() {
176
+ const currentTime = now();
177
+ const latestSentAt = latestLifecycleOrder?.sentAt ?? Number.NEGATIVE_INFINITY;
178
+ return currentTime > latestSentAt ? currentTime : latestSentAt + 1;
179
+ }
180
+ const writeStorageNotification = ({
181
+ notification
182
+ }) => {
183
+ const storageNotification = channel ? notification : {
184
+ ...notification,
185
+ isBroadcastChannelFallback: true
186
+ };
187
+ try {
188
+ localStorage.setItem(tabSessionSyncStorageKey, JSON.stringify(storageNotification));
189
+ } catch {
190
+ // Local authentication must continue when storage is unavailable.
191
+ }
192
+ try {
193
+ localStorage.removeItem(tabSessionSyncStorageKey);
194
+ } catch {
195
+ // Local authentication must continue when storage is unavailable.
196
+ }
197
+ };
198
+ if (typeof BroadcastChannel === 'function') {
199
+ let nextChannel;
200
+ try {
201
+ nextChannel = new BroadcastChannel(tabSessionSyncChannelName);
202
+ nextChannel.addEventListener('message', handleChannelMessage);
203
+ channel = nextChannel;
204
+ } catch {
205
+ closeChannel(nextChannel);
206
+ channel = undefined;
207
+ }
208
+ }
209
+ attachStorageListener();
210
+ return {
211
+ broadcast: event => {
212
+ const sentAt = getNextLifecycleTimestamp();
213
+ const message = createTabSessionMessage({
214
+ event,
215
+ sentAt,
216
+ sourceTabID
217
+ });
218
+ const lifecycleOrder = getLifecycleOrder(message);
219
+ pendingStorageLifecycleOrder = undefined;
220
+ latestLifecycleOrder = lifecycleOrder;
221
+ if (channel) {
222
+ try {
223
+ channel.postMessage(message);
224
+ } catch {
225
+ downgradeToStorage();
226
+ }
227
+ }
228
+ if (lifecycleOrder.type !== TAB_SESSION_EVENT_TYPES.LOGGED_OUT) {
229
+ writeStorageNotification({
230
+ notification: lifecycleOrder
231
+ });
232
+ }
233
+ return lifecycleOrder;
234
+ },
235
+ broadcastLogoutSettlement: publication => {
236
+ const notification = {
237
+ ...publication,
238
+ sentAt: getNextLifecycleTimestamp(),
239
+ settlesSentAt: publication.sentAt
240
+ };
241
+ pendingStorageLifecycleOrder = undefined;
242
+ latestLifecycleOrder = notification;
243
+ writeStorageNotification({
244
+ notification
245
+ });
246
+ },
247
+ cleanup: () => {
248
+ isActive = false;
249
+ pendingStorageLifecycleOrder = undefined;
250
+ closeChannel(channel);
251
+ channel = undefined;
252
+ if (isStorageListenerInstalled) {
253
+ try {
254
+ window.removeEventListener('storage', handleStorageNotification);
255
+ } catch {
256
+ // Cross-tab synchronization cleanup is best-effort.
257
+ }
258
+ isStorageListenerInstalled = false;
259
+ }
260
+ }
261
+ };
262
+ }
263
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["TAB_SESSION_EVENT_TYPES","compareLifecycleOrders","createTabSessionMessage","getLifecycleOrder","isTabSessionMessage","parseTabSessionStorageNotification","tabSessionSyncChannelName","tabSessionSyncStorageKey","createTabSessionSync","getTokenExpirationMs","now","Date","onSessionExpired","onSessionLoggedOut","onSessionRefreshed","onTabSessionUnauthenticated","reconcileSession","sourceTabID","channel","isActive","isStorageListenerInstalled","latestLifecycleOrder","pendingStorageLifecycleOrder","onExpiredSession","lifecycleOrder","message","localExpirationMs","undefined","expiredTokenAt","onLoggedOutSession","onRefreshedSession","session","handleChannelMessage","data","lifecycleComparison","first","second","resolvesPendingStorageBarrier","type","EXPIRED","LOGGED_OUT","REFRESHED","reconcileStorageSession","notification","isTabSessionEventStale","then","result","status","affectedExpirationMs","expirationMs","refreshStartedAt","sentAt","catch","handleStorageNotification","event","isBroadcastChannelFallback","closeChannel","channelToClose","removeEventListener","close","attachStorageListener","window","addEventListener","downgradeToStorage","getNextLifecycleTimestamp","currentTime","latestSentAt","Number","NEGATIVE_INFINITY","writeStorageNotification","storageNotification","localStorage","setItem","JSON","stringify","removeItem","BroadcastChannel","nextChannel","broadcast","postMessage","broadcastLogoutSettlement","publication","settlesSentAt","cleanup"],"sources":["../../../../src/providers/Auth/tabSessionSync/index.ts"],"sourcesContent":["import type {\n CreateTabSessionSyncArgs,\n OnExpiredSession,\n OnLoggedOutSession,\n OnRefreshedSession,\n ReconcileStorageSession,\n TabSessionEvent,\n TabSessionLifecycleOrder,\n TabSessionLogoutPublication,\n TabSessionPublication,\n TabSessionStorageNotification,\n WriteStorageNotification,\n} from './types.js'\n\nimport { TAB_SESSION_EVENT_TYPES } from './types.js'\nimport {\n compareLifecycleOrders,\n createTabSessionMessage,\n getLifecycleOrder,\n isTabSessionMessage,\n parseTabSessionStorageNotification,\n tabSessionSyncChannelName,\n tabSessionSyncStorageKey,\n} from './utilities.js'\n\nexport type {\n CreateTabSessionSyncArgs,\n TabSessionEvent,\n TabSessionEventType,\n TabSessionLogoutPublication,\n TabSessionMessage,\n TabSessionPublication,\n TabSessionReconciliationOptions,\n TabSessionReconciliationResult,\n} from './types.js'\n\nexport { TAB_SESSION_EVENT_TYPES } from './types.js'\n\n/**\n * Coordinates authentication session state across browser tabs.\n *\n * BroadcastChannel carries complete session events when available. Transient localStorage\n * notifications carry lifecycle metadata only and allow tabs to reconcile through\n * `reconcileSession` when BroadcastChannel is unavailable. Lifecycle ordering prevents older async\n * work from replacing a newer session state.\n */\nexport function createTabSessionSync({\n getTokenExpirationMs,\n now = Date.now,\n onSessionExpired,\n onSessionLoggedOut,\n onSessionRefreshed,\n onTabSessionUnauthenticated,\n reconcileSession,\n sourceTabID,\n}: CreateTabSessionSyncArgs): {\n broadcast: (event: TabSessionEvent) => TabSessionPublication\n broadcastLogoutSettlement: (publication: TabSessionLogoutPublication) => void\n cleanup: () => void\n} {\n let channel: BroadcastChannel | undefined\n let isActive = true\n let isStorageListenerInstalled = false\n let latestLifecycleOrder: TabSessionLifecycleOrder | undefined\n let pendingStorageLifecycleOrder: TabSessionLifecycleOrder | undefined\n\n const onExpiredSession: OnExpiredSession = ({ lifecycleOrder, message }) => {\n const localExpirationMs = getTokenExpirationMs()\n\n if (localExpirationMs === undefined || localExpirationMs <= message.expiredTokenAt) {\n pendingStorageLifecycleOrder = undefined\n latestLifecycleOrder = lifecycleOrder\n onSessionExpired(message.expiredTokenAt)\n }\n }\n\n const onLoggedOutSession: OnLoggedOutSession = ({ lifecycleOrder }) => {\n pendingStorageLifecycleOrder = undefined\n latestLifecycleOrder = lifecycleOrder\n onSessionLoggedOut()\n }\n\n const onRefreshedSession: OnRefreshedSession = ({ lifecycleOrder, message }) => {\n pendingStorageLifecycleOrder = undefined\n latestLifecycleOrder = lifecycleOrder\n onSessionRefreshed(message.session)\n }\n\n const handleChannelMessage = ({ data }: MessageEvent<unknown>) => {\n if (!isTabSessionMessage(data) || data.sourceTabID === sourceTabID) {\n return\n }\n\n const lifecycleOrder = getLifecycleOrder(data)\n const lifecycleComparison = latestLifecycleOrder\n ? compareLifecycleOrders({ first: lifecycleOrder, second: latestLifecycleOrder })\n : undefined\n const resolvesPendingStorageBarrier =\n lifecycleComparison === 0 && pendingStorageLifecycleOrder === latestLifecycleOrder\n\n if (\n lifecycleComparison !== undefined &&\n (lifecycleComparison < 0 || (lifecycleComparison === 0 && !resolvesPendingStorageBarrier))\n ) {\n return\n }\n\n switch (data.type) {\n case TAB_SESSION_EVENT_TYPES.EXPIRED:\n onExpiredSession({ lifecycleOrder, message: data })\n break\n\n case TAB_SESSION_EVENT_TYPES.LOGGED_OUT:\n onLoggedOutSession({ lifecycleOrder })\n break\n\n case TAB_SESSION_EVENT_TYPES.REFRESHED:\n onRefreshedSession({ lifecycleOrder, message: data })\n break\n }\n }\n\n const reconcileStorageSession: ReconcileStorageSession = ({ notification }) => {\n const isTabSessionEventStale = () => !isActive || latestLifecycleOrder !== notification\n\n void reconcileSession({ isTabSessionEventStale })\n .then((result) => {\n if (!isTabSessionEventStale()) {\n if (result.status === 'authenticated') {\n pendingStorageLifecycleOrder = undefined\n latestLifecycleOrder = {\n type: TAB_SESSION_EVENT_TYPES.REFRESHED,\n affectedExpirationMs: result.expirationMs,\n refreshStartedAt:\n notification.type === TAB_SESSION_EVENT_TYPES.REFRESHED\n ? notification.refreshStartedAt\n : notification.sentAt,\n sentAt: notification.sentAt,\n sourceTabID: notification.sourceTabID,\n }\n } else if (result.status === 'unauthenticated') {\n pendingStorageLifecycleOrder = undefined\n latestLifecycleOrder = {\n type: TAB_SESSION_EVENT_TYPES.LOGGED_OUT,\n affectedExpirationMs: 0,\n sentAt: notification.sentAt,\n sourceTabID: notification.sourceTabID,\n }\n\n onTabSessionUnauthenticated()\n }\n }\n })\n .catch(() => undefined)\n }\n\n const handleStorageNotification = (event: StorageEvent) => {\n const notification = parseTabSessionStorageNotification(event)\n\n if (!notification || notification.sourceTabID === sourceTabID) {\n return\n }\n\n if (channel && !notification.isBroadcastChannelFallback) {\n return\n }\n\n if (\n latestLifecycleOrder &&\n compareLifecycleOrders({ first: notification, second: latestLifecycleOrder }) <= 0\n ) {\n return\n }\n\n pendingStorageLifecycleOrder = notification\n latestLifecycleOrder = notification\n\n if (notification.type === TAB_SESSION_EVENT_TYPES.LOGGED_OUT) {\n onLoggedOutSession({ lifecycleOrder: notification })\n } else {\n reconcileStorageSession({ notification })\n }\n }\n\n function closeChannel(channelToClose: BroadcastChannel | undefined): void {\n if (!channelToClose) {\n return\n }\n\n try {\n channelToClose.removeEventListener('message', handleChannelMessage)\n } catch {\n // Cross-tab synchronization cleanup is best-effort.\n }\n\n try {\n channelToClose.close()\n } catch {\n // Cross-tab synchronization cleanup is best-effort.\n }\n }\n\n function attachStorageListener(): void {\n if (isStorageListenerInstalled) {\n return\n }\n\n try {\n window.addEventListener('storage', handleStorageNotification)\n isStorageListenerInstalled = true\n } catch {\n // Local authentication must continue when storage events are unavailable.\n }\n }\n\n function downgradeToStorage(): void {\n closeChannel(channel)\n channel = undefined\n attachStorageListener()\n }\n\n function getNextLifecycleTimestamp(): number {\n const currentTime = now()\n const latestSentAt = latestLifecycleOrder?.sentAt ?? Number.NEGATIVE_INFINITY\n\n return currentTime > latestSentAt ? currentTime : latestSentAt + 1\n }\n\n const writeStorageNotification: WriteStorageNotification = ({ notification }) => {\n const storageNotification: TabSessionStorageNotification = channel\n ? notification\n : { ...notification, isBroadcastChannelFallback: true }\n\n try {\n localStorage.setItem(tabSessionSyncStorageKey, JSON.stringify(storageNotification))\n } catch {\n // Local authentication must continue when storage is unavailable.\n }\n\n try {\n localStorage.removeItem(tabSessionSyncStorageKey)\n } catch {\n // Local authentication must continue when storage is unavailable.\n }\n }\n\n if (typeof BroadcastChannel === 'function') {\n let nextChannel: BroadcastChannel | undefined\n\n try {\n nextChannel = new BroadcastChannel(tabSessionSyncChannelName)\n\n nextChannel.addEventListener('message', handleChannelMessage)\n channel = nextChannel\n } catch {\n closeChannel(nextChannel)\n channel = undefined\n }\n }\n\n attachStorageListener()\n\n return {\n broadcast: (event) => {\n const sentAt = getNextLifecycleTimestamp()\n const message = createTabSessionMessage({ event, sentAt, sourceTabID })\n const lifecycleOrder = getLifecycleOrder(message)\n\n pendingStorageLifecycleOrder = undefined\n latestLifecycleOrder = lifecycleOrder\n\n if (channel) {\n try {\n channel.postMessage(message)\n } catch {\n downgradeToStorage()\n }\n }\n\n if (lifecycleOrder.type !== TAB_SESSION_EVENT_TYPES.LOGGED_OUT) {\n writeStorageNotification({ notification: lifecycleOrder })\n }\n\n return lifecycleOrder\n },\n broadcastLogoutSettlement: (publication) => {\n const notification: TabSessionStorageNotification = {\n ...publication,\n sentAt: getNextLifecycleTimestamp(),\n settlesSentAt: publication.sentAt,\n }\n\n pendingStorageLifecycleOrder = undefined\n latestLifecycleOrder = notification\n writeStorageNotification({ notification })\n },\n cleanup: () => {\n isActive = false\n pendingStorageLifecycleOrder = undefined\n closeChannel(channel)\n channel = undefined\n\n if (isStorageListenerInstalled) {\n try {\n window.removeEventListener('storage', handleStorageNotification)\n } catch {\n // Cross-tab synchronization cleanup is best-effort.\n }\n\n isStorageListenerInstalled = false\n }\n },\n }\n}\n"],"mappings":"AAcA,SAASA,uBAAuB,QAAQ;AACxC,SACEC,sBAAsB,EACtBC,uBAAuB,EACvBC,iBAAiB,EACjBC,mBAAmB,EACnBC,kCAAkC,EAClCC,yBAAyB,EACzBC,wBAAwB,QACnB;AAaP,SAASP,uBAAuB,QAAQ;AAExC;;;;;;;;AAQA,OAAO,SAASQ,qBAAqB;EACnCC,oBAAoB;EACpBC,GAAA,GAAMC,IAAA,CAAKD,GAAG;EACdE,gBAAgB;EAChBC,kBAAkB;EAClBC,kBAAkB;EAClBC,2BAA2B;EAC3BC,gBAAgB;EAChBC;AAAW,CACc;EAKzB,IAAIC,OAAA;EACJ,IAAIC,QAAA,GAAW;EACf,IAAIC,0BAAA,GAA6B;EACjC,IAAIC,oBAAA;EACJ,IAAIC,4BAAA;EAEJ,MAAMC,gBAAA,GAAqCA,CAAC;IAAEC,cAAc;IAAEC;EAAO,CAAE;IACrE,MAAMC,iBAAA,GAAoBjB,oBAAA;IAE1B,IAAIiB,iBAAA,KAAsBC,SAAA,IAAaD,iBAAA,IAAqBD,OAAA,CAAQG,cAAc,EAAE;MAClFN,4BAAA,GAA+BK,SAAA;MAC/BN,oBAAA,GAAuBG,cAAA;MACvBZ,gBAAA,CAAiBa,OAAA,CAAQG,cAAc;IACzC;EACF;EAEA,MAAMC,kBAAA,GAAyCA,CAAC;IAAEL;EAAc,CAAE;IAChEF,4BAAA,GAA+BK,SAAA;IAC/BN,oBAAA,GAAuBG,cAAA;IACvBX,kBAAA;EACF;EAEA,MAAMiB,kBAAA,GAAyCA,CAAC;IAAEN,cAAc;IAAEC;EAAO,CAAE;IACzEH,4BAAA,GAA+BK,SAAA;IAC/BN,oBAAA,GAAuBG,cAAA;IACvBV,kBAAA,CAAmBW,OAAA,CAAQM,OAAO;EACpC;EAEA,MAAMC,oBAAA,GAAuBA,CAAC;IAAEC;EAAI,CAAyB;IAC3D,IAAI,CAAC7B,mBAAA,CAAoB6B,IAAA,KAASA,IAAA,CAAKhB,WAAW,KAAKA,WAAA,EAAa;MAClE;IACF;IAEA,MAAMO,cAAA,GAAiBrB,iBAAA,CAAkB8B,IAAA;IACzC,MAAMC,mBAAA,GAAsBb,oBAAA,GACxBpB,sBAAA,CAAuB;MAAEkC,KAAA,EAAOX,cAAA;MAAgBY,MAAA,EAAQf;IAAqB,KAC7EM,SAAA;IACJ,MAAMU,6BAAA,GACJH,mBAAA,KAAwB,KAAKZ,4BAAA,KAAiCD,oBAAA;IAEhE,IACEa,mBAAA,KAAwBP,SAAA,KACvBO,mBAAA,GAAsB,KAAMA,mBAAA,KAAwB,KAAK,CAACG,6BAA6B,GACxF;MACA;IACF;IAEA,QAAQJ,IAAA,CAAKK,IAAI;MACf,KAAKtC,uBAAA,CAAwBuC,OAAO;QAClChB,gBAAA,CAAiB;UAAEC,cAAA;UAAgBC,OAAA,EAASQ;QAAK;QACjD;MAEF,KAAKjC,uBAAA,CAAwBwC,UAAU;QACrCX,kBAAA,CAAmB;UAAEL;QAAe;QACpC;MAEF,KAAKxB,uBAAA,CAAwByC,SAAS;QACpCX,kBAAA,CAAmB;UAAEN,cAAA;UAAgBC,OAAA,EAASQ;QAAK;QACnD;IACJ;EACF;EAEA,MAAMS,uBAAA,GAAmDA,CAAC;IAAEC;EAAY,CAAE;IACxE,MAAMC,sBAAA,GAAyBA,CAAA,KAAM,CAACzB,QAAA,IAAYE,oBAAA,KAAyBsB,YAAA;IAE3E,KAAK3B,gBAAA,CAAiB;MAAE4B;IAAuB,GAC5CC,IAAI,CAAEC,MAAA;MACL,IAAI,CAACF,sBAAA,IAA0B;QAC7B,IAAIE,MAAA,CAAOC,MAAM,KAAK,iBAAiB;UACrCzB,4BAAA,GAA+BK,SAAA;UAC/BN,oBAAA,GAAuB;YACrBiB,IAAA,EAAMtC,uBAAA,CAAwByC,SAAS;YACvCO,oBAAA,EAAsBF,MAAA,CAAOG,YAAY;YACzCC,gBAAA,EACEP,YAAA,CAAaL,IAAI,KAAKtC,uBAAA,CAAwByC,SAAS,GACnDE,YAAA,CAAaO,gBAAgB,GAC7BP,YAAA,CAAaQ,MAAM;YACzBA,MAAA,EAAQR,YAAA,CAAaQ,MAAM;YAC3BlC,WAAA,EAAa0B,YAAA,CAAa1B;UAC5B;QACF,OAAO,IAAI6B,MAAA,CAAOC,MAAM,KAAK,mBAAmB;UAC9CzB,4BAAA,GAA+BK,SAAA;UAC/BN,oBAAA,GAAuB;YACrBiB,IAAA,EAAMtC,uBAAA,CAAwBwC,UAAU;YACxCQ,oBAAA,EAAsB;YACtBG,MAAA,EAAQR,YAAA,CAAaQ,MAAM;YAC3BlC,WAAA,EAAa0B,YAAA,CAAa1B;UAC5B;UAEAF,2BAAA;QACF;MACF;IACF,GACCqC,KAAK,CAAC,MAAMzB,SAAA;EACjB;EAEA,MAAM0B,yBAAA,GAA6BC,KAAA;IACjC,MAAMX,YAAA,GAAetC,kCAAA,CAAmCiD,KAAA;IAExD,IAAI,CAACX,YAAA,IAAgBA,YAAA,CAAa1B,WAAW,KAAKA,WAAA,EAAa;MAC7D;IACF;IAEA,IAAIC,OAAA,IAAW,CAACyB,YAAA,CAAaY,0BAA0B,EAAE;MACvD;IACF;IAEA,IACElC,oBAAA,IACApB,sBAAA,CAAuB;MAAEkC,KAAA,EAAOQ,YAAA;MAAcP,MAAA,EAAQf;IAAqB,MAAM,GACjF;MACA;IACF;IAEAC,4BAAA,GAA+BqB,YAAA;IAC/BtB,oBAAA,GAAuBsB,YAAA;IAEvB,IAAIA,YAAA,CAAaL,IAAI,KAAKtC,uBAAA,CAAwBwC,UAAU,EAAE;MAC5DX,kBAAA,CAAmB;QAAEL,cAAA,EAAgBmB;MAAa;IACpD,OAAO;MACLD,uBAAA,CAAwB;QAAEC;MAAa;IACzC;EACF;EAEA,SAASa,aAAaC,cAA4C;IAChE,IAAI,CAACA,cAAA,EAAgB;MACnB;IACF;IAEA,IAAI;MACFA,cAAA,CAAeC,mBAAmB,CAAC,WAAW1B,oBAAA;IAChD,EAAE,MAAM;MACN;IAAA;IAGF,IAAI;MACFyB,cAAA,CAAeE,KAAK;IACtB,EAAE,MAAM;MACN;IAAA;EAEJ;EAEA,SAASC,sBAAA;IACP,IAAIxC,0BAAA,EAA4B;MAC9B;IACF;IAEA,IAAI;MACFyC,MAAA,CAAOC,gBAAgB,CAAC,WAAWT,yBAAA;MACnCjC,0BAAA,GAA6B;IAC/B,EAAE,MAAM;MACN;IAAA;EAEJ;EAEA,SAAS2C,mBAAA;IACPP,YAAA,CAAatC,OAAA;IACbA,OAAA,GAAUS,SAAA;IACViC,qBAAA;EACF;EAEA,SAASI,0BAAA;IACP,MAAMC,WAAA,GAAcvD,GAAA;IACpB,MAAMwD,YAAA,GAAe7C,oBAAA,EAAsB8B,MAAA,IAAUgB,MAAA,CAAOC,iBAAiB;IAE7E,OAAOH,WAAA,GAAcC,YAAA,GAAeD,WAAA,GAAcC,YAAA,GAAe;EACnE;EAEA,MAAMG,wBAAA,GAAqDA,CAAC;IAAE1B;EAAY,CAAE;IAC1E,MAAM2B,mBAAA,GAAqDpD,OAAA,GACvDyB,YAAA,GACA;MAAE,GAAGA,YAAY;MAAEY,0BAAA,EAA4B;IAAK;IAExD,IAAI;MACFgB,YAAA,CAAaC,OAAO,CAACjE,wBAAA,EAA0BkE,IAAA,CAAKC,SAAS,CAACJ,mBAAA;IAChE,EAAE,MAAM;MACN;IAAA;IAGF,IAAI;MACFC,YAAA,CAAaI,UAAU,CAACpE,wBAAA;IAC1B,EAAE,MAAM;MACN;IAAA;EAEJ;EAEA,IAAI,OAAOqE,gBAAA,KAAqB,YAAY;IAC1C,IAAIC,WAAA;IAEJ,IAAI;MACFA,WAAA,GAAc,IAAID,gBAAA,CAAiBtE,yBAAA;MAEnCuE,WAAA,CAAYf,gBAAgB,CAAC,WAAW9B,oBAAA;MACxCd,OAAA,GAAU2D,WAAA;IACZ,EAAE,MAAM;MACNrB,YAAA,CAAaqB,WAAA;MACb3D,OAAA,GAAUS,SAAA;IACZ;EACF;EAEAiC,qBAAA;EAEA,OAAO;IACLkB,SAAA,EAAYxB,KAAA;MACV,MAAMH,MAAA,GAASa,yBAAA;MACf,MAAMvC,OAAA,GAAUvB,uBAAA,CAAwB;QAAEoD,KAAA;QAAOH,MAAA;QAAQlC;MAAY;MACrE,MAAMO,cAAA,GAAiBrB,iBAAA,CAAkBsB,OAAA;MAEzCH,4BAAA,GAA+BK,SAAA;MAC/BN,oBAAA,GAAuBG,cAAA;MAEvB,IAAIN,OAAA,EAAS;QACX,IAAI;UACFA,OAAA,CAAQ6D,WAAW,CAACtD,OAAA;QACtB,EAAE,MAAM;UACNsC,kBAAA;QACF;MACF;MAEA,IAAIvC,cAAA,CAAec,IAAI,KAAKtC,uBAAA,CAAwBwC,UAAU,EAAE;QAC9D6B,wBAAA,CAAyB;UAAE1B,YAAA,EAAcnB;QAAe;MAC1D;MAEA,OAAOA,cAAA;IACT;IACAwD,yBAAA,EAA4BC,WAAA;MAC1B,MAAMtC,YAAA,GAA8C;QAClD,GAAGsC,WAAW;QACd9B,MAAA,EAAQa,yBAAA;QACRkB,aAAA,EAAeD,WAAA,CAAY9B;MAC7B;MAEA7B,4BAAA,GAA+BK,SAAA;MAC/BN,oBAAA,GAAuBsB,YAAA;MACvB0B,wBAAA,CAAyB;QAAE1B;MAAa;IAC1C;IACAwC,OAAA,EAASA,CAAA;MACPhE,QAAA,GAAW;MACXG,4BAAA,GAA+BK,SAAA;MAC/B6B,YAAA,CAAatC,OAAA;MACbA,OAAA,GAAUS,SAAA;MAEV,IAAIP,0BAAA,EAA4B;QAC9B,IAAI;UACFyC,MAAA,CAAOH,mBAAmB,CAAC,WAAWL,yBAAA;QACxC,EAAE,MAAM;UACN;QAAA;QAGFjC,0BAAA,GAA6B;MAC/B;IACF;EACF;AACF","ignoreList":[]}