@qlover/create-app 0.11.0 → 1.0.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +3 -3
  4. package/dist/templates/next-app/eslint.config.mjs +76 -1
  5. package/dist/templates/next-app/next.config.ts +4 -3
  6. package/dist/templates/next-app/package.json +15 -11
  7. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +2 -3
  8. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +6 -4
  9. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +14 -10
  10. package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +2 -2
  11. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +1 -4
  12. package/dist/templates/next-app/src/base/services/I18nService.ts +6 -2
  13. package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +11 -7
  14. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +11 -10
  15. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +16 -12
  16. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +19 -19
  17. package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +26 -21
  18. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +15 -6
  19. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +12 -14
  20. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +34 -17
  21. package/dist/templates/next-app/src/server/NextApiServer.ts +10 -4
  22. package/dist/templates/next-app/src/server/PasswordEncrypt.ts +2 -2
  23. package/dist/templates/next-app/src/server/controllers/UserController.ts +2 -2
  24. package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -2
  25. package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +6 -7
  26. package/dist/templates/next-app/src/server/services/UserService.ts +2 -2
  27. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +11 -47
  28. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +3 -2
  29. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +6 -1
  30. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +21 -61
  31. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +29 -51
  32. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapClient.test.ts +8 -26
  33. package/dist/templates/react-app/__tests__/src/main.test.tsx +2 -2
  34. package/dist/templates/react-app/config/IOCIdentifier.ts +1 -1
  35. package/dist/templates/react-app/docs/en/test-guide.md +5 -5
  36. package/dist/templates/react-app/docs/zh/test-guide.md +5 -5
  37. package/dist/templates/react-app/eslint.config.mjs +79 -7
  38. package/dist/templates/react-app/package.json +4 -3
  39. package/dist/templates/react-app/src/base/apis/AiApi.ts +20 -12
  40. package/dist/templates/react-app/src/base/apis/feApi/FeApi.ts +14 -5
  41. package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +29 -13
  42. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +35 -34
  43. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +23 -17
  44. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +11 -5
  45. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +19 -6
  46. package/dist/templates/react-app/src/base/cases/RequestLogger.ts +11 -11
  47. package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +11 -5
  48. package/dist/templates/react-app/src/base/services/I18nService.ts +3 -0
  49. package/dist/templates/react-app/src/base/services/IdentifierService.ts +24 -0
  50. package/dist/templates/react-app/src/base/services/UserBootstrap.ts +9 -7
  51. package/dist/templates/react-app/src/base/services/UserService.ts +4 -5
  52. package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +6 -7
  53. package/dist/templates/react-app/src/main.tsx +1 -1
  54. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +5 -5
  55. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +3 -1
  56. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +6 -2
  57. package/dist/templates/react-app/src/pages/base/MessagePage.tsx +34 -3
  58. package/dist/templates/react-app/src/uikit/bridges/ExecutorPageBridge.ts +13 -7
  59. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +6 -1
  60. package/dist/templates/react-app/src/uikit/components/MessageBaseList.tsx +26 -11
  61. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatMessageBridge.ts +7 -1
  62. package/dist/templates/react-app/src/uikit/components/chatMessage/FocusBar.tsx +3 -1
  63. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageApi.ts +16 -7
  64. package/dist/templates/react-app/src/vite-env.d.ts +1 -1
  65. package/dist/templates/react-app/tsconfig.e2e.json +5 -2
  66. package/dist/templates/react-app/tsconfig.json +7 -0
  67. package/dist/templates/react-app/tsconfig.node.json +4 -2
  68. package/dist/templates/react-app/tsconfig.test.json +4 -1
  69. package/package.json +4 -3
  70. package/dist/templates/react-app/src/base/cases/AppError.ts +0 -10
  71. package/dist/templates/react-app/src/base/port/ProcesserExecutorInterface.ts +0 -20
  72. package/dist/templates/react-app/src/base/services/UserGatewayPlugin.ts +0 -20
@@ -5,6 +5,7 @@ import importPlugin from 'eslint-plugin-import';
5
5
  import prettierPlugin from 'eslint-plugin-prettier';
6
6
  import unusedImports from 'eslint-plugin-unused-imports';
7
7
  import qloverEslint from '@qlover/eslint-plugin';
8
+ import tseslint from 'typescript-eslint';
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url);
10
11
  const __dirname = dirname(__filename);
@@ -106,7 +107,7 @@ const eslintConfig = [
106
107
  rules: {
107
108
  '@qlover-eslint/ts-class-method-return': 'error',
108
109
  '@qlover-eslint/ts-class-member-accessibility': 'error',
109
- '@qlover-eslint/ts-class-override': 'error',
110
+ '@qlover-eslint/ts-class-override': 'off',
110
111
  '@qlover-eslint/require-root-testid': ['error', {
111
112
  exclude: ['/Provider$/']
112
113
  }],
@@ -188,6 +189,80 @@ const eslintConfig = [
188
189
  'import/no-default-export': 'error',
189
190
  }
190
191
  },
192
+ // TypeScript files with type checking for ts-class-override rule
193
+ // The ts-class-override rule requires full type information to accurately detect:
194
+ // - Methods that override parent class methods (via extends)
195
+ // - Methods that implement interface methods (via implements)
196
+ // Without type checking, the rule falls back to AST-based heuristics which are less accurate
197
+ // This separate config block enables type checking only for TypeScript files to provide
198
+ // accurate override detection while maintaining good performance
199
+ ...tseslint.configs.recommendedTypeChecked.map((config) => ({
200
+ ...config,
201
+ files: ['src/**/*.{ts,tsx}'],
202
+ ignores: [
203
+ '**/dist/**',
204
+ '**/build/**',
205
+ '**/ts-build/**',
206
+ '**/node_modules/**',
207
+ '**/.nx/**',
208
+ '**/.cache/**',
209
+ '**/coverage/**',
210
+ '**/*.d.ts',
211
+ '**/*.config.ts',
212
+ '**/*.test.ts',
213
+ '**/__mocks__/**',
214
+ '**/__tests__/**',
215
+ '**/*.spec.ts',
216
+ ...(config.ignores || [])
217
+ ],
218
+ languageOptions: {
219
+ ...config.languageOptions,
220
+ parserOptions: {
221
+ ...config.languageOptions?.parserOptions,
222
+ project: './tsconfig.json',
223
+ tsconfigRootDir: __dirname
224
+ }
225
+ },
226
+ plugins: {
227
+ ...config.plugins,
228
+ '@qlover-eslint': qloverEslint
229
+ },
230
+ rules: {
231
+ ...config.rules,
232
+ // Enable ts-class-override rule with full type information
233
+ // This rule is disabled in the base config above and only enabled here where
234
+ // type information is available, ensuring accurate detection of override relationships
235
+ '@qlover-eslint/ts-class-override': 'error',
236
+ // Disable other type-checked rules to avoid performance impact
237
+ // We only need type checking for ts-class-override, so we disable other
238
+ // type-aware rules that would slow down linting without providing value
239
+ '@typescript-eslint/ban-ts-comment': 'off',
240
+ '@typescript-eslint/restrict-template-expressions': 'off',
241
+ '@typescript-eslint/no-unsafe-assignment': 'off',
242
+ '@typescript-eslint/no-unnecessary-type-assertion': 'off',
243
+ '@typescript-eslint/no-redundant-type-constituents': 'off',
244
+ '@typescript-eslint/no-unsafe-return': 'off',
245
+ '@typescript-eslint/no-empty-object-type': 'off',
246
+ '@typescript-eslint/no-unsafe-call': 'off',
247
+ '@typescript-eslint/no-unsafe-member-access': 'off',
248
+ '@typescript-eslint/no-unsafe-argument': 'off',
249
+ '@typescript-eslint/no-unsafe-enum-comparison': 'off',
250
+ '@typescript-eslint/no-unsafe-literal-comparison': 'off',
251
+ '@typescript-eslint/no-unsafe-nullish-coalescing': 'off',
252
+ '@typescript-eslint/no-unsafe-optional-chaining': 'off',
253
+ '@typescript-eslint/unbound-method': 'off',
254
+ '@typescript-eslint/await-thenable': 'off',
255
+ '@typescript-eslint/no-floating-promises': 'off',
256
+ '@typescript-eslint/no-misused-promises': 'off',
257
+ '@typescript-eslint/require-await': 'off',
258
+ '@typescript-eslint/no-base-to-string': 'off',
259
+ '@typescript-eslint/prefer-promise-reject-errors': 'off',
260
+ '@typescript-eslint/no-duplicate-type-constituents': 'off',
261
+ // Disable @typescript-eslint/no-unused-vars as we use unused-imports/no-unused-vars instead
262
+ '@typescript-eslint/no-unused-vars': 'off',
263
+ '@typescript-eslint/only-throw-error': 'off'
264
+ }
265
+ })),
191
266
  // 为特定文件允许 default export
192
267
  {
193
268
  files: [
@@ -11,9 +11,10 @@ generateLocales().catch((error) => {
11
11
 
12
12
  const nextConfig: NextConfig = {
13
13
  // reactStrictMode: false,
14
- turbopack: {
15
- root: __dirname // 明确指定根目录
16
- },
14
+ // turbopack 在接下本地 file: 依赖时支持还不够好
15
+ // turbopack: {
16
+ // root: __dirname // 明确指定根目录
17
+ // },
17
18
  transpilePackages: ['@qlover/corekit-bridge', '@qlover/fe-corekit'],
18
19
  env: {
19
20
  APP_ENV: process.env.APP_ENV
@@ -3,18 +3,20 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "dev": "cross-env APP_ENV=localhost next dev --turbopack --port 3100",
7
- "dev:staging": "cross-env APP_ENV=staging next dev --turbopack --port 3100",
8
- "dev:prod": "cross-env APP_ENV=production next dev --turbopack --port 3100",
9
- "build": "cross-env APP_ENV=localhost next build --turbopack",
10
- "build:staging": "cross-env APP_ENV=staging next build --turbopack",
11
- "build:prod": "cross-env APP_ENV=production next build --turbopack",
6
+ "dev": "cross-env APP_ENV=localhost next dev --port 3100",
7
+ "dev:staging": "cross-env APP_ENV=staging next dev --port 3100",
8
+ "dev:prod": "cross-env APP_ENV=production next dev --port 3100",
9
+ "dev:turbo": "cross-env APP_ENV=localhost next dev --turbopack --port 3100",
10
+ "build": "cross-env APP_ENV=localhost next build",
11
+ "build:staging": "cross-env APP_ENV=staging next build",
12
+ "build:prod": "cross-env APP_ENV=production next build",
12
13
  "start": "next start --port 3101",
13
- "lint": "tsc --noEmit && eslint ./src ./config ./make ./migrations",
14
- "lint:fix": "tsc --noEmit && eslint ./src ./config ./make ./migrations --ext .ts,.tsx --fix",
14
+ "lint": "npm run type-check && eslint ./src ./config ./make ./migrations",
15
+ "lint:fix": "npm run type-check && eslint ./src ./config ./make ./migrations --ext .ts,.tsx --fix",
15
16
  "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
16
17
  "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
17
- "fix": "npm run lint:fix && npm run format"
18
+ "fix": "npm run lint:fix && npm run format",
19
+ "type-check": "tsc --build --force"
18
20
  },
19
21
  "dependencies": {
20
22
  "@ant-design/icons": "^6.0.0",
@@ -24,8 +26,9 @@
24
26
  "@brain-toolkit/antd-theme-override": "^0.0.3",
25
27
  "@brain-toolkit/bridge": "^0.0.1",
26
28
  "@brain-toolkit/react-kit": "^0.1.0",
27
- "@qlover/corekit-bridge": "latest",
28
- "@qlover/fe-corekit": "latest",
29
+ "@qlover/corekit-bridge": "../../../corekit-bridge",
30
+ "@qlover/fe-corekit": "../../../fe-corekit",
31
+ "@qlover/logger": "../../../logger",
29
32
  "@qlover/slice-store-react": "^1.4.1",
30
33
  "@supabase/postgrest-js": "^2.87.1",
31
34
  "@supabase/ssr": "^0.7.0",
@@ -69,6 +72,7 @@
69
72
  "eslint-plugin-prettier": "^5.5.4",
70
73
  "eslint-plugin-unused-imports": "^4.2.0",
71
74
  "prettier": "^3.6.2",
75
+ "typescript-eslint": "^8.15.0",
72
76
  "tailwindcss": "^4",
73
77
  "typescript": "^5"
74
78
  },
@@ -19,7 +19,6 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
19
19
  const { tt } = props;
20
20
  const t = useWarnTranslations();
21
21
  const userService = useIOC(I.UserServiceInterface);
22
- const logger = useIOC(I.Logger);
23
22
  const appConfig = useIOC(I.AppConfig);
24
23
  const routerService = useIOC(I.RouterServiceInterface);
25
24
  const [loading, setLoading] = useState(false);
@@ -29,8 +28,8 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
29
28
  setLoading(true);
30
29
  await userService.login(values);
31
30
  routerService.replaceHome();
32
- } catch (error) {
33
- logger.error(error);
31
+ } catch {
32
+ // do nothing
34
33
  } finally {
35
34
  setLoading(false);
36
35
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
+ ExecutorContextInterface,
2
3
  ExecutorError,
3
- type ExecutorContext,
4
- type ExecutorPlugin
4
+ LifecyclePluginInterface
5
5
  } from '@qlover/fe-corekit';
6
6
  import { inject, injectable } from 'inversify';
7
7
  import { i18nKeySchema } from '@config/i18n/i18nKeyScheam';
@@ -13,7 +13,9 @@ import type { I18nServiceInterface } from '../port/I18nServiceInterface';
13
13
  import type { UIDialogInterface } from '@qlover/corekit-bridge';
14
14
 
15
15
  @injectable()
16
- export class DialogErrorPlugin implements ExecutorPlugin {
16
+ export class DialogErrorPlugin
17
+ implements LifecyclePluginInterface<ExecutorContextInterface<unknown>>
18
+ {
17
19
  public readonly pluginName = 'DialogErrorPlugin';
18
20
 
19
21
  constructor(
@@ -27,7 +29,7 @@ export class DialogErrorPlugin implements ExecutorPlugin {
27
29
  /**
28
30
  * @override
29
31
  */
30
- public onError(context: ExecutorContext<unknown>): void | Promise<void> {
32
+ public onError(context: ExecutorContextInterface<unknown>): void {
31
33
  const { error, hooksRuntimes } = context;
32
34
  const runtimesError = hooksRuntimes.returnValue;
33
35
 
@@ -1,14 +1,13 @@
1
1
  import { clone, isObject } from 'lodash';
2
2
  import type {
3
- Encryptor,
4
- ExecutorContext,
5
- ExecutorPlugin,
6
- RequestAdapterConfig
3
+ ExecutorContextInterface,
4
+ LifecyclePluginInterface,
5
+ RequestAdapterConfig,
6
+ EncryptorInterface
7
7
  } from '@qlover/fe-corekit';
8
8
 
9
- export interface RequestEncryptPluginProps<
10
- Request = unknown
11
- > extends RequestAdapterConfig<Request> {
9
+ export interface RequestEncryptPluginProps<Request = unknown>
10
+ extends RequestAdapterConfig<Request> {
12
11
  /**
13
12
  * 加密密码在 HTTP 请求中
14
13
  *
@@ -19,16 +18,21 @@ export interface RequestEncryptPluginProps<
19
18
  encryptProps?: string[] | string;
20
19
  }
21
20
 
22
- export class RequestEncryptPlugin implements ExecutorPlugin<RequestEncryptPluginProps> {
21
+ export class RequestEncryptPlugin
22
+ implements
23
+ LifecyclePluginInterface<
24
+ ExecutorContextInterface<RequestEncryptPluginProps>
25
+ >
26
+ {
23
27
  public readonly pluginName = 'RequestEncryptPlugin';
24
28
 
25
- constructor(protected encryptor: Encryptor<string, string>) {}
29
+ constructor(protected encryptor: EncryptorInterface<string, string>) {}
26
30
 
27
31
  /**
28
32
  * @override
29
33
  */
30
34
  public onBefore(
31
- context: ExecutorContext<RequestEncryptPluginProps>
35
+ context: ExecutorContextInterface<RequestEncryptPluginProps>
32
36
  ): void | Promise<void> {
33
37
  const { responseType, encryptProps } = context.parameters;
34
38
 
@@ -1,10 +1,10 @@
1
- import { Base64Serializer, type Encryptor } from '@qlover/fe-corekit';
1
+ import { Base64Serializer, type EncryptorInterface } from '@qlover/fe-corekit';
2
2
  import { inject, injectable } from 'inversify';
3
3
  import { I } from '@config/IOCIdentifier';
4
4
  import type { AppConfig } from './AppConfig';
5
5
 
6
6
  @injectable()
7
- export class StringEncryptor implements Encryptor<string, string> {
7
+ export class StringEncryptor implements EncryptorInterface<string, string> {
8
8
  private readonly key;
9
9
 
10
10
  constructor(
@@ -13,10 +13,7 @@ export class I18nServiceState implements StoreStateInterface {
13
13
  public loading: boolean = false;
14
14
  constructor(public language: I18nServiceLocale) {}
15
15
  }
16
- export abstract class I18nServiceInterface
17
- extends StoreInterface<I18nServiceState>
18
- implements I18nServiceInterface
19
- {
16
+ export abstract class I18nServiceInterface extends StoreInterface<I18nServiceState> {
20
17
  /**
21
18
  * @override
22
19
  */
@@ -5,10 +5,14 @@ import {
5
5
  } from '../port/I18nServiceInterface';
6
6
  import type { I18nServiceLocale } from '../port/I18nServiceInterface';
7
7
  import type { useTranslations } from 'next-intl';
8
+ import { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
8
9
 
9
10
  type TranslationFunction = ReturnType<typeof useTranslations>;
10
11
 
11
- export class I18nService extends I18nServiceInterface {
12
+ export class I18nService
13
+ extends I18nServiceInterface
14
+ implements BootstrapExecutorPlugin
15
+ {
12
16
  public readonly pluginName = 'I18nService';
13
17
  protected pathname: string = '';
14
18
  protected translator: TranslationFunction | null = null;
@@ -34,7 +38,7 @@ export class I18nService extends I18nServiceInterface {
34
38
  /**
35
39
  * @override
36
40
  */
37
- public async onBefore(): Promise<void> {}
41
+ public onBefore(): void {}
38
42
 
39
43
  public override async changeLanguage(
40
44
  language: I18nServiceLocale
@@ -1,16 +1,20 @@
1
1
  import {
2
- FetchAbortPlugin,
2
+ ExecutorContextInterface,
3
3
  RequestAdapterFetch,
4
- RequestTransaction
4
+ RequestExecutor
5
5
  } from '@qlover/fe-corekit';
6
- import { inject, injectable } from 'inversify';
6
+ import { injectable } from 'inversify';
7
7
  import type { AppApiConfig } from '../appApi/AppApiRequester';
8
8
 
9
+ export interface AdminApiRequesterContext
10
+ extends ExecutorContextInterface<AppApiConfig> {}
11
+
9
12
  @injectable()
10
- export class AdminApiRequester extends RequestTransaction<AppApiConfig> {
11
- constructor(
12
- @inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin
13
- ) {
13
+ export class AdminApiRequester extends RequestExecutor<
14
+ AppApiConfig,
15
+ AdminApiRequesterContext
16
+ > {
17
+ constructor() {
14
18
  super(
15
19
  new RequestAdapterFetch({
16
20
  baseURL: '/api/admin',
@@ -8,7 +8,8 @@ import {
8
8
  type AppApiTransaction
9
9
  } from '../appApi/AppApiRequester';
10
10
  import type { ResourceInterface, ResourceQuery } from '@qlover/corekit-bridge';
11
- import type { RequestTransaction } from '@qlover/fe-corekit';
11
+ import { RequestExecutor } from '@qlover/fe-corekit';
12
+ import { AdminApiRequesterContext } from './AdminApiRequester';
12
13
 
13
14
  export type AdminLocalesListTransaction = AppApiTransaction<
14
15
  ResourceQuery,
@@ -24,17 +25,17 @@ export type AdminLocalesUpdateTransaction = AppApiTransaction<
24
25
  export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
25
26
  constructor(
26
27
  @inject(AppApiRequester)
27
- protected client: RequestTransaction<AppApiConfig>
28
+ protected client: RequestExecutor<AppApiConfig, AdminApiRequesterContext>
28
29
  ) {}
29
30
 
30
31
  /**
31
32
  * @override
32
33
  */
33
34
  public create(data: LocalesSchema): Promise<unknown> {
34
- return this.client.request<AdminLocalesListTransaction>({
35
+ return this.client.request({
35
36
  url: '/admin/locales/create',
36
37
  method: 'POST',
37
- data: data as unknown as Record<string, unknown>
38
+ data: data
38
39
  });
39
40
  }
40
41
 
@@ -42,10 +43,10 @@ export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
42
43
  * @override
43
44
  */
44
45
  public remove(data: Partial<LocalesSchema>): Promise<unknown> {
45
- return this.client.request<AdminLocalesListTransaction>({
46
+ return this.client.request({
46
47
  url: '/admin/locales',
47
48
  method: 'DELETE',
48
- data: data as unknown as Record<string, unknown>
49
+ data
49
50
  });
50
51
  }
51
52
 
@@ -55,7 +56,7 @@ export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
55
56
  public search(
56
57
  params: ResourceQuery
57
58
  ): Promise<AdminLocalesListTransaction['response']> {
58
- return this.client.request<AdminLocalesListTransaction>({
59
+ return this.client.request({
59
60
  url: '/admin/locales',
60
61
  method: 'GET',
61
62
  params: params as unknown as Record<string, unknown>
@@ -66,10 +67,10 @@ export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
66
67
  * @override
67
68
  */
68
69
  public export(data: LocalesSchema): Promise<unknown> {
69
- return this.client.request<AdminLocalesListTransaction>({
70
+ return this.client.request({
70
71
  url: '/admin/locales',
71
72
  method: 'GET',
72
- data: data as unknown as Record<string, unknown>
73
+ data
73
74
  });
74
75
  }
75
76
 
@@ -79,7 +80,7 @@ export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
79
80
  public update(
80
81
  data: Partial<LocalesSchema>
81
82
  ): Promise<AdminLocalesUpdateTransaction['response']> {
82
- return this.client.request<AdminLocalesUpdateTransaction>({
83
+ return this.client.request({
83
84
  url: `/admin/locales/update`,
84
85
  method: 'POST',
85
86
  data
@@ -7,7 +7,8 @@ import {
7
7
  type AppApiTransaction
8
8
  } from '../appApi/AppApiRequester';
9
9
  import type { ResourceInterface, ResourceQuery } from '@qlover/corekit-bridge';
10
- import type { RequestTransaction } from '@qlover/fe-corekit';
10
+ import { RequestExecutor } from '@qlover/fe-corekit';
11
+ import { AdminApiRequesterContext } from './AdminApiRequester';
11
12
 
12
13
  export type AdminUserListTransaction = AppApiTransaction<
13
14
  ResourceQuery,
@@ -18,7 +19,7 @@ export type AdminUserListTransaction = AppApiTransaction<
18
19
  export class AdminUserApi implements ResourceInterface<UserSchema> {
19
20
  constructor(
20
21
  @inject(AppApiRequester)
21
- protected client: RequestTransaction<AppApiConfig>
22
+ protected client: RequestExecutor<AppApiConfig, AdminApiRequesterContext>
22
23
  ) {}
23
24
 
24
25
  /**
@@ -27,10 +28,13 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
27
28
  public async search(
28
29
  params: AdminUserListTransaction['request']
29
30
  ): Promise<AdminUserListTransaction['response']> {
30
- const response = await this.client.request<AdminUserListTransaction>({
31
+ const response = await this.client.request<
32
+ AdminUserListTransaction['response'],
33
+ AdminUserListTransaction['request']
34
+ >({
31
35
  url: '/admin/users',
32
36
  method: 'GET',
33
- params: params as unknown as Record<string, unknown>
37
+ params
34
38
  });
35
39
 
36
40
  return response;
@@ -40,10 +44,10 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
40
44
  * @override
41
45
  */
42
46
  public create(data: UserSchema): Promise<unknown> {
43
- return this.client.request<AdminUserListTransaction>({
47
+ return this.client.request({
44
48
  url: '/admin/users',
45
49
  method: 'POST',
46
- data: data as unknown as Record<string, unknown>
50
+ data
47
51
  });
48
52
  }
49
53
 
@@ -51,10 +55,10 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
51
55
  * @override
52
56
  */
53
57
  public remove(data: UserSchema): Promise<unknown> {
54
- return this.client.request<AdminUserListTransaction>({
58
+ return this.client.request({
55
59
  url: '/admin/users',
56
60
  method: 'DELETE',
57
- data: data as unknown as Record<string, unknown>
61
+ data
58
62
  });
59
63
  }
60
64
 
@@ -62,10 +66,10 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
62
66
  * @override
63
67
  */
64
68
  public update(data: UserSchema): Promise<unknown> {
65
- return this.client.request<AdminUserListTransaction>({
69
+ return this.client.request({
66
70
  url: '/admin/users',
67
71
  method: 'PUT',
68
- data: data as unknown as Record<string, unknown>
72
+ data
69
73
  });
70
74
  }
71
75
 
@@ -73,10 +77,10 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
73
77
  * @override
74
78
  */
75
79
  public export(data: UserSchema): Promise<unknown> {
76
- return this.client.request<AdminUserListTransaction>({
80
+ return this.client.request({
77
81
  url: '/admin/users',
78
82
  method: 'GET',
79
- data: data as unknown as Record<string, unknown>
83
+ data
80
84
  });
81
85
  }
82
86
  }
@@ -1,14 +1,16 @@
1
1
  import {
2
+ ExecutorContextInterface,
2
3
  ExecutorError,
3
- RequestError,
4
- type ExecutorContext,
5
- type ExecutorPlugin
4
+ isRequestAdapterResponse,
5
+ LifecyclePluginInterface
6
6
  } from '@qlover/fe-corekit';
7
7
  import type { AppApiErrorInterface } from '@/base/port/AppApiInterface';
8
8
  import type { AppApiConfig } from './AppApiRequester';
9
9
  import type { LoggerInterface } from '@qlover/logger';
10
10
 
11
- export class AppApiPlugin implements ExecutorPlugin {
11
+ export class AppApiPlugin implements LifecyclePluginInterface<
12
+ ExecutorContextInterface<AppApiConfig>
13
+ > {
12
14
  public readonly pluginName = 'AppApiPlugin';
13
15
 
14
16
  constructor(protected logger: LoggerInterface) {}
@@ -30,48 +32,46 @@ export class AppApiPlugin implements ExecutorPlugin {
30
32
  /**
31
33
  * @override
32
34
  */
33
- public onSuccess(
34
- context: ExecutorContext<AppApiConfig>
35
- ): void | Promise<void> {
35
+ public onSuccess(context: ExecutorContextInterface<AppApiConfig>): void {
36
36
  const response = context.returnValue;
37
37
  const { parameters } = context;
38
38
 
39
+ // Important: 当响应数据失败则抛出错误
40
+ if (isRequestAdapterResponse(response)) {
41
+ if (this.isAppApiErrorInterface(response.data)) {
42
+ throw new ExecutorError(response.data.message || response.data.id, response);
43
+ }
44
+ }
45
+
39
46
  this.logger.info(
40
47
  `%c[AppApi ${parameters.method} ${parameters.url}]%c - ${new Date().toLocaleString()}`,
41
48
  'color: #0f0;',
42
49
  'color: inherit;',
43
50
  response
44
51
  );
45
-
46
- if (this.isAppApiErrorInterface(response)) {
47
- throw new Error(response.message || response.id);
48
- }
49
52
  }
50
53
 
51
54
  /**
52
55
  * @override
53
56
  */
54
57
  public async onError(
55
- context: ExecutorContext<AppApiConfig>
58
+ context: ExecutorContextInterface<AppApiConfig>
56
59
  ): Promise<ExecutorError | void> {
57
60
  const { error, parameters } = context;
58
61
 
59
62
  this.loggerError(parameters, error);
60
63
 
61
- if (error instanceof RequestError && parameters.responseType === 'json') {
62
- // @ts-expect-error response is not defined in Error
63
- let response = error?.response;
64
+ if (error instanceof Error && parameters.responseType === 'json') {
65
+ let response = error?.cause;
64
66
 
65
67
  if (response instanceof Response) {
66
68
  // clone the response to avoid mutating the original response
67
69
  response = response.clone();
68
70
 
69
- const json = await this.getResponseJson(response);
71
+ const json = await this.getResponseJson(response as Response);
70
72
 
71
73
  if (this.isAppApiErrorInterface(json)) {
72
- const newError = new ExecutorError(json.id, json.message);
73
- // context.error = newError;
74
- return newError;
74
+ return new ExecutorError(json.id, json);
75
75
  }
76
76
  }
77
77
  }
@@ -1,19 +1,24 @@
1
- import {
2
- FetchAbortPlugin,
3
- RequestAdapterFetch,
4
- RequestTransaction
5
- } from '@qlover/fe-corekit';
6
- import { inject, injectable } from 'inversify';
1
+ import { LifecycleExecutor, RequestAdapterFetch, RequestExecutor } from '@qlover/fe-corekit';
2
+ import { injectable } from 'inversify';
7
3
  import type { RequestEncryptPluginProps } from '@/base/cases/RequestEncryptPlugin';
8
4
  import type { AppApiResult } from '@/base/port/AppApiInterface';
9
5
  import type {
6
+ ExecutorContextInterface,
10
7
  RequestAdapterConfig,
11
- RequestAdapterResponse,
12
- RequestTransactionInterface
8
+ RequestAdapterResponse
13
9
  } from '@qlover/fe-corekit';
14
10
 
11
+ export interface RequestTransactionInterface<Request, Response> {
12
+ request: Request;
13
+ response: Response;
14
+ }
15
+
15
16
  export interface AppApiConfig<Request = unknown>
16
- extends RequestAdapterConfig<Request>, RequestEncryptPluginProps<Request> {}
17
+ extends RequestAdapterConfig<Request>,
18
+ RequestEncryptPluginProps<Request> {}
19
+
20
+ export interface AppApiRequesterContext
21
+ extends ExecutorContextInterface<AppApiConfig> {}
17
22
 
18
23
  /**
19
24
  * UserApiResponse
@@ -32,26 +37,26 @@ export type AppApiResponse<
32
37
  /**
33
38
  * UserApi common transaction
34
39
  */
35
- export interface AppApiTransaction<
36
- Request = unknown,
37
- Response = unknown
38
- > extends RequestTransactionInterface<
39
- AppApiConfig<Request>,
40
- AppApiResponse<Request, Response>
41
- > {
40
+ export interface AppApiTransaction<Request = unknown, Response = unknown>
41
+ extends RequestTransactionInterface<
42
+ AppApiConfig<Request>,
43
+ AppApiResponse<Request, Response>
44
+ > {
42
45
  data: AppApiConfig<Request>['data'];
43
46
  }
44
47
 
45
48
  @injectable()
46
- export class AppApiRequester extends RequestTransaction<AppApiConfig> {
47
- constructor(
48
- @inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin
49
- ) {
49
+ export class AppApiRequester extends RequestExecutor<
50
+ AppApiConfig,
51
+ AppApiRequesterContext
52
+ > {
53
+ constructor() {
50
54
  super(
51
55
  new RequestAdapterFetch({
52
56
  baseURL: '/api',
53
57
  responseType: 'json'
54
- })
58
+ }),
59
+ new LifecycleExecutor()
55
60
  );
56
61
  }
57
62
  }