@auth-craft/client 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -8
- package/dist/core/core.d.ts +72 -0
- package/dist/core/core.d.ts.map +1 -0
- package/dist/core/core.js +105 -0
- package/dist/core/core.js.map +1 -0
- package/dist/core/proxy.d.ts +79 -0
- package/dist/core/proxy.d.ts.map +1 -0
- package/dist/core/proxy.js +99 -0
- package/dist/core/proxy.js.map +1 -0
- package/dist/index.d.ts +15 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -55
- package/dist/index.js.map +1 -1
- package/dist/transport/client.d.ts +250 -0
- package/dist/transport/client.d.ts.map +1 -0
- package/dist/transport/client.js +940 -0
- package/dist/transport/client.js.map +1 -0
- package/dist/transport/constants.d.ts +9 -0
- package/dist/transport/constants.d.ts.map +1 -0
- package/dist/transport/constants.js +9 -0
- package/dist/transport/constants.js.map +1 -0
- package/dist/transport/error-codes.d.ts +41 -0
- package/dist/transport/error-codes.d.ts.map +1 -0
- package/dist/transport/error-codes.js +38 -0
- package/dist/transport/error-codes.js.map +1 -0
- package/dist/transport/errors.d.ts +55 -0
- package/dist/transport/errors.d.ts.map +1 -0
- package/dist/transport/errors.js +55 -0
- package/dist/transport/errors.js.map +1 -0
- package/dist/transport/helpers.d.ts +135 -0
- package/dist/transport/helpers.d.ts.map +1 -0
- package/dist/transport/helpers.js +185 -0
- package/dist/transport/helpers.js.map +1 -0
- package/dist/transport/index.d.ts +22 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +32 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/messages.d.ts +104 -0
- package/dist/transport/messages.d.ts.map +1 -0
- package/dist/transport/messages.js +24 -0
- package/dist/transport/messages.js.map +1 -0
- package/dist/transport/provider/create-provider.d.ts +38 -0
- package/dist/transport/provider/create-provider.d.ts.map +1 -0
- package/dist/transport/provider/create-provider.js +119 -0
- package/dist/transport/provider/create-provider.js.map +1 -0
- package/dist/transport/provider/parser/body.d.ts +7 -0
- package/dist/transport/provider/parser/body.d.ts.map +1 -0
- package/dist/transport/provider/parser/body.js +17 -0
- package/dist/transport/provider/parser/body.js.map +1 -0
- package/dist/transport/provider/parser/cookie.d.ts +8 -0
- package/dist/transport/provider/parser/cookie.d.ts.map +1 -0
- package/dist/transport/provider/parser/cookie.js +17 -0
- package/dist/transport/provider/parser/cookie.js.map +1 -0
- package/dist/transport/provider/parser/normalize.d.ts +6 -0
- package/dist/transport/provider/parser/normalize.d.ts.map +1 -0
- package/dist/transport/provider/parser/normalize.js +21 -0
- package/dist/transport/provider/parser/normalize.js.map +1 -0
- package/dist/transport/provider/presets.d.ts +31 -0
- package/dist/transport/provider/presets.d.ts.map +1 -0
- package/dist/transport/provider/presets.js +35 -0
- package/dist/transport/provider/presets.js.map +1 -0
- package/dist/transport/provider/register-presets.d.ts +10 -0
- package/dist/transport/provider/register-presets.d.ts.map +1 -0
- package/dist/transport/provider/register-presets.js +32 -0
- package/dist/transport/provider/register-presets.js.map +1 -0
- package/dist/transport/provider/storage/indexeddb.d.ts +26 -0
- package/dist/transport/provider/storage/indexeddb.d.ts.map +1 -0
- package/dist/transport/provider/storage/indexeddb.js +72 -0
- package/dist/transport/provider/storage/indexeddb.js.map +1 -0
- package/dist/transport/provider/strategy/body.d.ts +28 -0
- package/dist/transport/provider/strategy/body.d.ts.map +1 -0
- package/dist/transport/provider/strategy/body.js +73 -0
- package/dist/transport/provider/strategy/body.js.map +1 -0
- package/dist/transport/provider/strategy/cookie.d.ts +29 -0
- package/dist/transport/provider/strategy/cookie.d.ts.map +1 -0
- package/dist/transport/provider/strategy/cookie.js +70 -0
- package/dist/transport/provider/strategy/cookie.js.map +1 -0
- package/dist/transport/result.d.ts +28 -0
- package/dist/transport/result.d.ts.map +1 -0
- package/dist/transport/result.js +87 -0
- package/dist/transport/result.js.map +1 -0
- package/dist/transport/types.d.ts +488 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/types.js +3 -0
- package/dist/transport/types.js.map +1 -0
- package/dist/transport/utils/binary.d.ts +20 -0
- package/dist/transport/utils/binary.d.ts.map +1 -0
- package/dist/transport/utils/binary.js +51 -0
- package/dist/transport/utils/binary.js.map +1 -0
- package/dist/transport/utils/formdata.d.ts +26 -0
- package/dist/transport/utils/formdata.d.ts.map +1 -0
- package/dist/transport/utils/formdata.js +77 -0
- package/dist/transport/utils/formdata.js.map +1 -0
- package/dist/transport/utils/registry.d.ts +26 -0
- package/dist/transport/utils/registry.d.ts.map +1 -0
- package/dist/transport/utils/registry.js +51 -0
- package/dist/transport/utils/registry.js.map +1 -0
- package/dist/transport/worker-post.d.ts +44 -0
- package/dist/transport/worker-post.d.ts.map +1 -0
- package/dist/transport/worker-post.js +104 -0
- package/dist/transport/worker-post.js.map +1 -0
- package/dist/transport/worker.d.ts +10 -0
- package/dist/transport/worker.d.ts.map +1 -0
- package/dist/transport/worker.js +421 -0
- package/dist/transport/worker.js.map +1 -0
- package/dist/types.test-d.js +3 -9
- package/dist/types.test-d.js.map +1 -1
- package/eslint-isolation.mjs +82 -0
- package/package.json +30 -16
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IndexedDB storage - only stores refresh token in IndexedDB
|
|
3
|
+
* Suitable for body-based refresh strategy
|
|
4
|
+
* Persists refresh token for reuse after reload
|
|
5
|
+
*
|
|
6
|
+
* @param options - Storage options or legacy dbName string
|
|
7
|
+
* @param legacyRefreshTokenKey - Legacy refreshTokenKey (for backward compatibility)
|
|
8
|
+
*/
|
|
9
|
+
export function createIndexedDBStorage(options = 'FetchGuardDB', legacyRefreshTokenKey) {
|
|
10
|
+
// Support both new options object and legacy string arguments
|
|
11
|
+
const config = typeof options === 'string'
|
|
12
|
+
? { dbName: options, refreshTokenKey: legacyRefreshTokenKey ?? 'refreshToken', onError: undefined }
|
|
13
|
+
: {
|
|
14
|
+
dbName: options.dbName ?? 'FetchGuardDB',
|
|
15
|
+
refreshTokenKey: options.refreshTokenKey ?? 'refreshToken',
|
|
16
|
+
onError: options.onError,
|
|
17
|
+
};
|
|
18
|
+
const { dbName, refreshTokenKey, onError } = config;
|
|
19
|
+
const storeName = 'tokens';
|
|
20
|
+
const openDB = () => {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const request = indexedDB.open(dbName, 1);
|
|
23
|
+
request.onerror = () => reject(request.error);
|
|
24
|
+
request.onsuccess = () => resolve(request.result);
|
|
25
|
+
request.onupgradeneeded = (event) => {
|
|
26
|
+
const db = event.target.result;
|
|
27
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
28
|
+
const store = db.createObjectStore(storeName, { keyPath: 'key' });
|
|
29
|
+
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
const promisifyRequest = (request) => {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
request.onsuccess = () => resolve(request.result);
|
|
37
|
+
request.onerror = () => reject(request.error);
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
async get() {
|
|
42
|
+
try {
|
|
43
|
+
const db = await openDB();
|
|
44
|
+
const transaction = db.transaction([storeName], 'readonly');
|
|
45
|
+
const store = transaction.objectStore(storeName);
|
|
46
|
+
const result = await promisifyRequest(store.get(refreshTokenKey));
|
|
47
|
+
return result?.value || null;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
onError?.(error, 'get');
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
async set(token) {
|
|
55
|
+
try {
|
|
56
|
+
const db = await openDB();
|
|
57
|
+
const transaction = db.transaction([storeName], 'readwrite');
|
|
58
|
+
const store = transaction.objectStore(storeName);
|
|
59
|
+
if (token) {
|
|
60
|
+
await promisifyRequest(store.put({ key: refreshTokenKey, value: token, timestamp: Date.now() }));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
await promisifyRequest(store.delete(refreshTokenKey));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
onError?.(error, token ? 'set' : 'delete');
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=indexeddb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexeddb.js","sourceRoot":"","sources":["../../../../src/transport/provider/storage/indexeddb.ts"],"names":[],"mappings":"AAkBA;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,UAA4C,cAAc,EAC1D,qBAA8B;IAE9B,8DAA8D;IAC9D,MAAM,MAAM,GACV,OAAO,OAAO,KAAK,QAAQ;QACzB,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,qBAAqB,IAAI,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE;QACnG,CAAC,CAAC;YACE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,cAAc;YACxC,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,cAAc;YAC1D,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC;IAER,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IACpD,MAAM,SAAS,GAAG,QAAQ,CAAC;IAE3B,MAAM,MAAM,GAAG,GAAyB,EAAE;QACxC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAE1C,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC9C,OAAO,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAElD,OAAO,CAAC,eAAe,GAAG,CAAC,KAAK,EAAE,EAAE;gBAClC,MAAM,EAAE,GAAI,KAAK,CAAC,MAA2B,CAAC,MAAM,CAAC;gBACrD,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7C,MAAM,KAAK,GAAG,EAAE,CAAC,iBAAiB,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;oBAClE,KAAK,CAAC,WAAW,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,CAAI,OAAsB,EAAc,EAAE;QACjE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,OAAO,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClD,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,GAAG;YACP,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,MAAM,MAAM,EAAE,CAAC;gBAC1B,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC;gBAC5D,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;gBACjD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;gBAClE,OAAO,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC;YAC/B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,EAAE,CAAC,KAAc,EAAE,KAAK,CAAC,CAAC;gBACjC,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,KAAK;YACb,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,MAAM,MAAM,EAAE,CAAC;gBAC1B,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;gBAC7D,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;gBAEjD,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;gBACnG,CAAC;qBAAM,CAAC;oBACN,MAAM,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;gBACxD,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,EAAE,CAAC,KAAc,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AuthStrategy } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Body auth strategy - all auth operations via request body
|
|
4
|
+
* Suitable for SPA applications
|
|
5
|
+
*
|
|
6
|
+
* All tokens/credentials are sent in request body
|
|
7
|
+
*
|
|
8
|
+
* Login URL can be:
|
|
9
|
+
* - Configured once: loginUrl: 'https://api.example.com/auth/login'
|
|
10
|
+
* - Passed per call: login(payload, 'https://...')
|
|
11
|
+
*
|
|
12
|
+
* Header priority (lowest to highest):
|
|
13
|
+
* - defaultHeaders (from FetchGuardOptions)
|
|
14
|
+
* - headers (from ProviderPresetConfig)
|
|
15
|
+
* - Content-Type: application/json
|
|
16
|
+
*/
|
|
17
|
+
export declare function createBodyStrategy(config: {
|
|
18
|
+
refreshUrl: string;
|
|
19
|
+
loginUrl: string;
|
|
20
|
+
logoutUrl: string;
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
defaultHeaders?: Record<string, string>;
|
|
23
|
+
}): AuthStrategy;
|
|
24
|
+
/**
|
|
25
|
+
* Standard body strategy
|
|
26
|
+
*/
|
|
27
|
+
export declare const bodyStrategy: AuthStrategy;
|
|
28
|
+
//# sourceMappingURL=body.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../../../../src/transport/provider/strategy/body.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAwB,MAAM,aAAa,CAAC;AAEtE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC,GAAG,YAAY,CAqDf;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,cAIvB,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Body auth strategy - all auth operations via request body
|
|
3
|
+
* Suitable for SPA applications
|
|
4
|
+
*
|
|
5
|
+
* All tokens/credentials are sent in request body
|
|
6
|
+
*
|
|
7
|
+
* Login URL can be:
|
|
8
|
+
* - Configured once: loginUrl: 'https://api.example.com/auth/login'
|
|
9
|
+
* - Passed per call: login(payload, 'https://...')
|
|
10
|
+
*
|
|
11
|
+
* Header priority (lowest to highest):
|
|
12
|
+
* - defaultHeaders (from FetchGuardOptions)
|
|
13
|
+
* - headers (from ProviderPresetConfig)
|
|
14
|
+
* - Content-Type: application/json
|
|
15
|
+
*/
|
|
16
|
+
export function createBodyStrategy(config) {
|
|
17
|
+
const baseHeaders = {
|
|
18
|
+
...config.defaultHeaders,
|
|
19
|
+
...config.headers,
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
async refresh(refreshToken) {
|
|
24
|
+
if (!refreshToken) {
|
|
25
|
+
throw new Error('No refresh token available');
|
|
26
|
+
}
|
|
27
|
+
return fetch(config.refreshUrl, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: baseHeaders,
|
|
30
|
+
body: JSON.stringify({ refreshToken }),
|
|
31
|
+
credentials: 'include',
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
async login(payload, url) {
|
|
35
|
+
return fetch(url || config.loginUrl, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: baseHeaders,
|
|
38
|
+
body: JSON.stringify(payload),
|
|
39
|
+
credentials: 'include',
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
async logout(payload) {
|
|
43
|
+
return fetch(config.logoutUrl, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: baseHeaders,
|
|
46
|
+
body: payload ? JSON.stringify(payload) : undefined,
|
|
47
|
+
credentials: 'include',
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
async exchangeToken(accessToken, url, options = {}) {
|
|
51
|
+
const { method = 'POST', payload, headers } = options;
|
|
52
|
+
return fetch(url, {
|
|
53
|
+
method,
|
|
54
|
+
headers: {
|
|
55
|
+
...baseHeaders,
|
|
56
|
+
...headers,
|
|
57
|
+
Authorization: `Bearer ${accessToken}`,
|
|
58
|
+
},
|
|
59
|
+
body: payload ? JSON.stringify(payload) : undefined,
|
|
60
|
+
credentials: 'include',
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Standard body strategy
|
|
67
|
+
*/
|
|
68
|
+
export const bodyStrategy = createBodyStrategy({
|
|
69
|
+
refreshUrl: '/auth/refresh',
|
|
70
|
+
loginUrl: '/auth/login',
|
|
71
|
+
logoutUrl: '/auth/logout',
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=body.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.js","sourceRoot":"","sources":["../../../../src/transport/provider/strategy/body.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAMlC;IACC,MAAM,WAAW,GAAG;QAClB,GAAG,MAAM,CAAC,cAAc;QACxB,GAAG,MAAM,CAAC,OAAO;QACjB,cAAc,EAAE,kBAAkB;KACnC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,YAAY;YACxB,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAChD,CAAC;YAED,OAAO,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE;gBAC9B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,WAAW;gBACpB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;gBACtC,WAAW,EAAE,SAAS;aACvB,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG;YACtB,OAAO,KAAK,CAAC,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE;gBACnC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,WAAW;gBACpB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC7B,WAAW,EAAE,SAAS;aACvB,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,OAAO,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE;gBAC7B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,WAAW;gBACpB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;gBACnD,WAAW,EAAE,SAAS;aACvB,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,WAAmB,EAAE,GAAW,EAAE,UAAgC,EAAE;YACtF,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;YACtD,OAAO,KAAK,CAAC,GAAG,EAAE;gBAChB,MAAM;gBACN,OAAO,EAAE;oBACP,GAAG,WAAW;oBACd,GAAG,OAAO;oBACV,aAAa,EAAE,UAAU,WAAW,EAAE;iBACvC;gBACD,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;gBACnD,WAAW,EAAE,SAAS;aACvB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,kBAAkB,CAAC;IAC7C,UAAU,EAAE,eAAe;IAC3B,QAAQ,EAAE,aAAa;IACvB,SAAS,EAAE,cAAc;CAC1B,CAAC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AuthStrategy } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Cookie auth strategy - all auth operations via httpOnly cookies
|
|
4
|
+
* Suitable for SSR and cross-domain authentication
|
|
5
|
+
*
|
|
6
|
+
* Refresh token is sent automatically via httpOnly cookie
|
|
7
|
+
* Credentials are sent in request body
|
|
8
|
+
*
|
|
9
|
+
* Login URL can be:
|
|
10
|
+
* - Configured once: loginUrl: 'https://api.example.com/auth/login'
|
|
11
|
+
* - Passed per call: login(payload, 'https://...')
|
|
12
|
+
*
|
|
13
|
+
* Header priority (lowest to highest):
|
|
14
|
+
* - defaultHeaders (from FetchGuardOptions)
|
|
15
|
+
* - headers (from ProviderPresetConfig)
|
|
16
|
+
* - Content-Type: application/json
|
|
17
|
+
*/
|
|
18
|
+
export declare function createCookieStrategy(config: {
|
|
19
|
+
refreshUrl: string;
|
|
20
|
+
loginUrl: string;
|
|
21
|
+
logoutUrl: string;
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
defaultHeaders?: Record<string, string>;
|
|
24
|
+
}): AuthStrategy;
|
|
25
|
+
/**
|
|
26
|
+
* Standard cookie strategy
|
|
27
|
+
*/
|
|
28
|
+
export declare const cookieStrategy: AuthStrategy;
|
|
29
|
+
//# sourceMappingURL=cookie.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../../../src/transport/provider/strategy/cookie.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAwB,MAAM,aAAa,CAAC;AAEtE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC,GAAG,YAAY,CAgDf;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,cAIzB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie auth strategy - all auth operations via httpOnly cookies
|
|
3
|
+
* Suitable for SSR and cross-domain authentication
|
|
4
|
+
*
|
|
5
|
+
* Refresh token is sent automatically via httpOnly cookie
|
|
6
|
+
* Credentials are sent in request body
|
|
7
|
+
*
|
|
8
|
+
* Login URL can be:
|
|
9
|
+
* - Configured once: loginUrl: 'https://api.example.com/auth/login'
|
|
10
|
+
* - Passed per call: login(payload, 'https://...')
|
|
11
|
+
*
|
|
12
|
+
* Header priority (lowest to highest):
|
|
13
|
+
* - defaultHeaders (from FetchGuardOptions)
|
|
14
|
+
* - headers (from ProviderPresetConfig)
|
|
15
|
+
* - Content-Type: application/json
|
|
16
|
+
*/
|
|
17
|
+
export function createCookieStrategy(config) {
|
|
18
|
+
const baseHeaders = {
|
|
19
|
+
...config.defaultHeaders,
|
|
20
|
+
...config.headers,
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
async refresh() {
|
|
25
|
+
return fetch(config.refreshUrl, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: baseHeaders,
|
|
28
|
+
credentials: 'include',
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
async login(payload, url) {
|
|
32
|
+
return fetch(url || config.loginUrl, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: baseHeaders,
|
|
35
|
+
body: JSON.stringify(payload),
|
|
36
|
+
credentials: 'include',
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
async logout(payload) {
|
|
40
|
+
return fetch(config.logoutUrl, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: baseHeaders,
|
|
43
|
+
body: payload ? JSON.stringify(payload) : undefined,
|
|
44
|
+
credentials: 'include',
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
async exchangeToken(accessToken, url, options = {}) {
|
|
48
|
+
const { method = 'POST', payload, headers } = options;
|
|
49
|
+
return fetch(url, {
|
|
50
|
+
method,
|
|
51
|
+
headers: {
|
|
52
|
+
...baseHeaders,
|
|
53
|
+
...headers,
|
|
54
|
+
Authorization: `Bearer ${accessToken}`,
|
|
55
|
+
},
|
|
56
|
+
body: payload ? JSON.stringify(payload) : undefined,
|
|
57
|
+
credentials: 'include',
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Standard cookie strategy
|
|
64
|
+
*/
|
|
65
|
+
export const cookieStrategy = createCookieStrategy({
|
|
66
|
+
refreshUrl: '/auth/refresh',
|
|
67
|
+
loginUrl: '/auth/login',
|
|
68
|
+
logoutUrl: '/auth/logout',
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=cookie.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie.js","sourceRoot":"","sources":["../../../../src/transport/provider/strategy/cookie.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAMpC;IACC,MAAM,WAAW,GAAG;QAClB,GAAG,MAAM,CAAC,cAAc;QACxB,GAAG,MAAM,CAAC,OAAO;QACjB,cAAc,EAAE,kBAAkB;KACnC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,OAAO;YACX,OAAO,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE;gBAC9B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,WAAW;gBACpB,WAAW,EAAE,SAAS;aACvB,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG;YACtB,OAAO,KAAK,CAAC,GAAG,IAAI,MAAM,CAAC,QAAQ,EAAE;gBACnC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,WAAW;gBACpB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC7B,WAAW,EAAE,SAAS;aACvB,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,OAAO;YAClB,OAAO,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE;gBAC7B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,WAAW;gBACpB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;gBACnD,WAAW,EAAE,SAAS;aACvB,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,WAAmB,EAAE,GAAW,EAAE,UAAgC,EAAE;YACtF,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;YACtD,OAAO,KAAK,CAAC,GAAG,EAAE;gBAChB,MAAM;gBACN,OAAO,EAAE;oBACP,GAAG,WAAW;oBACd,GAAG,OAAO;oBACV,aAAa,EAAE,UAAU,WAAW,EAAE;iBACvC;gBACD,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;gBACnD,WAAW,EAAE,SAAS;aACvB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,oBAAoB,CAAC;IACjD,UAAU,EAAE,eAAe;IAC3B,QAAQ,EAAE,aAAa;IACvB,SAAS,EAAE,cAAc;CAC1B,CAAC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result parser — turn a raw FetchEnvelope (or transport-level error) into a
|
|
3
|
+
* domain `Result<T>` by trusting the backend's ts-micro-result envelope.
|
|
4
|
+
*
|
|
5
|
+
* This is the piece that lets a typed client auto-bind: instead of every SDK
|
|
6
|
+
* method hand-writing "judge status + JSON.parse + map errors", the transport
|
|
7
|
+
* exposes one place that deserializes `{ ok, data, errors, meta }`. The Web
|
|
8
|
+
* Worker token isolation is untouched — this runs on the result the client
|
|
9
|
+
* already resolved, after the Worker has done the fetch.
|
|
10
|
+
*/
|
|
11
|
+
import { type Result } from 'ts-micro-result';
|
|
12
|
+
import type { FetchEnvelope } from './types';
|
|
13
|
+
/**
|
|
14
|
+
* Deserialize a JSON body that is expected to be a ts-micro-result envelope.
|
|
15
|
+
* Falls back to treating the parsed JSON as raw data when it isn't an envelope.
|
|
16
|
+
*/
|
|
17
|
+
export declare function deserializeResult<T>(jsonString: string): Result<T>;
|
|
18
|
+
/**
|
|
19
|
+
* Convert `Result<FetchEnvelope>` (what client.get/post/... resolve to) into a
|
|
20
|
+
* domain `Result<T>`.
|
|
21
|
+
*
|
|
22
|
+
* - network/transport error → mapped network error
|
|
23
|
+
* - 204 → ok(undefined)
|
|
24
|
+
* - 5xx → backend Result if present, else a SERVER_ERROR
|
|
25
|
+
* - 2xx–4xx → trust the backend's envelope (ok/err)
|
|
26
|
+
*/
|
|
27
|
+
export declare function toResult<T>(envelopeResult: Result<FetchEnvelope>): Result<T>;
|
|
28
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/transport/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAA6B,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAiBlE;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,aAAa,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAsB5E"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result parser — turn a raw FetchEnvelope (or transport-level error) into a
|
|
3
|
+
* domain `Result<T>` by trusting the backend's ts-micro-result envelope.
|
|
4
|
+
*
|
|
5
|
+
* This is the piece that lets a typed client auto-bind: instead of every SDK
|
|
6
|
+
* method hand-writing "judge status + JSON.parse + map errors", the transport
|
|
7
|
+
* exposes one place that deserializes `{ ok, data, errors, meta }`. The Web
|
|
8
|
+
* Worker token isolation is untouched — this runs on the result the client
|
|
9
|
+
* already resolved, after the Worker has done the fetch.
|
|
10
|
+
*/
|
|
11
|
+
import { err, ok } from 'ts-micro-result';
|
|
12
|
+
import { ERROR_CODES } from './error-codes';
|
|
13
|
+
/**
|
|
14
|
+
* Deserialize a JSON body that is expected to be a ts-micro-result envelope.
|
|
15
|
+
* Falls back to treating the parsed JSON as raw data when it isn't an envelope.
|
|
16
|
+
*/
|
|
17
|
+
export function deserializeResult(jsonString) {
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(jsonString);
|
|
20
|
+
if (parsed && parsed.ok === true) {
|
|
21
|
+
return ok(parsed.data, parsed.meta);
|
|
22
|
+
}
|
|
23
|
+
if (parsed && parsed.ok === false && Array.isArray(parsed.errors)) {
|
|
24
|
+
return err(parsed.errors, parsed.meta);
|
|
25
|
+
}
|
|
26
|
+
// Not an envelope — treat the whole payload as the data.
|
|
27
|
+
return ok(parsed);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return err({ code: 'PARSE_ERROR', message: 'Failed to parse response JSON' });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** True when the body parses to a ts-micro-result envelope (has a boolean `ok`). */
|
|
34
|
+
function isResultEnvelope(body) {
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(body);
|
|
37
|
+
return parsed && typeof parsed.ok === 'boolean';
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Map a transport-level (network) error to a stable domain error.
|
|
45
|
+
*/
|
|
46
|
+
function mapNetworkError(errors) {
|
|
47
|
+
const code = errors[0]?.code;
|
|
48
|
+
if (code === ERROR_CODES.NETWORK_ERROR) {
|
|
49
|
+
return err({ code: 'NETWORK_ERROR', message: 'Connection failed' });
|
|
50
|
+
}
|
|
51
|
+
if (code === ERROR_CODES.REQUEST_CANCELLED) {
|
|
52
|
+
return err({ code: 'REQUEST_CANCELLED', message: 'Request was cancelled' });
|
|
53
|
+
}
|
|
54
|
+
if (code === ERROR_CODES.REQUEST_TIMEOUT) {
|
|
55
|
+
return err({ code: 'REQUEST_TIMEOUT', message: 'Request timed out' });
|
|
56
|
+
}
|
|
57
|
+
return err(errors);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Convert `Result<FetchEnvelope>` (what client.get/post/... resolve to) into a
|
|
61
|
+
* domain `Result<T>`.
|
|
62
|
+
*
|
|
63
|
+
* - network/transport error → mapped network error
|
|
64
|
+
* - 204 → ok(undefined)
|
|
65
|
+
* - 5xx → backend Result if present, else a SERVER_ERROR
|
|
66
|
+
* - 2xx–4xx → trust the backend's envelope (ok/err)
|
|
67
|
+
*/
|
|
68
|
+
export function toResult(envelopeResult) {
|
|
69
|
+
if (!envelopeResult.ok) {
|
|
70
|
+
return mapNetworkError(envelopeResult.errors);
|
|
71
|
+
}
|
|
72
|
+
const { status, body } = envelopeResult.data;
|
|
73
|
+
if (status === 204) {
|
|
74
|
+
return ok(undefined);
|
|
75
|
+
}
|
|
76
|
+
if (status >= 500) {
|
|
77
|
+
// Forward a real backend error envelope if present; otherwise (HTML error
|
|
78
|
+
// page, gateway text, unparseable body) surface a clear SERVER_ERROR rather
|
|
79
|
+
// than a misleading PARSE_ERROR.
|
|
80
|
+
if (isResultEnvelope(body)) {
|
|
81
|
+
return deserializeResult(body);
|
|
82
|
+
}
|
|
83
|
+
return err({ code: 'SERVER_ERROR', message: `Internal server error (${status})` });
|
|
84
|
+
}
|
|
85
|
+
return deserializeResult(body);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/transport/result.ts"],"names":[],"mappings":"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"}
|