Skip to content

Commit 2072f58

Browse files
JOU-amjsallenli178子殊
authored andcommitted
feat(packages): add subpackage @sa/alova (#640)
* feat(packages): add @sa/alova * typo(packages): add types & update code * feat: add subpackage @sa/alova --------- Co-authored-by: allenli178 <[email protected]> Co-authored-by: 子殊 <[email protected]>
1 parent cfaab85 commit 2072f58

File tree

7 files changed

+197
-0
lines changed

7 files changed

+197
-0
lines changed

packages/alova/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "@sa/alova",
3+
"version": "0.1.0",
4+
"exports": {
5+
".": "./src/index.ts",
6+
"./client": "./src/client.ts"
7+
},
8+
"typesVersions": {
9+
"*": {
10+
"*": ["./src/*"]
11+
}
12+
},
13+
"dependencies": {
14+
"@sa/utils": "workspace:*",
15+
"alova": "3.0.19"
16+
}
17+
}

packages/alova/src/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from 'alova/client';

packages/alova/src/constant.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/** the backend error code key */
2+
export const BACKEND_ERROR_CODE = 'BACKEND_ERROR';

packages/alova/src/index.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { createAlova } from 'alova';
2+
import type { AlovaDefaultCacheAdapter, AlovaGenerics, AlovaGlobalCacheAdapter, AlovaRequestAdapter } from 'alova';
3+
import VueHook from 'alova/vue';
4+
import type { VueHookType } from 'alova/vue';
5+
import adapterFetch from 'alova/fetch';
6+
import { createServerTokenAuthentication } from 'alova/client';
7+
import type { FetchRequestInit } from 'alova/fetch';
8+
import { BACKEND_ERROR_CODE } from './constant';
9+
import type { CustomAlovaConfig, RequestOptions } from './type';
10+
11+
export const createAlovaRequest = <
12+
RequestConfig = FetchRequestInit,
13+
ResponseType = Response,
14+
ResponseHeader = Headers,
15+
L1Cache extends AlovaGlobalCacheAdapter = AlovaDefaultCacheAdapter,
16+
L2Cache extends AlovaGlobalCacheAdapter = AlovaDefaultCacheAdapter
17+
>(
18+
customConfig: CustomAlovaConfig<
19+
AlovaGenerics<any, any, RequestConfig, ResponseType, ResponseHeader, L1Cache, L2Cache, any>
20+
>,
21+
options: RequestOptions<AlovaGenerics<any, any, RequestConfig, ResponseType, ResponseHeader, L1Cache, L2Cache, any>>
22+
) => {
23+
const { tokenRefresher } = options;
24+
const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication<
25+
VueHookType,
26+
AlovaRequestAdapter<RequestConfig, ResponseType, ResponseHeader>
27+
>({
28+
refreshTokenOnSuccess: {
29+
isExpired: (response, method) => tokenRefresher?.isExpired(response, method) || false,
30+
handler: async (response, method) => tokenRefresher?.handler(response, method)
31+
},
32+
refreshTokenOnError: {
33+
isExpired: (response, method) => tokenRefresher?.isExpired(response, method) || false,
34+
handler: async (response, method) => tokenRefresher?.handler(response, method)
35+
}
36+
});
37+
38+
const instance = createAlova({
39+
...customConfig,
40+
timeout: customConfig.timeout ?? 10 * 1000,
41+
requestAdapter: (customConfig.requestAdapter as any) ?? adapterFetch(),
42+
statesHook: VueHook,
43+
beforeRequest: onAuthRequired(options.onRequest as any),
44+
responded: onResponseRefreshToken({
45+
onSuccess: async (response, method) => {
46+
// check if http status is success
47+
let error: any = null;
48+
let transformedData: any = null;
49+
try {
50+
if (await options.isBackendSuccess(response)) {
51+
transformedData = await options.transformBackendResponse(response);
52+
} else {
53+
error = new Error('the backend request error');
54+
error.code = BACKEND_ERROR_CODE;
55+
}
56+
} catch (err) {
57+
error = err;
58+
}
59+
60+
if (error) {
61+
await options.onError?.(error, response, method);
62+
throw error;
63+
}
64+
65+
return transformedData;
66+
},
67+
onComplete: options.onComplete,
68+
onError: (error, method) => options.onError?.(error, null, method)
69+
})
70+
});
71+
72+
return instance;
73+
};
74+
75+
export { BACKEND_ERROR_CODE };
76+
export type * from './type';
77+
export type * from 'alova';

packages/alova/src/type.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { AlovaGenerics, AlovaOptions, AlovaRequestAdapter, Method, ResponseCompleteHandler } from 'alova';
2+
3+
export type CustomAlovaConfig<AG extends AlovaGenerics> = Omit<
4+
AlovaOptions<AG>,
5+
'statesHook' | 'beforeRequest' | 'responded' | 'requestAdapter'
6+
> & {
7+
/** request adapter. all request of alova will be sent by it. */
8+
requestAdapter?: AlovaRequestAdapter<AG['RequestConfig'], AG['Response'], AG['ResponseHeader']>;
9+
};
10+
11+
export interface RequestOptions<AG extends AlovaGenerics> {
12+
/**
13+
* The hook before request
14+
*
15+
* For example: You can add header token in this hook
16+
*
17+
* @param method alova Method Instance
18+
*/
19+
onRequest?: AlovaOptions<AG>['beforeRequest'];
20+
/**
21+
* The hook to check backend response is success or not
22+
*
23+
* @param response alova response
24+
*/
25+
isBackendSuccess: (response: AG['Response']) => Promise<boolean>;
26+
27+
/** The config to refresh token */
28+
tokenRefresher?: {
29+
/** detect the token is expired */
30+
isExpired(response: AG['Response'], Method: Method<AG>): Promise<boolean> | boolean;
31+
/** refresh token handler */
32+
handler(response: AG['Response'], Method: Method<AG>): Promise<void>;
33+
};
34+
35+
/** The hook after backend request complete */
36+
onComplete?: ResponseCompleteHandler<AG>;
37+
38+
/**
39+
* The hook to handle error
40+
*
41+
* For example: You can show error message in this hook
42+
*
43+
* @param error
44+
*/
45+
onError?: (error: any, response: AG['Response'] | null, methodInstance: Method<AG>) => any | Promise<any>;
46+
/**
47+
* transform backend response when the responseType is json
48+
*
49+
* @param response alova response
50+
*/
51+
transformBackendResponse: (response: AG['Response']) => any;
52+
}

packages/alova/tsconfig.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"jsx": "preserve",
5+
"lib": ["DOM", "ESNext"],
6+
"baseUrl": ".",
7+
"module": "ESNext",
8+
"moduleResolution": "node",
9+
"resolveJsonModule": true,
10+
"types": ["node"],
11+
"strict": true,
12+
"strictNullChecks": true,
13+
"noUnusedLocals": true,
14+
"allowSyntheticDefaultImports": true,
15+
"esModuleInterop": true,
16+
"forceConsistentCasingInFileNames": true
17+
},
18+
"include": ["src/**/*"],
19+
"exclude": ["node_modules", "dist"]
20+
}

pnpm-lock.yaml

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)