fetchguard 1.3.2 → 1.5.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 +10 -10
- package/dist/index.d.ts +36 -30
- package/dist/index.js +41 -26
- package/dist/index.js.map +1 -1
- package/dist/worker.js +90 -36
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -449,11 +449,7 @@ The library targets modern browsers with Web Worker and (optionally) IndexedDB s
|
|
|
449
449
|
- Config object: `{ type: 'cookie-auth' | 'body-auth', refreshUrl, loginUrl, logoutUrl, ... }`
|
|
450
450
|
- String: Registered provider name
|
|
451
451
|
- `allowedDomains?`: `string[]` - Domain whitelist (supports wildcards)
|
|
452
|
-
- `debug?`: `boolean` - Enable debug logging in worker
|
|
453
452
|
- `refreshEarlyMs?`: `number` - Refresh token X ms before expiry (default: 60000)
|
|
454
|
-
- `defaultTimeoutMs?`: `number` - Request timeout (default: 120000)
|
|
455
|
-
- `retryCount?`: `number` - Retry failed requests (default: 3)
|
|
456
|
-
- `retryDelayMs?`: `number` - Delay between retries (default: 1000)
|
|
457
453
|
|
|
458
454
|
### FetchGuardClient Methods
|
|
459
455
|
|
|
@@ -463,8 +459,8 @@ The library targets modern browsers with Web Worker and (optionally) IndexedDB s
|
|
|
463
459
|
- `onReady(callback)`: `() => void` - Subscribe to ready event (returns unsubscribe function)
|
|
464
460
|
|
|
465
461
|
**HTTP Methods:**
|
|
466
|
-
- `fetch(url, options?)`: `Promise<Result<ApiResponse
|
|
467
|
-
- `get/post/put/patch/delete(...)`: `Promise<Result<ApiResponse
|
|
462
|
+
- `fetch(url, options?)`: `Promise<Result<ApiResponse>>`
|
|
463
|
+
- `get/post/put/patch/delete(...)`: `Promise<Result<ApiResponse>>`
|
|
468
464
|
- `fetchWithId(url, options?)`: `{ id, result, cancel }`
|
|
469
465
|
- `cancel(id)`: Cancel pending request
|
|
470
466
|
|
|
@@ -483,7 +479,11 @@ The library targets modern browsers with Web Worker and (optionally) IndexedDB s
|
|
|
483
479
|
|
|
484
480
|
Types:
|
|
485
481
|
|
|
486
|
-
- ApiResponse
|
|
482
|
+
- ApiResponse = { body: string; status: number; contentType: string; headers: Record<string, string> }
|
|
483
|
+
- `body`: Raw string (text/JSON) or base64 (for binary content like images, PDFs)
|
|
484
|
+
- `contentType`: Content type header (always present, e.g., 'application/json', 'image/png')
|
|
485
|
+
- Use `isBinaryContentType(contentType)` to detect binary responses
|
|
486
|
+
- Use `base64ToArrayBuffer(body)` to decode binary data
|
|
487
487
|
- AuthResult = { authenticated: boolean; user?: unknown; expiresAt?: number | null }
|
|
488
488
|
- FetchGuardRequestInit extends RequestInit with:
|
|
489
489
|
- requiresAuth?: boolean // default true
|
|
@@ -491,11 +491,11 @@ Types:
|
|
|
491
491
|
|
|
492
492
|
## Message Protocol (pairs, summary)
|
|
493
493
|
|
|
494
|
-
- Main -> Worker: SETUP -> Worker -> Main: READY
|
|
494
|
+
- Main -> Worker: SETUP -> Worker -> Main: READY | SETUP_ERROR
|
|
495
495
|
- Main -> Worker: FETCH -> Worker -> Main: FETCH_RESULT | FETCH_ERROR
|
|
496
|
-
- Main -> Worker: AUTH_CALL(login/logout/...) -> Worker -> Main:
|
|
496
|
+
- Main -> Worker: AUTH_CALL(login/logout/...) -> Worker -> Main: AUTH_CALL_RESULT | ERROR (and AUTH_STATE_CHANGED event)
|
|
497
497
|
- Main -> Worker: CANCEL -> aborts in-worker fetch (no explicit response)
|
|
498
|
-
- Main -> Worker: PING -> Worker -> Main: PONG
|
|
498
|
+
- Main -> Worker: PING -> Worker -> Main: PONG | ERROR
|
|
499
499
|
|
|
500
500
|
## Error Handling
|
|
501
501
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts_micro_result from 'ts-micro-result';
|
|
2
|
-
import { Result,
|
|
2
|
+
import { Result, ErrorDetail } from 'ts-micro-result';
|
|
3
3
|
import * as ts_micro_result_factories_errors_advanced from 'ts-micro-result/factories/errors-advanced';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -69,6 +69,9 @@ interface TokenProvider {
|
|
|
69
69
|
* Custom auth methods (optional)
|
|
70
70
|
* Examples: loginWithPhone, loginWithGoogle, loginWithFacebook, etc.
|
|
71
71
|
* All must return Result<TokenInfo> for token retrieval
|
|
72
|
+
*
|
|
73
|
+
* Note: Using any[] for args to allow flexible custom auth methods
|
|
74
|
+
* while maintaining type compatibility with specific method signatures above
|
|
72
75
|
*/
|
|
73
76
|
[key: string]: (...args: any[]) => Promise<Result<TokenInfo>>;
|
|
74
77
|
}
|
|
@@ -130,27 +133,15 @@ interface FetchGuardOptions {
|
|
|
130
133
|
provider: TokenProvider | ProviderPresetConfig | string;
|
|
131
134
|
/** List of allowed domains (wildcard supported) */
|
|
132
135
|
allowedDomains?: string[];
|
|
133
|
-
/** Debug mode */
|
|
134
|
-
debug?: boolean;
|
|
135
136
|
/** Early refresh time for tokens (ms) */
|
|
136
137
|
refreshEarlyMs?: number;
|
|
137
|
-
/** Default timeout for requests (ms) */
|
|
138
|
-
defaultTimeoutMs?: number;
|
|
139
|
-
/** Default retry count */
|
|
140
|
-
retryCount?: number;
|
|
141
|
-
/** Delay between retries (ms) */
|
|
142
|
-
retryDelayMs?: number;
|
|
143
138
|
}
|
|
144
139
|
/**
|
|
145
140
|
* Internal worker configuration
|
|
146
141
|
*/
|
|
147
142
|
interface WorkerConfig {
|
|
148
143
|
allowedDomains: string[];
|
|
149
|
-
debug: boolean;
|
|
150
144
|
refreshEarlyMs: number;
|
|
151
|
-
defaultTimeoutMs: number;
|
|
152
|
-
retryCount: number;
|
|
153
|
-
retryDelayMs: number;
|
|
154
145
|
}
|
|
155
146
|
/**
|
|
156
147
|
* Extended RequestInit with FetchGuard-specific options
|
|
@@ -162,11 +153,16 @@ interface FetchGuardRequestInit extends RequestInit {
|
|
|
162
153
|
includeHeaders?: boolean;
|
|
163
154
|
}
|
|
164
155
|
/**
|
|
165
|
-
* API response
|
|
156
|
+
* API response structure
|
|
157
|
+
* - body: string (text/JSON) or base64 (binary)
|
|
158
|
+
* - contentType: always present, indicates how to decode body
|
|
159
|
+
* - headers: empty object if includeHeaders: false
|
|
160
|
+
* - status: HTTP status code
|
|
166
161
|
*/
|
|
167
|
-
interface ApiResponse
|
|
168
|
-
|
|
162
|
+
interface ApiResponse {
|
|
163
|
+
body: string;
|
|
169
164
|
status: number;
|
|
165
|
+
contentType: string;
|
|
170
166
|
headers: Record<string, string>;
|
|
171
167
|
}
|
|
172
168
|
/**
|
|
@@ -243,10 +239,10 @@ declare class FetchGuardClient {
|
|
|
243
239
|
* Convenience methods
|
|
244
240
|
*/
|
|
245
241
|
get(url: string, options?: Omit<FetchGuardRequestInit, 'method' | 'body'>): Promise<Result<ApiResponse>>;
|
|
246
|
-
post(url: string, body?:
|
|
247
|
-
put(url: string, body?:
|
|
242
|
+
post(url: string, body?: unknown, options?: Omit<FetchGuardRequestInit, 'method' | 'body'>): Promise<Result<ApiResponse>>;
|
|
243
|
+
put(url: string, body?: unknown, options?: Omit<FetchGuardRequestInit, 'method' | 'body'>): Promise<Result<ApiResponse>>;
|
|
248
244
|
delete(url: string, options?: Omit<FetchGuardRequestInit, 'method' | 'body'>): Promise<Result<ApiResponse>>;
|
|
249
|
-
patch(url: string, body?:
|
|
245
|
+
patch(url: string, body?: unknown, options?: Omit<FetchGuardRequestInit, 'method' | 'body'>): Promise<Result<ApiResponse>>;
|
|
250
246
|
/**
|
|
251
247
|
* Generic method to call any auth method on provider
|
|
252
248
|
* @param method - Method name (login, logout, loginWithPhone, etc.)
|
|
@@ -362,10 +358,13 @@ interface MainPayloads {
|
|
|
362
358
|
* Payloads for messages sent from Worker thread → Main thread
|
|
363
359
|
*/
|
|
364
360
|
interface WorkerPayloads {
|
|
365
|
-
|
|
366
|
-
|
|
361
|
+
ERROR: {
|
|
362
|
+
errors: ErrorDetail[];
|
|
367
363
|
};
|
|
368
364
|
READY: undefined;
|
|
365
|
+
SETUP_ERROR: {
|
|
366
|
+
error: string;
|
|
367
|
+
};
|
|
369
368
|
PONG: {
|
|
370
369
|
timestamp: number;
|
|
371
370
|
};
|
|
@@ -375,11 +374,7 @@ interface WorkerPayloads {
|
|
|
375
374
|
};
|
|
376
375
|
AUTH_STATE_CHANGED: AuthResult;
|
|
377
376
|
AUTH_CALL_RESULT: AuthResult;
|
|
378
|
-
FETCH_RESULT:
|
|
379
|
-
status: number;
|
|
380
|
-
headers?: Record<string, string>;
|
|
381
|
-
body: string;
|
|
382
|
-
};
|
|
377
|
+
FETCH_RESULT: ApiResponse;
|
|
383
378
|
FETCH_ERROR: {
|
|
384
379
|
error: string;
|
|
385
380
|
status?: number;
|
|
@@ -495,7 +490,7 @@ declare function clearProviders(): void;
|
|
|
495
490
|
/**
|
|
496
491
|
* Custom auth method type
|
|
497
492
|
*/
|
|
498
|
-
type CustomAuthMethod = (...args:
|
|
493
|
+
type CustomAuthMethod = (...args: unknown[]) => Promise<Result<TokenInfo>>;
|
|
499
494
|
/**
|
|
500
495
|
* Configuration for creating provider
|
|
501
496
|
*
|
|
@@ -622,10 +617,21 @@ declare function deserializeFormData(serialized: SerializedFormData): FormData;
|
|
|
622
617
|
/**
|
|
623
618
|
* Check if body is FormData
|
|
624
619
|
*/
|
|
625
|
-
declare function isFormData(body:
|
|
620
|
+
declare function isFormData(body: unknown): body is FormData;
|
|
626
621
|
/**
|
|
627
622
|
* Check if serialized body is SerializedFormData
|
|
628
623
|
*/
|
|
629
|
-
declare function isSerializedFormData(body:
|
|
624
|
+
declare function isSerializedFormData(body: unknown): body is SerializedFormData;
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Convert base64 string to ArrayBuffer
|
|
628
|
+
* Used in client to decode binary responses
|
|
629
|
+
*/
|
|
630
|
+
declare function base64ToArrayBuffer(base64: string): ArrayBuffer;
|
|
631
|
+
/**
|
|
632
|
+
* Check if content type is binary (should be base64 encoded)
|
|
633
|
+
* Returns true for images, PDFs, videos, etc.
|
|
634
|
+
*/
|
|
635
|
+
declare function isBinaryContentType(contentType: string): boolean;
|
|
630
636
|
|
|
631
|
-
export { type ApiResponse, AuthErrors, type AuthResult, type AuthStrategy, DomainErrors, FetchGuardClient, type FetchGuardOptions, type FetchGuardRequestInit, GeneralErrors, InitErrors, MSG, type MainToWorkerMessage, type MessageType, NetworkErrors, type ProviderConfig, type ProviderPresetConfig, type RefreshTokenStorage, RequestErrors, type SerializedFile, type SerializedFormData, type SerializedFormDataEntry, type TokenInfo, type TokenParser, type TokenProvider, type WorkerConfig, type WorkerToMainMessage, bodyParser, bodyStrategy, clearProviders, cookieParser, cookieStrategy, createBodyProvider, createBodyStrategy, createClient, createCookieProvider, createCookieStrategy, createIndexedDBStorage, createProvider, deserializeFormData, getProvider, hasProvider, isFormData, isSerializedFormData, listProviders, registerProvider, serializeFormData, unregisterProvider };
|
|
637
|
+
export { type ApiResponse, AuthErrors, type AuthResult, type AuthStrategy, DomainErrors, FetchGuardClient, type FetchGuardOptions, type FetchGuardRequestInit, GeneralErrors, InitErrors, MSG, type MainToWorkerMessage, type MessageType, NetworkErrors, type ProviderConfig, type ProviderPresetConfig, type RefreshTokenStorage, RequestErrors, type SerializedFile, type SerializedFormData, type SerializedFormDataEntry, type TokenInfo, type TokenParser, type TokenProvider, type WorkerConfig, type WorkerToMainMessage, base64ToArrayBuffer, bodyParser, bodyStrategy, clearProviders, cookieParser, cookieStrategy, createBodyProvider, createBodyStrategy, createClient, createCookieProvider, createCookieStrategy, createIndexedDBStorage, createProvider, deserializeFormData, getProvider, hasProvider, isBinaryContentType, isFormData, isSerializedFormData, listProviders, registerProvider, serializeFormData, unregisterProvider };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
|
-
import {
|
|
2
|
+
import { ok, err } from "ts-micro-result";
|
|
3
3
|
|
|
4
4
|
// src/messages.ts
|
|
5
5
|
var MSG = Object.freeze({
|
|
@@ -10,8 +10,9 @@ var MSG = Object.freeze({
|
|
|
10
10
|
CANCEL: "CANCEL",
|
|
11
11
|
PING: "PING",
|
|
12
12
|
// Worker -> Main messages
|
|
13
|
-
|
|
13
|
+
ERROR: "ERROR",
|
|
14
14
|
READY: "READY",
|
|
15
|
+
SETUP_ERROR: "SETUP_ERROR",
|
|
15
16
|
PONG: "PONG",
|
|
16
17
|
LOG: "LOG",
|
|
17
18
|
AUTH_STATE_CHANGED: "AUTH_STATE_CHANGED",
|
|
@@ -22,9 +23,6 @@ var MSG = Object.freeze({
|
|
|
22
23
|
|
|
23
24
|
// src/constants.ts
|
|
24
25
|
var DEFAULT_REFRESH_EARLY_MS = 6e4;
|
|
25
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
26
|
-
var DEFAULT_RETRY_COUNT = 3;
|
|
27
|
-
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
28
26
|
|
|
29
27
|
// src/errors.ts
|
|
30
28
|
import { defineError, defineErrorAdvanced } from "ts-micro-result";
|
|
@@ -106,13 +104,15 @@ function isFormData(body) {
|
|
|
106
104
|
return body instanceof FormData;
|
|
107
105
|
}
|
|
108
106
|
function isSerializedFormData(body) {
|
|
109
|
-
return body && typeof body === "object" && body._type === "FormData";
|
|
107
|
+
return body !== null && typeof body === "object" && body._type === "FormData";
|
|
110
108
|
}
|
|
111
109
|
|
|
112
110
|
// src/client.ts
|
|
113
111
|
var FetchGuardClient = class {
|
|
114
112
|
worker;
|
|
115
113
|
messageId = 0;
|
|
114
|
+
// Using unknown because different messages have different response types
|
|
115
|
+
// (ApiResponse for FETCH, AuthResult for AUTH_CALL, etc.)
|
|
116
116
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
117
117
|
authListeners = /* @__PURE__ */ new Set();
|
|
118
118
|
readyListeners = /* @__PURE__ */ new Set();
|
|
@@ -137,11 +137,7 @@ var FetchGuardClient = class {
|
|
|
137
137
|
async initializeWorker(options) {
|
|
138
138
|
const config = {
|
|
139
139
|
allowedDomains: options.allowedDomains || [],
|
|
140
|
-
|
|
141
|
-
refreshEarlyMs: options.refreshEarlyMs ?? DEFAULT_REFRESH_EARLY_MS,
|
|
142
|
-
defaultTimeoutMs: options.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
143
|
-
retryCount: options.retryCount ?? DEFAULT_RETRY_COUNT,
|
|
144
|
-
retryDelayMs: options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS
|
|
140
|
+
refreshEarlyMs: options.refreshEarlyMs ?? DEFAULT_REFRESH_EARLY_MS
|
|
145
141
|
};
|
|
146
142
|
let providerConfig = null;
|
|
147
143
|
if (typeof options.provider === "string") {
|
|
@@ -187,13 +183,8 @@ var FetchGuardClient = class {
|
|
|
187
183
|
const status = payload?.status ?? 200;
|
|
188
184
|
const headers = payload?.headers ?? {};
|
|
189
185
|
const body = String(payload?.body ?? "");
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
data = JSON.parse(body);
|
|
193
|
-
} catch {
|
|
194
|
-
data = body;
|
|
195
|
-
}
|
|
196
|
-
request.resolve(ok({ data, status, headers }));
|
|
186
|
+
const contentType = String(payload?.contentType ?? "application/octet-stream");
|
|
187
|
+
request.resolve(ok({ body, status, contentType, headers }));
|
|
197
188
|
return;
|
|
198
189
|
}
|
|
199
190
|
if (type === MSG.FETCH_ERROR) {
|
|
@@ -206,17 +197,19 @@ var FetchGuardClient = class {
|
|
|
206
197
|
return;
|
|
207
198
|
}
|
|
208
199
|
}
|
|
209
|
-
if (type === MSG.
|
|
200
|
+
if (type === MSG.ERROR) {
|
|
210
201
|
const request = this.pendingRequests.get(id);
|
|
211
202
|
if (!request) return;
|
|
212
203
|
this.pendingRequests.delete(id);
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
204
|
+
const errors = payload?.errors || [];
|
|
205
|
+
request.resolve(err(errors));
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (type === MSG.SETUP_ERROR) {
|
|
209
|
+
if (this.setupReject) {
|
|
210
|
+
this.setupReject(new Error(`Worker setup failed: ${payload?.error || "Unknown error"}`));
|
|
211
|
+
this.setupResolve = void 0;
|
|
212
|
+
this.setupReject = void 0;
|
|
220
213
|
}
|
|
221
214
|
return;
|
|
222
215
|
}
|
|
@@ -833,6 +826,26 @@ function createBodyProvider(config) {
|
|
|
833
826
|
strategy: createBodyStrategy(config)
|
|
834
827
|
});
|
|
835
828
|
}
|
|
829
|
+
|
|
830
|
+
// src/utils/binary.ts
|
|
831
|
+
function base64ToArrayBuffer(base64) {
|
|
832
|
+
const binary = atob(base64);
|
|
833
|
+
const bytes = new Uint8Array(binary.length);
|
|
834
|
+
for (let i = 0; i < binary.length; i++) {
|
|
835
|
+
bytes[i] = binary.charCodeAt(i);
|
|
836
|
+
}
|
|
837
|
+
return bytes.buffer;
|
|
838
|
+
}
|
|
839
|
+
function isBinaryContentType(contentType) {
|
|
840
|
+
const normalized = contentType.toLowerCase();
|
|
841
|
+
if (normalized.includes("text/")) return false;
|
|
842
|
+
if (normalized.includes("json")) return false;
|
|
843
|
+
if (normalized.includes("xml")) return false;
|
|
844
|
+
if (normalized.includes("javascript")) return false;
|
|
845
|
+
if (normalized.includes("ecmascript")) return false;
|
|
846
|
+
if (normalized.includes("html")) return false;
|
|
847
|
+
return true;
|
|
848
|
+
}
|
|
836
849
|
export {
|
|
837
850
|
AuthErrors,
|
|
838
851
|
DomainErrors,
|
|
@@ -842,6 +855,7 @@ export {
|
|
|
842
855
|
MSG,
|
|
843
856
|
NetworkErrors,
|
|
844
857
|
RequestErrors,
|
|
858
|
+
base64ToArrayBuffer,
|
|
845
859
|
bodyParser,
|
|
846
860
|
bodyStrategy,
|
|
847
861
|
clearProviders,
|
|
@@ -857,6 +871,7 @@ export {
|
|
|
857
871
|
deserializeFormData,
|
|
858
872
|
getProvider,
|
|
859
873
|
hasProvider,
|
|
874
|
+
isBinaryContentType,
|
|
860
875
|
isFormData,
|
|
861
876
|
isSerializedFormData,
|
|
862
877
|
listProviders,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/messages.ts","../src/constants.ts","../src/errors.ts","../src/utils/formdata.ts","../src/utils/registry.ts","../src/provider/create-provider.ts","../src/provider/storage/indexeddb.ts","../src/provider/parser/body.ts","../src/provider/parser/cookie.ts","../src/provider/strategy/cookie.ts","../src/provider/strategy/body.ts","../src/provider/presets.ts"],"sourcesContent":["import type {\r\n FetchGuardOptions,\r\n FetchGuardRequestInit,\r\n WorkerConfig,\r\n ApiResponse,\r\n ProviderPresetConfig,\r\n AuthResult\r\n} from './types'\r\nimport { fromJSON, ok, err, type Result } from 'ts-micro-result'\r\nimport { MSG } from './messages'\r\nimport {\r\n DEFAULT_REFRESH_EARLY_MS,\r\n DEFAULT_TIMEOUT_MS,\r\n DEFAULT_RETRY_COUNT,\r\n DEFAULT_RETRY_DELAY_MS\r\n} from './constants'\r\nimport { GeneralErrors, NetworkErrors } from './errors'\r\nimport { serializeFormData, isFormData } from './utils/formdata'\r\n\r\n/**\r\n * Queue item for sequential message processing\r\n * Inspired by old-workers/ApiWorkerService.ts\r\n */\r\ninterface QueueItem {\r\n id: string\r\n message: any\r\n resolve: (response: any) => void\r\n reject: (error: Error) => void\r\n timeout: ReturnType<typeof setTimeout>\r\n}\r\n\r\n/**\r\n * FetchGuard Client - main interface cho việc gọi API thông qua Web Worker\r\n */\r\nexport class FetchGuardClient {\r\n private worker: Worker\r\n private messageId = 0\r\n private pendingRequests = new Map<string, {\r\n resolve: (value: any) => void\r\n reject: (error: Error) => void\r\n }>()\r\n private authListeners = new Set<(state: AuthResult) => void>()\r\n private readyListeners = new Set<() => void>()\r\n private isReady = false\r\n\r\n private requestQueue: QueueItem[] = []\r\n private isProcessingQueue = false\r\n private queueTimeout = 30000 // 30 seconds\r\n private setupResolve?: () => void\r\n private setupReject?: (error: Error) => void\r\n\r\n constructor(options: FetchGuardOptions) {\r\n this.worker = new Worker(new URL('./worker.js', import.meta.url), { \r\n type: 'module' \r\n })\r\n\r\n this.worker.onmessage = this.handleWorkerMessage.bind(this)\r\n this.worker.onerror = this.handleWorkerError.bind(this)\r\n\r\n this.initializeWorker(options)\r\n }\r\n\r\n /**\r\n * Initialize worker with config and provider\r\n */\r\n private async initializeWorker(options: FetchGuardOptions): Promise<void> {\r\n const config: WorkerConfig = {\r\n allowedDomains: options.allowedDomains || [],\r\n debug: options.debug || false,\r\n refreshEarlyMs: options.refreshEarlyMs ?? DEFAULT_REFRESH_EARLY_MS,\r\n defaultTimeoutMs: options.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS,\r\n retryCount: options.retryCount ?? DEFAULT_RETRY_COUNT,\r\n retryDelayMs: options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS\r\n }\r\n\r\n // Serialize provider config based on type\r\n let providerConfig: ProviderPresetConfig | string | null = null\r\n\r\n if (typeof options.provider === 'string') {\r\n // String = registry lookup (advanced usage)\r\n providerConfig = options.provider\r\n } else if ('type' in options.provider && options.provider.type) {\r\n // ProviderPresetConfig object (recommended)\r\n providerConfig = options.provider as ProviderPresetConfig\r\n } else {\r\n // TokenProvider instance - NOT SUPPORTED\r\n throw new Error(\r\n 'Direct TokenProvider instance is not supported. Use ProviderPresetConfig instead:\\n' +\r\n ' { type: \"cookie-auth\", refreshUrl: \"...\", loginUrl: \"...\", logoutUrl: \"...\" }\\n' +\r\n 'Or for custom providers, register in worker code and use string name.'\r\n )\r\n }\r\n\r\n const message = {\r\n id: this.generateMessageId(),\r\n type: MSG.SETUP,\r\n payload: {\r\n config,\r\n providerConfig\r\n }\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n // Setup will respond with READY (no id, so we track separately)\r\n this.setupResolve = resolve\r\n this.setupReject = reject\r\n\r\n this.worker.postMessage(message)\r\n\r\n setTimeout(() => {\r\n if (this.setupReject) {\r\n this.setupReject(new Error('Worker setup timeout'))\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n }, 10000)\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Handle worker messages\r\n */\r\n private handleWorkerMessage(event: MessageEvent): void {\r\n const { id, type, payload } = event.data\r\n\r\n if (type === MSG.FETCH_RESULT || type === MSG.FETCH_ERROR) {\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n this.pendingRequests.delete(id)\r\n\r\n if (type === MSG.FETCH_RESULT) {\r\n const status = payload?.status ?? 200\r\n const headers = payload?.headers ?? {}\r\n const body = String(payload?.body ?? '')\r\n let data: any\r\n try { data = JSON.parse(body) } catch { data = body }\r\n request.resolve(ok<ApiResponse>({ data, status, headers }))\r\n return\r\n }\r\n\r\n if (type === MSG.FETCH_ERROR) {\r\n const status = typeof payload?.status === 'number' ? payload.status : undefined\r\n if (typeof status === 'number') {\r\n request.resolve(err(NetworkErrors.HttpError({ message: String(payload?.error || 'HTTP error') }), undefined, status))\r\n } else {\r\n request.resolve(err(NetworkErrors.NetworkError({ message: String(payload?.error || 'Network error') })))\r\n }\r\n return\r\n }\r\n }\r\n\r\n if (type === MSG.RESULT) {\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n this.pendingRequests.delete(id)\r\n\r\n if (payload && payload.result) {\r\n try {\r\n const result = fromJSON(JSON.stringify(payload.result))\r\n request.resolve(result)\r\n } catch (e) {\r\n request.resolve(err(GeneralErrors.ResultParse({ message: String(e) })))\r\n }\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.READY) {\r\n this.isReady = true\r\n\r\n // Notify ready listeners\r\n for (const listener of this.readyListeners) {\r\n listener()\r\n }\r\n\r\n if (this.setupResolve) {\r\n this.setupResolve()\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.PONG) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.resolve(ok({ timestamp: payload?.timestamp }))\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.AUTH_STATE_CHANGED) {\r\n for (const cb of this.authListeners) cb(payload)\r\n return\r\n }\r\n\r\n if (type === MSG.AUTH_CALL_RESULT) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.resolve(ok(payload)) // payload is AuthResult\r\n }\r\n return\r\n }\r\n }\r\n\r\n /**\r\n * Handle worker errors\r\n */\r\n private handleWorkerError(error: ErrorEvent): void {\r\n console.error('Worker error:', error)\r\n // Reject all pending requests\r\n for (const [id, request] of this.pendingRequests) {\r\n request.reject(new Error(`Worker error: ${error.message}`))\r\n }\r\n this.pendingRequests.clear()\r\n }\r\n\r\n /**\r\n * Generate unique message ID\r\n */\r\n private generateMessageId(): string {\r\n return `msg_${++this.messageId}_${Date.now()}`\r\n }\r\n\r\n /**\r\n * Make API request\r\n */\r\n async fetch(url: string, options: FetchGuardRequestInit = {}): Promise<Result<ApiResponse>> {\r\n const { result } = this.fetchWithId(url, options)\r\n return result\r\n }\r\n\r\n /**\r\n * Fetch with id for external cancellation\r\n * Returns { id, result, cancel }\r\n * Now uses queue system for sequential processing\r\n */\r\n fetchWithId(url: string, options: FetchGuardRequestInit = {}): {\r\n id: string\r\n result: Promise<Result<ApiResponse>>\r\n cancel: () => void\r\n } {\r\n const id = this.generateMessageId()\r\n\r\n // Serialize FormData if present (async operation)\r\n const result = new Promise<Result<ApiResponse>>(async (resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (response) => resolve(response),\r\n reject: (error) => reject(error)\r\n })\r\n\r\n try {\r\n let serializedOptions = { ...options }\r\n\r\n // Serialize FormData body before sending to worker\r\n if (options.body && isFormData(options.body)) {\r\n const serializedBody = await serializeFormData(options.body)\r\n serializedOptions.body = serializedBody as any\r\n }\r\n\r\n // Serialize Headers object to plain object (Headers cannot be cloned)\r\n if (options.headers) {\r\n if (options.headers instanceof Headers) {\r\n const plainHeaders: Record<string, string> = {}\r\n options.headers.forEach((value, key) => {\r\n plainHeaders[key] = value\r\n })\r\n serializedOptions.headers = plainHeaders\r\n }\r\n }\r\n\r\n const message = { id, type: MSG.FETCH, payload: { url, options: serializedOptions } }\r\n\r\n await this.sendMessageQueued(message, 30000)\r\n } catch (error) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error instanceof Error ? error : new Error(String(error)))\r\n }\r\n }\r\n })\r\n\r\n const cancel = () => this.cancel(id)\r\n\r\n return { id, result, cancel }\r\n }\r\n\r\n /**\r\n * Cancel a pending request by ID\r\n */\r\n cancel(id: string): void {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n this.worker.postMessage({ id, type: MSG.CANCEL })\r\n request.reject(new Error('Request cancelled'))\r\n }\r\n }\r\n\r\n /**\r\n * Convenience methods\r\n */\r\n async get(url: string, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n return this.fetch(url, { ...options, method: 'GET' })\r\n }\r\n\r\n async post(url: string, body?: any, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'POST',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'POST',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n async put(url: string, body?: any, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'PUT',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'PUT',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n async delete(url: string, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n return this.fetch(url, { ...options, method: 'DELETE' })\r\n }\r\n\r\n async patch(url: string, body?: any, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'PATCH',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'PATCH',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n /**\r\n * Generic method to call any auth method on provider\r\n * @param method - Method name (login, logout, loginWithPhone, etc.)\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n * @param args - Arguments to pass to the method\r\n * @returns Promise<Result<AuthResult>> - Always returns AuthResult\r\n */\r\n async call(method: string, emitEvent?: boolean, ...args: unknown[]): Promise<Result<AuthResult>> {\r\n const id = this.generateMessageId()\r\n const message = { id, type: MSG.AUTH_CALL, payload: { method, args, emitEvent } }\r\n\r\n return new Promise((resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (r: any) => resolve(r),\r\n reject: (e: Error) => reject(e)\r\n })\r\n\r\n this.sendMessageQueued(message, 15000).catch((error) => {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error)\r\n }\r\n })\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Convenience wrapper for login\r\n * @param payload - Login credentials\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async login(payload?: unknown, emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n const args = typeof payload === 'undefined' ? [] : [payload]\r\n return this.call('login', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Convenience wrapper for logout\r\n * @param payload - Optional logout payload\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async logout(payload?: unknown, emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n const args = typeof payload === 'undefined' ? [] : [payload]\r\n return this.call('logout', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Convenience wrapper for refreshToken\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async refreshToken(emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n return this.call('refreshToken', emitEvent)\r\n }\r\n\r\n /**\r\n * Check if worker is ready\r\n */\r\n ready(): boolean {\r\n return this.isReady\r\n }\r\n\r\n /**\r\n * Wait for worker to be ready\r\n * Returns immediately if already ready\r\n */\r\n async whenReady(): Promise<void> {\r\n if (this.isReady) return Promise.resolve()\r\n\r\n return new Promise<void>((resolve) => {\r\n this.readyListeners.add(resolve)\r\n })\r\n }\r\n\r\n /**\r\n * Subscribe to ready event\r\n * Callback is called immediately if already ready\r\n */\r\n onReady(callback: () => void): () => void {\r\n if (this.isReady) {\r\n // Already ready - call immediately\r\n callback()\r\n }\r\n\r\n this.readyListeners.add(callback)\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n this.readyListeners.delete(callback)\r\n }\r\n }\r\n\r\n /**\r\n * Subscribe to auth state changes\r\n */\r\n onAuthStateChanged(cb: (state: AuthResult) => void): () => void {\r\n this.authListeners.add(cb)\r\n return () => this.authListeners.delete(cb)\r\n }\r\n\r\n /** Send PING and await PONG */\r\n async ping(): Promise<Result<{ timestamp: number }>> {\r\n const id = this.generateMessageId()\r\n const message = { id, type: MSG.PING, payload: { timestamp: Date.now() } }\r\n\r\n return new Promise((resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (r: any) => resolve(r),\r\n reject: (e: Error) => reject(e)\r\n })\r\n\r\n this.sendMessageQueued(message, 5000).catch((error) => {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error)\r\n }\r\n })\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Send message through queue system\r\n * All messages go through queue for sequential processing\r\n */\r\n private sendMessageQueued<T = any>(message: any, timeoutMs: number = this.queueTimeout): Promise<T> {\r\n return new Promise((resolve, reject) => {\r\n const timeout = setTimeout(() => {\r\n const index = this.requestQueue.findIndex(item => item.id === message.id)\r\n if (index !== -1) {\r\n this.requestQueue.splice(index, 1)\r\n }\r\n this.pendingRequests.delete(message.id)\r\n reject(new Error('Request timeout'))\r\n }, timeoutMs)\r\n\r\n const queueItem: QueueItem = {\r\n id: message.id,\r\n message,\r\n resolve,\r\n reject,\r\n timeout\r\n }\r\n\r\n this.requestQueue.push(queueItem)\r\n\r\n this.processQueue()\r\n })\r\n }\r\n\r\n /**\r\n * Process message queue sequentially\r\n * Benefits:\r\n * - Sequential processing prevents worker overload\r\n * - Better error isolation (one failure doesn't affect others)\r\n * - 50ms delay between requests for backpressure\r\n */\r\n private async processQueue(): Promise<void> {\r\n if (this.isProcessingQueue || this.requestQueue.length === 0) {\r\n return\r\n }\r\n\r\n this.isProcessingQueue = true\r\n\r\n while (this.requestQueue.length > 0) {\r\n const item = this.requestQueue.shift()\r\n if (!item) continue\r\n\r\n try {\r\n this.worker.postMessage(item.message)\r\n\r\n await new Promise(resolve => setTimeout(resolve, 50))\r\n } catch (error) {\r\n clearTimeout(item.timeout)\r\n item.reject(error instanceof Error ? error : new Error(String(error)))\r\n }\r\n }\r\n\r\n this.isProcessingQueue = false\r\n }\r\n\r\n /**\r\n * Cleanup - terminate worker\r\n */\r\n destroy(): void {\r\n this.worker.terminate()\r\n this.pendingRequests.clear()\r\n\r\n for (const item of this.requestQueue) {\r\n clearTimeout(item.timeout)\r\n item.reject(new Error('Client destroyed'))\r\n }\r\n this.requestQueue = []\r\n }\r\n}\r\n\r\n/**\r\n * Factory function to create FetchGuard client\r\n */\r\nexport function createClient(options: FetchGuardOptions): FetchGuardClient {\r\n return new FetchGuardClient(options)\r\n}\r\n","import type { SerializedResult } from 'ts-micro-result'\r\nimport type { WorkerConfig, FetchGuardRequestInit, ProviderPresetConfig, AuthResult } from './types'\r\n\r\n/**\r\n * MESSAGE PAYLOADS - SINGLE SOURCE OF TRUTH\r\n *\r\n * Define all message payloads here. Type unions and MSG constants are auto-generated.\r\n *\r\n * USAGE:\r\n * - To add a new message: Just add one line to the appropriate interface\r\n * - Payload types are automatically inferred\r\n * - MSG constants are automatically generated\r\n *\r\n * EXAMPLE:\r\n * ```typescript\r\n * interface MainPayloads {\r\n * NEW_MESSAGE: { foo: string } // Add this line\r\n * }\r\n * // => Automatically get MainToWorkerMessage union with NEW_MESSAGE\r\n * // => Automatically get MSG.NEW_MESSAGE = 'NEW_MESSAGE'\r\n * ```\r\n */\r\n\r\n/**\r\n * Payloads for messages sent from Main thread → Worker thread\r\n */\r\nexport interface MainPayloads {\r\n SETUP: { config: WorkerConfig; providerConfig: ProviderPresetConfig | string | null }\r\n FETCH: { url: string; options?: FetchGuardRequestInit }\r\n AUTH_CALL: { method: string; args: unknown[]; emitEvent?: boolean } // Generic auth method call (login, logout, loginWithPhone, etc.)\r\n CANCEL: undefined\r\n PING: { timestamp: number }\r\n}\r\n\r\n/**\r\n * Payloads for messages sent from Worker thread → Main thread\r\n */\r\nexport interface WorkerPayloads {\r\n RESULT: { result: SerializedResult | object }\r\n READY: undefined\r\n PONG: { timestamp: number }\r\n LOG: { level: 'info' | 'warn' | 'error'; message: string }\r\n AUTH_STATE_CHANGED: AuthResult\r\n AUTH_CALL_RESULT: AuthResult\r\n FETCH_RESULT: { status: number; headers?: Record<string, string>; body: string }\r\n FETCH_ERROR: { error: string; status?: number }\r\n}\r\n\r\n/**\r\n * Generate message type from payload definition\r\n * Handles optional payloads (undefined) gracefully\r\n */\r\ntype MessageFromPayloads<P> = {\r\n [K in keyof P]: { id: string; type: K } & (\r\n P[K] extends undefined ? {} : { payload: P[K] }\r\n )\r\n}[keyof P]\r\n\r\n/**\r\n * Message type unions - auto-generated from payload interfaces\r\n */\r\nexport type MainToWorkerMessage = MessageFromPayloads<MainPayloads>\r\nexport type WorkerToMainMessage = MessageFromPayloads<WorkerPayloads>\r\n\r\n/**\r\n * Message type unions for compile-time type checking\r\n */\r\nexport type MainType = keyof MainPayloads\r\nexport type WorkerType = keyof WorkerPayloads\r\nexport type MessageType = MainType | WorkerType\r\n\r\n/**\r\n * MSG constants object\r\n * Usage: MSG.SETUP, MSG.FETCH, etc.\r\n */\r\nexport const MSG = Object.freeze({\r\n // Main -> Worker messages\r\n SETUP: 'SETUP',\r\n FETCH: 'FETCH',\r\n AUTH_CALL: 'AUTH_CALL',\r\n CANCEL: 'CANCEL',\r\n PING: 'PING',\r\n\r\n // Worker -> Main messages\r\n RESULT: 'RESULT',\r\n READY: 'READY',\r\n PONG: 'PONG',\r\n LOG: 'LOG',\r\n AUTH_STATE_CHANGED: 'AUTH_STATE_CHANGED',\r\n AUTH_CALL_RESULT: 'AUTH_CALL_RESULT',\r\n FETCH_RESULT: 'FETCH_RESULT',\r\n FETCH_ERROR: 'FETCH_ERROR'\r\n}) as { readonly [K in MessageType]: K }\r\n","/**\n * FetchGuard Default Configuration Values\n */\n\n/**\n * Default time (in milliseconds) to refresh token before expiry\n * @default 60000 (60 seconds)\n */\nexport const DEFAULT_REFRESH_EARLY_MS = 60_000\n\n/**\n * Default timeout for API requests (in milliseconds)\n * @default 30000 (30 seconds)\n */\nexport const DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Default number of retry attempts for failed requests\n * @default 3\n */\nexport const DEFAULT_RETRY_COUNT = 3\n\n/**\n * Default delay between retry attempts (in milliseconds)\n * @default 1000 (1 second)\n */\nexport const DEFAULT_RETRY_DELAY_MS = 1_000\n","/**\n * Error definitions organized by domain\n * Using ts-micro-result's defineError for consistency\n */\n\nimport { defineError, defineErrorAdvanced } from 'ts-micro-result'\n\n/**\n * General errors\n */\nexport const GeneralErrors = {\n Unexpected: defineError('UNEXPECTED', 'Unexpected error', 500),\n UnknownMessage: defineError('UNKNOWN_MESSAGE', 'Unknown message type', 400),\n ResultParse: defineError('RESULT_PARSE_ERROR', 'Failed to parse result', 500),\n} as const\n\n/**\n * Initialization errors\n */\nexport const InitErrors = {\n NotInitialized: defineError('INIT_ERROR', 'Worker not initialized', 500),\n ProviderInitFailed: defineError('PROVIDER_INIT_FAILED', 'Failed to initialize provider', 500),\n InitFailed: defineError('INIT_FAILED', 'Initialization failed', 500),\n} as const\n\n/**\n * Authentication & Token errors\n */\nexport const AuthErrors = {\n TokenRefreshFailed: defineError('TOKEN_REFRESH_FAILED', 'Token refresh failed', 401),\n LoginFailed: defineError('LOGIN_FAILED', 'Login failed', 401),\n LogoutFailed: defineError('LOGOUT_FAILED', 'Logout failed', 500),\n NotAuthenticated: defineError('NOT_AUTHENTICATED', 'User is not authenticated', 401),\n} as const\n\n/**\n * Domain validation errors\n */\nexport const DomainErrors = {\n NotAllowed: defineErrorAdvanced('DOMAIN_NOT_ALLOWED', 'Domain not allowed: {url}', 403),\n} as const\n\n/**\n * Network & HTTP errors\n */\nexport const NetworkErrors = {\n NetworkError: defineError('NETWORK_ERROR', 'Network error', 500),\n HttpError: defineError('HTTP_ERROR', 'HTTP error', 500),\n FetchError: defineError('FETCH_ERROR', 'Fetch error', 500),\n} as const\n\n/**\n * Request errors\n */\nexport const RequestErrors = {\n Cancelled: defineError('REQUEST_CANCELLED', 'Request was cancelled', 499),\n Timeout: defineError('REQUEST_TIMEOUT', 'Request timeout', 408),\n} as const\n","import type { SerializedFormData, SerializedFormDataEntry, SerializedFile } from '../types'\r\n\r\n/**\r\n * Serialize FormData for transfer over postMessage\r\n * Inspired by api-worker.js:484-518\r\n *\r\n * FormData cannot be cloned via postMessage, so we need to serialize it first\r\n * Files are converted to ArrayBuffer -> number[] for transfer\r\n */\r\nexport async function serializeFormData(formData: FormData): Promise<SerializedFormData> {\r\n const entries: Array<[string, SerializedFormDataEntry]> = []\r\n\r\n // Use forEach instead of entries() for better TS compatibility\r\n formData.forEach((value, key) => {\r\n // Push async operations to promises array for parallel processing\r\n if (value instanceof File) {\r\n // We need to handle this synchronously in forEach, so we'll collect promises\r\n // and await them all at once\r\n } else {\r\n entries.push([key, String(value)])\r\n }\r\n })\r\n\r\n // Handle File entries separately with Promise.all\r\n const filePromises: Promise<void>[] = []\r\n formData.forEach((value, key) => {\r\n if (value instanceof File) {\r\n const promise = (async () => {\r\n const arrayBuffer = await value.arrayBuffer()\r\n const uint8Array = new Uint8Array(arrayBuffer)\r\n const serializedFile: SerializedFile = {\r\n name: value.name,\r\n type: value.type,\r\n data: Array.from(uint8Array) // Convert to number array\r\n }\r\n entries.push([key, serializedFile])\r\n })()\r\n filePromises.push(promise)\r\n }\r\n })\r\n\r\n await Promise.all(filePromises)\r\n\r\n return {\r\n _type: 'FormData',\r\n entries\r\n }\r\n}\r\n\r\n/**\r\n * Deserialize SerializedFormData back to FormData in worker\r\n * Reconstructs File objects from serialized data\r\n */\r\nexport function deserializeFormData(serialized: SerializedFormData): FormData {\r\n const formData = new FormData()\r\n\r\n for (const [key, value] of serialized.entries) {\r\n if (typeof value === 'string') {\r\n formData.append(key, value)\r\n } else {\r\n // Reconstruct File from SerializedFile\r\n const uint8Array = new Uint8Array(value.data)\r\n const file = new File([uint8Array], value.name, { type: value.type })\r\n formData.append(key, file)\r\n }\r\n }\r\n\r\n return formData\r\n}\r\n\r\n/**\r\n * Check if body is FormData\r\n */\r\nexport function isFormData(body: any): body is FormData {\r\n return body instanceof FormData\r\n}\r\n\r\n/**\r\n * Check if serialized body is SerializedFormData\r\n */\r\nexport function isSerializedFormData(body: any): body is SerializedFormData {\r\n return body && typeof body === 'object' && body._type === 'FormData'\r\n}\r\n","import type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Registry to manage token providers\r\n */\r\nconst registry = new Map<string, TokenProvider>()\r\n\r\n/**\r\n * Register a token provider with name\r\n */\r\nexport function registerProvider(name: string, provider: TokenProvider): void {\r\n if (typeof name !== 'string' || !name.trim()) {\r\n throw new Error('Provider name must be a non-empty string')\r\n }\r\n \r\n if (!provider || typeof provider.refreshToken !== 'function') {\r\n throw new Error('Provider must implement TokenProvider interface')\r\n }\r\n \r\n registry.set(name, provider)\r\n}\r\n\r\n/**\r\n * Get provider by name\r\n */\r\nexport function getProvider(name: string): TokenProvider {\r\n const provider = registry.get(name)\r\n if (!provider) {\r\n throw new Error(`Provider '${name}' not found. Available providers: ${Array.from(registry.keys()).join(', ')}`)\r\n }\r\n return provider\r\n}\r\n\r\n/**\r\n * Check if provider exists\r\n */\r\nexport function hasProvider(name: string): boolean {\r\n return registry.has(name)\r\n}\r\n\r\n/**\r\n * Get list of all provider names\r\n */\r\nexport function listProviders(): string[] {\r\n return Array.from(registry.keys())\r\n}\r\n\r\n/**\r\n * Remove provider\r\n */\r\nexport function unregisterProvider(name: string): boolean {\r\n return registry.delete(name)\r\n}\r\n\r\n/**\r\n * Remove all providers\r\n */\r\nexport function clearProviders(): void {\r\n registry.clear()\r\n}\r\n","import type {\r\n TokenProvider,\r\n RefreshTokenStorage,\r\n TokenParser,\r\n AuthStrategy,\r\n TokenInfo\r\n} from '../types'\r\nimport { ok, err, type Result } from 'ts-micro-result'\r\nimport { AuthErrors, NetworkErrors } from '../errors'\r\n\r\n/**\r\n * Custom auth method type\r\n */\r\ntype CustomAuthMethod = (...args: any[]) => Promise<Result<TokenInfo>>\r\n\r\n/**\r\n * Configuration for creating provider\r\n *\r\n * refreshStorage: OPTIONAL - to load refresh token initially when worker starts\r\n * - undefined: cookie-based auth (httpOnly cookie, no need to load)\r\n * - RefreshTokenStorage: body-based auth (load from IndexedDB on startup)\r\n *\r\n * strategy: AuthStrategy with refresh (required), login/logout (required)\r\n *\r\n * customMethods: OPTIONAL - custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n */\r\nexport interface ProviderConfig {\r\n refreshStorage?: RefreshTokenStorage\r\n parser: TokenParser\r\n strategy: AuthStrategy\r\n customMethods?: Record<string, CustomAuthMethod>\r\n}\r\n\r\n/**\r\n * Factory function to create TokenProvider from modular components\r\n *\r\n * Provider automatically handles refresh token:\r\n * - If refreshToken is null and storage exists → load from storage initially\r\n * - If refreshToken exists → use token from worker memory\r\n * - Cookie-based (no storage) → always null\r\n *\r\n * Custom methods:\r\n * - User can add custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n * - Custom methods will be spread into provider object\r\n */\r\nexport function createProvider(config: ProviderConfig): TokenProvider {\r\n const baseProvider: Pick<TokenProvider, 'refreshToken' | 'login' | 'logout'> = {\r\n async refreshToken(refreshToken: string | null) {\r\n let currentRefreshToken = refreshToken\r\n if (currentRefreshToken === null && config.refreshStorage) {\r\n currentRefreshToken = await config.refreshStorage.get()\r\n }\r\n\r\n try {\r\n const response = await config.strategy.refresh(currentRefreshToken)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async login(payload: unknown) {\r\n try {\r\n const response = await config.strategy.login(payload)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.LoginFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.LoginFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async logout(payload?: unknown) {\r\n try {\r\n const response = await config.strategy.logout(payload)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.LogoutFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n if (config.refreshStorage) {\r\n await config.refreshStorage.set(null)\r\n }\r\n\r\n return ok({\r\n token: '',\r\n refreshToken: undefined,\r\n expiresAt: undefined,\r\n user: null // Explicitly clear user on logout\r\n })\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n }\r\n }\r\n\r\n // Merge custom methods if provided\r\n if (config.customMethods) {\r\n return {\r\n ...baseProvider,\r\n ...config.customMethods\r\n } as TokenProvider\r\n }\r\n\r\n return baseProvider as TokenProvider\r\n}\r\n","import type { RefreshTokenStorage } from '../../types'\r\n\r\n/**\r\n * IndexedDB storage - only stores refresh token in IndexedDB\r\n * Suitable for body-based refresh strategy\r\n * Persists refresh token for reuse after reload\r\n */\r\nexport function createIndexedDBStorage(dbName = 'FetchGuardDB', refreshTokenKey = 'refreshToken'): RefreshTokenStorage {\r\n const storeName = 'tokens'\r\n\r\n const openDB = (): Promise<IDBDatabase> => {\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDB.open(dbName, 1)\r\n \r\n request.onerror = () => reject(request.error)\r\n request.onsuccess = () => resolve(request.result)\r\n \r\n request.onupgradeneeded = (event) => {\r\n const db = (event.target as IDBOpenDBRequest).result\r\n if (!db.objectStoreNames.contains(storeName)) {\r\n const store = db.createObjectStore(storeName, { keyPath: 'key' })\r\n store.createIndex('timestamp', 'timestamp', { unique: false })\r\n }\r\n }\r\n })\r\n }\r\n\r\n const promisifyRequest = <T>(request: IDBRequest<T>): Promise<T> => {\r\n return new Promise((resolve, reject) => {\r\n request.onsuccess = () => resolve(request.result)\r\n request.onerror = () => reject(request.error)\r\n })\r\n }\r\n\r\n return {\r\n async get() {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readonly')\r\n const store = transaction.objectStore(storeName)\r\n const result = await promisifyRequest(store.get(refreshTokenKey))\r\n return result?.value || null\r\n } catch (error) {\r\n console.warn('Failed to get refresh token from IndexedDB:', error)\r\n return null\r\n }\r\n },\r\n async set(token) {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readwrite')\r\n const store = transaction.objectStore(storeName)\r\n\r\n if (token) {\r\n await promisifyRequest(store.put({ key: refreshTokenKey, value: token, timestamp: Date.now() }))\r\n } else {\r\n await promisifyRequest(store.delete(refreshTokenKey))\r\n }\r\n } catch (error) {\r\n console.warn('Failed to save refresh token to IndexedDB:', error)\r\n }\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\n\r\n/**\r\n * Body parser - parse token from response body (JSON)\r\n * Expects response format: { data: { accessToken, refreshToken, expiresAt?, user? } }\r\n */\r\nexport const bodyParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n refreshToken: json.data.refreshToken,\r\n expiresAt: json.data.expiresAt,\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\n\r\n/**\r\n * Cookie parser - parse access token from response body\r\n * Expects response format: { data: { accessToken, expiresAt?, user? } }\r\n * Refresh token is automatically set by backend into httpOnly cookie\r\n */\r\nexport const cookieParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n expiresAt: json.data.expiresAt,\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { AuthStrategy } from '../../types'\r\n\r\n/**\r\n * Cookie auth strategy - all auth operations via httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Refresh token is sent automatically via httpOnly cookie\r\n * Credentials are sent in request body\r\n */\r\nexport function createCookieStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): AuthStrategy {\r\n return {\r\n async refresh() {\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload) {\r\n return fetch(config.loginUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard cookie strategy\r\n */\r\nexport const cookieStrategy = createCookieStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import type { AuthStrategy } from '../../types'\r\n\r\n/**\r\n * Body auth strategy - all auth operations via request body\r\n * Suitable for SPA applications\r\n *\r\n * All tokens/credentials are sent in request body\r\n */\r\nexport function createBodyStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): AuthStrategy {\r\n return {\r\n async refresh(refreshToken) {\r\n if (!refreshToken) {\r\n throw new Error('No refresh token available')\r\n }\r\n\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ refreshToken }),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload) {\r\n return fetch(config.loginUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard body strategy\r\n */\r\nexport const bodyStrategy = createBodyStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import { createProvider } from './create-provider'\r\nimport { createIndexedDBStorage } from './storage/indexeddb'\r\nimport { bodyParser } from './parser/body'\r\nimport { cookieParser } from './parser/cookie'\r\nimport { createCookieStrategy } from './strategy/cookie'\r\nimport { createBodyStrategy } from './strategy/body'\r\nimport type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Cookie Provider - uses httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: httpOnly cookie (managed by backend)\r\n */\r\nexport function createCookieProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: undefined,\r\n parser: cookieParser,\r\n strategy: createCookieStrategy(config)\r\n })\r\n}\r\n\r\n/**\r\n * Body Provider - refresh token in response body, persisted to IndexedDB\r\n * Suitable for SPA applications\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: IndexedDB (persists across reload)\r\n */\r\nexport function createBodyProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n refreshTokenKey?: string\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: createIndexedDBStorage('FetchGuardDB', config.refreshTokenKey || 'refreshToken'),\r\n parser: bodyParser,\r\n strategy: createBodyStrategy(config)\r\n })\r\n}\r\n"],"mappings":"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","names":["ok","err","err","ok"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/messages.ts","../src/constants.ts","../src/errors.ts","../src/utils/formdata.ts","../src/utils/registry.ts","../src/provider/create-provider.ts","../src/provider/storage/indexeddb.ts","../src/provider/parser/body.ts","../src/provider/parser/cookie.ts","../src/provider/strategy/cookie.ts","../src/provider/strategy/body.ts","../src/provider/presets.ts","../src/utils/binary.ts"],"sourcesContent":["import type {\r\n FetchGuardOptions,\r\n FetchGuardRequestInit,\r\n WorkerConfig,\r\n ApiResponse,\r\n ProviderPresetConfig,\r\n AuthResult\r\n} from './types'\r\nimport type { MainToWorkerMessage } from './messages'\r\nimport { ok, err, type Result } from 'ts-micro-result'\r\nimport { MSG } from './messages'\r\nimport { DEFAULT_REFRESH_EARLY_MS } from './constants'\r\nimport { NetworkErrors } from './errors'\r\nimport { serializeFormData, isFormData } from './utils/formdata'\r\n\r\n/**\r\n * Queue item for sequential message processing\r\n */\r\ninterface QueueItem {\r\n id: string\r\n message: MainToWorkerMessage\r\n resolve: (response: unknown) => void\r\n reject: (error: Error) => void\r\n timeout: ReturnType<typeof setTimeout>\r\n}\r\n\r\n/**\r\n * FetchGuard Client - main interface cho việc gọi API thông qua Web Worker\r\n */\r\nexport class FetchGuardClient {\r\n private worker: Worker\r\n private messageId = 0\r\n // Using unknown because different messages have different response types\r\n // (ApiResponse for FETCH, AuthResult for AUTH_CALL, etc.)\r\n private pendingRequests = new Map<string, {\r\n resolve: (value: unknown) => void\r\n reject: (error: Error) => void\r\n }>()\r\n private authListeners = new Set<(state: AuthResult) => void>()\r\n private readyListeners = new Set<() => void>()\r\n private isReady = false\r\n\r\n private requestQueue: QueueItem[] = []\r\n private isProcessingQueue = false\r\n private queueTimeout = 30000 // 30 seconds\r\n private setupResolve?: () => void\r\n private setupReject?: (error: Error) => void\r\n\r\n constructor(options: FetchGuardOptions) {\r\n this.worker = new Worker(new URL('./worker.js', import.meta.url), { \r\n type: 'module' \r\n })\r\n\r\n this.worker.onmessage = this.handleWorkerMessage.bind(this)\r\n this.worker.onerror = this.handleWorkerError.bind(this)\r\n\r\n this.initializeWorker(options)\r\n }\r\n\r\n /**\r\n * Initialize worker with config and provider\r\n */\r\n private async initializeWorker(options: FetchGuardOptions): Promise<void> {\r\n const config: WorkerConfig = {\r\n allowedDomains: options.allowedDomains || [],\r\n refreshEarlyMs: options.refreshEarlyMs ?? DEFAULT_REFRESH_EARLY_MS\r\n }\r\n\r\n // Serialize provider config based on type\r\n let providerConfig: ProviderPresetConfig | string | null = null\r\n\r\n if (typeof options.provider === 'string') {\r\n // String = registry lookup (advanced usage)\r\n providerConfig = options.provider\r\n } else if ('type' in options.provider && options.provider.type) {\r\n // ProviderPresetConfig object (recommended)\r\n providerConfig = options.provider as ProviderPresetConfig\r\n } else {\r\n // TokenProvider instance - NOT SUPPORTED\r\n throw new Error(\r\n 'Direct TokenProvider instance is not supported. Use ProviderPresetConfig instead:\\n' +\r\n ' { type: \"cookie-auth\", refreshUrl: \"...\", loginUrl: \"...\", logoutUrl: \"...\" }\\n' +\r\n 'Or for custom providers, register in worker code and use string name.'\r\n )\r\n }\r\n\r\n const message = {\r\n id: this.generateMessageId(),\r\n type: MSG.SETUP,\r\n payload: {\r\n config,\r\n providerConfig\r\n }\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n // Setup will respond with READY (no id, so we track separately)\r\n this.setupResolve = resolve\r\n this.setupReject = reject\r\n\r\n this.worker.postMessage(message)\r\n\r\n setTimeout(() => {\r\n if (this.setupReject) {\r\n this.setupReject(new Error('Worker setup timeout'))\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n }, 10000)\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Handle worker messages\r\n */\r\n private handleWorkerMessage(event: MessageEvent): void {\r\n const { id, type, payload } = event.data\r\n\r\n if (type === MSG.FETCH_RESULT || type === MSG.FETCH_ERROR) {\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n this.pendingRequests.delete(id)\r\n\r\n if (type === MSG.FETCH_RESULT) {\r\n const status = payload?.status ?? 200\r\n const headers = payload?.headers ?? {}\r\n const body = String(payload?.body ?? '')\r\n const contentType = String(payload?.contentType ?? 'application/octet-stream')\r\n request.resolve(ok<ApiResponse>({ body, status, contentType, headers }))\r\n return\r\n }\r\n\r\n if (type === MSG.FETCH_ERROR) {\r\n const status = typeof payload?.status === 'number' ? payload.status : undefined\r\n if (typeof status === 'number') {\r\n request.resolve(err(NetworkErrors.HttpError({ message: String(payload?.error || 'HTTP error') }), undefined, status))\r\n } else {\r\n request.resolve(err(NetworkErrors.NetworkError({ message: String(payload?.error || 'Network error') })))\r\n }\r\n return\r\n }\r\n }\r\n\r\n if (type === MSG.ERROR) {\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n this.pendingRequests.delete(id)\r\n\r\n // Reconstruct error Result from ErrorDetail[]\r\n const errors = payload?.errors || []\r\n request.resolve(err(errors))\r\n return\r\n }\r\n\r\n if (type === MSG.SETUP_ERROR) {\r\n // Setup failed - reject setup promise\r\n if (this.setupReject) {\r\n this.setupReject(new Error(`Worker setup failed: ${payload?.error || 'Unknown error'}`))\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.READY) {\r\n this.isReady = true\r\n\r\n // Notify ready listeners\r\n for (const listener of this.readyListeners) {\r\n listener()\r\n }\r\n\r\n if (this.setupResolve) {\r\n this.setupResolve()\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.PONG) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.resolve(ok({ timestamp: payload?.timestamp }))\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.AUTH_STATE_CHANGED) {\r\n for (const cb of this.authListeners) cb(payload)\r\n return\r\n }\r\n\r\n if (type === MSG.AUTH_CALL_RESULT) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.resolve(ok(payload)) // payload is AuthResult\r\n }\r\n return\r\n }\r\n }\r\n\r\n /**\r\n * Handle worker errors\r\n */\r\n private handleWorkerError(error: ErrorEvent): void {\r\n console.error('Worker error:', error)\r\n // Reject all pending requests\r\n for (const [id, request] of this.pendingRequests) {\r\n request.reject(new Error(`Worker error: ${error.message}`))\r\n }\r\n this.pendingRequests.clear()\r\n }\r\n\r\n /**\r\n * Generate unique message ID\r\n */\r\n private generateMessageId(): string {\r\n return `msg_${++this.messageId}_${Date.now()}`\r\n }\r\n\r\n /**\r\n * Make API request\r\n */\r\n async fetch(url: string, options: FetchGuardRequestInit = {}): Promise<Result<ApiResponse>> {\r\n const { result } = this.fetchWithId(url, options)\r\n return result\r\n }\r\n\r\n /**\r\n * Fetch with id for external cancellation\r\n * Returns { id, result, cancel }\r\n * Now uses queue system for sequential processing\r\n */\r\n fetchWithId(url: string, options: FetchGuardRequestInit = {}): {\r\n id: string\r\n result: Promise<Result<ApiResponse>>\r\n cancel: () => void\r\n } {\r\n const id = this.generateMessageId()\r\n\r\n // Serialize FormData if present (async operation)\r\n const result = new Promise<Result<ApiResponse>>(async (resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (response) => resolve(response as Result<ApiResponse>),\r\n reject: (error) => reject(error)\r\n })\r\n\r\n try {\r\n let serializedOptions = { ...options }\r\n\r\n // Serialize FormData body before sending to worker\r\n if (options.body && isFormData(options.body)) {\r\n const serializedBody = await serializeFormData(options.body)\r\n // SerializedFormData will be deserialized back to FormData in worker\r\n serializedOptions.body = serializedBody as unknown as BodyInit\r\n }\r\n\r\n // Serialize Headers object to plain object (Headers cannot be cloned)\r\n if (options.headers) {\r\n if (options.headers instanceof Headers) {\r\n const plainHeaders: Record<string, string> = {}\r\n options.headers.forEach((value, key) => {\r\n plainHeaders[key] = value\r\n })\r\n serializedOptions.headers = plainHeaders\r\n }\r\n }\r\n\r\n const message = { id, type: MSG.FETCH, payload: { url, options: serializedOptions } }\r\n\r\n await this.sendMessageQueued(message, 30000)\r\n } catch (error) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error instanceof Error ? error : new Error(String(error)))\r\n }\r\n }\r\n })\r\n\r\n const cancel = () => this.cancel(id)\r\n\r\n return { id, result, cancel }\r\n }\r\n\r\n /**\r\n * Cancel a pending request by ID\r\n */\r\n cancel(id: string): void {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n this.worker.postMessage({ id, type: MSG.CANCEL })\r\n request.reject(new Error('Request cancelled'))\r\n }\r\n }\r\n\r\n /**\r\n * Convenience methods\r\n */\r\n async get(url: string, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n return this.fetch(url, { ...options, method: 'GET' })\r\n }\r\n\r\n async post(url: string, body?: unknown, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'POST',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'POST',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n async put(url: string, body?: unknown, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'PUT',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'PUT',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n async delete(url: string, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n return this.fetch(url, { ...options, method: 'DELETE' })\r\n }\r\n\r\n async patch(url: string, body?: unknown, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<ApiResponse>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'PATCH',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'PATCH',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n /**\r\n * Generic method to call any auth method on provider\r\n * @param method - Method name (login, logout, loginWithPhone, etc.)\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n * @param args - Arguments to pass to the method\r\n * @returns Promise<Result<AuthResult>> - Always returns AuthResult\r\n */\r\n async call(method: string, emitEvent?: boolean, ...args: unknown[]): Promise<Result<AuthResult>> {\r\n const id = this.generateMessageId()\r\n const message = { id, type: MSG.AUTH_CALL, payload: { method, args, emitEvent } }\r\n\r\n return new Promise<Result<AuthResult>>((resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (r) => resolve(r as Result<AuthResult>),\r\n reject: (e: Error) => reject(e)\r\n })\r\n\r\n this.sendMessageQueued(message, 15000).catch((error) => {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error)\r\n }\r\n })\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Convenience wrapper for login\r\n * @param payload - Login credentials\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async login(payload?: unknown, emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n const args = typeof payload === 'undefined' ? [] : [payload]\r\n return this.call('login', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Convenience wrapper for logout\r\n * @param payload - Optional logout payload\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async logout(payload?: unknown, emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n const args = typeof payload === 'undefined' ? [] : [payload]\r\n return this.call('logout', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Convenience wrapper for refreshToken\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async refreshToken(emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n return this.call('refreshToken', emitEvent)\r\n }\r\n\r\n /**\r\n * Check if worker is ready\r\n */\r\n ready(): boolean {\r\n return this.isReady\r\n }\r\n\r\n /**\r\n * Wait for worker to be ready\r\n * Returns immediately if already ready\r\n */\r\n async whenReady(): Promise<void> {\r\n if (this.isReady) return Promise.resolve()\r\n\r\n return new Promise<void>((resolve) => {\r\n this.readyListeners.add(resolve)\r\n })\r\n }\r\n\r\n /**\r\n * Subscribe to ready event\r\n * Callback is called immediately if already ready\r\n */\r\n onReady(callback: () => void): () => void {\r\n if (this.isReady) {\r\n // Already ready - call immediately\r\n callback()\r\n }\r\n\r\n this.readyListeners.add(callback)\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n this.readyListeners.delete(callback)\r\n }\r\n }\r\n\r\n /**\r\n * Subscribe to auth state changes\r\n */\r\n onAuthStateChanged(cb: (state: AuthResult) => void): () => void {\r\n this.authListeners.add(cb)\r\n return () => this.authListeners.delete(cb)\r\n }\r\n\r\n /** Send PING and await PONG */\r\n async ping(): Promise<Result<{ timestamp: number }>> {\r\n const id = this.generateMessageId()\r\n const message = { id, type: MSG.PING, payload: { timestamp: Date.now() } }\r\n\r\n return new Promise<Result<{ timestamp: number }>>((resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (r) => resolve(r as Result<{ timestamp: number }>),\r\n reject: (e: Error) => reject(e)\r\n })\r\n\r\n this.sendMessageQueued(message, 5000).catch((error) => {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error)\r\n }\r\n })\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Send message through queue system\r\n * All messages go through queue for sequential processing\r\n */\r\n private sendMessageQueued<T = unknown>(message: MainToWorkerMessage, timeoutMs: number = this.queueTimeout): Promise<T> {\r\n return new Promise((resolve, reject) => {\r\n const timeout = setTimeout(() => {\r\n const index = this.requestQueue.findIndex(item => item.id === message.id)\r\n if (index !== -1) {\r\n this.requestQueue.splice(index, 1)\r\n }\r\n this.pendingRequests.delete(message.id)\r\n reject(new Error('Request timeout'))\r\n }, timeoutMs)\r\n\r\n const queueItem: QueueItem = {\r\n id: message.id,\r\n message,\r\n resolve: resolve as (response: unknown) => void,\r\n reject,\r\n timeout\r\n }\r\n\r\n this.requestQueue.push(queueItem)\r\n\r\n this.processQueue()\r\n })\r\n }\r\n\r\n /**\r\n * Process message queue sequentially\r\n * Benefits:\r\n * - Sequential processing prevents worker overload\r\n * - Better error isolation (one failure doesn't affect others)\r\n * - 50ms delay between requests for backpressure\r\n */\r\n private async processQueue(): Promise<void> {\r\n if (this.isProcessingQueue || this.requestQueue.length === 0) {\r\n return\r\n }\r\n\r\n this.isProcessingQueue = true\r\n\r\n while (this.requestQueue.length > 0) {\r\n const item = this.requestQueue.shift()\r\n if (!item) continue\r\n\r\n try {\r\n this.worker.postMessage(item.message)\r\n\r\n await new Promise(resolve => setTimeout(resolve, 50))\r\n } catch (error) {\r\n clearTimeout(item.timeout)\r\n item.reject(error instanceof Error ? error : new Error(String(error)))\r\n }\r\n }\r\n\r\n this.isProcessingQueue = false\r\n }\r\n\r\n /**\r\n * Cleanup - terminate worker\r\n */\r\n destroy(): void {\r\n this.worker.terminate()\r\n this.pendingRequests.clear()\r\n\r\n for (const item of this.requestQueue) {\r\n clearTimeout(item.timeout)\r\n item.reject(new Error('Client destroyed'))\r\n }\r\n this.requestQueue = []\r\n }\r\n}\r\n\r\n/**\r\n * Factory function to create FetchGuard client\r\n */\r\nexport function createClient(options: FetchGuardOptions): FetchGuardClient {\r\n return new FetchGuardClient(options)\r\n}\r\n","import type { ErrorDetail } from 'ts-micro-result'\r\nimport type { WorkerConfig, FetchGuardRequestInit, ProviderPresetConfig, AuthResult, ApiResponse } from './types'\r\n\r\n/**\r\n * MESSAGE PAYLOADS - SINGLE SOURCE OF TRUTH\r\n *\r\n * Define all message payloads here. Type unions and MSG constants are auto-generated.\r\n *\r\n * USAGE:\r\n * - To add a new message: Just add one line to the appropriate interface\r\n * - Payload types are automatically inferred\r\n * - MSG constants are automatically generated\r\n *\r\n * EXAMPLE:\r\n * ```typescript\r\n * interface MainPayloads {\r\n * NEW_MESSAGE: { foo: string } // Add this line\r\n * }\r\n * // => Automatically get MainToWorkerMessage union with NEW_MESSAGE\r\n * // => Automatically get MSG.NEW_MESSAGE = 'NEW_MESSAGE'\r\n * ```\r\n */\r\n\r\n/**\r\n * Payloads for messages sent from Main thread → Worker thread\r\n */\r\nexport interface MainPayloads {\r\n SETUP: { config: WorkerConfig; providerConfig: ProviderPresetConfig | string | null }\r\n FETCH: { url: string; options?: FetchGuardRequestInit }\r\n AUTH_CALL: { method: string; args: unknown[]; emitEvent?: boolean } // Generic auth method call (login, logout, loginWithPhone, etc.)\r\n CANCEL: undefined\r\n PING: { timestamp: number }\r\n}\r\n\r\n/**\r\n * Payloads for messages sent from Worker thread → Main thread\r\n */\r\nexport interface WorkerPayloads {\r\n ERROR: { errors: ErrorDetail[] }\r\n READY: undefined\r\n SETUP_ERROR: { error: string }\r\n PONG: { timestamp: number }\r\n LOG: { level: 'info' | 'warn' | 'error'; message: string }\r\n AUTH_STATE_CHANGED: AuthResult\r\n AUTH_CALL_RESULT: AuthResult\r\n FETCH_RESULT: ApiResponse\r\n FETCH_ERROR: { error: string; status?: number }\r\n}\r\n\r\n/**\r\n * Generate message type from payload definition\r\n * Handles optional payloads (undefined) gracefully\r\n */\r\ntype MessageFromPayloads<P> = {\r\n [K in keyof P]: { id: string; type: K } & (\r\n P[K] extends undefined ? {} : { payload: P[K] }\r\n )\r\n}[keyof P]\r\n\r\n/**\r\n * Message type unions - auto-generated from payload interfaces\r\n */\r\nexport type MainToWorkerMessage = MessageFromPayloads<MainPayloads>\r\nexport type WorkerToMainMessage = MessageFromPayloads<WorkerPayloads>\r\n\r\n/**\r\n * Message type unions for compile-time type checking\r\n */\r\nexport type MainType = keyof MainPayloads\r\nexport type WorkerType = keyof WorkerPayloads\r\nexport type MessageType = MainType | WorkerType\r\n\r\n/**\r\n * MSG constants object\r\n * Usage: MSG.SETUP, MSG.FETCH, etc.\r\n */\r\nexport const MSG = Object.freeze({\r\n // Main -> Worker messages\r\n SETUP: 'SETUP',\r\n FETCH: 'FETCH',\r\n AUTH_CALL: 'AUTH_CALL',\r\n CANCEL: 'CANCEL',\r\n PING: 'PING',\r\n\r\n // Worker -> Main messages\r\n ERROR: 'ERROR',\r\n READY: 'READY',\r\n SETUP_ERROR: 'SETUP_ERROR',\r\n PONG: 'PONG',\r\n LOG: 'LOG',\r\n AUTH_STATE_CHANGED: 'AUTH_STATE_CHANGED',\r\n AUTH_CALL_RESULT: 'AUTH_CALL_RESULT',\r\n FETCH_RESULT: 'FETCH_RESULT',\r\n FETCH_ERROR: 'FETCH_ERROR'\r\n}) as { readonly [K in MessageType]: K }\r\n","/**\n * FetchGuard Default Configuration Values\n */\n\n/**\n * Default time (in milliseconds) to refresh token before expiry\n * @default 60000 (60 seconds)\n */\nexport const DEFAULT_REFRESH_EARLY_MS = 60_000\n","/**\n * Error definitions organized by domain\n * Using ts-micro-result's defineError for consistency\n */\n\nimport { defineError, defineErrorAdvanced } from 'ts-micro-result'\n\n/**\n * General errors\n */\nexport const GeneralErrors = {\n Unexpected: defineError('UNEXPECTED', 'Unexpected error', 500),\n UnknownMessage: defineError('UNKNOWN_MESSAGE', 'Unknown message type', 400),\n ResultParse: defineError('RESULT_PARSE_ERROR', 'Failed to parse result', 500),\n} as const\n\n/**\n * Initialization errors\n */\nexport const InitErrors = {\n NotInitialized: defineError('INIT_ERROR', 'Worker not initialized', 500),\n ProviderInitFailed: defineError('PROVIDER_INIT_FAILED', 'Failed to initialize provider', 500),\n InitFailed: defineError('INIT_FAILED', 'Initialization failed', 500),\n} as const\n\n/**\n * Authentication & Token errors\n */\nexport const AuthErrors = {\n TokenRefreshFailed: defineError('TOKEN_REFRESH_FAILED', 'Token refresh failed', 401),\n LoginFailed: defineError('LOGIN_FAILED', 'Login failed', 401),\n LogoutFailed: defineError('LOGOUT_FAILED', 'Logout failed', 500),\n NotAuthenticated: defineError('NOT_AUTHENTICATED', 'User is not authenticated', 401),\n} as const\n\n/**\n * Domain validation errors\n */\nexport const DomainErrors = {\n NotAllowed: defineErrorAdvanced('DOMAIN_NOT_ALLOWED', 'Domain not allowed: {url}', 403),\n} as const\n\n/**\n * Network & HTTP errors\n */\nexport const NetworkErrors = {\n NetworkError: defineError('NETWORK_ERROR', 'Network error', 500),\n HttpError: defineError('HTTP_ERROR', 'HTTP error', 500),\n FetchError: defineError('FETCH_ERROR', 'Fetch error', 500),\n} as const\n\n/**\n * Request errors\n */\nexport const RequestErrors = {\n Cancelled: defineError('REQUEST_CANCELLED', 'Request was cancelled', 499),\n Timeout: defineError('REQUEST_TIMEOUT', 'Request timeout', 408),\n} as const\n","import type { SerializedFormData, SerializedFormDataEntry, SerializedFile } from '../types'\r\n\r\n/**\r\n * Serialize FormData for transfer over postMessage\r\n * Inspired by api-worker.js:484-518\r\n *\r\n * FormData cannot be cloned via postMessage, so we need to serialize it first\r\n * Files are converted to ArrayBuffer -> number[] for transfer\r\n */\r\nexport async function serializeFormData(formData: FormData): Promise<SerializedFormData> {\r\n const entries: Array<[string, SerializedFormDataEntry]> = []\r\n\r\n // Use forEach instead of entries() for better TS compatibility\r\n formData.forEach((value, key) => {\r\n // Push async operations to promises array for parallel processing\r\n if (value instanceof File) {\r\n // We need to handle this synchronously in forEach, so we'll collect promises\r\n // and await them all at once\r\n } else {\r\n entries.push([key, String(value)])\r\n }\r\n })\r\n\r\n // Handle File entries separately with Promise.all\r\n const filePromises: Promise<void>[] = []\r\n formData.forEach((value, key) => {\r\n if (value instanceof File) {\r\n const promise = (async () => {\r\n const arrayBuffer = await value.arrayBuffer()\r\n const uint8Array = new Uint8Array(arrayBuffer)\r\n const serializedFile: SerializedFile = {\r\n name: value.name,\r\n type: value.type,\r\n data: Array.from(uint8Array) // Convert to number array\r\n }\r\n entries.push([key, serializedFile])\r\n })()\r\n filePromises.push(promise)\r\n }\r\n })\r\n\r\n await Promise.all(filePromises)\r\n\r\n return {\r\n _type: 'FormData',\r\n entries\r\n }\r\n}\r\n\r\n/**\r\n * Deserialize SerializedFormData back to FormData in worker\r\n * Reconstructs File objects from serialized data\r\n */\r\nexport function deserializeFormData(serialized: SerializedFormData): FormData {\r\n const formData = new FormData()\r\n\r\n for (const [key, value] of serialized.entries) {\r\n if (typeof value === 'string') {\r\n formData.append(key, value)\r\n } else {\r\n // Reconstruct File from SerializedFile\r\n const uint8Array = new Uint8Array(value.data)\r\n const file = new File([uint8Array], value.name, { type: value.type })\r\n formData.append(key, file)\r\n }\r\n }\r\n\r\n return formData\r\n}\r\n\r\n/**\r\n * Check if body is FormData\r\n */\r\nexport function isFormData(body: unknown): body is FormData {\r\n return body instanceof FormData\r\n}\r\n\r\n/**\r\n * Check if serialized body is SerializedFormData\r\n */\r\nexport function isSerializedFormData(body: unknown): body is SerializedFormData {\r\n return body !== null && typeof body === 'object' && (body as SerializedFormData)._type === 'FormData'\r\n}\r\n","import type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Registry to manage token providers\r\n */\r\nconst registry = new Map<string, TokenProvider>()\r\n\r\n/**\r\n * Register a token provider with name\r\n */\r\nexport function registerProvider(name: string, provider: TokenProvider): void {\r\n if (typeof name !== 'string' || !name.trim()) {\r\n throw new Error('Provider name must be a non-empty string')\r\n }\r\n \r\n if (!provider || typeof provider.refreshToken !== 'function') {\r\n throw new Error('Provider must implement TokenProvider interface')\r\n }\r\n \r\n registry.set(name, provider)\r\n}\r\n\r\n/**\r\n * Get provider by name\r\n */\r\nexport function getProvider(name: string): TokenProvider {\r\n const provider = registry.get(name)\r\n if (!provider) {\r\n throw new Error(`Provider '${name}' not found. Available providers: ${Array.from(registry.keys()).join(', ')}`)\r\n }\r\n return provider\r\n}\r\n\r\n/**\r\n * Check if provider exists\r\n */\r\nexport function hasProvider(name: string): boolean {\r\n return registry.has(name)\r\n}\r\n\r\n/**\r\n * Get list of all provider names\r\n */\r\nexport function listProviders(): string[] {\r\n return Array.from(registry.keys())\r\n}\r\n\r\n/**\r\n * Remove provider\r\n */\r\nexport function unregisterProvider(name: string): boolean {\r\n return registry.delete(name)\r\n}\r\n\r\n/**\r\n * Remove all providers\r\n */\r\nexport function clearProviders(): void {\r\n registry.clear()\r\n}\r\n","import type {\r\n TokenProvider,\r\n RefreshTokenStorage,\r\n TokenParser,\r\n AuthStrategy,\r\n TokenInfo\r\n} from '../types'\r\nimport { ok, err, type Result } from 'ts-micro-result'\r\nimport { AuthErrors, NetworkErrors } from '../errors'\r\n\r\n/**\r\n * Custom auth method type\r\n */\r\ntype CustomAuthMethod = (...args: unknown[]) => Promise<Result<TokenInfo>>\r\n\r\n/**\r\n * Configuration for creating provider\r\n *\r\n * refreshStorage: OPTIONAL - to load refresh token initially when worker starts\r\n * - undefined: cookie-based auth (httpOnly cookie, no need to load)\r\n * - RefreshTokenStorage: body-based auth (load from IndexedDB on startup)\r\n *\r\n * strategy: AuthStrategy with refresh (required), login/logout (required)\r\n *\r\n * customMethods: OPTIONAL - custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n */\r\nexport interface ProviderConfig {\r\n refreshStorage?: RefreshTokenStorage\r\n parser: TokenParser\r\n strategy: AuthStrategy\r\n customMethods?: Record<string, CustomAuthMethod>\r\n}\r\n\r\n/**\r\n * Factory function to create TokenProvider from modular components\r\n *\r\n * Provider automatically handles refresh token:\r\n * - If refreshToken is null and storage exists → load from storage initially\r\n * - If refreshToken exists → use token from worker memory\r\n * - Cookie-based (no storage) → always null\r\n *\r\n * Custom methods:\r\n * - User can add custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n * - Custom methods will be spread into provider object\r\n */\r\nexport function createProvider(config: ProviderConfig): TokenProvider {\r\n const baseProvider: Pick<TokenProvider, 'refreshToken' | 'login' | 'logout'> = {\r\n async refreshToken(refreshToken: string | null) {\r\n let currentRefreshToken = refreshToken\r\n if (currentRefreshToken === null && config.refreshStorage) {\r\n currentRefreshToken = await config.refreshStorage.get()\r\n }\r\n\r\n try {\r\n const response = await config.strategy.refresh(currentRefreshToken)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async login(payload: unknown) {\r\n try {\r\n const response = await config.strategy.login(payload)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.LoginFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.LoginFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async logout(payload?: unknown) {\r\n try {\r\n const response = await config.strategy.logout(payload)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.LogoutFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n if (config.refreshStorage) {\r\n await config.refreshStorage.set(null)\r\n }\r\n\r\n return ok({\r\n token: '',\r\n refreshToken: undefined,\r\n expiresAt: undefined,\r\n user: null // Explicitly clear user on logout\r\n })\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n }\r\n }\r\n\r\n // Merge custom methods if provided\r\n if (config.customMethods) {\r\n return {\r\n ...baseProvider,\r\n ...config.customMethods\r\n } as TokenProvider\r\n }\r\n\r\n return baseProvider as TokenProvider\r\n}\r\n","import type { RefreshTokenStorage } from '../../types'\r\n\r\n/**\r\n * IndexedDB storage - only stores refresh token in IndexedDB\r\n * Suitable for body-based refresh strategy\r\n * Persists refresh token for reuse after reload\r\n */\r\nexport function createIndexedDBStorage(dbName = 'FetchGuardDB', refreshTokenKey = 'refreshToken'): RefreshTokenStorage {\r\n const storeName = 'tokens'\r\n\r\n const openDB = (): Promise<IDBDatabase> => {\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDB.open(dbName, 1)\r\n \r\n request.onerror = () => reject(request.error)\r\n request.onsuccess = () => resolve(request.result)\r\n \r\n request.onupgradeneeded = (event) => {\r\n const db = (event.target as IDBOpenDBRequest).result\r\n if (!db.objectStoreNames.contains(storeName)) {\r\n const store = db.createObjectStore(storeName, { keyPath: 'key' })\r\n store.createIndex('timestamp', 'timestamp', { unique: false })\r\n }\r\n }\r\n })\r\n }\r\n\r\n const promisifyRequest = <T>(request: IDBRequest<T>): Promise<T> => {\r\n return new Promise((resolve, reject) => {\r\n request.onsuccess = () => resolve(request.result)\r\n request.onerror = () => reject(request.error)\r\n })\r\n }\r\n\r\n return {\r\n async get() {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readonly')\r\n const store = transaction.objectStore(storeName)\r\n const result = await promisifyRequest(store.get(refreshTokenKey))\r\n return result?.value || null\r\n } catch (error) {\r\n console.warn('Failed to get refresh token from IndexedDB:', error)\r\n return null\r\n }\r\n },\r\n async set(token) {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readwrite')\r\n const store = transaction.objectStore(storeName)\r\n\r\n if (token) {\r\n await promisifyRequest(store.put({ key: refreshTokenKey, value: token, timestamp: Date.now() }))\r\n } else {\r\n await promisifyRequest(store.delete(refreshTokenKey))\r\n }\r\n } catch (error) {\r\n console.warn('Failed to save refresh token to IndexedDB:', error)\r\n }\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\n\r\n/**\r\n * Body parser - parse token from response body (JSON)\r\n * Expects response format: { data: { accessToken, refreshToken, expiresAt?, user? } }\r\n */\r\nexport const bodyParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n refreshToken: json.data.refreshToken,\r\n expiresAt: json.data.expiresAt,\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\n\r\n/**\r\n * Cookie parser - parse access token from response body\r\n * Expects response format: { data: { accessToken, expiresAt?, user? } }\r\n * Refresh token is automatically set by backend into httpOnly cookie\r\n */\r\nexport const cookieParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n expiresAt: json.data.expiresAt,\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { AuthStrategy } from '../../types'\r\n\r\n/**\r\n * Cookie auth strategy - all auth operations via httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Refresh token is sent automatically via httpOnly cookie\r\n * Credentials are sent in request body\r\n */\r\nexport function createCookieStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): AuthStrategy {\r\n return {\r\n async refresh() {\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload) {\r\n return fetch(config.loginUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard cookie strategy\r\n */\r\nexport const cookieStrategy = createCookieStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import type { AuthStrategy } from '../../types'\r\n\r\n/**\r\n * Body auth strategy - all auth operations via request body\r\n * Suitable for SPA applications\r\n *\r\n * All tokens/credentials are sent in request body\r\n */\r\nexport function createBodyStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): AuthStrategy {\r\n return {\r\n async refresh(refreshToken) {\r\n if (!refreshToken) {\r\n throw new Error('No refresh token available')\r\n }\r\n\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ refreshToken }),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload) {\r\n return fetch(config.loginUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard body strategy\r\n */\r\nexport const bodyStrategy = createBodyStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import { createProvider } from './create-provider'\r\nimport { createIndexedDBStorage } from './storage/indexeddb'\r\nimport { bodyParser } from './parser/body'\r\nimport { cookieParser } from './parser/cookie'\r\nimport { createCookieStrategy } from './strategy/cookie'\r\nimport { createBodyStrategy } from './strategy/body'\r\nimport type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Cookie Provider - uses httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: httpOnly cookie (managed by backend)\r\n */\r\nexport function createCookieProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: undefined,\r\n parser: cookieParser,\r\n strategy: createCookieStrategy(config)\r\n })\r\n}\r\n\r\n/**\r\n * Body Provider - refresh token in response body, persisted to IndexedDB\r\n * Suitable for SPA applications\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: IndexedDB (persists across reload)\r\n */\r\nexport function createBodyProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n refreshTokenKey?: string\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: createIndexedDBStorage('FetchGuardDB', config.refreshTokenKey || 'refreshToken'),\r\n parser: bodyParser,\r\n strategy: createBodyStrategy(config)\r\n })\r\n}\r\n","/**\r\n * Binary data utilities for Worker\r\n * Handles ArrayBuffer <-> Base64 conversion for binary responses\r\n */\r\n\r\n/**\r\n * Convert ArrayBuffer to base64 string\r\n * Used in worker to encode binary responses for postMessage transfer\r\n */\r\nexport function arrayBufferToBase64(buffer: ArrayBuffer): string {\r\n const bytes = new Uint8Array(buffer)\r\n let binary = ''\r\n for (let i = 0; i < bytes.length; i++) {\r\n binary += String.fromCharCode(bytes[i])\r\n }\r\n return btoa(binary)\r\n}\r\n\r\n/**\r\n * Convert base64 string to ArrayBuffer\r\n * Used in client to decode binary responses\r\n */\r\nexport function base64ToArrayBuffer(base64: string): ArrayBuffer {\r\n const binary = atob(base64)\r\n const bytes = new Uint8Array(binary.length)\r\n for (let i = 0; i < binary.length; i++) {\r\n bytes[i] = binary.charCodeAt(i)\r\n }\r\n return bytes.buffer\r\n}\r\n\r\n/**\r\n * Check if content type is binary (should be base64 encoded)\r\n * Returns true for images, PDFs, videos, etc.\r\n */\r\nexport function isBinaryContentType(contentType: string): boolean {\r\n const normalized = contentType.toLowerCase()\r\n\r\n // Text types - NOT binary\r\n if (normalized.includes('text/')) return false\r\n if (normalized.includes('json')) return false\r\n if (normalized.includes('xml')) return false\r\n if (normalized.includes('javascript')) return false\r\n if (normalized.includes('ecmascript')) return false\r\n if (normalized.includes('html')) return false\r\n\r\n // Everything else is considered binary\r\n return true\r\n}\r\n"],"mappings":"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","names":["ok","err","err","ok"]}
|
package/dist/worker.js
CHANGED
|
@@ -10,8 +10,9 @@ var MSG = Object.freeze({
|
|
|
10
10
|
CANCEL: "CANCEL",
|
|
11
11
|
PING: "PING",
|
|
12
12
|
// Worker -> Main messages
|
|
13
|
-
|
|
13
|
+
ERROR: "ERROR",
|
|
14
14
|
READY: "READY",
|
|
15
|
+
SETUP_ERROR: "SETUP_ERROR",
|
|
15
16
|
PONG: "PONG",
|
|
16
17
|
LOG: "LOG",
|
|
17
18
|
AUTH_STATE_CHANGED: "AUTH_STATE_CHANGED",
|
|
@@ -59,18 +60,18 @@ function post(message) {
|
|
|
59
60
|
;
|
|
60
61
|
self.postMessage(message);
|
|
61
62
|
}
|
|
62
|
-
function
|
|
63
|
+
function sendError(id, result) {
|
|
63
64
|
post({
|
|
64
|
-
type: MSG.
|
|
65
|
+
type: MSG.ERROR,
|
|
65
66
|
id,
|
|
66
|
-
payload: {
|
|
67
|
+
payload: { errors: result.errors || [] }
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
|
-
function sendFetchResult(id,
|
|
70
|
+
function sendFetchResult(id, response) {
|
|
70
71
|
post({
|
|
71
72
|
type: MSG.FETCH_RESULT,
|
|
72
73
|
id,
|
|
73
|
-
payload:
|
|
74
|
+
payload: response
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
77
|
function sendFetchError(id, error, status) {
|
|
@@ -86,6 +87,13 @@ function sendReady() {
|
|
|
86
87
|
id: `evt_${Date.now()}`
|
|
87
88
|
});
|
|
88
89
|
}
|
|
90
|
+
function sendSetupError(error) {
|
|
91
|
+
post({
|
|
92
|
+
type: MSG.SETUP_ERROR,
|
|
93
|
+
id: `evt_${Date.now()}`,
|
|
94
|
+
payload: { error }
|
|
95
|
+
});
|
|
96
|
+
}
|
|
89
97
|
function sendPong(id, timestamp) {
|
|
90
98
|
post({
|
|
91
99
|
type: MSG.PONG,
|
|
@@ -375,7 +383,7 @@ function buildProviderFromPreset(config) {
|
|
|
375
383
|
refreshTokenKey: config.refreshTokenKey
|
|
376
384
|
});
|
|
377
385
|
default:
|
|
378
|
-
throw new Error(`Unknown provider type: ${config.type}`);
|
|
386
|
+
throw new Error(`Unknown provider type: ${String(config.type)}`);
|
|
379
387
|
}
|
|
380
388
|
}
|
|
381
389
|
|
|
@@ -394,7 +402,27 @@ function deserializeFormData(serialized) {
|
|
|
394
402
|
return formData;
|
|
395
403
|
}
|
|
396
404
|
function isSerializedFormData(body) {
|
|
397
|
-
return body && typeof body === "object" && body._type === "FormData";
|
|
405
|
+
return body !== null && typeof body === "object" && body._type === "FormData";
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/utils/binary.ts
|
|
409
|
+
function arrayBufferToBase64(buffer) {
|
|
410
|
+
const bytes = new Uint8Array(buffer);
|
|
411
|
+
let binary = "";
|
|
412
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
413
|
+
binary += String.fromCharCode(bytes[i]);
|
|
414
|
+
}
|
|
415
|
+
return btoa(binary);
|
|
416
|
+
}
|
|
417
|
+
function isBinaryContentType(contentType) {
|
|
418
|
+
const normalized = contentType.toLowerCase();
|
|
419
|
+
if (normalized.includes("text/")) return false;
|
|
420
|
+
if (normalized.includes("json")) return false;
|
|
421
|
+
if (normalized.includes("xml")) return false;
|
|
422
|
+
if (normalized.includes("javascript")) return false;
|
|
423
|
+
if (normalized.includes("ecmascript")) return false;
|
|
424
|
+
if (normalized.includes("html")) return false;
|
|
425
|
+
return true;
|
|
398
426
|
}
|
|
399
427
|
|
|
400
428
|
// src/worker.ts
|
|
@@ -408,6 +436,9 @@ function isSerializedFormData(body) {
|
|
|
408
436
|
const pendingControllers = /* @__PURE__ */ new Map();
|
|
409
437
|
let refreshPromise = null;
|
|
410
438
|
async function ensureValidToken() {
|
|
439
|
+
if (!provider) {
|
|
440
|
+
return err2(InitErrors.NotInitialized());
|
|
441
|
+
}
|
|
411
442
|
if (accessToken && expiresAt) {
|
|
412
443
|
const refreshEarlyMs = config?.refreshEarlyMs ?? DEFAULT_REFRESH_EARLY_MS;
|
|
413
444
|
const timeLeft = expiresAt - Date.now();
|
|
@@ -421,12 +452,18 @@ function isSerializedFormData(body) {
|
|
|
421
452
|
}
|
|
422
453
|
refreshPromise = (async () => {
|
|
423
454
|
try {
|
|
455
|
+
if (!provider) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
424
458
|
const valueRes = await provider.refreshToken(refreshToken);
|
|
425
459
|
if (valueRes.isError()) {
|
|
426
460
|
setTokenState({ token: null, expiresAt: null, user: void 0, refreshToken: void 0 });
|
|
427
461
|
return;
|
|
428
462
|
}
|
|
429
463
|
const tokenInfo = valueRes.data;
|
|
464
|
+
if (!tokenInfo) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
430
467
|
setTokenState(tokenInfo);
|
|
431
468
|
} finally {
|
|
432
469
|
refreshPromise = null;
|
|
@@ -472,9 +509,7 @@ function isSerializedFormData(body) {
|
|
|
472
509
|
}
|
|
473
510
|
const requiresAuth = options.requiresAuth !== false;
|
|
474
511
|
const includeHeaders = options.includeHeaders === true;
|
|
475
|
-
const
|
|
476
|
-
delete fetchOptions.requiresAuth;
|
|
477
|
-
delete fetchOptions.includeHeaders;
|
|
512
|
+
const { requiresAuth: _, includeHeaders: __, ...fetchOptions } = options;
|
|
478
513
|
if (fetchOptions.body && isSerializedFormData(fetchOptions.body)) {
|
|
479
514
|
fetchOptions.body = deserializeFormData(fetchOptions.body);
|
|
480
515
|
}
|
|
@@ -488,29 +523,37 @@ function isSerializedFormData(body) {
|
|
|
488
523
|
}
|
|
489
524
|
if (requiresAuth) {
|
|
490
525
|
const tokenRes = await ensureValidToken();
|
|
491
|
-
if (tokenRes.isError())
|
|
526
|
+
if (tokenRes.isError()) {
|
|
527
|
+
return err2(tokenRes.errors);
|
|
528
|
+
}
|
|
492
529
|
const token = tokenRes.data;
|
|
493
530
|
if (token) {
|
|
494
531
|
headers["Authorization"] = `Bearer ${token}`;
|
|
495
532
|
}
|
|
496
533
|
}
|
|
497
|
-
let response
|
|
498
|
-
|
|
499
|
-
|
|
534
|
+
let response;
|
|
535
|
+
try {
|
|
536
|
+
response = await fetch(url, { ...fetchOptions, headers, credentials: "include" });
|
|
537
|
+
} catch (e) {
|
|
500
538
|
const aborted = e && e.name === "AbortError";
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
539
|
+
return aborted ? err2(RequestErrors.Cancelled()) : err2(NetworkErrors.NetworkError({ message: String(e) }));
|
|
540
|
+
}
|
|
541
|
+
const contentType = response.headers.get("content-type") || "application/octet-stream";
|
|
542
|
+
const isBinary = isBinaryContentType(contentType);
|
|
543
|
+
let body;
|
|
544
|
+
if (isBinary) {
|
|
545
|
+
const buffer = await response.arrayBuffer();
|
|
546
|
+
body = arrayBufferToBase64(buffer);
|
|
547
|
+
} else {
|
|
548
|
+
body = await response.text();
|
|
549
|
+
}
|
|
550
|
+
const responseHeaders = {};
|
|
507
551
|
if (includeHeaders) {
|
|
508
|
-
responseHeaders = {};
|
|
509
552
|
response.headers.forEach((value, key) => {
|
|
510
553
|
responseHeaders[key] = value;
|
|
511
554
|
});
|
|
512
555
|
}
|
|
513
|
-
return response.ok ? ok2({ body, status: response.status, headers: responseHeaders }) : err2(NetworkErrors.HttpError({ message: `HTTP ${response.status}: ${body}` }));
|
|
556
|
+
return response.ok ? ok2({ body, status: response.status, contentType, headers: responseHeaders }) : err2(NetworkErrors.HttpError({ message: `HTTP ${response.status}: ${body}` }));
|
|
514
557
|
}
|
|
515
558
|
function setTokenState(tokenInfo, emitEvent = true) {
|
|
516
559
|
if ("token" in tokenInfo) {
|
|
@@ -551,11 +594,18 @@ function isSerializedFormData(body) {
|
|
|
551
594
|
} else if (providerConfig && typeof providerConfig === "object" && "type" in providerConfig) {
|
|
552
595
|
provider = buildProviderFromPreset(providerConfig);
|
|
553
596
|
} else {
|
|
554
|
-
|
|
597
|
+
sendSetupError("Invalid provider config");
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
if (!provider) {
|
|
601
|
+
sendSetupError("Provider initialization failed - provider is null");
|
|
602
|
+
break;
|
|
555
603
|
}
|
|
556
604
|
sendReady();
|
|
557
605
|
} catch (error) {
|
|
558
|
-
|
|
606
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
607
|
+
console.error("[FetchGuard Worker] Setup failed:", errorMessage);
|
|
608
|
+
sendSetupError(errorMessage);
|
|
559
609
|
}
|
|
560
610
|
break;
|
|
561
611
|
}
|
|
@@ -567,9 +617,8 @@ function isSerializedFormData(body) {
|
|
|
567
617
|
pendingControllers.set(id, controller);
|
|
568
618
|
const merged = { ...options || {}, signal: controller.signal };
|
|
569
619
|
const result = await makeApiRequest(url, merged);
|
|
570
|
-
if (result.
|
|
571
|
-
|
|
572
|
-
sendFetchResult(id, response.status, response.body, response.headers);
|
|
620
|
+
if (result.isOkWithData()) {
|
|
621
|
+
sendFetchResult(id, result.data);
|
|
573
622
|
} else {
|
|
574
623
|
const error = result.errors?.[0];
|
|
575
624
|
const message = error?.message || "Unknown error";
|
|
@@ -588,16 +637,24 @@ function isSerializedFormData(body) {
|
|
|
588
637
|
try {
|
|
589
638
|
const { method, args, emitEvent } = payload;
|
|
590
639
|
const shouldEmitEvent = emitEvent ?? true;
|
|
640
|
+
if (!provider) {
|
|
641
|
+
sendError(id, err2(InitErrors.NotInitialized()));
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
591
644
|
if (typeof provider[method] !== "function") {
|
|
592
|
-
|
|
645
|
+
sendError(id, err2(GeneralErrors.Unexpected({ message: `Method '${method}' not found on provider` })));
|
|
593
646
|
break;
|
|
594
647
|
}
|
|
595
648
|
const result = await provider[method](...args);
|
|
596
649
|
if (result.isError()) {
|
|
597
|
-
|
|
650
|
+
sendError(id, result);
|
|
598
651
|
break;
|
|
599
652
|
}
|
|
600
653
|
const tokenInfo = result.data;
|
|
654
|
+
if (!tokenInfo) {
|
|
655
|
+
sendError(id, err2(GeneralErrors.Unexpected({ message: "Provider returned null token info" })));
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
601
658
|
setTokenState(tokenInfo, shouldEmitEvent);
|
|
602
659
|
const now = Date.now();
|
|
603
660
|
const authenticated = accessToken !== null && accessToken !== "" && (expiresAt === null || expiresAt > now);
|
|
@@ -607,7 +664,7 @@ function isSerializedFormData(body) {
|
|
|
607
664
|
user: currentUser
|
|
608
665
|
});
|
|
609
666
|
} catch (error) {
|
|
610
|
-
|
|
667
|
+
sendError(id, err2(GeneralErrors.Unexpected({ message: error instanceof Error ? error.message : String(error) })));
|
|
611
668
|
}
|
|
612
669
|
break;
|
|
613
670
|
}
|
|
@@ -620,9 +677,6 @@ function isSerializedFormData(body) {
|
|
|
620
677
|
pendingControllers.delete(id);
|
|
621
678
|
}
|
|
622
679
|
} catch (error) {
|
|
623
|
-
if (config?.debug) {
|
|
624
|
-
console.error("CANCEL error:", error);
|
|
625
|
-
}
|
|
626
680
|
}
|
|
627
681
|
break;
|
|
628
682
|
}
|
|
@@ -632,13 +686,13 @@ function isSerializedFormData(body) {
|
|
|
632
686
|
const ts = data.payload?.timestamp ?? Date.now();
|
|
633
687
|
sendPong(id, ts);
|
|
634
688
|
} catch (error) {
|
|
635
|
-
|
|
689
|
+
sendError(id, err2(GeneralErrors.Unexpected({ message: error instanceof Error ? error.message : String(error) })));
|
|
636
690
|
}
|
|
637
691
|
break;
|
|
638
692
|
}
|
|
639
693
|
default: {
|
|
640
694
|
const anyData = data;
|
|
641
|
-
|
|
695
|
+
sendError(anyData.id, err2(GeneralErrors.UnknownMessage({ message: `Unknown message type: ${String(anyData.type)}` })));
|
|
642
696
|
}
|
|
643
697
|
}
|
|
644
698
|
};
|
package/dist/worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts","../src/messages.ts","../src/constants.ts","../src/errors.ts","../src/worker-post.ts","../src/utils/registry.ts","../src/provider/create-provider.ts","../src/provider/storage/indexeddb.ts","../src/provider/parser/body.ts","../src/provider/parser/cookie.ts","../src/provider/strategy/cookie.ts","../src/provider/strategy/body.ts","../src/provider/presets.ts","../src/provider/register-presets.ts","../src/utils/formdata.ts"],"sourcesContent":["/// <reference lib=\"webworker\" />\n\nimport type { WorkerConfig } from './types'\nimport type { MainToWorkerMessage } from './messages'\nimport { ok, err, type Result } from 'ts-micro-result'\nimport { MSG } from './messages'\nimport { DEFAULT_REFRESH_EARLY_MS } from './constants'\nimport {\n InitErrors,\n DomainErrors,\n NetworkErrors,\n RequestErrors,\n GeneralErrors\n} from './errors'\nimport { sendAuthStateChanged, sendAuthCallResult, sendPong, sendReady, sendResult, sendFetchResult, sendFetchError } from './worker-post'\nimport { getProvider } from './utils/registry'\nimport { buildProviderFromPreset } from './provider/register-presets'\nimport { deserializeFormData, isSerializedFormData } from './utils/formdata'\n\n/**\n * IIFE Closure to protect sensitive tokens from external access\n * Inspired by api-worker.js security pattern\n */\n;(function () {\n let config: WorkerConfig | null = null\n let provider: any = null\n let accessToken: string | null = null\n let refreshToken: string | null = null\n let expiresAt: number | null = null\n let currentUser: unknown | undefined\n const pendingControllers = new Map<string, AbortController>()\n let refreshPromise: Promise<void> | null = null\n\n/**\n * Ensure we have a valid access token (not expired).\n * If token is missing or expired, refresh it.\n * Prevents concurrent refresh attempts.\n */\nasync function ensureValidToken(): Promise<Result<string | null>> {\n if (accessToken && expiresAt) {\n const refreshEarlyMs = config?.refreshEarlyMs ?? DEFAULT_REFRESH_EARLY_MS\n const timeLeft = expiresAt - Date.now()\n if (timeLeft > refreshEarlyMs) {\n return ok(accessToken)\n }\n }\n\n if (refreshPromise) {\n await refreshPromise\n return ok(accessToken)\n }\n\n refreshPromise = (async () => {\n try {\n const valueRes = await provider.refreshToken(refreshToken)\n\n if (valueRes.isError()) {\n setTokenState({ token: null, expiresAt: null, user: undefined, refreshToken: undefined })\n return\n }\n\n const tokenInfo = valueRes.data\n\n setTokenState(tokenInfo)\n } finally {\n refreshPromise = null\n }\n })()\n\n await refreshPromise\n return ok(accessToken)\n}\n\n/**\n * Validate domain against allowed domains\n */\nfunction validateDomain(url: string): boolean {\n if (!config?.allowedDomains?.length) {\n return true\n }\n\n try {\n const urlObj = new URL(url)\n const hostname = urlObj.hostname\n const port = urlObj.port\n\n for (const entry of config.allowedDomains) {\n const idx = entry.lastIndexOf(':')\n const hasPort = idx > -1 && entry.indexOf(':') === idx\n const pattern = hasPort ? entry.slice(0, idx) : entry\n const entryPort = hasPort ? entry.slice(idx + 1) : ''\n const isWildcard = pattern.startsWith('*.')\n const base = isWildcard ? pattern.slice(2) : pattern\n\n const hostnameMatch = isWildcard\n ? (hostname === base || hostname.endsWith('.' + base))\n : (hostname === base)\n \n if (!hostnameMatch) continue\n\n if (hasPort) {\n if (port === entryPort) return true\n continue\n }\n return true\n }\n return false\n } catch {\n return false\n }\n}\n\n/**\n * Make API request with proactive token management\n */\nasync function makeApiRequest(url: string, options: any = {}) {\n if (!config) {\n return err(InitErrors.NotInitialized())\n }\n\n if (!validateDomain(url)) {\n return err(DomainErrors.NotAllowed({ url }))\n }\n\n const requiresAuth = options.requiresAuth !== false\n const includeHeaders = options.includeHeaders === true\n const fetchOptions: RequestInit = { ...options }\n delete (fetchOptions as any).requiresAuth\n delete (fetchOptions as any).includeHeaders\n\n // Deserialize FormData if present (inspired by api-worker.js:484-518)\n if (fetchOptions.body && isSerializedFormData(fetchOptions.body)) {\n fetchOptions.body = deserializeFormData(fetchOptions.body)\n }\n\n const headers: Record<string, string> = {\n ...(fetchOptions.headers as Record<string, string> || {})\n }\n\n // Don't set Content-Type for FormData - browser will set it with boundary\n if (!headers['Content-Type'] && !headers['content-type'] && fetchOptions.body) {\n if (typeof fetchOptions.body === 'object' && !(fetchOptions.body instanceof FormData) && !(fetchOptions.body instanceof URLSearchParams)) {\n headers['Content-Type'] = 'application/json'\n }\n }\n\n if (requiresAuth) {\n const tokenRes = await ensureValidToken()\n if (tokenRes.isError()) return tokenRes\n\n const token = tokenRes.data\n if (token) {\n headers['Authorization'] = `Bearer ${token}`\n }\n }\n\n let response: Response | null = null\n let networkErr: Result<never> | null = null\n response = await fetch(url, { ...fetchOptions, headers, credentials: 'include' }).catch((e) => {\n const aborted = (e && (e as any).name === 'AbortError')\n networkErr = aborted ? err(RequestErrors.Cancelled()) : err(NetworkErrors.NetworkError({ message: String(e) }))\n return null as any\n })\n if (!response) return (networkErr ?? err(NetworkErrors.NetworkError({ message: 'Unknown network error' })))\n\n const body = await response.text()\n let responseHeaders: Record<string, string> | undefined\n if (includeHeaders) {\n responseHeaders = {}\n response.headers.forEach((value, key) => {\n (responseHeaders as Record<string, string>)[key] = value\n })\n }\n\n return response.ok\n ? ok({ body, status: response.status, headers: responseHeaders })\n : err(NetworkErrors.HttpError({ message: `HTTP ${response.status}: ${body}` }))\n}\n\n/**\n * Update token state from TokenInfo and auto emit AUTH_STATE_CHANGED\n *\n * Smart update logic for ALL fields:\n * - Only update field if key exists in tokenInfo\n * - If key exists with value: update to that value (including null)\n * - If key doesn't exist: preserve existing value\n *\n * This allows flexible custom auth methods:\n * - Standard login/refresh: returns { token, user, expiresAt }\n * - Update user info: may only return { user: {...} } (no token change)\n * - Verify OTP: may return {} (just validation, no state change)\n * - Logout: returns { token: null, user: null, ... } to clear all\n */\nfunction setTokenState(tokenInfo: { token?: string | null; expiresAt?: number | null; user?: unknown; refreshToken?: string | null }, emitEvent: boolean = true) {\n // Apply smart preservation to ALL fields\n if ('token' in tokenInfo) {\n accessToken = tokenInfo.token ?? null\n }\n\n if ('expiresAt' in tokenInfo) {\n expiresAt = tokenInfo.expiresAt ?? null\n }\n\n if ('user' in tokenInfo) {\n currentUser = tokenInfo.user\n }\n\n if ('refreshToken' in tokenInfo) {\n refreshToken = tokenInfo.refreshToken ?? null\n }\n\n if (emitEvent) {\n postAuthChanged()\n }\n}\n\n/**\n * Emit AUTH_STATE_CHANGED event based on current state\n * authenticated = true if we have valid non-expired token\n */\nfunction postAuthChanged() {\n const now = Date.now()\n const authenticated = accessToken !== null && accessToken !== '' && (expiresAt === null || expiresAt > now)\n sendAuthStateChanged({\n authenticated,\n expiresAt,\n user: currentUser\n })\n}\n\n/**\n * Main message handler\n * Each case has its own try-catch for better error isolation\n * Inspired by old-workers/api-worker.ts:24-123\n */\nself.onmessage = async (event: MessageEvent<MainToWorkerMessage>) => {\n const data = event.data\n switch (data.type) {\n case MSG.SETUP: {\n try {\n const payload = data.payload\n config = payload.config\n\n const providerConfig = payload.providerConfig\n\n // Build provider from config\n if (typeof providerConfig === 'string') {\n // Registry lookup\n provider = getProvider(providerConfig)\n } else if (providerConfig && typeof providerConfig === 'object' && 'type' in providerConfig) {\n // ProviderPresetConfig object\n provider = buildProviderFromPreset(providerConfig as any)\n } else {\n throw new Error('Invalid provider config')\n }\n\n sendReady()\n } catch (error) {\n console.error('[FetchGuard Worker] Setup failed:', error)\n }\n break\n }\n\n case MSG.FETCH: {\n const { id } = data\n try {\n const { url, options } = data.payload\n const controller = new AbortController()\n pendingControllers.set(id, controller)\n const merged: RequestInit = { ...(options || {}), signal: controller.signal }\n const result = await makeApiRequest(url, merged)\n\n if (result.isOk()) {\n const response = result.data as { body: string; status: number; headers?: Record<string, string> }\n sendFetchResult(id, response.status, response.body, response.headers)\n } else {\n const error = result.errors?.[0]\n const message = error?.message || 'Unknown error'\n const status = result.status\n sendFetchError(id, message, status)\n }\n\n pendingControllers.delete(id)\n } catch (error) {\n pendingControllers.delete(id)\n sendFetchError(id, error instanceof Error ? error.message : String(error), undefined)\n }\n break\n }\n\n case MSG.AUTH_CALL: {\n const { id, payload } = data\n try {\n const { method, args, emitEvent } = payload\n const shouldEmitEvent = emitEvent ?? true // Default: emit event\n\n if (typeof provider[method] !== 'function') {\n sendResult(id, err(GeneralErrors.Unexpected({ message: `Method '${method}' not found on provider` })))\n break\n }\n\n const result = await provider[method](...args)\n if (result.isError()) {\n sendResult(id, result)\n break\n }\n\n const tokenInfo = result.data\n\n // Update token state and optionally emit event\n setTokenState(tokenInfo, shouldEmitEvent)\n\n // Always send AuthResult back\n const now = Date.now()\n const authenticated =\n accessToken !== null && accessToken !== '' && (expiresAt === null || expiresAt > now)\n\n sendAuthCallResult(id, {\n authenticated,\n expiresAt,\n user: currentUser\n })\n } catch (error) {\n sendResult(id, err(GeneralErrors.Unexpected({ message: error instanceof Error ? error.message : String(error) })))\n }\n break\n }\n\n case MSG.CANCEL: {\n try {\n const { id } = data\n const controller = pendingControllers.get(id)\n if (controller) {\n controller.abort()\n pendingControllers.delete(id)\n }\n } catch (error) {\n if (config?.debug) {\n console.error('CANCEL error:', error)\n }\n }\n break\n }\n\n case MSG.PING: {\n const { id } = data\n try {\n const ts = data.payload?.timestamp ?? Date.now()\n sendPong(id, ts)\n } catch (error) {\n sendResult(id, err(GeneralErrors.Unexpected({ message: error instanceof Error ? error.message : String(error) })))\n }\n break\n }\n\n default: {\n const anyData: any = data\n sendResult(anyData.id, err(GeneralErrors.UnknownMessage({ message: `Unknown message type: ${String(anyData.type)}` })))\n }\n }\n}\n\n})() // End IIFE - Immediately Invoked Function Expression\n","import type { SerializedResult } from 'ts-micro-result'\r\nimport type { WorkerConfig, FetchGuardRequestInit, ProviderPresetConfig, AuthResult } from './types'\r\n\r\n/**\r\n * MESSAGE PAYLOADS - SINGLE SOURCE OF TRUTH\r\n *\r\n * Define all message payloads here. Type unions and MSG constants are auto-generated.\r\n *\r\n * USAGE:\r\n * - To add a new message: Just add one line to the appropriate interface\r\n * - Payload types are automatically inferred\r\n * - MSG constants are automatically generated\r\n *\r\n * EXAMPLE:\r\n * ```typescript\r\n * interface MainPayloads {\r\n * NEW_MESSAGE: { foo: string } // Add this line\r\n * }\r\n * // => Automatically get MainToWorkerMessage union with NEW_MESSAGE\r\n * // => Automatically get MSG.NEW_MESSAGE = 'NEW_MESSAGE'\r\n * ```\r\n */\r\n\r\n/**\r\n * Payloads for messages sent from Main thread → Worker thread\r\n */\r\nexport interface MainPayloads {\r\n SETUP: { config: WorkerConfig; providerConfig: ProviderPresetConfig | string | null }\r\n FETCH: { url: string; options?: FetchGuardRequestInit }\r\n AUTH_CALL: { method: string; args: unknown[]; emitEvent?: boolean } // Generic auth method call (login, logout, loginWithPhone, etc.)\r\n CANCEL: undefined\r\n PING: { timestamp: number }\r\n}\r\n\r\n/**\r\n * Payloads for messages sent from Worker thread → Main thread\r\n */\r\nexport interface WorkerPayloads {\r\n RESULT: { result: SerializedResult | object }\r\n READY: undefined\r\n PONG: { timestamp: number }\r\n LOG: { level: 'info' | 'warn' | 'error'; message: string }\r\n AUTH_STATE_CHANGED: AuthResult\r\n AUTH_CALL_RESULT: AuthResult\r\n FETCH_RESULT: { status: number; headers?: Record<string, string>; body: string }\r\n FETCH_ERROR: { error: string; status?: number }\r\n}\r\n\r\n/**\r\n * Generate message type from payload definition\r\n * Handles optional payloads (undefined) gracefully\r\n */\r\ntype MessageFromPayloads<P> = {\r\n [K in keyof P]: { id: string; type: K } & (\r\n P[K] extends undefined ? {} : { payload: P[K] }\r\n )\r\n}[keyof P]\r\n\r\n/**\r\n * Message type unions - auto-generated from payload interfaces\r\n */\r\nexport type MainToWorkerMessage = MessageFromPayloads<MainPayloads>\r\nexport type WorkerToMainMessage = MessageFromPayloads<WorkerPayloads>\r\n\r\n/**\r\n * Message type unions for compile-time type checking\r\n */\r\nexport type MainType = keyof MainPayloads\r\nexport type WorkerType = keyof WorkerPayloads\r\nexport type MessageType = MainType | WorkerType\r\n\r\n/**\r\n * MSG constants object\r\n * Usage: MSG.SETUP, MSG.FETCH, etc.\r\n */\r\nexport const MSG = Object.freeze({\r\n // Main -> Worker messages\r\n SETUP: 'SETUP',\r\n FETCH: 'FETCH',\r\n AUTH_CALL: 'AUTH_CALL',\r\n CANCEL: 'CANCEL',\r\n PING: 'PING',\r\n\r\n // Worker -> Main messages\r\n RESULT: 'RESULT',\r\n READY: 'READY',\r\n PONG: 'PONG',\r\n LOG: 'LOG',\r\n AUTH_STATE_CHANGED: 'AUTH_STATE_CHANGED',\r\n AUTH_CALL_RESULT: 'AUTH_CALL_RESULT',\r\n FETCH_RESULT: 'FETCH_RESULT',\r\n FETCH_ERROR: 'FETCH_ERROR'\r\n}) as { readonly [K in MessageType]: K }\r\n","/**\n * FetchGuard Default Configuration Values\n */\n\n/**\n * Default time (in milliseconds) to refresh token before expiry\n * @default 60000 (60 seconds)\n */\nexport const DEFAULT_REFRESH_EARLY_MS = 60_000\n\n/**\n * Default timeout for API requests (in milliseconds)\n * @default 30000 (30 seconds)\n */\nexport const DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Default number of retry attempts for failed requests\n * @default 3\n */\nexport const DEFAULT_RETRY_COUNT = 3\n\n/**\n * Default delay between retry attempts (in milliseconds)\n * @default 1000 (1 second)\n */\nexport const DEFAULT_RETRY_DELAY_MS = 1_000\n","/**\n * Error definitions organized by domain\n * Using ts-micro-result's defineError for consistency\n */\n\nimport { defineError, defineErrorAdvanced } from 'ts-micro-result'\n\n/**\n * General errors\n */\nexport const GeneralErrors = {\n Unexpected: defineError('UNEXPECTED', 'Unexpected error', 500),\n UnknownMessage: defineError('UNKNOWN_MESSAGE', 'Unknown message type', 400),\n ResultParse: defineError('RESULT_PARSE_ERROR', 'Failed to parse result', 500),\n} as const\n\n/**\n * Initialization errors\n */\nexport const InitErrors = {\n NotInitialized: defineError('INIT_ERROR', 'Worker not initialized', 500),\n ProviderInitFailed: defineError('PROVIDER_INIT_FAILED', 'Failed to initialize provider', 500),\n InitFailed: defineError('INIT_FAILED', 'Initialization failed', 500),\n} as const\n\n/**\n * Authentication & Token errors\n */\nexport const AuthErrors = {\n TokenRefreshFailed: defineError('TOKEN_REFRESH_FAILED', 'Token refresh failed', 401),\n LoginFailed: defineError('LOGIN_FAILED', 'Login failed', 401),\n LogoutFailed: defineError('LOGOUT_FAILED', 'Logout failed', 500),\n NotAuthenticated: defineError('NOT_AUTHENTICATED', 'User is not authenticated', 401),\n} as const\n\n/**\n * Domain validation errors\n */\nexport const DomainErrors = {\n NotAllowed: defineErrorAdvanced('DOMAIN_NOT_ALLOWED', 'Domain not allowed: {url}', 403),\n} as const\n\n/**\n * Network & HTTP errors\n */\nexport const NetworkErrors = {\n NetworkError: defineError('NETWORK_ERROR', 'Network error', 500),\n HttpError: defineError('HTTP_ERROR', 'HTTP error', 500),\n FetchError: defineError('FETCH_ERROR', 'Fetch error', 500),\n} as const\n\n/**\n * Request errors\n */\nexport const RequestErrors = {\n Cancelled: defineError('REQUEST_CANCELLED', 'Request was cancelled', 499),\n Timeout: defineError('REQUEST_TIMEOUT', 'Request timeout', 408),\n} as const\n","/**\r\n * Worker postMessage helpers\r\n * Utilities to send messages from worker thread to main thread\r\n */\r\n\r\nimport type { WorkerToMainMessage } from './messages'\r\nimport type { Result } from 'ts-micro-result'\r\nimport type { AuthResult } from './types'\r\nimport { MSG } from './messages'\r\n\r\n/**\r\n * Internal helper to post message to main thread\r\n */\r\nfunction post(message: WorkerToMainMessage): void {\r\n ;(self as DedicatedWorkerGlobalScope).postMessage(message)\r\n}\r\n\r\n/**\r\n * Send generic Result for non-fetch operations (INIT, LOGIN, LOGOUT, etc.)\r\n */\r\nexport function sendResult(id: string, result: Result<any>): void {\r\n post({\r\n type: MSG.RESULT,\r\n id,\r\n payload: { result: result.toJSON() }\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send successful fetch response\r\n */\r\nexport function sendFetchResult(id: string, status: number, body: string, headers?: Record<string, string>): void {\r\n post({\r\n type: MSG.FETCH_RESULT,\r\n id,\r\n payload: headers ? ({ status, body, headers }) : ({ status, body })\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send fetch error response\r\n */\r\nexport function sendFetchError(id: string, error: string, status?: number): void {\r\n post({\r\n type: MSG.FETCH_ERROR,\r\n id,\r\n payload: { error, status }\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send READY event (worker initialized)\r\n */\r\nexport function sendReady(): void {\r\n post({\r\n type: MSG.READY,\r\n id: `evt_${Date.now()}`\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send PONG response to PING\r\n */\r\nexport function sendPong(id: string, timestamp: number): void {\r\n post({\r\n type: MSG.PONG,\r\n id,\r\n payload: { timestamp }\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send AUTH_STATE_CHANGED event\r\n */\r\nexport function sendAuthStateChanged(authResult: AuthResult): void {\r\n post({\r\n type: MSG.AUTH_STATE_CHANGED,\r\n id: `evt_${Date.now()}`,\r\n payload: authResult\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send AUTH_CALL_RESULT (auth method result)\r\n */\r\nexport function sendAuthCallResult(id: string, authResult: AuthResult): void {\r\n post({\r\n type: MSG.AUTH_CALL_RESULT,\r\n id,\r\n payload: authResult\r\n } as any)\r\n}\r\n","import type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Registry to manage token providers\r\n */\r\nconst registry = new Map<string, TokenProvider>()\r\n\r\n/**\r\n * Register a token provider with name\r\n */\r\nexport function registerProvider(name: string, provider: TokenProvider): void {\r\n if (typeof name !== 'string' || !name.trim()) {\r\n throw new Error('Provider name must be a non-empty string')\r\n }\r\n \r\n if (!provider || typeof provider.refreshToken !== 'function') {\r\n throw new Error('Provider must implement TokenProvider interface')\r\n }\r\n \r\n registry.set(name, provider)\r\n}\r\n\r\n/**\r\n * Get provider by name\r\n */\r\nexport function getProvider(name: string): TokenProvider {\r\n const provider = registry.get(name)\r\n if (!provider) {\r\n throw new Error(`Provider '${name}' not found. Available providers: ${Array.from(registry.keys()).join(', ')}`)\r\n }\r\n return provider\r\n}\r\n\r\n/**\r\n * Check if provider exists\r\n */\r\nexport function hasProvider(name: string): boolean {\r\n return registry.has(name)\r\n}\r\n\r\n/**\r\n * Get list of all provider names\r\n */\r\nexport function listProviders(): string[] {\r\n return Array.from(registry.keys())\r\n}\r\n\r\n/**\r\n * Remove provider\r\n */\r\nexport function unregisterProvider(name: string): boolean {\r\n return registry.delete(name)\r\n}\r\n\r\n/**\r\n * Remove all providers\r\n */\r\nexport function clearProviders(): void {\r\n registry.clear()\r\n}\r\n","import type {\r\n TokenProvider,\r\n RefreshTokenStorage,\r\n TokenParser,\r\n AuthStrategy,\r\n TokenInfo\r\n} from '../types'\r\nimport { ok, err, type Result } from 'ts-micro-result'\r\nimport { AuthErrors, NetworkErrors } from '../errors'\r\n\r\n/**\r\n * Custom auth method type\r\n */\r\ntype CustomAuthMethod = (...args: any[]) => Promise<Result<TokenInfo>>\r\n\r\n/**\r\n * Configuration for creating provider\r\n *\r\n * refreshStorage: OPTIONAL - to load refresh token initially when worker starts\r\n * - undefined: cookie-based auth (httpOnly cookie, no need to load)\r\n * - RefreshTokenStorage: body-based auth (load from IndexedDB on startup)\r\n *\r\n * strategy: AuthStrategy with refresh (required), login/logout (required)\r\n *\r\n * customMethods: OPTIONAL - custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n */\r\nexport interface ProviderConfig {\r\n refreshStorage?: RefreshTokenStorage\r\n parser: TokenParser\r\n strategy: AuthStrategy\r\n customMethods?: Record<string, CustomAuthMethod>\r\n}\r\n\r\n/**\r\n * Factory function to create TokenProvider from modular components\r\n *\r\n * Provider automatically handles refresh token:\r\n * - If refreshToken is null and storage exists → load from storage initially\r\n * - If refreshToken exists → use token from worker memory\r\n * - Cookie-based (no storage) → always null\r\n *\r\n * Custom methods:\r\n * - User can add custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n * - Custom methods will be spread into provider object\r\n */\r\nexport function createProvider(config: ProviderConfig): TokenProvider {\r\n const baseProvider: Pick<TokenProvider, 'refreshToken' | 'login' | 'logout'> = {\r\n async refreshToken(refreshToken: string | null) {\r\n let currentRefreshToken = refreshToken\r\n if (currentRefreshToken === null && config.refreshStorage) {\r\n currentRefreshToken = await config.refreshStorage.get()\r\n }\r\n\r\n try {\r\n const response = await config.strategy.refresh(currentRefreshToken)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async login(payload: unknown) {\r\n try {\r\n const response = await config.strategy.login(payload)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.LoginFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.LoginFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async logout(payload?: unknown) {\r\n try {\r\n const response = await config.strategy.logout(payload)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.LogoutFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n if (config.refreshStorage) {\r\n await config.refreshStorage.set(null)\r\n }\r\n\r\n return ok({\r\n token: '',\r\n refreshToken: undefined,\r\n expiresAt: undefined,\r\n user: null // Explicitly clear user on logout\r\n })\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n }\r\n }\r\n\r\n // Merge custom methods if provided\r\n if (config.customMethods) {\r\n return {\r\n ...baseProvider,\r\n ...config.customMethods\r\n } as TokenProvider\r\n }\r\n\r\n return baseProvider as TokenProvider\r\n}\r\n","import type { RefreshTokenStorage } from '../../types'\r\n\r\n/**\r\n * IndexedDB storage - only stores refresh token in IndexedDB\r\n * Suitable for body-based refresh strategy\r\n * Persists refresh token for reuse after reload\r\n */\r\nexport function createIndexedDBStorage(dbName = 'FetchGuardDB', refreshTokenKey = 'refreshToken'): RefreshTokenStorage {\r\n const storeName = 'tokens'\r\n\r\n const openDB = (): Promise<IDBDatabase> => {\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDB.open(dbName, 1)\r\n \r\n request.onerror = () => reject(request.error)\r\n request.onsuccess = () => resolve(request.result)\r\n \r\n request.onupgradeneeded = (event) => {\r\n const db = (event.target as IDBOpenDBRequest).result\r\n if (!db.objectStoreNames.contains(storeName)) {\r\n const store = db.createObjectStore(storeName, { keyPath: 'key' })\r\n store.createIndex('timestamp', 'timestamp', { unique: false })\r\n }\r\n }\r\n })\r\n }\r\n\r\n const promisifyRequest = <T>(request: IDBRequest<T>): Promise<T> => {\r\n return new Promise((resolve, reject) => {\r\n request.onsuccess = () => resolve(request.result)\r\n request.onerror = () => reject(request.error)\r\n })\r\n }\r\n\r\n return {\r\n async get() {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readonly')\r\n const store = transaction.objectStore(storeName)\r\n const result = await promisifyRequest(store.get(refreshTokenKey))\r\n return result?.value || null\r\n } catch (error) {\r\n console.warn('Failed to get refresh token from IndexedDB:', error)\r\n return null\r\n }\r\n },\r\n async set(token) {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readwrite')\r\n const store = transaction.objectStore(storeName)\r\n\r\n if (token) {\r\n await promisifyRequest(store.put({ key: refreshTokenKey, value: token, timestamp: Date.now() }))\r\n } else {\r\n await promisifyRequest(store.delete(refreshTokenKey))\r\n }\r\n } catch (error) {\r\n console.warn('Failed to save refresh token to IndexedDB:', error)\r\n }\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\n\r\n/**\r\n * Body parser - parse token from response body (JSON)\r\n * Expects response format: { data: { accessToken, refreshToken, expiresAt?, user? } }\r\n */\r\nexport const bodyParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n refreshToken: json.data.refreshToken,\r\n expiresAt: json.data.expiresAt,\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\n\r\n/**\r\n * Cookie parser - parse access token from response body\r\n * Expects response format: { data: { accessToken, expiresAt?, user? } }\r\n * Refresh token is automatically set by backend into httpOnly cookie\r\n */\r\nexport const cookieParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n expiresAt: json.data.expiresAt,\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { AuthStrategy } from '../../types'\r\n\r\n/**\r\n * Cookie auth strategy - all auth operations via httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Refresh token is sent automatically via httpOnly cookie\r\n * Credentials are sent in request body\r\n */\r\nexport function createCookieStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): AuthStrategy {\r\n return {\r\n async refresh() {\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload) {\r\n return fetch(config.loginUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard cookie strategy\r\n */\r\nexport const cookieStrategy = createCookieStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import type { AuthStrategy } from '../../types'\r\n\r\n/**\r\n * Body auth strategy - all auth operations via request body\r\n * Suitable for SPA applications\r\n *\r\n * All tokens/credentials are sent in request body\r\n */\r\nexport function createBodyStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): AuthStrategy {\r\n return {\r\n async refresh(refreshToken) {\r\n if (!refreshToken) {\r\n throw new Error('No refresh token available')\r\n }\r\n\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ refreshToken }),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload) {\r\n return fetch(config.loginUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard body strategy\r\n */\r\nexport const bodyStrategy = createBodyStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import { createProvider } from './create-provider'\r\nimport { createIndexedDBStorage } from './storage/indexeddb'\r\nimport { bodyParser } from './parser/body'\r\nimport { cookieParser } from './parser/cookie'\r\nimport { createCookieStrategy } from './strategy/cookie'\r\nimport { createBodyStrategy } from './strategy/body'\r\nimport type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Cookie Provider - uses httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: httpOnly cookie (managed by backend)\r\n */\r\nexport function createCookieProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: undefined,\r\n parser: cookieParser,\r\n strategy: createCookieStrategy(config)\r\n })\r\n}\r\n\r\n/**\r\n * Body Provider - refresh token in response body, persisted to IndexedDB\r\n * Suitable for SPA applications\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: IndexedDB (persists across reload)\r\n */\r\nexport function createBodyProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n refreshTokenKey?: string\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: createIndexedDBStorage('FetchGuardDB', config.refreshTokenKey || 'refreshToken'),\r\n parser: bodyParser,\r\n strategy: createBodyStrategy(config)\r\n })\r\n}\r\n","import { createCookieProvider, createBodyProvider } from './presets'\r\nimport type { TokenProvider, ProviderPresetConfig } from '../types'\r\n\r\n/**\r\n * Build provider from preset config\r\n * This is called in worker when receiving SETUP message\r\n */\r\nexport function buildProviderFromPreset(config: ProviderPresetConfig): TokenProvider {\r\n switch (config.type) {\r\n case 'cookie-auth':\r\n return createCookieProvider({\r\n refreshUrl: config.refreshUrl,\r\n loginUrl: config.loginUrl,\r\n logoutUrl: config.logoutUrl\r\n })\r\n\r\n case 'body-auth':\r\n return createBodyProvider({\r\n refreshUrl: config.refreshUrl,\r\n loginUrl: config.loginUrl,\r\n logoutUrl: config.logoutUrl,\r\n refreshTokenKey: config.refreshTokenKey\r\n })\r\n\r\n default:\r\n throw new Error(`Unknown provider type: ${(config as any).type}`)\r\n }\r\n}\r\n","import type { SerializedFormData, SerializedFormDataEntry, SerializedFile } from '../types'\r\n\r\n/**\r\n * Serialize FormData for transfer over postMessage\r\n * Inspired by api-worker.js:484-518\r\n *\r\n * FormData cannot be cloned via postMessage, so we need to serialize it first\r\n * Files are converted to ArrayBuffer -> number[] for transfer\r\n */\r\nexport async function serializeFormData(formData: FormData): Promise<SerializedFormData> {\r\n const entries: Array<[string, SerializedFormDataEntry]> = []\r\n\r\n // Use forEach instead of entries() for better TS compatibility\r\n formData.forEach((value, key) => {\r\n // Push async operations to promises array for parallel processing\r\n if (value instanceof File) {\r\n // We need to handle this synchronously in forEach, so we'll collect promises\r\n // and await them all at once\r\n } else {\r\n entries.push([key, String(value)])\r\n }\r\n })\r\n\r\n // Handle File entries separately with Promise.all\r\n const filePromises: Promise<void>[] = []\r\n formData.forEach((value, key) => {\r\n if (value instanceof File) {\r\n const promise = (async () => {\r\n const arrayBuffer = await value.arrayBuffer()\r\n const uint8Array = new Uint8Array(arrayBuffer)\r\n const serializedFile: SerializedFile = {\r\n name: value.name,\r\n type: value.type,\r\n data: Array.from(uint8Array) // Convert to number array\r\n }\r\n entries.push([key, serializedFile])\r\n })()\r\n filePromises.push(promise)\r\n }\r\n })\r\n\r\n await Promise.all(filePromises)\r\n\r\n return {\r\n _type: 'FormData',\r\n entries\r\n }\r\n}\r\n\r\n/**\r\n * Deserialize SerializedFormData back to FormData in worker\r\n * Reconstructs File objects from serialized data\r\n */\r\nexport function deserializeFormData(serialized: SerializedFormData): FormData {\r\n const formData = new FormData()\r\n\r\n for (const [key, value] of serialized.entries) {\r\n if (typeof value === 'string') {\r\n formData.append(key, value)\r\n } else {\r\n // Reconstruct File from SerializedFile\r\n const uint8Array = new Uint8Array(value.data)\r\n const file = new File([uint8Array], value.name, { type: value.type })\r\n formData.append(key, file)\r\n }\r\n }\r\n\r\n return formData\r\n}\r\n\r\n/**\r\n * Check if body is FormData\r\n */\r\nexport function isFormData(body: any): body is FormData {\r\n return body instanceof FormData\r\n}\r\n\r\n/**\r\n * Check if serialized body is SerializedFormData\r\n */\r\nexport function isSerializedFormData(body: any): body is SerializedFormData {\r\n return body && typeof body === 'object' && body._type === 'FormData'\r\n}\r\n"],"mappings":"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","names":["ok","err","ok","err"]}
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts","../src/messages.ts","../src/constants.ts","../src/errors.ts","../src/worker-post.ts","../src/utils/registry.ts","../src/provider/create-provider.ts","../src/provider/storage/indexeddb.ts","../src/provider/parser/body.ts","../src/provider/parser/cookie.ts","../src/provider/strategy/cookie.ts","../src/provider/strategy/body.ts","../src/provider/presets.ts","../src/provider/register-presets.ts","../src/utils/formdata.ts","../src/utils/binary.ts"],"sourcesContent":["/// <reference lib=\"webworker\" />\n\nimport type { WorkerConfig, ApiResponse, TokenProvider, FetchGuardRequestInit, ProviderPresetConfig } from './types'\nimport type { MainToWorkerMessage } from './messages'\nimport { ok, err, type Result } from 'ts-micro-result'\nimport { MSG } from './messages'\nimport { DEFAULT_REFRESH_EARLY_MS } from './constants'\nimport {\n InitErrors,\n DomainErrors,\n NetworkErrors,\n RequestErrors,\n GeneralErrors\n} from './errors'\nimport { sendAuthStateChanged, sendAuthCallResult, sendPong, sendReady, sendSetupError, sendError, sendFetchResult, sendFetchError } from './worker-post'\nimport { getProvider } from './utils/registry'\nimport { buildProviderFromPreset } from './provider/register-presets'\nimport { deserializeFormData, isSerializedFormData } from './utils/formdata'\nimport { arrayBufferToBase64, isBinaryContentType } from './utils/binary'\n\n/**\n * IIFE Closure to protect sensitive tokens from external access\n * Inspired by api-worker.js security pattern\n */\n;(function () {\n let config: WorkerConfig | null = null\n let provider: TokenProvider | null = null\n let accessToken: string | null = null\n let refreshToken: string | null = null\n let expiresAt: number | null = null\n let currentUser: unknown | undefined\n const pendingControllers = new Map<string, AbortController>()\n let refreshPromise: Promise<void> | null = null\n\n/**\n * Ensure we have a valid access token (not expired).\n * If token is missing or expired, refresh it.\n * Prevents concurrent refresh attempts.\n */\nasync function ensureValidToken(): Promise<Result<string | null>> {\n // Provider must be initialized via SETUP first\n if (!provider) {\n return err(InitErrors.NotInitialized())\n }\n\n if (accessToken && expiresAt) {\n const refreshEarlyMs = config?.refreshEarlyMs ?? DEFAULT_REFRESH_EARLY_MS\n const timeLeft = expiresAt - Date.now()\n if (timeLeft > refreshEarlyMs) {\n return ok(accessToken)\n }\n }\n\n if (refreshPromise) {\n await refreshPromise\n return ok(accessToken)\n }\n\n refreshPromise = (async () => {\n try {\n // Provider already checked above, TypeScript needs assertion\n if (!provider) {\n return\n }\n\n const valueRes = await provider.refreshToken(refreshToken)\n\n if (valueRes.isError()) {\n setTokenState({ token: null, expiresAt: null, user: undefined, refreshToken: undefined })\n return\n }\n\n const tokenInfo = valueRes.data\n if (!tokenInfo) {\n return\n }\n\n setTokenState(tokenInfo)\n } finally {\n refreshPromise = null\n }\n })()\n\n await refreshPromise\n return ok(accessToken)\n}\n\n/**\n * Validate domain against allowed domains\n */\nfunction validateDomain(url: string): boolean {\n if (!config?.allowedDomains?.length) {\n return true\n }\n\n try {\n const urlObj = new URL(url)\n const hostname = urlObj.hostname\n const port = urlObj.port\n\n for (const entry of config.allowedDomains) {\n const idx = entry.lastIndexOf(':')\n const hasPort = idx > -1 && entry.indexOf(':') === idx\n const pattern = hasPort ? entry.slice(0, idx) : entry\n const entryPort = hasPort ? entry.slice(idx + 1) : ''\n const isWildcard = pattern.startsWith('*.')\n const base = isWildcard ? pattern.slice(2) : pattern\n\n const hostnameMatch = isWildcard\n ? (hostname === base || hostname.endsWith('.' + base))\n : (hostname === base)\n \n if (!hostnameMatch) continue\n\n if (hasPort) {\n if (port === entryPort) return true\n continue\n }\n return true\n }\n return false\n } catch {\n return false\n }\n}\n\n/**\n * Make API request with proactive token management\n */\nasync function makeApiRequest(url: string, options: FetchGuardRequestInit = {}): Promise<Result<ApiResponse>> {\n if (!config) {\n return err(InitErrors.NotInitialized())\n }\n\n if (!validateDomain(url)) {\n return err(DomainErrors.NotAllowed({ url }))\n }\n\n const requiresAuth = options.requiresAuth !== false\n const includeHeaders = options.includeHeaders === true\n // Extract FetchGuard-specific options and keep only standard RequestInit\n const { requiresAuth: _, includeHeaders: __, ...fetchOptions } = options\n\n // Deserialize FormData if present (inspired by api-worker.js:484-518)\n if (fetchOptions.body && isSerializedFormData(fetchOptions.body)) {\n fetchOptions.body = deserializeFormData(fetchOptions.body)\n }\n\n const headers: Record<string, string> = {\n ...(fetchOptions.headers as Record<string, string> || {})\n }\n\n // Don't set Content-Type for FormData - browser will set it with boundary\n if (!headers['Content-Type'] && !headers['content-type'] && fetchOptions.body) {\n if (typeof fetchOptions.body === 'object' && !(fetchOptions.body instanceof FormData) && !(fetchOptions.body instanceof URLSearchParams)) {\n headers['Content-Type'] = 'application/json'\n }\n }\n\n if (requiresAuth) {\n const tokenRes = await ensureValidToken()\n if (tokenRes.isError()) {\n // Propagate error - cast to correct return type\n return err(tokenRes.errors)\n }\n\n const token = tokenRes.data\n if (token) {\n headers['Authorization'] = `Bearer ${token}`\n }\n }\n\n let response: Response\n try {\n response = await fetch(url, { ...fetchOptions, headers, credentials: 'include' })\n } catch (e) {\n const aborted = (e && (e as any).name === 'AbortError')\n return aborted\n ? err(RequestErrors.Cancelled())\n : err(NetworkErrors.NetworkError({ message: String(e) }))\n }\n\n // Extract content-type (always needed for binary detection)\n const contentType = response.headers.get('content-type') || 'application/octet-stream'\n\n // Determine if response is binary\n const isBinary = isBinaryContentType(contentType)\n\n // Get body as text or base64\n let body: string\n if (isBinary) {\n const buffer = await response.arrayBuffer()\n body = arrayBufferToBase64(buffer)\n } else {\n body = await response.text()\n }\n\n // Extract headers if requested\n const responseHeaders: Record<string, string> = {}\n if (includeHeaders) {\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value\n })\n }\n\n return response.ok\n ? ok({ body, status: response.status, contentType, headers: responseHeaders })\n : err(NetworkErrors.HttpError({ message: `HTTP ${response.status}: ${body}` }))\n}\n\n/**\n * Update token state from TokenInfo and auto emit AUTH_STATE_CHANGED\n *\n * Smart update logic for ALL fields:\n * - Only update field if key exists in tokenInfo\n * - If key exists with value: update to that value (including null)\n * - If key doesn't exist: preserve existing value\n *\n * This allows flexible custom auth methods:\n * - Standard login/refresh: returns { token, user, expiresAt }\n * - Update user info: may only return { user: {...} } (no token change)\n * - Verify OTP: may return {} (just validation, no state change)\n * - Logout: returns { token: null, user: null, ... } to clear all\n */\nfunction setTokenState(tokenInfo: { token?: string | null; expiresAt?: number | null; user?: unknown; refreshToken?: string | null }, emitEvent: boolean = true) {\n // Apply smart preservation to ALL fields\n if ('token' in tokenInfo) {\n accessToken = tokenInfo.token ?? null\n }\n\n if ('expiresAt' in tokenInfo) {\n expiresAt = tokenInfo.expiresAt ?? null\n }\n\n if ('user' in tokenInfo) {\n currentUser = tokenInfo.user\n }\n\n if ('refreshToken' in tokenInfo) {\n refreshToken = tokenInfo.refreshToken ?? null\n }\n\n if (emitEvent) {\n postAuthChanged()\n }\n}\n\n/**\n * Emit AUTH_STATE_CHANGED event based on current state\n * authenticated = true if we have valid non-expired token\n */\nfunction postAuthChanged() {\n const now = Date.now()\n const authenticated = accessToken !== null && accessToken !== '' && (expiresAt === null || expiresAt > now)\n sendAuthStateChanged({\n authenticated,\n expiresAt,\n user: currentUser\n })\n}\n\n/**\n * Main message handler\n * Each case has its own try-catch for better error isolation\n */\nself.onmessage = async (event: MessageEvent<MainToWorkerMessage>) => {\n const data = event.data\n switch (data.type) {\n case MSG.SETUP: {\n try {\n const payload = data.payload\n config = payload.config\n\n const providerConfig = payload.providerConfig\n\n // Build provider from config\n if (typeof providerConfig === 'string') {\n // Registry lookup\n provider = getProvider(providerConfig)\n } else if (providerConfig && typeof providerConfig === 'object' && 'type' in providerConfig) {\n // ProviderPresetConfig object\n provider = buildProviderFromPreset(providerConfig as ProviderPresetConfig)\n } else {\n sendSetupError('Invalid provider config')\n break\n }\n\n // Validate provider was successfully created\n if (!provider) {\n sendSetupError('Provider initialization failed - provider is null')\n break\n }\n\n sendReady()\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error('[FetchGuard Worker] Setup failed:', errorMessage)\n sendSetupError(errorMessage)\n }\n break\n }\n\n case MSG.FETCH: {\n const { id } = data\n try {\n const { url, options } = data.payload\n const controller = new AbortController()\n pendingControllers.set(id, controller)\n const merged: RequestInit = { ...(options || {}), signal: controller.signal }\n const result = await makeApiRequest(url, merged)\n\n if (result.isOkWithData()) {\n sendFetchResult(id, result.data)\n } else {\n const error = result.errors?.[0]\n const message = error?.message || 'Unknown error'\n const status = result.status\n sendFetchError(id, message, status)\n }\n\n pendingControllers.delete(id)\n } catch (error) {\n pendingControllers.delete(id)\n sendFetchError(id, error instanceof Error ? error.message : String(error), undefined)\n }\n break\n }\n\n case MSG.AUTH_CALL: {\n const { id, payload } = data\n try {\n const { method, args, emitEvent } = payload\n const shouldEmitEvent = emitEvent ?? true // Default: emit event\n\n if (!provider) {\n sendError(id, err(InitErrors.NotInitialized()))\n break\n }\n\n if (typeof provider[method] !== 'function') {\n sendError(id, err(GeneralErrors.Unexpected({ message: `Method '${method}' not found on provider` })))\n break\n }\n\n const result = await provider[method](...args)\n if (result.isError()) {\n sendError(id, result)\n break\n }\n\n const tokenInfo = result.data\n if (!tokenInfo) {\n sendError(id, err(GeneralErrors.Unexpected({ message: 'Provider returned null token info' })))\n break\n }\n\n // Update token state and optionally emit event\n setTokenState(tokenInfo, shouldEmitEvent)\n\n // Always send AuthResult back\n const now = Date.now()\n const authenticated =\n accessToken !== null && accessToken !== '' && (expiresAt === null || expiresAt > now)\n\n sendAuthCallResult(id, {\n authenticated,\n expiresAt,\n user: currentUser\n })\n } catch (error) {\n sendError(id, err(GeneralErrors.Unexpected({ message: error instanceof Error ? error.message : String(error) })))\n }\n break\n }\n\n case MSG.CANCEL: {\n try {\n const { id } = data\n const controller = pendingControllers.get(id)\n if (controller) {\n controller.abort()\n pendingControllers.delete(id)\n }\n } catch (error) {\n // Silently ignore cancel errors\n }\n break\n }\n\n case MSG.PING: {\n const { id } = data\n try {\n const ts = data.payload?.timestamp ?? Date.now()\n sendPong(id, ts)\n } catch (error) {\n sendError(id, err(GeneralErrors.Unexpected({ message: error instanceof Error ? error.message : String(error) })))\n }\n break\n }\n\n default: {\n const anyData: any = data\n sendError(anyData.id, err(GeneralErrors.UnknownMessage({ message: `Unknown message type: ${String(anyData.type)}` })))\n }\n }\n}\n\n})() // End IIFE - Immediately Invoked Function Expression\n","import type { ErrorDetail } from 'ts-micro-result'\r\nimport type { WorkerConfig, FetchGuardRequestInit, ProviderPresetConfig, AuthResult, ApiResponse } from './types'\r\n\r\n/**\r\n * MESSAGE PAYLOADS - SINGLE SOURCE OF TRUTH\r\n *\r\n * Define all message payloads here. Type unions and MSG constants are auto-generated.\r\n *\r\n * USAGE:\r\n * - To add a new message: Just add one line to the appropriate interface\r\n * - Payload types are automatically inferred\r\n * - MSG constants are automatically generated\r\n *\r\n * EXAMPLE:\r\n * ```typescript\r\n * interface MainPayloads {\r\n * NEW_MESSAGE: { foo: string } // Add this line\r\n * }\r\n * // => Automatically get MainToWorkerMessage union with NEW_MESSAGE\r\n * // => Automatically get MSG.NEW_MESSAGE = 'NEW_MESSAGE'\r\n * ```\r\n */\r\n\r\n/**\r\n * Payloads for messages sent from Main thread → Worker thread\r\n */\r\nexport interface MainPayloads {\r\n SETUP: { config: WorkerConfig; providerConfig: ProviderPresetConfig | string | null }\r\n FETCH: { url: string; options?: FetchGuardRequestInit }\r\n AUTH_CALL: { method: string; args: unknown[]; emitEvent?: boolean } // Generic auth method call (login, logout, loginWithPhone, etc.)\r\n CANCEL: undefined\r\n PING: { timestamp: number }\r\n}\r\n\r\n/**\r\n * Payloads for messages sent from Worker thread → Main thread\r\n */\r\nexport interface WorkerPayloads {\r\n ERROR: { errors: ErrorDetail[] }\r\n READY: undefined\r\n SETUP_ERROR: { error: string }\r\n PONG: { timestamp: number }\r\n LOG: { level: 'info' | 'warn' | 'error'; message: string }\r\n AUTH_STATE_CHANGED: AuthResult\r\n AUTH_CALL_RESULT: AuthResult\r\n FETCH_RESULT: ApiResponse\r\n FETCH_ERROR: { error: string; status?: number }\r\n}\r\n\r\n/**\r\n * Generate message type from payload definition\r\n * Handles optional payloads (undefined) gracefully\r\n */\r\ntype MessageFromPayloads<P> = {\r\n [K in keyof P]: { id: string; type: K } & (\r\n P[K] extends undefined ? {} : { payload: P[K] }\r\n )\r\n}[keyof P]\r\n\r\n/**\r\n * Message type unions - auto-generated from payload interfaces\r\n */\r\nexport type MainToWorkerMessage = MessageFromPayloads<MainPayloads>\r\nexport type WorkerToMainMessage = MessageFromPayloads<WorkerPayloads>\r\n\r\n/**\r\n * Message type unions for compile-time type checking\r\n */\r\nexport type MainType = keyof MainPayloads\r\nexport type WorkerType = keyof WorkerPayloads\r\nexport type MessageType = MainType | WorkerType\r\n\r\n/**\r\n * MSG constants object\r\n * Usage: MSG.SETUP, MSG.FETCH, etc.\r\n */\r\nexport const MSG = Object.freeze({\r\n // Main -> Worker messages\r\n SETUP: 'SETUP',\r\n FETCH: 'FETCH',\r\n AUTH_CALL: 'AUTH_CALL',\r\n CANCEL: 'CANCEL',\r\n PING: 'PING',\r\n\r\n // Worker -> Main messages\r\n ERROR: 'ERROR',\r\n READY: 'READY',\r\n SETUP_ERROR: 'SETUP_ERROR',\r\n PONG: 'PONG',\r\n LOG: 'LOG',\r\n AUTH_STATE_CHANGED: 'AUTH_STATE_CHANGED',\r\n AUTH_CALL_RESULT: 'AUTH_CALL_RESULT',\r\n FETCH_RESULT: 'FETCH_RESULT',\r\n FETCH_ERROR: 'FETCH_ERROR'\r\n}) as { readonly [K in MessageType]: K }\r\n","/**\n * FetchGuard Default Configuration Values\n */\n\n/**\n * Default time (in milliseconds) to refresh token before expiry\n * @default 60000 (60 seconds)\n */\nexport const DEFAULT_REFRESH_EARLY_MS = 60_000\n","/**\n * Error definitions organized by domain\n * Using ts-micro-result's defineError for consistency\n */\n\nimport { defineError, defineErrorAdvanced } from 'ts-micro-result'\n\n/**\n * General errors\n */\nexport const GeneralErrors = {\n Unexpected: defineError('UNEXPECTED', 'Unexpected error', 500),\n UnknownMessage: defineError('UNKNOWN_MESSAGE', 'Unknown message type', 400),\n ResultParse: defineError('RESULT_PARSE_ERROR', 'Failed to parse result', 500),\n} as const\n\n/**\n * Initialization errors\n */\nexport const InitErrors = {\n NotInitialized: defineError('INIT_ERROR', 'Worker not initialized', 500),\n ProviderInitFailed: defineError('PROVIDER_INIT_FAILED', 'Failed to initialize provider', 500),\n InitFailed: defineError('INIT_FAILED', 'Initialization failed', 500),\n} as const\n\n/**\n * Authentication & Token errors\n */\nexport const AuthErrors = {\n TokenRefreshFailed: defineError('TOKEN_REFRESH_FAILED', 'Token refresh failed', 401),\n LoginFailed: defineError('LOGIN_FAILED', 'Login failed', 401),\n LogoutFailed: defineError('LOGOUT_FAILED', 'Logout failed', 500),\n NotAuthenticated: defineError('NOT_AUTHENTICATED', 'User is not authenticated', 401),\n} as const\n\n/**\n * Domain validation errors\n */\nexport const DomainErrors = {\n NotAllowed: defineErrorAdvanced('DOMAIN_NOT_ALLOWED', 'Domain not allowed: {url}', 403),\n} as const\n\n/**\n * Network & HTTP errors\n */\nexport const NetworkErrors = {\n NetworkError: defineError('NETWORK_ERROR', 'Network error', 500),\n HttpError: defineError('HTTP_ERROR', 'HTTP error', 500),\n FetchError: defineError('FETCH_ERROR', 'Fetch error', 500),\n} as const\n\n/**\n * Request errors\n */\nexport const RequestErrors = {\n Cancelled: defineError('REQUEST_CANCELLED', 'Request was cancelled', 499),\n Timeout: defineError('REQUEST_TIMEOUT', 'Request timeout', 408),\n} as const\n","/**\r\n * Worker postMessage helpers\r\n * Utilities to send messages from worker thread to main thread\r\n */\r\n\r\nimport type { WorkerToMainMessage } from './messages'\r\nimport type { Result } from 'ts-micro-result'\r\nimport type { AuthResult, ApiResponse } from './types'\r\nimport { MSG } from './messages'\r\n\r\n/**\r\n * Internal helper to post message to main thread\r\n */\r\nfunction post(message: WorkerToMainMessage): void {\r\n ;(self as DedicatedWorkerGlobalScope).postMessage(message)\r\n}\r\n\r\n/**\r\n * Send error result (generic errors for auth operations, etc.)\r\n */\r\nexport function sendError(id: string, result: Result<unknown>): void {\r\n post({\r\n type: MSG.ERROR,\r\n id,\r\n payload: { errors: result.errors || [] }\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send successful fetch response\r\n */\r\nexport function sendFetchResult(id: string, response: ApiResponse): void {\r\n post({\r\n type: MSG.FETCH_RESULT,\r\n id,\r\n payload: response\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send fetch error response\r\n */\r\nexport function sendFetchError(id: string, error: string, status?: number): void {\r\n post({\r\n type: MSG.FETCH_ERROR,\r\n id,\r\n payload: { error, status }\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send READY event (worker initialized)\r\n */\r\nexport function sendReady(): void {\r\n post({\r\n type: MSG.READY,\r\n id: `evt_${Date.now()}`\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send SETUP_ERROR event (worker setup failed)\r\n */\r\nexport function sendSetupError(error: string): void {\r\n post({\r\n type: MSG.SETUP_ERROR,\r\n id: `evt_${Date.now()}`,\r\n payload: { error }\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send PONG response to PING\r\n */\r\nexport function sendPong(id: string, timestamp: number): void {\r\n post({\r\n type: MSG.PONG,\r\n id,\r\n payload: { timestamp }\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send AUTH_STATE_CHANGED event\r\n */\r\nexport function sendAuthStateChanged(authResult: AuthResult): void {\r\n post({\r\n type: MSG.AUTH_STATE_CHANGED,\r\n id: `evt_${Date.now()}`,\r\n payload: authResult\r\n } as any)\r\n}\r\n\r\n/**\r\n * Send AUTH_CALL_RESULT (auth method result)\r\n */\r\nexport function sendAuthCallResult(id: string, authResult: AuthResult): void {\r\n post({\r\n type: MSG.AUTH_CALL_RESULT,\r\n id,\r\n payload: authResult\r\n } as any)\r\n}\r\n","import type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Registry to manage token providers\r\n */\r\nconst registry = new Map<string, TokenProvider>()\r\n\r\n/**\r\n * Register a token provider with name\r\n */\r\nexport function registerProvider(name: string, provider: TokenProvider): void {\r\n if (typeof name !== 'string' || !name.trim()) {\r\n throw new Error('Provider name must be a non-empty string')\r\n }\r\n \r\n if (!provider || typeof provider.refreshToken !== 'function') {\r\n throw new Error('Provider must implement TokenProvider interface')\r\n }\r\n \r\n registry.set(name, provider)\r\n}\r\n\r\n/**\r\n * Get provider by name\r\n */\r\nexport function getProvider(name: string): TokenProvider {\r\n const provider = registry.get(name)\r\n if (!provider) {\r\n throw new Error(`Provider '${name}' not found. Available providers: ${Array.from(registry.keys()).join(', ')}`)\r\n }\r\n return provider\r\n}\r\n\r\n/**\r\n * Check if provider exists\r\n */\r\nexport function hasProvider(name: string): boolean {\r\n return registry.has(name)\r\n}\r\n\r\n/**\r\n * Get list of all provider names\r\n */\r\nexport function listProviders(): string[] {\r\n return Array.from(registry.keys())\r\n}\r\n\r\n/**\r\n * Remove provider\r\n */\r\nexport function unregisterProvider(name: string): boolean {\r\n return registry.delete(name)\r\n}\r\n\r\n/**\r\n * Remove all providers\r\n */\r\nexport function clearProviders(): void {\r\n registry.clear()\r\n}\r\n","import type {\r\n TokenProvider,\r\n RefreshTokenStorage,\r\n TokenParser,\r\n AuthStrategy,\r\n TokenInfo\r\n} from '../types'\r\nimport { ok, err, type Result } from 'ts-micro-result'\r\nimport { AuthErrors, NetworkErrors } from '../errors'\r\n\r\n/**\r\n * Custom auth method type\r\n */\r\ntype CustomAuthMethod = (...args: unknown[]) => Promise<Result<TokenInfo>>\r\n\r\n/**\r\n * Configuration for creating provider\r\n *\r\n * refreshStorage: OPTIONAL - to load refresh token initially when worker starts\r\n * - undefined: cookie-based auth (httpOnly cookie, no need to load)\r\n * - RefreshTokenStorage: body-based auth (load from IndexedDB on startup)\r\n *\r\n * strategy: AuthStrategy with refresh (required), login/logout (required)\r\n *\r\n * customMethods: OPTIONAL - custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n */\r\nexport interface ProviderConfig {\r\n refreshStorage?: RefreshTokenStorage\r\n parser: TokenParser\r\n strategy: AuthStrategy\r\n customMethods?: Record<string, CustomAuthMethod>\r\n}\r\n\r\n/**\r\n * Factory function to create TokenProvider from modular components\r\n *\r\n * Provider automatically handles refresh token:\r\n * - If refreshToken is null and storage exists → load from storage initially\r\n * - If refreshToken exists → use token from worker memory\r\n * - Cookie-based (no storage) → always null\r\n *\r\n * Custom methods:\r\n * - User can add custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n * - Custom methods will be spread into provider object\r\n */\r\nexport function createProvider(config: ProviderConfig): TokenProvider {\r\n const baseProvider: Pick<TokenProvider, 'refreshToken' | 'login' | 'logout'> = {\r\n async refreshToken(refreshToken: string | null) {\r\n let currentRefreshToken = refreshToken\r\n if (currentRefreshToken === null && config.refreshStorage) {\r\n currentRefreshToken = await config.refreshStorage.get()\r\n }\r\n\r\n try {\r\n const response = await config.strategy.refresh(currentRefreshToken)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async login(payload: unknown) {\r\n try {\r\n const response = await config.strategy.login(payload)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.LoginFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.LoginFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async logout(payload?: unknown) {\r\n try {\r\n const response = await config.strategy.logout(payload)\r\n\r\n if (!response.ok) {\r\n return err(AuthErrors.LogoutFailed({ message: `HTTP ${response.status}` }))\r\n }\r\n\r\n if (config.refreshStorage) {\r\n await config.refreshStorage.set(null)\r\n }\r\n\r\n return ok({\r\n token: '',\r\n refreshToken: undefined,\r\n expiresAt: undefined,\r\n user: null // Explicitly clear user on logout\r\n })\r\n } catch (error) {\r\n return err(NetworkErrors.NetworkError({ message: String(error) }))\r\n }\r\n }\r\n }\r\n\r\n // Merge custom methods if provided\r\n if (config.customMethods) {\r\n return {\r\n ...baseProvider,\r\n ...config.customMethods\r\n } as TokenProvider\r\n }\r\n\r\n return baseProvider as TokenProvider\r\n}\r\n","import type { RefreshTokenStorage } from '../../types'\r\n\r\n/**\r\n * IndexedDB storage - only stores refresh token in IndexedDB\r\n * Suitable for body-based refresh strategy\r\n * Persists refresh token for reuse after reload\r\n */\r\nexport function createIndexedDBStorage(dbName = 'FetchGuardDB', refreshTokenKey = 'refreshToken'): RefreshTokenStorage {\r\n const storeName = 'tokens'\r\n\r\n const openDB = (): Promise<IDBDatabase> => {\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDB.open(dbName, 1)\r\n \r\n request.onerror = () => reject(request.error)\r\n request.onsuccess = () => resolve(request.result)\r\n \r\n request.onupgradeneeded = (event) => {\r\n const db = (event.target as IDBOpenDBRequest).result\r\n if (!db.objectStoreNames.contains(storeName)) {\r\n const store = db.createObjectStore(storeName, { keyPath: 'key' })\r\n store.createIndex('timestamp', 'timestamp', { unique: false })\r\n }\r\n }\r\n })\r\n }\r\n\r\n const promisifyRequest = <T>(request: IDBRequest<T>): Promise<T> => {\r\n return new Promise((resolve, reject) => {\r\n request.onsuccess = () => resolve(request.result)\r\n request.onerror = () => reject(request.error)\r\n })\r\n }\r\n\r\n return {\r\n async get() {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readonly')\r\n const store = transaction.objectStore(storeName)\r\n const result = await promisifyRequest(store.get(refreshTokenKey))\r\n return result?.value || null\r\n } catch (error) {\r\n console.warn('Failed to get refresh token from IndexedDB:', error)\r\n return null\r\n }\r\n },\r\n async set(token) {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readwrite')\r\n const store = transaction.objectStore(storeName)\r\n\r\n if (token) {\r\n await promisifyRequest(store.put({ key: refreshTokenKey, value: token, timestamp: Date.now() }))\r\n } else {\r\n await promisifyRequest(store.delete(refreshTokenKey))\r\n }\r\n } catch (error) {\r\n console.warn('Failed to save refresh token to IndexedDB:', error)\r\n }\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\n\r\n/**\r\n * Body parser - parse token from response body (JSON)\r\n * Expects response format: { data: { accessToken, refreshToken, expiresAt?, user? } }\r\n */\r\nexport const bodyParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n refreshToken: json.data.refreshToken,\r\n expiresAt: json.data.expiresAt,\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\n\r\n/**\r\n * Cookie parser - parse access token from response body\r\n * Expects response format: { data: { accessToken, expiresAt?, user? } }\r\n * Refresh token is automatically set by backend into httpOnly cookie\r\n */\r\nexport const cookieParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n expiresAt: json.data.expiresAt,\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { AuthStrategy } from '../../types'\r\n\r\n/**\r\n * Cookie auth strategy - all auth operations via httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Refresh token is sent automatically via httpOnly cookie\r\n * Credentials are sent in request body\r\n */\r\nexport function createCookieStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): AuthStrategy {\r\n return {\r\n async refresh() {\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload) {\r\n return fetch(config.loginUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard cookie strategy\r\n */\r\nexport const cookieStrategy = createCookieStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import type { AuthStrategy } from '../../types'\r\n\r\n/**\r\n * Body auth strategy - all auth operations via request body\r\n * Suitable for SPA applications\r\n *\r\n * All tokens/credentials are sent in request body\r\n */\r\nexport function createBodyStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): AuthStrategy {\r\n return {\r\n async refresh(refreshToken) {\r\n if (!refreshToken) {\r\n throw new Error('No refresh token available')\r\n }\r\n\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ refreshToken }),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload) {\r\n return fetch(config.loginUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard body strategy\r\n */\r\nexport const bodyStrategy = createBodyStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import { createProvider } from './create-provider'\r\nimport { createIndexedDBStorage } from './storage/indexeddb'\r\nimport { bodyParser } from './parser/body'\r\nimport { cookieParser } from './parser/cookie'\r\nimport { createCookieStrategy } from './strategy/cookie'\r\nimport { createBodyStrategy } from './strategy/body'\r\nimport type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Cookie Provider - uses httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: httpOnly cookie (managed by backend)\r\n */\r\nexport function createCookieProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: undefined,\r\n parser: cookieParser,\r\n strategy: createCookieStrategy(config)\r\n })\r\n}\r\n\r\n/**\r\n * Body Provider - refresh token in response body, persisted to IndexedDB\r\n * Suitable for SPA applications\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: IndexedDB (persists across reload)\r\n */\r\nexport function createBodyProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n refreshTokenKey?: string\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: createIndexedDBStorage('FetchGuardDB', config.refreshTokenKey || 'refreshToken'),\r\n parser: bodyParser,\r\n strategy: createBodyStrategy(config)\r\n })\r\n}\r\n","import { createCookieProvider, createBodyProvider } from './presets'\r\nimport type { TokenProvider, ProviderPresetConfig } from '../types'\r\n\r\n/**\r\n * Build provider from preset config\r\n * This is called in worker when receiving SETUP message\r\n */\r\nexport function buildProviderFromPreset(config: ProviderPresetConfig): TokenProvider {\r\n switch (config.type) {\r\n case 'cookie-auth':\r\n return createCookieProvider({\r\n refreshUrl: config.refreshUrl,\r\n loginUrl: config.loginUrl,\r\n logoutUrl: config.logoutUrl\r\n })\r\n\r\n case 'body-auth':\r\n return createBodyProvider({\r\n refreshUrl: config.refreshUrl,\r\n loginUrl: config.loginUrl,\r\n logoutUrl: config.logoutUrl,\r\n refreshTokenKey: config.refreshTokenKey\r\n })\r\n\r\n default:\r\n throw new Error(`Unknown provider type: ${String((config as { type?: unknown }).type)}`)\r\n }\r\n}\r\n","import type { SerializedFormData, SerializedFormDataEntry, SerializedFile } from '../types'\r\n\r\n/**\r\n * Serialize FormData for transfer over postMessage\r\n * Inspired by api-worker.js:484-518\r\n *\r\n * FormData cannot be cloned via postMessage, so we need to serialize it first\r\n * Files are converted to ArrayBuffer -> number[] for transfer\r\n */\r\nexport async function serializeFormData(formData: FormData): Promise<SerializedFormData> {\r\n const entries: Array<[string, SerializedFormDataEntry]> = []\r\n\r\n // Use forEach instead of entries() for better TS compatibility\r\n formData.forEach((value, key) => {\r\n // Push async operations to promises array for parallel processing\r\n if (value instanceof File) {\r\n // We need to handle this synchronously in forEach, so we'll collect promises\r\n // and await them all at once\r\n } else {\r\n entries.push([key, String(value)])\r\n }\r\n })\r\n\r\n // Handle File entries separately with Promise.all\r\n const filePromises: Promise<void>[] = []\r\n formData.forEach((value, key) => {\r\n if (value instanceof File) {\r\n const promise = (async () => {\r\n const arrayBuffer = await value.arrayBuffer()\r\n const uint8Array = new Uint8Array(arrayBuffer)\r\n const serializedFile: SerializedFile = {\r\n name: value.name,\r\n type: value.type,\r\n data: Array.from(uint8Array) // Convert to number array\r\n }\r\n entries.push([key, serializedFile])\r\n })()\r\n filePromises.push(promise)\r\n }\r\n })\r\n\r\n await Promise.all(filePromises)\r\n\r\n return {\r\n _type: 'FormData',\r\n entries\r\n }\r\n}\r\n\r\n/**\r\n * Deserialize SerializedFormData back to FormData in worker\r\n * Reconstructs File objects from serialized data\r\n */\r\nexport function deserializeFormData(serialized: SerializedFormData): FormData {\r\n const formData = new FormData()\r\n\r\n for (const [key, value] of serialized.entries) {\r\n if (typeof value === 'string') {\r\n formData.append(key, value)\r\n } else {\r\n // Reconstruct File from SerializedFile\r\n const uint8Array = new Uint8Array(value.data)\r\n const file = new File([uint8Array], value.name, { type: value.type })\r\n formData.append(key, file)\r\n }\r\n }\r\n\r\n return formData\r\n}\r\n\r\n/**\r\n * Check if body is FormData\r\n */\r\nexport function isFormData(body: unknown): body is FormData {\r\n return body instanceof FormData\r\n}\r\n\r\n/**\r\n * Check if serialized body is SerializedFormData\r\n */\r\nexport function isSerializedFormData(body: unknown): body is SerializedFormData {\r\n return body !== null && typeof body === 'object' && (body as SerializedFormData)._type === 'FormData'\r\n}\r\n","/**\r\n * Binary data utilities for Worker\r\n * Handles ArrayBuffer <-> Base64 conversion for binary responses\r\n */\r\n\r\n/**\r\n * Convert ArrayBuffer to base64 string\r\n * Used in worker to encode binary responses for postMessage transfer\r\n */\r\nexport function arrayBufferToBase64(buffer: ArrayBuffer): string {\r\n const bytes = new Uint8Array(buffer)\r\n let binary = ''\r\n for (let i = 0; i < bytes.length; i++) {\r\n binary += String.fromCharCode(bytes[i])\r\n }\r\n return btoa(binary)\r\n}\r\n\r\n/**\r\n * Convert base64 string to ArrayBuffer\r\n * Used in client to decode binary responses\r\n */\r\nexport function base64ToArrayBuffer(base64: string): ArrayBuffer {\r\n const binary = atob(base64)\r\n const bytes = new Uint8Array(binary.length)\r\n for (let i = 0; i < binary.length; i++) {\r\n bytes[i] = binary.charCodeAt(i)\r\n }\r\n return bytes.buffer\r\n}\r\n\r\n/**\r\n * Check if content type is binary (should be base64 encoded)\r\n * Returns true for images, PDFs, videos, etc.\r\n */\r\nexport function isBinaryContentType(contentType: string): boolean {\r\n const normalized = contentType.toLowerCase()\r\n\r\n // Text types - NOT binary\r\n if (normalized.includes('text/')) return false\r\n if (normalized.includes('json')) return false\r\n if (normalized.includes('xml')) return false\r\n if (normalized.includes('javascript')) return false\r\n if (normalized.includes('ecmascript')) return false\r\n if (normalized.includes('html')) return false\r\n\r\n // Everything else is considered binary\r\n return true\r\n}\r\n"],"mappings":"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","names":["ok","err","err","ok"]}
|