fetchguard 2.2.0 → 2.2.1

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.
@@ -0,0 +1,643 @@
1
+ import { Result } from 'ts-micro-result';
2
+
3
+ /**
4
+ * FetchGuard Business Types
5
+ *
6
+ * Contains domain logic types: Provider, Config, API Response, etc.
7
+ * For message protocol types, see messages.ts
8
+ */
9
+ /**
10
+ * Token info returned from provider
11
+ *
12
+ * All fields are optional to support various custom auth methods:
13
+ * - Standard login/refresh: returns token + optional fields
14
+ * - Update user info: may only return user (no token update)
15
+ * - Verify OTP: may return nothing (just validation)
16
+ * - Custom auth flows: flexible field combinations
17
+ */
18
+ interface TokenInfo {
19
+ token?: string | null;
20
+ expiresAt?: number | null;
21
+ refreshToken?: string | null;
22
+ user?: unknown;
23
+ }
24
+ /**
25
+ * Options for token exchange operation
26
+ *
27
+ * Used when switching tenant, changing scope, or any operation
28
+ * that exchanges current token for a new one with different claims
29
+ */
30
+ interface ExchangeTokenOptions {
31
+ /** HTTP method to use. Default: 'POST' */
32
+ method?: 'POST' | 'PUT';
33
+ /** Payload to send with the request (e.g., tenantId, scope) */
34
+ payload?: Record<string, unknown>;
35
+ /** Custom headers for this request. Overrides defaultHeaders if same key. */
36
+ headers?: Record<string, string>;
37
+ }
38
+ /**
39
+ * Auth result returned from auth operations and auth state changes
40
+ * Used by: login(), logout(), refreshToken(), onAuthStateChanged()
41
+ */
42
+ interface AuthResult {
43
+ /** Whether user is authenticated (has valid non-expired token) */
44
+ authenticated: boolean;
45
+ /** User info from token (if available) */
46
+ user?: unknown;
47
+ /** Token expiry timestamp in milliseconds (if available) */
48
+ expiresAt?: number | null;
49
+ }
50
+ /**
51
+ * Interface for token provider
52
+ *
53
+ * Provider has 3 required methods:
54
+ * - refreshToken: Refresh access token when expired
55
+ * - login: Login with credentials
56
+ * - logout: Logout (clear tokens)
57
+ *
58
+ * User can add custom auth methods (loginWithPhone, loginWithGoogle, etc.)
59
+ * All custom methods must return Result<TokenInfo> for token retrieval
60
+ */
61
+ interface TokenProvider {
62
+ /**
63
+ * Refresh tokens (required)
64
+ * @param refreshToken - Current refresh token (from worker memory, null if not available)
65
+ * @returns Result<TokenInfo> with new tokens
66
+ */
67
+ refreshToken(refreshToken: string | null): Promise<Result<TokenInfo>>;
68
+ /**
69
+ * Login with credentials (required)
70
+ * @param payload - Login credentials (email/password, etc.)
71
+ * @param url - Optional URL override (if not provided, uses configured loginUrl)
72
+ * @returns Result<TokenInfo> with tokens
73
+ */
74
+ login(payload: unknown, url?: string): Promise<Result<TokenInfo>>;
75
+ /**
76
+ * Logout - clear tokens (required)
77
+ * @param payload - Optional logout payload
78
+ * @returns Result<TokenInfo> with all fields reset (token = '', refreshToken = undefined, user = undefined)
79
+ */
80
+ logout(payload?: unknown): Promise<Result<TokenInfo>>;
81
+ /**
82
+ * Exchange current token for a new one with different context
83
+ *
84
+ * Useful for switching tenants, changing scopes, or any operation
85
+ * that requires exchanging the current token for a new one.
86
+ *
87
+ * @param accessToken - Current access token (injected by worker)
88
+ * @param url - URL to call for token exchange
89
+ * @param options - Exchange options (method, payload)
90
+ * @returns Result<TokenInfo> with new tokens
91
+ */
92
+ exchangeToken(accessToken: string, url: string, options?: ExchangeTokenOptions): Promise<Result<TokenInfo>>;
93
+ /**
94
+ * Custom auth methods (optional)
95
+ * Examples: loginWithPhone, loginWithGoogle, loginWithFacebook, etc.
96
+ * All must return Result<TokenInfo> for token retrieval
97
+ *
98
+ * Note: Using any[] for args to allow flexible custom auth methods
99
+ * while maintaining type compatibility with specific method signatures above
100
+ */
101
+ [key: string]: (...args: any[]) => Promise<Result<TokenInfo>>;
102
+ }
103
+ /**
104
+ * Storage error context for debugging
105
+ */
106
+ type StorageErrorContext = 'get' | 'set' | 'delete' | 'open';
107
+ /**
108
+ * Storage error callback type
109
+ * Called when IndexedDB operations fail (quota exceeded, permission denied, etc.)
110
+ * Storage still fails closed (returns null), but this allows logging/debugging.
111
+ */
112
+ type StorageErrorCallback = (error: Error, context: StorageErrorContext) => void;
113
+ /**
114
+ * Interface for refresh token storage - only stores refresh token
115
+ *
116
+ * Access token is always stored in worker memory.
117
+ * Refresh token storage is OPTIONAL:
118
+ * - If available (IndexedDB): persist refresh token for reuse after reload
119
+ * - If not (undefined): cookie-based auth (httpOnly cookie)
120
+ */
121
+ interface RefreshTokenStorage {
122
+ get(): Promise<string | null>;
123
+ set(token: string | null): Promise<void>;
124
+ }
125
+ /**
126
+ * Interface for token parser - parse token from backend response
127
+ * Parser returns complete TokenInfo (including user data)
128
+ */
129
+ interface TokenParser {
130
+ parse(response: Response): Promise<TokenInfo>;
131
+ }
132
+ /**
133
+ * Interface for auth strategy - defines how to call auth APIs
134
+ *
135
+ * Strategy focuses only on API calls, returns Response
136
+ * Provider handles parsing and storage
137
+ *
138
+ * All methods are required
139
+ */
140
+ interface AuthStrategy {
141
+ /** Refresh access token */
142
+ refresh(refreshToken: string | null): Promise<Response>;
143
+ /**
144
+ * Login with credentials
145
+ * @param payload - Login credentials
146
+ * @param url - Optional URL override (if not provided, uses configured loginUrl)
147
+ */
148
+ login(payload: unknown, url?: string): Promise<Response>;
149
+ /** Logout */
150
+ logout(payload?: unknown): Promise<Response>;
151
+ /**
152
+ * Exchange current token for a new one with different context
153
+ * @param accessToken - Current access token
154
+ * @param url - URL to call for token exchange
155
+ * @param options - Exchange options (method, payload)
156
+ */
157
+ exchangeToken(accessToken: string, url: string, options?: ExchangeTokenOptions): Promise<Response>;
158
+ }
159
+ /**
160
+ * Provider preset configuration for built-in auth strategies
161
+ */
162
+ interface ProviderPresetConfig {
163
+ type: 'cookie-auth' | 'body-auth';
164
+ refreshUrl: string;
165
+ loginUrl: string;
166
+ logoutUrl: string;
167
+ refreshTokenKey?: string;
168
+ /** Custom headers to include in all auth requests (login, logout, refresh) */
169
+ headers?: Record<string, string>;
170
+ }
171
+ /**
172
+ * Configuration for FetchGuard client
173
+ */
174
+ interface FetchGuardOptions {
175
+ /**
176
+ * Token provider - 3 options:
177
+ * 1. TokenProvider instance (for custom providers)
178
+ * 2. ProviderPresetConfig object (for built-in presets)
179
+ * 3. string (for registry lookup - advanced usage)
180
+ */
181
+ provider: TokenProvider | ProviderPresetConfig | string;
182
+ /** List of allowed domains (wildcard supported) */
183
+ allowedDomains?: string[];
184
+ /** Early refresh time for tokens (ms) */
185
+ refreshEarlyMs?: number;
186
+ /** Default headers to include in all requests */
187
+ defaultHeaders?: Record<string, string>;
188
+ /**
189
+ * Maximum concurrent requests to worker (default: 6)
190
+ * Controls how many requests can be in-flight simultaneously.
191
+ * Set to 1 for strictly sequential processing.
192
+ * Higher values increase throughput but may cause worker congestion.
193
+ */
194
+ maxConcurrent?: number;
195
+ /**
196
+ * Maximum queue size for pending requests (default: 1000)
197
+ * When queue is full, new requests will immediately fail with QUEUE_FULL error.
198
+ * Prevents memory leak if worker is unresponsive.
199
+ */
200
+ maxQueueSize?: number;
201
+ /**
202
+ * Worker setup timeout in milliseconds (default: 10000)
203
+ * How long to wait for worker to be ready before failing.
204
+ */
205
+ setupTimeout?: number;
206
+ /**
207
+ * Default request timeout in milliseconds (default: 30000)
208
+ * How long to wait for a request to complete before timing out.
209
+ * Can be overridden per-request via fetch options.
210
+ */
211
+ requestTimeout?: number;
212
+ /**
213
+ * Debug hooks for observing operations (logging, monitoring)
214
+ * All hooks are observe-only - they cannot modify requests/responses.
215
+ */
216
+ debug?: DebugHooks;
217
+ /**
218
+ * Retry configuration for network errors
219
+ * Only retries on transport failures, NOT on HTTP errors (4xx/5xx)
220
+ */
221
+ retry?: RetryConfig;
222
+ /**
223
+ * Request deduplication configuration
224
+ * When enabled, duplicate requests to the same URL within a time window
225
+ * will share the same response instead of making multiple requests.
226
+ */
227
+ dedupe?: DedupeConfig;
228
+ /**
229
+ * Custom worker factory function
230
+ *
231
+ * Use this when you need a custom provider with parser/strategy functions.
232
+ * Create a custom worker file that imports 'fetchguard/worker' and registers
233
+ * your provider, then pass a factory function that creates that worker.
234
+ *
235
+ * @example
236
+ * ```ts
237
+ * // my-worker.ts
238
+ * import 'fetchguard/worker'
239
+ * import { registerProvider, createProvider, ... } from 'fetchguard'
240
+ * const myProvider = createProvider({ parser: myParser, strategy: myStrategy, ... })
241
+ * registerProvider('my-auth', myProvider)
242
+ *
243
+ * // main.ts
244
+ * import MyWorker from './my-worker?worker'
245
+ * const api = createClient({
246
+ * provider: 'my-auth',
247
+ * workerFactory: () => new MyWorker()
248
+ * })
249
+ * ```
250
+ */
251
+ workerFactory?: () => Worker;
252
+ }
253
+ /**
254
+ * Internal worker configuration
255
+ */
256
+ interface WorkerConfig {
257
+ allowedDomains: string[];
258
+ refreshEarlyMs: number;
259
+ defaultHeaders: Record<string, string>;
260
+ }
261
+ /**
262
+ * Extended RequestInit with FetchGuard-specific options
263
+ */
264
+ interface FetchGuardRequestInit extends RequestInit {
265
+ /** Whether this request requires authentication. Default: true */
266
+ requiresAuth?: boolean;
267
+ /** Include response headers in result metadata and FETCH_RESULT payload */
268
+ includeHeaders?: boolean;
269
+ }
270
+ /**
271
+ * Fetch envelope - raw HTTP response from worker
272
+ *
273
+ * Worker only fetches and returns raw data, does NOT judge HTTP status.
274
+ * Client receives envelope and decides ok/err based on business logic.
275
+ *
276
+ * - status: HTTP status code (2xx, 3xx, 4xx, 5xx)
277
+ * - body: string (text/JSON) or base64 (binary)
278
+ * - contentType: always present, indicates how to decode body
279
+ * - headers: empty object if includeHeaders: false
280
+ */
281
+ interface FetchEnvelope {
282
+ status: number;
283
+ body: string;
284
+ contentType: string;
285
+ headers: Record<string, string>;
286
+ }
287
+ /**
288
+ * Serialized file data for transfer over postMessage
289
+ * Uses ArrayBuffer for zero-copy transfer via Transferable
290
+ */
291
+ interface SerializedFile {
292
+ name: string;
293
+ type: string;
294
+ /** ArrayBuffer - transferred via postMessage Transferable for zero-copy */
295
+ buffer: ArrayBuffer;
296
+ }
297
+ /**
298
+ * Serialized FormData entry - can be string or file
299
+ */
300
+ type SerializedFormDataEntry = string | SerializedFile;
301
+ /**
302
+ * Serialized FormData for transfer over postMessage
303
+ */
304
+ interface SerializedFormData {
305
+ _type: 'FormData';
306
+ entries: Array<[string, SerializedFormDataEntry]>;
307
+ }
308
+ /**
309
+ * Result of FormData serialization with transferables
310
+ * Used for zero-copy transfer via postMessage
311
+ */
312
+ interface SerializedFormDataResult {
313
+ data: SerializedFormData;
314
+ /** ArrayBuffers to transfer - pass to postMessage as second argument */
315
+ transferables: ArrayBuffer[];
316
+ }
317
+ /**
318
+ * Network error detail for transport failures
319
+ * Used when no HTTP response is received (connection failed, timeout, cancelled)
320
+ */
321
+ interface NetworkErrorDetail {
322
+ code: 'NETWORK_ERROR' | 'REQUEST_CANCELLED' | 'RESPONSE_PARSE_FAILED';
323
+ message: string;
324
+ }
325
+ /**
326
+ * Reason for token refresh
327
+ */
328
+ type RefreshReason = 'expired' | 'proactive' | 'manual';
329
+ /**
330
+ * Request timing metrics for performance monitoring
331
+ *
332
+ * All times are in milliseconds.
333
+ */
334
+ interface RequestMetrics {
335
+ /** When request was initiated (Date.now()) */
336
+ startTime: number;
337
+ /** When response was received (Date.now()) */
338
+ endTime: number;
339
+ /** Total duration (endTime - startTime) */
340
+ duration: number;
341
+ /** Time spent waiting in queue before processing */
342
+ queueTime: number;
343
+ /** Time spent in IPC (postMessage round-trip overhead) */
344
+ ipcTime: number;
345
+ }
346
+ /**
347
+ * Debug hooks for observing FetchGuard operations
348
+ *
349
+ * All hooks are observe-only - they cannot modify requests/responses.
350
+ * Useful for logging, debugging, and monitoring.
351
+ *
352
+ * Note: Hooks run synchronously and should not perform heavy operations.
353
+ */
354
+ interface DebugHooks {
355
+ /**
356
+ * Called before each request is sent to worker
357
+ * @param url - Request URL
358
+ * @param options - Request options (method, headers, etc.)
359
+ */
360
+ onRequest?: (url: string, options: FetchGuardRequestInit) => void;
361
+ /**
362
+ * Called when response is received from worker
363
+ * @param url - Request URL
364
+ * @param envelope - Response envelope (status, body, headers)
365
+ * @param metrics - Request timing metrics (optional, for performance monitoring)
366
+ */
367
+ onResponse?: (url: string, envelope: FetchEnvelope, metrics?: RequestMetrics) => void;
368
+ /**
369
+ * Called when token refresh occurs
370
+ * @param reason - Why refresh happened: 'expired', 'proactive', or 'manual'
371
+ */
372
+ onRefresh?: (reason: RefreshReason) => void;
373
+ /**
374
+ * Called when transport error occurs (network failure, timeout, cancelled)
375
+ * @param url - Request URL
376
+ * @param error - Error detail with code and message
377
+ * @param metrics - Request timing metrics (optional)
378
+ */
379
+ onError?: (url: string, error: NetworkErrorDetail, metrics?: RequestMetrics) => void;
380
+ /**
381
+ * Called when worker is ready after initialization
382
+ */
383
+ onWorkerReady?: () => void;
384
+ /**
385
+ * Called when worker encounters a fatal error
386
+ * @param error - Error event from worker
387
+ */
388
+ onWorkerError?: (error: ErrorEvent) => void;
389
+ }
390
+ /**
391
+ * Retry configuration for network errors
392
+ *
393
+ * Only retries on transport failures (network error, timeout).
394
+ * Does NOT retry on HTTP errors (4xx/5xx) - those are valid responses.
395
+ * Does NOT retry cancelled requests.
396
+ */
397
+ interface RetryConfig {
398
+ /**
399
+ * Maximum number of retry attempts (default: 0 = no retry)
400
+ */
401
+ maxAttempts?: number;
402
+ /**
403
+ * Delay between retries in milliseconds (default: 1000)
404
+ */
405
+ delay?: number;
406
+ /**
407
+ * Exponential backoff multiplier (default: 1 = no backoff)
408
+ * Example: delay=1000, backoff=2 => 1s, 2s, 4s, 8s...
409
+ */
410
+ backoff?: number;
411
+ /**
412
+ * Maximum delay in milliseconds (default: 30000)
413
+ * Caps the delay when using exponential backoff
414
+ */
415
+ maxDelay?: number;
416
+ /**
417
+ * Jitter factor to add randomness to retry delays (default: 0 = no jitter)
418
+ * Range: 0 to 1 (e.g., 0.5 = ±50% randomness)
419
+ * Helps prevent thundering herd when many clients retry simultaneously.
420
+ *
421
+ * Note: Jitter is only applied when shouldRetry returns true.
422
+ * If request fails permanently, no jitter delay occurs.
423
+ *
424
+ * Example: delay=1000, jitter=0.5 => delay between 500ms and 1500ms
425
+ */
426
+ jitter?: number;
427
+ /**
428
+ * Custom condition to determine if error should be retried
429
+ * Default: retry on NETWORK_ERROR only
430
+ * @param error - The error that occurred
431
+ * @returns true to retry, false to fail immediately
432
+ */
433
+ shouldRetry?: (error: NetworkErrorDetail) => boolean;
434
+ }
435
+ /**
436
+ * Request deduplication configuration
437
+ *
438
+ * When enabled, identical GET requests (same URL) within a time window
439
+ * will share the same in-flight request instead of making duplicates.
440
+ *
441
+ * IMPORTANT:
442
+ * - Only applies to GET requests (POST/PUT/DELETE are never deduplicated)
443
+ * - Only deduplicates in-flight requests (not caching)
444
+ * - Safe for most read operations
445
+ */
446
+ interface DedupeConfig {
447
+ /**
448
+ * Enable deduplication (default: false)
449
+ */
450
+ enabled?: boolean;
451
+ /**
452
+ * Time window in milliseconds to consider requests as duplicates (default: 0)
453
+ * 0 = only dedupe concurrent/in-flight requests
454
+ * >0 = also dedupe requests within this time window after completion
455
+ */
456
+ window?: number;
457
+ /**
458
+ * Custom key generator for deduplication
459
+ * Default: uses URL only for GET requests
460
+ * @param url - Request URL
461
+ * @param options - Request options
462
+ * @returns Key string, or null to skip deduplication for this request
463
+ */
464
+ keyGenerator?: (url: string, options: FetchGuardRequestInit) => string | null;
465
+ }
466
+ /**
467
+ * Transport result - represents the outcome of a network request
468
+ *
469
+ * IMPORTANT: This is a TRANSPORT result, not a business result.
470
+ * - ok = HTTP response received (check envelope.status for 2xx/4xx/5xx)
471
+ * - err = Network failure (no response received)
472
+ *
473
+ * Example:
474
+ * ```typescript
475
+ * const result = await api.get('/users')
476
+ * if (result.ok) {
477
+ * // Transport succeeded - got HTTP response
478
+ * if (result.data.status >= 200 && result.data.status < 400) {
479
+ * // Business success
480
+ * } else {
481
+ * // Business error (4xx/5xx) - still has response body
482
+ * }
483
+ * } else {
484
+ * // Transport failed - no response (network error, timeout, cancelled)
485
+ * }
486
+ * ```
487
+ */
488
+ type TransportResult = Result<FetchEnvelope>;
489
+
490
+ /**
491
+ * Register a token provider with name
492
+ */
493
+ declare function registerProvider(name: string, provider: TokenProvider): void;
494
+ /**
495
+ * Get provider by name
496
+ */
497
+ declare function getProvider(name: string): TokenProvider;
498
+ /**
499
+ * Check if provider exists
500
+ */
501
+ declare function hasProvider(name: string): boolean;
502
+ /**
503
+ * Get list of all provider names
504
+ */
505
+ declare function listProviders(): string[];
506
+ /**
507
+ * Remove provider
508
+ */
509
+ declare function unregisterProvider(name: string): boolean;
510
+ /**
511
+ * Remove all providers
512
+ */
513
+ declare function clearProviders(): void;
514
+
515
+ /**
516
+ * Custom auth method type
517
+ */
518
+ type CustomAuthMethod = (...args: unknown[]) => Promise<Result<TokenInfo>>;
519
+ /**
520
+ * Configuration for creating provider
521
+ *
522
+ * refreshStorage: OPTIONAL - to load refresh token initially when worker starts
523
+ * - undefined: cookie-based auth (httpOnly cookie, no need to load)
524
+ * - RefreshTokenStorage: body-based auth (load from IndexedDB on startup)
525
+ *
526
+ * strategy: AuthStrategy with refresh (required), login/logout (required)
527
+ *
528
+ * customMethods: OPTIONAL - custom auth methods (loginWithPhone, loginWithGoogle, etc.)
529
+ */
530
+ interface ProviderConfig {
531
+ refreshStorage?: RefreshTokenStorage;
532
+ parser: TokenParser;
533
+ strategy: AuthStrategy;
534
+ customMethods?: Record<string, CustomAuthMethod>;
535
+ }
536
+ /**
537
+ * Factory function to create TokenProvider from modular components
538
+ *
539
+ * Provider automatically handles refresh token:
540
+ * - If refreshToken is null and storage exists → load from storage initially
541
+ * - If refreshToken exists → use token from worker memory
542
+ * - Cookie-based (no storage) → always null
543
+ *
544
+ * Custom methods:
545
+ * - User can add custom auth methods (loginWithPhone, loginWithGoogle, etc.)
546
+ * - Custom methods will be spread into provider object
547
+ */
548
+ declare function createProvider(config: ProviderConfig): TokenProvider;
549
+
550
+ /**
551
+ * IndexedDB storage options
552
+ */
553
+ interface IndexedDBStorageOptions {
554
+ /** Database name (default: 'FetchGuardDB') */
555
+ dbName?: string;
556
+ /** Key for refresh token (default: 'refreshToken') */
557
+ refreshTokenKey?: string;
558
+ /**
559
+ * Error callback for debugging storage failures
560
+ * Called when IndexedDB operations fail (quota exceeded, permission denied, etc.)
561
+ * Storage still fails closed (returns null), but this allows logging/debugging.
562
+ */
563
+ onError?: StorageErrorCallback;
564
+ }
565
+ /**
566
+ * IndexedDB storage - only stores refresh token in IndexedDB
567
+ * Suitable for body-based refresh strategy
568
+ * Persists refresh token for reuse after reload
569
+ *
570
+ * @param options - Storage options or legacy dbName string
571
+ * @param legacyRefreshTokenKey - Legacy refreshTokenKey (for backward compatibility)
572
+ */
573
+ declare function createIndexedDBStorage(options?: IndexedDBStorageOptions | string, legacyRefreshTokenKey?: string): RefreshTokenStorage;
574
+
575
+ /**
576
+ * Body parser - parse token from response body (JSON)
577
+ * Expects response format: { data: { accessToken, refreshToken, expiresAt?, user? } }
578
+ */
579
+ declare const bodyParser: TokenParser;
580
+
581
+ /**
582
+ * Cookie parser - parse access token from response body
583
+ * Expects response format: { data: { accessToken, expiresAt?, user? } }
584
+ * Refresh token is automatically set by backend into httpOnly cookie
585
+ */
586
+ declare const cookieParser: TokenParser;
587
+
588
+ /**
589
+ * Cookie auth strategy - all auth operations via httpOnly cookies
590
+ * Suitable for SSR and cross-domain authentication
591
+ *
592
+ * Refresh token is sent automatically via httpOnly cookie
593
+ * Credentials are sent in request body
594
+ *
595
+ * Login URL can be:
596
+ * - Configured once: loginUrl: 'https://api.example.com/auth/login'
597
+ * - Passed per call: login(payload, 'https://...')
598
+ *
599
+ * Header priority (lowest to highest):
600
+ * - defaultHeaders (from FetchGuardOptions)
601
+ * - headers (from ProviderPresetConfig)
602
+ * - Content-Type: application/json
603
+ */
604
+ declare function createCookieStrategy(config: {
605
+ refreshUrl: string;
606
+ loginUrl: string;
607
+ logoutUrl: string;
608
+ headers?: Record<string, string>;
609
+ defaultHeaders?: Record<string, string>;
610
+ }): AuthStrategy;
611
+ /**
612
+ * Standard cookie strategy
613
+ */
614
+ declare const cookieStrategy: AuthStrategy;
615
+
616
+ /**
617
+ * Body auth strategy - all auth operations via request body
618
+ * Suitable for SPA applications
619
+ *
620
+ * All tokens/credentials are sent in request body
621
+ *
622
+ * Login URL can be:
623
+ * - Configured once: loginUrl: 'https://api.example.com/auth/login'
624
+ * - Passed per call: login(payload, 'https://...')
625
+ *
626
+ * Header priority (lowest to highest):
627
+ * - defaultHeaders (from FetchGuardOptions)
628
+ * - headers (from ProviderPresetConfig)
629
+ * - Content-Type: application/json
630
+ */
631
+ declare function createBodyStrategy(config: {
632
+ refreshUrl: string;
633
+ loginUrl: string;
634
+ logoutUrl: string;
635
+ headers?: Record<string, string>;
636
+ defaultHeaders?: Record<string, string>;
637
+ }): AuthStrategy;
638
+ /**
639
+ * Standard body strategy
640
+ */
641
+ declare const bodyStrategy: AuthStrategy;
642
+
643
+ export { type AuthResult as A, cookieStrategy as B, createCookieStrategy as C, type DebugHooks as D, type ExchangeTokenOptions as E, type FetchGuardOptions as F, bodyStrategy as G, createBodyStrategy as H, type IndexedDBStorageOptions as I, type NetworkErrorDetail as N, type ProviderPresetConfig as P, type RefreshReason as R, type SerializedFormDataResult as S, type TokenProvider as T, type WorkerConfig as W, type FetchGuardRequestInit as a, type FetchEnvelope as b, type SerializedFormData as c, type RefreshTokenStorage as d, type TokenParser as e, type AuthStrategy as f, type TokenInfo as g, type SerializedFile as h, type SerializedFormDataEntry as i, type TransportResult as j, type StorageErrorContext as k, type StorageErrorCallback as l, type RetryConfig as m, type DedupeConfig as n, type RequestMetrics as o, getProvider as p, hasProvider as q, registerProvider as r, listProviders as s, clearProviders as t, unregisterProvider as u, createProvider as v, type ProviderConfig as w, createIndexedDBStorage as x, bodyParser as y, cookieParser as z };
package/dist/worker.d.ts CHANGED
@@ -1,2 +1,2 @@
1
-
2
- export { }
1
+ export { f as AuthStrategy, E as ExchangeTokenOptions, d as RefreshTokenStorage, e as TokenParser, T as TokenProvider, y as bodyParser, t as clearProviders, z as cookieParser, H as createBodyStrategy, C as createCookieStrategy, x as createIndexedDBStorage, v as createProvider, q as hasProvider, s as listProviders, r as registerProvider, u as unregisterProvider } from './worker-DBL8XAZJ.js';
2
+ import 'ts-micro-result';
package/dist/worker.js CHANGED
@@ -162,6 +162,15 @@ function sendTokenRefreshed(reason) {
162
162
 
163
163
  // src/utils/registry.ts
164
164
  var registry = /* @__PURE__ */ new Map();
165
+ function registerProvider(name, provider) {
166
+ if (typeof name !== "string" || !name.trim()) {
167
+ throw new Error("Provider name must be a non-empty string");
168
+ }
169
+ if (!provider || typeof provider.refreshToken !== "function") {
170
+ throw new Error("Provider must implement TokenProvider interface");
171
+ }
172
+ registry.set(name, provider);
173
+ }
165
174
  function getProvider(name) {
166
175
  const provider = registry.get(name);
167
176
  if (!provider) {
@@ -169,6 +178,18 @@ function getProvider(name) {
169
178
  }
170
179
  return provider;
171
180
  }
181
+ function hasProvider(name) {
182
+ return registry.has(name);
183
+ }
184
+ function listProviders() {
185
+ return Array.from(registry.keys());
186
+ }
187
+ function unregisterProvider(name) {
188
+ return registry.delete(name);
189
+ }
190
+ function clearProviders() {
191
+ registry.clear();
192
+ }
172
193
 
173
194
  // src/provider/create-provider.ts
174
195
  import { ok, err } from "ts-micro-result";
@@ -856,4 +877,17 @@ function isBinaryContentType(contentType) {
856
877
  }
857
878
  };
858
879
  })();
880
+ export {
881
+ bodyParser,
882
+ clearProviders,
883
+ cookieParser,
884
+ createBodyStrategy,
885
+ createCookieStrategy,
886
+ createIndexedDBStorage,
887
+ createProvider,
888
+ hasProvider,
889
+ listProviders,
890
+ registerProvider,
891
+ unregisterProvider
892
+ };
859
893
  //# sourceMappingURL=worker.js.map