@qlover/create-app 0.1.14 → 0.1.16

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.
Files changed (112) hide show
  1. package/CHANGELOG.md +41 -16
  2. package/bin/create-app.js +0 -0
  3. package/configs/_common/package.json.template +10 -0
  4. package/configs/react-app/eslint.config.js +70 -42
  5. package/dist/index.cjs +1 -3631
  6. package/dist/index.js +1 -3625
  7. package/package.json +7 -7
  8. package/templates/react-app/.env +1 -0
  9. package/templates/react-app/config/ErrorIdentifier.ts +27 -0
  10. package/templates/react-app/config/app.router.json +9 -0
  11. package/templates/react-app/config/common.ts +12 -0
  12. package/templates/react-app/config/feapi.mock.json +15 -2
  13. package/templates/react-app/config/i18n.ts +3 -11
  14. package/templates/react-app/package.json +7 -4
  15. package/templates/react-app/pnpm-lock.yaml +176 -25
  16. package/templates/react-app/public/locales/en/common.json +6 -2
  17. package/templates/react-app/public/locales/zh/common.json +6 -3
  18. package/templates/react-app/src/App.tsx +8 -8
  19. package/templates/react-app/src/base/apis/AiApi.ts +55 -0
  20. package/templates/react-app/src/base/apis/feApi/FeApi.ts +13 -44
  21. package/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +14 -0
  22. package/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +67 -0
  23. package/templates/react-app/src/base/apis/feApi/FeApiType.ts +2 -35
  24. package/templates/react-app/src/base/apis/userApi/UserApi.ts +64 -0
  25. package/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +14 -0
  26. package/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +75 -0
  27. package/templates/react-app/src/base/apis/userApi/UserApiType.ts +52 -0
  28. package/templates/react-app/src/base/cases/RequestLogger.ts +71 -0
  29. package/templates/react-app/src/base/cases/RequestStatusCatcher.ts +41 -0
  30. package/templates/react-app/src/base/cases/appError/AppError.ts +13 -0
  31. package/templates/react-app/{lib/router-loader/RouterLoader.ts → src/base/cases/router-loader/index.ts} +1 -1
  32. package/templates/react-app/src/base/port/ApiTransactionInterface.ts +7 -0
  33. package/templates/react-app/src/base/port/InversifyIocInterface.ts +1 -1
  34. package/templates/react-app/src/base/port/LoginInterface.ts +4 -0
  35. package/templates/react-app/src/base/port/RequestCatcherInterface.ts +12 -0
  36. package/templates/react-app/src/{services → base/services}/I18nService.ts +7 -2
  37. package/templates/react-app/src/{services/processer → base/services}/ProcesserService.ts +10 -11
  38. package/templates/react-app/src/base/types/Page.ts +1 -13
  39. package/templates/react-app/src/core/AppConfig.ts +14 -5
  40. package/templates/react-app/src/core/IOC.ts +77 -37
  41. package/templates/react-app/src/core/bootstrap.ts +38 -25
  42. package/templates/react-app/src/core/bootstraps/BootstrapApp.ts +7 -0
  43. package/templates/react-app/src/core/bootstraps/index.ts +12 -0
  44. package/templates/react-app/src/core/globals.ts +7 -10
  45. package/templates/react-app/src/core/registers/RegisterApi.ts +1 -52
  46. package/templates/react-app/src/core/registers/RegisterCommon.ts +44 -6
  47. package/templates/react-app/src/core/registers/RegisterControllers.ts +5 -34
  48. package/templates/react-app/src/core/registers/RegisterGlobals.ts +8 -2
  49. package/templates/react-app/src/core/registers/index.ts +2 -1
  50. package/templates/react-app/src/main.tsx +4 -1
  51. package/templates/react-app/src/pages/auth/Layout.tsx +4 -3
  52. package/templates/react-app/src/pages/auth/Login.tsx +5 -3
  53. package/templates/react-app/src/pages/base/ErrorIdentifier.tsx +39 -0
  54. package/templates/react-app/src/pages/base/Executor.tsx +31 -10
  55. package/templates/react-app/src/pages/base/Home.tsx +58 -30
  56. package/templates/react-app/src/pages/base/JSONStorage.tsx +2 -4
  57. package/templates/react-app/src/pages/base/Request.tsx +318 -73
  58. package/templates/react-app/src/pages/base/components/BaseHeader.tsx +2 -2
  59. package/templates/react-app/src/{components → uikit/components}/RouterRenderComponent.tsx +1 -1
  60. package/templates/react-app/src/{components → uikit/components}/ThemeSwitcher.tsx +6 -6
  61. package/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +6 -3
  62. package/templates/react-app/src/uikit/controllers/ExecutorController.ts +52 -22
  63. package/templates/react-app/src/uikit/controllers/JSONStorageController.ts +7 -3
  64. package/templates/react-app/src/uikit/controllers/RequestController.ts +65 -11
  65. package/templates/react-app/src/uikit/controllers/RouterController.ts +8 -7
  66. package/templates/react-app/src/uikit/controllers/UserController.ts +48 -54
  67. package/templates/react-app/src/uikit/hooks/useLanguageGuard.ts +1 -2
  68. package/templates/react-app/src/uikit/providers/ProcessProvider.tsx +4 -4
  69. package/templates/react-app/src/uikit/styles/css/index.css +1 -1
  70. package/templates/react-app/src/uikit/styles/css/page.css +1 -1
  71. package/templates/react-app/tailwind.config.js +2 -2
  72. package/templates/react-app/tsconfig.json +0 -1
  73. package/templates/react-app/tsconfig.node.json +1 -2
  74. package/templates/react-app/vite.config.ts +21 -26
  75. package/templates/react-app/.env.local +0 -24
  76. package/templates/react-app/lib/bootstrap/Bootstrap.ts +0 -36
  77. package/templates/react-app/lib/bootstrap/BootstrapExecutorPlugin.ts +0 -20
  78. package/templates/react-app/lib/bootstrap/IOCContainerInterface.ts +0 -33
  79. package/templates/react-app/lib/bootstrap/IOCManagerInterface.ts +0 -12
  80. package/templates/react-app/lib/bootstrap/index.ts +0 -7
  81. package/templates/react-app/lib/bootstrap/plugins/InjectEnv.ts +0 -61
  82. package/templates/react-app/lib/bootstrap/plugins/InjectGlobal.ts +0 -36
  83. package/templates/react-app/lib/bootstrap/plugins/InjectIOC.ts +0 -24
  84. package/templates/react-app/lib/env-config/injectPkgConfig.ts +0 -11
  85. package/templates/react-app/lib/fe-react-controller/FeController.ts +0 -19
  86. package/templates/react-app/lib/fe-react-controller/index.ts +0 -2
  87. package/templates/react-app/lib/fe-react-controller/useController.ts +0 -71
  88. package/templates/react-app/lib/fe-react-theme/ThemeController.ts +0 -40
  89. package/templates/react-app/lib/fe-react-theme/ThemeStateGetter.ts +0 -53
  90. package/templates/react-app/lib/fe-react-theme/index.ts +0 -3
  91. package/templates/react-app/lib/fe-react-theme/type.ts +0 -21
  92. package/templates/react-app/lib/openAiApi/OpenAIAuthPlugin.ts +0 -29
  93. package/templates/react-app/lib/openAiApi/OpenAIClient.ts +0 -51
  94. package/templates/react-app/lib/openAiApi/StreamProcessor.ts +0 -81
  95. package/templates/react-app/lib/openAiApi/index.ts +0 -3
  96. package/templates/react-app/lib/request-common-plugin/index.ts +0 -169
  97. package/templates/react-app/lib/router-loader/Page.ts +0 -68
  98. package/templates/react-app/lib/tw-root10px/index.css +0 -4
  99. package/templates/react-app/src/base/apis/feApi/FeApiMockPlugin.ts +0 -44
  100. package/templates/react-app/src/base/apis/feApi/index.ts +0 -2
  101. package/templates/react-app/src/base/cases/UserToken.ts +0 -47
  102. package/templates/react-app/src/base/consts/IOCIdentifier.ts +0 -16
  103. package/templates/react-app/src/base/port/IOCFunctionInterface.ts +0 -39
  104. package/templates/react-app/src/base/port/StorageTokenInterface.ts +0 -27
  105. package/templates/react-app/src/core/AppIOCContainer.ts +0 -30
  106. package/templates/react-app/src/uikit/utils/RequestLogger.ts +0 -37
  107. package/templates/react-app/src/uikit/utils/datetime.ts +0 -30
  108. package/templates/react-app/src/uikit/utils/thread.ts +0 -3
  109. /package/templates/react-app/lib/{tw-root10px/index.js → tailwind/root10px.js} +0 -0
  110. /package/templates/react-app/lib/{fe-react-theme/tw-generator.js → tailwind/theme-generator.js} +0 -0
  111. /package/templates/react-app/src/{components → uikit/components}/Loading.tsx +0 -0
  112. /package/templates/react-app/src/{components → uikit/components}/LocaleLink.tsx +0 -0
@@ -1,36 +0,0 @@
1
- import type { ExecutorContext } from '@qlover/fe-utils';
2
- import type {
3
- BootstrapArgs,
4
- BootstrapExecutorPlugin
5
- } from '../BootstrapExecutorPlugin';
6
-
7
- export class InjectGlobal implements BootstrapExecutorPlugin {
8
- readonly pluginName = 'InjectGlobal';
9
-
10
- constructor(
11
- private sources: Record<string, unknown>,
12
- private target?: string | Record<string, unknown>
13
- ) {}
14
-
15
- onBefore(context: ExecutorContext<BootstrapArgs>): void {
16
- // if target is provided, inject globals to target
17
- if (typeof this.target === 'string') {
18
- Object.assign(context.parameters.root!, {
19
- [this.target]: Object.freeze(Object.assign({}, this.sources))
20
- });
21
- return;
22
- }
23
-
24
- const target = this.target || context.parameters.root;
25
-
26
- if (typeof target !== 'object' || target === null) {
27
- throw new Error('target must be an object');
28
- }
29
-
30
- // inject globals to root
31
- for (const key in this.sources) {
32
- const element = this.sources[key];
33
- Object.assign(target, { [key]: element });
34
- }
35
- }
36
- }
@@ -1,24 +0,0 @@
1
- import type { IOCRegisterInterface } from '../IOCContainerInterface';
2
- import type { IOCManagerInterface } from '../IOCManagerInterface';
3
- import type {
4
- BootstrapContext,
5
- BootstrapExecutorPlugin
6
- } from '../BootstrapExecutorPlugin';
7
- import { Container } from 'inversify';
8
-
9
- export class InjectIOC implements BootstrapExecutorPlugin {
10
- readonly pluginName = 'InjectIOC';
11
-
12
- constructor(
13
- private IOC: IOCManagerInterface,
14
- private registeres: IOCRegisterInterface<Container>[]
15
- ) {}
16
-
17
- onBefore(context: BootstrapContext): void {
18
- const { ioc } = context.parameters;
19
- this.IOC.implement(ioc);
20
-
21
- // maybe runtimes configure
22
- ioc.configure(this.registeres);
23
- }
24
- }
@@ -1,11 +0,0 @@
1
- export function injectPkgConfig(
2
- options: [string, string][],
3
- envPrefix: string = ''
4
- ) {
5
- options.forEach(([key, value]) => {
6
- const envKey = `${envPrefix}${key}`;
7
- if (!process.env[envKey]) {
8
- process.env[envKey] = value;
9
- }
10
- });
11
- }
@@ -1,19 +0,0 @@
1
- import { SliceStore } from '@qlover/slice-store-react';
2
-
3
- export class FeController<T> extends SliceStore<T> {
4
- constructor(private stateFactory: () => T) {
5
- super(stateFactory);
6
- }
7
-
8
- getState(): T {
9
- return this.state;
10
- }
11
-
12
- setState(state: Partial<T>): void {
13
- this.emit({ ...this.state, ...state });
14
- }
15
-
16
- reset(): void {
17
- this.emit(this.stateFactory());
18
- }
19
- }
@@ -1,2 +0,0 @@
1
- export * from './FeController';
2
- export * from './useController';
@@ -1,71 +0,0 @@
1
- import { useSliceStore } from '@qlover/slice-store-react';
2
- import { FeController } from './FeController';
3
- import { useEffect, useRef, useState } from 'react';
4
-
5
- /**
6
- * Hook for managing controller instances and their state.
7
- * Change controller to UIController
8
- * @param createController Factory function that creates a controller instance or controller instance
9
- * @returns Controller instance with precise type inference
10
- */
11
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- export function useController<C extends FeController<any>>(
13
- createController: (() => C) | C
14
- ): C {
15
- const controllerRef = useRef<C | null>(null);
16
- const [, forceUpdate] = useState(0);
17
-
18
- if (!controllerRef.current) {
19
- controllerRef.current =
20
- typeof createController === 'function'
21
- ? createController()
22
- : createController;
23
- }
24
-
25
- useEffect(() => {
26
- const controller = controllerRef.current!;
27
-
28
- const unsubscribe = controller.observe(() => {
29
- forceUpdate((prev) => prev + 1);
30
- });
31
-
32
- return () => {
33
- unsubscribe();
34
- };
35
- }, []);
36
-
37
- return controllerRef.current;
38
- }
39
-
40
- /**
41
- * Create a Controller instance and cache it to avoid repeated creation
42
- *
43
- * @see https://github.com/alibaba/hooks/blob/master/packages/hooks/src/useCreation/index.ts
44
- * @param Controller
45
- * @param args
46
- * @returns
47
- */
48
- export function useCreateController<
49
- T,
50
- Ctrl extends FeController<T>,
51
- Args extends unknown[]
52
- >(Controller: new (...args: Args) => Ctrl, ...args: Args): Ctrl {
53
- const { current } = useRef({
54
- target: undefined as undefined | Ctrl,
55
- initialized: false
56
- });
57
-
58
- if (!current.initialized) {
59
- current.target = new Controller(...args);
60
- current.initialized = true;
61
- }
62
-
63
- return current.target as Ctrl;
64
- }
65
-
66
- export function useControllerState<T, S = T>(
67
- controller: FeController<T>,
68
- selector?: (state: T) => S
69
- ): S {
70
- return useSliceStore(controller, selector);
71
- }
@@ -1,40 +0,0 @@
1
- import { FeController } from '@lib/fe-react-controller';
2
- import { ThemeControllerProps, ThemeControllerState } from './type';
3
- import { ThemeStateGetter } from './ThemeStateGetter';
4
-
5
- export class ThemeController extends FeController<ThemeControllerState> {
6
- constructor(private props: ThemeControllerProps) {
7
- super(() => ThemeStateGetter.create(props));
8
-
9
- this.bindToTheme();
10
- }
11
-
12
- getSupportedThemes(): string[] {
13
- return this.props.supportedThemes;
14
- }
15
-
16
- bindToTheme(): void {
17
- const { theme } = this.getState();
18
-
19
- const { domAttribute } = this.props;
20
-
21
- if (domAttribute) {
22
- document.documentElement.setAttribute(domAttribute, theme);
23
- }
24
- }
25
-
26
- changeTheme(theme: string): void {
27
- if (theme === ThemeStateGetter.SYSTEM_THEME) {
28
- theme = ThemeStateGetter.getSystemTheme();
29
- }
30
-
31
- this.setState({ theme });
32
-
33
- const { storage, storageKey } = this.props;
34
- if (storage && storageKey) {
35
- storage.setItem(storageKey, theme);
36
- }
37
-
38
- this.bindToTheme();
39
- }
40
- }
@@ -1,53 +0,0 @@
1
- import isString from 'lodash/isString';
2
- import { ThemeControllerProps, ThemeControllerState } from './type';
3
-
4
- export class ThemeStateGetter {
5
- static SYSTEM_THEME = 'system';
6
-
7
- static create(props: ThemeControllerProps): ThemeControllerState {
8
- const theme = ThemeStateGetter.getDefaultTheme(props);
9
-
10
- return {
11
- theme
12
- };
13
- }
14
-
15
- static getDefaultTheme(props: ThemeControllerProps): string {
16
- const { storage, storageKey, defaultTheme } = props;
17
-
18
- let theme;
19
-
20
- // Trying to use local storage
21
- if (storage && storageKey) {
22
- theme = storage.getItem(storageKey);
23
-
24
- if (isString(theme) && props.supportedThemes.includes(theme)) {
25
- return theme;
26
- }
27
- }
28
-
29
- if (theme === ThemeStateGetter.SYSTEM_THEME) {
30
- return ThemeStateGetter.getSystemTheme();
31
- }
32
-
33
- // if local storage does not have theme, use system theme
34
- if (defaultTheme) {
35
- if (defaultTheme === ThemeStateGetter.SYSTEM_THEME) {
36
- return ThemeStateGetter.getSystemTheme();
37
- }
38
-
39
- return defaultTheme;
40
- }
41
-
42
- return ThemeStateGetter.getSystemTheme();
43
- }
44
-
45
- static getSystemTheme(): 'dark' | 'light' {
46
- // use window.matchMedia to detect prefers-color-scheme media query
47
- const isDarkMode =
48
- window.matchMedia &&
49
- window.matchMedia('(prefers-color-scheme: dark)').matches;
50
-
51
- return isDarkMode ? 'dark' : 'light';
52
- }
53
- }
@@ -1,3 +0,0 @@
1
- export * from './ThemeController';
2
- export * from './ThemeStateGetter';
3
- export * from './type';
@@ -1,21 +0,0 @@
1
- import { JSONStorage } from '@qlover/fe-utils';
2
-
3
- export type ThemeControllerState = {
4
- theme: string;
5
- };
6
-
7
- export type ThemeConfig = {
8
- domAttribute: string;
9
- defaultTheme: string;
10
- target: string;
11
- supportedThemes: string[];
12
- storageKey: string;
13
- styleThemeKeyTemplate: string;
14
- styleKeyTemplate: string;
15
- colorsValueTemplate: string;
16
- colors: Record<string, unknown>;
17
- };
18
-
19
- export interface ThemeControllerProps extends ThemeConfig {
20
- storage?: JSONStorage;
21
- }
@@ -1,29 +0,0 @@
1
- import {
2
- ExecutorContext,
3
- ExecutorPlugin,
4
- RequestAdapterFetchConfig,
5
- RequestAdapterResponse
6
- } from '@qlover/fe-utils';
7
- import { StreamProcessor } from './StreamProcessor';
8
-
9
- export class OpenAIAuthPlugin
10
- implements ExecutorPlugin<RequestAdapterFetchConfig>
11
- {
12
- readonly pluginName = 'OpenAIAuthPlugin';
13
-
14
- async onSuccess(
15
- context: ExecutorContext<RequestAdapterFetchConfig>
16
- ): Promise<void> {
17
- const adapterResponse = context.returnValue as RequestAdapterResponse<
18
- Request,
19
- Response
20
- >;
21
-
22
- if (adapterResponse.data.body instanceof ReadableStream) {
23
- const processer = new StreamProcessor();
24
- const data = await processer.processStream(adapterResponse.data.body);
25
- // overried response data
26
- context.returnValue = data;
27
- }
28
- }
29
- }
@@ -1,51 +0,0 @@
1
- import {
2
- RequestScheduler,
3
- RequestAdapterConfig,
4
- RequestAdapterFetch,
5
- RequestAdapterFetchConfig,
6
- FetchURLPlugin,
7
- RequestAdapterResponse
8
- } from '@qlover/fe-utils';
9
- import { StreamResultType } from './StreamProcessor';
10
- import { OpenAIAuthPlugin } from './OpenAIAuthPlugin';
11
- import {
12
- RequestCommonPlugin,
13
- RequestCommonPluginConfig
14
- } from '@lib/request-common-plugin';
15
- export interface ApiMessage {
16
- content: string;
17
- role: 'user' | 'system' | 'assistant';
18
- }
19
-
20
- export interface OpenAIChatParmas {
21
- model?: string;
22
- messages: ApiMessage[];
23
- stream?: boolean;
24
- }
25
-
26
- export type OpenAIAargs = {
27
- commonPluginConfig: RequestCommonPluginConfig;
28
- models: string[];
29
- };
30
-
31
- export type OpenAIClientConfig = Partial<RequestAdapterFetchConfig> &
32
- OpenAIAargs;
33
-
34
- export class OpenAIClient extends RequestScheduler<RequestAdapterConfig> {
35
- constructor(config: OpenAIClientConfig) {
36
- const { commonPluginConfig, ...rest } = config;
37
- super(new RequestAdapterFetch(rest));
38
-
39
- this.usePlugin(new FetchURLPlugin());
40
- this.usePlugin(new RequestCommonPlugin(commonPluginConfig));
41
- this.usePlugin(new OpenAIAuthPlugin());
42
- }
43
-
44
- async completion(
45
- params: OpenAIChatParmas
46
- ): Promise<RequestAdapterResponse<StreamResultType>> {
47
- return this.post('/chat/completions', {
48
- data: params
49
- });
50
- }
51
- }
@@ -1,81 +0,0 @@
1
- export type StreamResultType = {
2
- content: string;
3
- };
4
-
5
- export class StreamProcessor {
6
- private decoder = new TextDecoder('utf-8');
7
- private buffer = '';
8
-
9
- async processStream(
10
- readableStream: ReadableStream<Uint8Array>
11
- ): Promise<StreamResultType> {
12
- let fullContent = '';
13
-
14
- const reader = readableStream.getReader();
15
-
16
- const shouldStop = (result: ReadableStreamReadResult<any>): boolean => {
17
- return result.done;
18
- };
19
-
20
- try {
21
- while (true) {
22
- const readResult = await reader.read();
23
- if (shouldStop(readResult)) break;
24
-
25
- const decodedChunk = this.decoder.decode(readResult.value, {
26
- stream: true
27
- });
28
-
29
- const processLineResult = await this.processChunk(decodedChunk);
30
-
31
- fullContent += processLineResult.value;
32
-
33
- if (processLineResult.done) break;
34
- }
35
- } finally {
36
- reader.releaseLock();
37
- }
38
-
39
- return {
40
- content: fullContent
41
- };
42
- }
43
-
44
- processLine(line: string) {
45
- const data = line.startsWith('data:') ? line.slice(6).trim() : '';
46
-
47
- if (data === '[DONE]') {
48
- return { done: true, value: '' };
49
- }
50
-
51
- const value = data ? JSON.parse(data).choices[0]?.delta?.content : '';
52
-
53
- return { done: false, value: value };
54
- }
55
-
56
- async processChunk(chunk: string) {
57
- let processedContent = '';
58
- this.buffer += chunk;
59
- const lines = this.buffer.split('\n');
60
- this.buffer = lines.pop() || '';
61
- for (const line of lines) {
62
- try {
63
- const lineResult = this.processLine(line);
64
-
65
- if (lineResult.done) {
66
- return { value: processedContent, done: true };
67
- }
68
-
69
- if (!lineResult.value) {
70
- continue;
71
- }
72
-
73
- processedContent += lineResult.value;
74
- } catch (error) {
75
- console.error('Parsing error:', error);
76
- this.buffer = line + '\n' + this.buffer;
77
- }
78
- }
79
- return { value: processedContent, done: false };
80
- }
81
- }
@@ -1,3 +0,0 @@
1
- export * from './OpenAIClient';
2
- export * from './OpenAIAuthPlugin';
3
- export * from './StreamProcessor';
@@ -1,169 +0,0 @@
1
- import { get, isUndefined, set } from 'lodash';
2
- import {
3
- ExecutorPlugin,
4
- RequestAdapterConfig,
5
- ExecutorContext,
6
- RequestAdapterResponse
7
- } from '@qlover/fe-utils';
8
-
9
- export type RequestCommonPluginConfig = {
10
- token?: string | (() => string | null);
11
-
12
- /**
13
- * token prefix.
14
- *
15
- * eg. `Bearer xxx`, `Token xxx`
16
- */
17
- tokenPrefix?: string;
18
-
19
- /**
20
- * @default `Authorization`
21
- */
22
- authKey?: string;
23
-
24
- /**
25
- * default request headers
26
- */
27
- defaultHeaders?: Record<string, string>;
28
-
29
- /**
30
- * Whether the token is required.
31
- *
32
- * if not token provided, throw error.
33
- *
34
- * @default `false`
35
- */
36
- requiredToken?: boolean;
37
-
38
- /**
39
- * defualt request data
40
- */
41
- defaultRequestData?: Record<string, unknown>;
42
-
43
- /**
44
- * transform request data before sending.
45
- *
46
- * @param data - request data
47
- * @param context - executor context
48
- * @returns - request data
49
- */
50
- requestDataSerializer?: (
51
- data: unknown,
52
- context: ExecutorContext<RequestAdapterConfig>
53
- ) => unknown;
54
- };
55
-
56
- /**
57
- * Represents a plugin for handling common request configurations and behaviors.
58
- *
59
- * The RequestCommonPlugin is designed to manage request headers, token handling,
60
- * and data serialization before sending requests. It ensures that the necessary
61
- * configurations are applied to each request, enhancing the flexibility and
62
- * reusability of request handling in the application.
63
- *
64
- * Main functions include:
65
- * - Appending default headers and token to requests.
66
- * - Merging default request data with provided parameters.
67
- * - Serializing request data before sending.
68
- *
69
- * @example
70
- * const plugin = new RequestCommonPlugin({
71
- * token: 'your-token',
72
- * tokenPrefix: 'Bearer',
73
- * defaultHeaders: { 'Custom-Header': 'value' },
74
- * requiredToken: true,
75
- * });
76
- */
77
- export class RequestCommonPlugin
78
- implements ExecutorPlugin<RequestAdapterConfig>
79
- {
80
- readonly pluginName = 'RequestCommonPlugin';
81
-
82
- constructor(readonly config: RequestCommonPluginConfig = {}) {
83
- if (config.requiredToken && !config.token) {
84
- throw new Error('Token is required!');
85
- }
86
- }
87
-
88
- onBefore(context: ExecutorContext<RequestAdapterConfig>): void {
89
- const {
90
- tokenPrefix,
91
- defaultHeaders,
92
- authKey = 'Authorization',
93
- defaultRequestData,
94
- requestDataSerializer
95
- } = this.config;
96
- const { parameters } = context;
97
-
98
- // append default headers
99
- parameters.headers = {
100
- ...defaultHeaders,
101
- ...parameters.headers
102
- };
103
-
104
- // append content type header
105
- if (
106
- !parameters.headers['Content-Type'] &&
107
- parameters.responseType === 'json'
108
- ) {
109
- parameters.headers['Content-Type'] = 'application/json';
110
- }
111
-
112
- // append token
113
- if (authKey && !parameters.headers[authKey]) {
114
- const authToken = this.getAuthToken();
115
- const authValue = tokenPrefix ? `${tokenPrefix} ${authToken}` : authToken;
116
- if (authValue) {
117
- parameters.headers[authKey] = authValue;
118
- }
119
- }
120
-
121
- // merge defaults request data
122
- if (defaultRequestData) {
123
- Object.entries(defaultRequestData).forEach(([key, value]) => {
124
- const prevValue = get(parameters.data, key);
125
- if (isUndefined(prevValue)) {
126
- set(parameters.data as Record<string, unknown>, key, value);
127
- }
128
- });
129
- }
130
-
131
- // serialize request data
132
- if (requestDataSerializer && parameters.data) {
133
- parameters.data = requestDataSerializer(parameters.data, context);
134
- }
135
- }
136
-
137
- async onSuccess(
138
- context: ExecutorContext<RequestAdapterConfig>
139
- ): Promise<void> {
140
- const { parameters, returnValue } = context;
141
- const response = (returnValue as RequestAdapterResponse<unknown, Response>)
142
- .data;
143
-
144
- if (response instanceof Response) {
145
- switch (parameters.responseType) {
146
- case 'json':
147
- context.returnValue = await response.json();
148
- break;
149
- case 'text':
150
- context.returnValue = await response.text();
151
- break;
152
- case 'blob':
153
- context.returnValue = await response.blob();
154
- break;
155
- // FIXME: adapter support `arraybuffer`
156
- // @ts-expect-error
157
- case 'arrayBuffer':
158
- case 'arraybuffer':
159
- context.returnValue = await response.arrayBuffer();
160
- break;
161
- }
162
- }
163
- }
164
-
165
- getAuthToken(): string {
166
- const { token } = this.config;
167
- return typeof token === 'function' ? (token() ?? '') : (token ?? '');
168
- }
169
- }
@@ -1,68 +0,0 @@
1
- import { LazyExoticComponent, PropsWithChildren } from 'react';
2
- import { RouteObject } from 'react-router-dom';
3
-
4
- import { TFunction } from 'i18next';
5
- import { UseTranslationResponse } from 'react-i18next';
6
-
7
- export interface BasePageProvider {
8
- meta: RouteMeta;
9
- i18n: UseTranslationResponse<string, string>;
10
- t: TFunction<string, string>;
11
- }
12
-
13
- export type RouteCategory = 'main' | 'auth' | 'common';
14
-
15
- export interface RouteMeta {
16
- category?: RouteCategory;
17
- /**
18
- * from app.router.json
19
- */
20
- title?: string;
21
- icon?: string;
22
- /**
23
- * from app.router.json
24
- *
25
- * @default 'common'
26
- */
27
- localNamespace?: string;
28
- }
29
-
30
- export type RouteType = RouteObject & {
31
- meta?: RouteMeta;
32
- };
33
-
34
- export type RouteConfig = {
35
- routes: RouteType[];
36
- };
37
-
38
- export type PagesMaps = Record<
39
- string,
40
- | (() => LazyExoticComponent<React.ComponentType<unknown>>)
41
- | (() => React.ComponentType<unknown>)
42
- >;
43
-
44
- export type LoadProps = {
45
- pagesMaps: PagesMaps;
46
- componentPath: string;
47
- route: RouteType;
48
- Provider?: React.ComponentType<PropsWithChildren<RouteMeta>>;
49
- };
50
-
51
- type ComponentValue =
52
- | React.ComponentType<unknown>
53
- | LazyExoticComponent<React.ComponentType<unknown>>;
54
-
55
- export type ComponentMaps = {
56
- /**
57
- * key: ./xxx/bbb.(jsx,js,tsx,ts)
58
- */
59
- [key: string]:
60
- | ComponentValue
61
- | (() => Promise<ComponentValue>)
62
- | (() => ComponentValue);
63
- };
64
-
65
- // new RouterManager({
66
- // routes: [],
67
- // componentMaps:
68
- // });