@auth0/auth0-spa-js 2.14.0 → 2.16.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/README.md +1 -1
- package/dist/auth0-spa-js.development.js +698 -641
- package/dist/auth0-spa-js.development.js.map +1 -1
- package/dist/auth0-spa-js.production.esm.js +1 -1
- package/dist/auth0-spa-js.production.esm.js.map +1 -1
- package/dist/auth0-spa-js.production.js +1 -1
- package/dist/auth0-spa-js.production.js.map +1 -1
- package/dist/lib/auth0-spa-js.cjs.js +888 -827
- package/dist/lib/auth0-spa-js.cjs.js.map +1 -1
- package/dist/typings/Auth0Client.d.ts +11 -8
- package/dist/typings/cache/cache-manager.d.ts +9 -5
- package/dist/typings/global.d.ts +21 -0
- package/dist/typings/lock.d.ts +32 -0
- package/dist/typings/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/Auth0Client.ts +144 -124
- package/src/cache/cache-manager.ts +11 -11
- package/src/global.ts +22 -0
- package/src/lock.ts +141 -0
- package/src/version.ts +1 -1
package/src/lock.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import BrowserTabsLock from 'browser-tabs-lock';
|
|
2
|
+
import { TimeoutError } from './errors';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Lock manager abstraction for cross-tab synchronization.
|
|
6
|
+
* Supports both modern Web Locks API and legacy localStorage-based locking.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Lock manager interface - callback pattern ensures automatic lock release */
|
|
10
|
+
export interface ILockManager {
|
|
11
|
+
/**
|
|
12
|
+
* Run callback while holding a lock.
|
|
13
|
+
* Lock is automatically released when callback completes or throws.
|
|
14
|
+
*
|
|
15
|
+
* @param key - Lock identifier
|
|
16
|
+
* @param timeout - Maximum time to wait for lock acquisition (ms)
|
|
17
|
+
* @param callback - Function to execute while holding the lock
|
|
18
|
+
* @returns Promise resolving to callback's return value
|
|
19
|
+
* @throws Error if lock cannot be acquired within timeout
|
|
20
|
+
*/
|
|
21
|
+
runWithLock<T>(
|
|
22
|
+
key: string,
|
|
23
|
+
timeout: number,
|
|
24
|
+
callback: () => Promise<T>
|
|
25
|
+
): Promise<T>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Web Locks API implementation - true mutex with OS-level queuing */
|
|
29
|
+
export class WebLocksApiManager implements ILockManager {
|
|
30
|
+
async runWithLock<T>(
|
|
31
|
+
key: string,
|
|
32
|
+
timeout: number,
|
|
33
|
+
callback: () => Promise<T>
|
|
34
|
+
): Promise<T> {
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
return await navigator.locks.request(
|
|
40
|
+
key,
|
|
41
|
+
{ mode: 'exclusive', signal: controller.signal },
|
|
42
|
+
async lock => {
|
|
43
|
+
clearTimeout(timeoutId);
|
|
44
|
+
if (!lock) throw new Error('Lock not available');
|
|
45
|
+
return await callback();
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
clearTimeout(timeoutId);
|
|
50
|
+
if (error?.name === 'AbortError') throw new TimeoutError();
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Legacy localStorage-based locking with retry logic for older browsers */
|
|
57
|
+
export class LegacyLockManager implements ILockManager {
|
|
58
|
+
private lock: BrowserTabsLock;
|
|
59
|
+
private activeLocks: Set<string> = new Set();
|
|
60
|
+
private pagehideHandler: () => void;
|
|
61
|
+
|
|
62
|
+
constructor() {
|
|
63
|
+
this.lock = new BrowserTabsLock();
|
|
64
|
+
|
|
65
|
+
this.pagehideHandler = () => {
|
|
66
|
+
this.activeLocks.forEach(key => this.lock.releaseLock(key));
|
|
67
|
+
this.activeLocks.clear();
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async runWithLock<T>(
|
|
72
|
+
key: string,
|
|
73
|
+
timeout: number,
|
|
74
|
+
callback: () => Promise<T>
|
|
75
|
+
): Promise<T> {
|
|
76
|
+
// Retry logic to handle race conditions in localStorage-based locking
|
|
77
|
+
const retryAttempts = 10;
|
|
78
|
+
let acquired = false;
|
|
79
|
+
|
|
80
|
+
for (let i = 0; i < retryAttempts && !acquired; i++) {
|
|
81
|
+
acquired = await this.lock.acquireLock(key, timeout);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!acquired) {
|
|
85
|
+
throw new TimeoutError();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.activeLocks.add(key);
|
|
89
|
+
|
|
90
|
+
// Add pagehide listener when acquiring first lock
|
|
91
|
+
if (this.activeLocks.size === 1 && typeof window !== 'undefined') {
|
|
92
|
+
window.addEventListener('pagehide', this.pagehideHandler);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
return await callback();
|
|
97
|
+
} finally {
|
|
98
|
+
this.activeLocks.delete(key);
|
|
99
|
+
await this.lock.releaseLock(key);
|
|
100
|
+
|
|
101
|
+
// Remove pagehide listener when all locks are released
|
|
102
|
+
if (this.activeLocks.size === 0 && typeof window !== 'undefined') {
|
|
103
|
+
window.removeEventListener('pagehide', this.pagehideHandler);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Feature detection for Web Locks API support
|
|
111
|
+
*/
|
|
112
|
+
function isWebLocksSupported(): boolean {
|
|
113
|
+
return (
|
|
114
|
+
typeof navigator !== 'undefined' &&
|
|
115
|
+
typeof navigator.locks?.request === 'function'
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function createLockManager(): ILockManager {
|
|
120
|
+
return isWebLocksSupported()
|
|
121
|
+
? new WebLocksApiManager()
|
|
122
|
+
: new LegacyLockManager();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get the singleton lock manager instance.
|
|
127
|
+
* Uses Web Locks API in modern browsers, falls back to localStorage in older browsers.
|
|
128
|
+
*/
|
|
129
|
+
let lockManager: ILockManager | null = null;
|
|
130
|
+
|
|
131
|
+
export function getLockManager(): ILockManager {
|
|
132
|
+
if (!lockManager) {
|
|
133
|
+
lockManager = createLockManager();
|
|
134
|
+
}
|
|
135
|
+
return lockManager;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// For testing: allow resetting the singleton
|
|
139
|
+
export function resetLockManager(): void {
|
|
140
|
+
lockManager = null;
|
|
141
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '2.
|
|
1
|
+
export default '2.16.0';
|