@qlover/create-app 0.9.0 → 0.10.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/.env.template +9 -10
  5. package/dist/templates/next-app/eslint.config.mjs +5 -0
  6. package/dist/templates/next-app/migrations/schema/UserSchema.ts +17 -3
  7. package/dist/templates/next-app/next.config.ts +1 -1
  8. package/dist/templates/next-app/package.json +2 -3
  9. package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
  10. package/dist/templates/next-app/src/app/[locale]/page.tsx +1 -1
  11. package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
  12. package/dist/templates/next-app/src/app/api/locales/json/route.ts +2 -1
  13. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +1 -2
  14. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +4 -5
  15. package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +44 -29
  16. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +1 -2
  17. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +22 -10
  18. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +17 -9
  19. package/dist/templates/next-app/src/base/services/ResourceService.ts +3 -4
  20. package/dist/templates/next-app/src/base/services/UserService.ts +37 -13
  21. package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +8 -7
  22. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +15 -26
  23. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +4 -3
  24. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +4 -3
  25. package/dist/templates/next-app/src/core/globals.ts +2 -1
  26. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +4 -3
  27. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +5 -6
  28. package/dist/templates/next-app/src/i18n/request.ts +2 -2
  29. package/dist/templates/next-app/src/server/UserCredentialToken.ts +1 -3
  30. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +1 -2
  31. package/dist/templates/react-app/__tests__/__mocks__/{MockAppConfit.ts → MockAppConfig.ts} +1 -1
  32. package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +10 -17
  33. package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +27 -8
  34. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +1 -1
  35. package/dist/templates/react-app/__tests__/__mocks__/i18nextHttpBackend.ts +110 -0
  36. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +3 -2
  37. package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +13 -0
  38. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +3 -1
  39. package/dist/templates/react-app/config/IOCIdentifier.ts +9 -6
  40. package/dist/templates/react-app/config/common.ts +38 -0
  41. package/dist/templates/react-app/config/feapi.mock.json +5 -12
  42. package/dist/templates/react-app/eslint.config.mjs +6 -5
  43. package/dist/templates/react-app/package.json +1 -1
  44. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +22 -13
  45. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +3 -3
  46. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +17 -12
  47. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +19 -2
  48. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +2 -4
  49. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +15 -9
  50. package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +55 -0
  51. package/dist/templates/react-app/src/base/services/I18nService.ts +1 -0
  52. package/dist/templates/react-app/src/base/services/UserBootstrap.ts +43 -0
  53. package/dist/templates/react-app/src/base/services/UserGatewayPlugin.ts +16 -0
  54. package/dist/templates/react-app/src/base/services/UserService.ts +51 -80
  55. package/dist/templates/react-app/src/core/bootstraps/BootstrapClient.ts +8 -3
  56. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +6 -6
  57. package/dist/templates/react-app/src/core/bootstraps/SaveAppInfo.ts +28 -0
  58. package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +24 -18
  59. package/dist/templates/react-app/src/core/globals.ts +10 -11
  60. package/dist/templates/react-app/src/main.tsx +1 -1
  61. package/dist/templates/react-app/src/pages/auth/Layout.tsx +4 -4
  62. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +1 -1
  63. package/dist/templates/react-app/src/pages/base/Layout.tsx +3 -3
  64. package/dist/templates/react-app/src/uikit/components/BaseLayoutProvider.tsx +44 -0
  65. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +1 -3
  66. package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +9 -0
  67. package/dist/templates/react-app/src/uikit/hooks/{useI18nGuard.ts → useRouterI18nGuard.ts} +7 -4
  68. package/dist/templates/react-app/tsconfig.app.json +1 -1
  69. package/package.json +3 -3
  70. package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +0 -102
  71. package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +0 -61
  72. package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +0 -57
  73. package/dist/templates/react-app/src/uikit/components/ProcessExecutorProvider.tsx +0 -28
  74. package/dist/templates/react-app/src/uikit/components/UserAuthProvider.tsx +0 -16
@@ -1,25 +1,14 @@
1
1
  import { inject, injectable } from 'inversify';
2
- import type { AppApiResult } from '@/base/port/AppApiInterface';
3
- import type { AppUserApiInterface } from '@/base/port/AppUserApiInterface';
2
+ import type {
3
+ AppUserApiInterface,
4
+ UserApiLoginTransaction,
5
+ UserApiLogoutTransaction,
6
+ UserApiRegisterTransaction
7
+ } from '@/base/port/AppUserApiInterface';
4
8
  import { AppApiRequester } from './AppApiRequester';
5
- import type { AppApiConfig, AppApiTransaction } from './AppApiRequester';
9
+ import type { AppApiConfig } from './AppApiRequester';
6
10
  import type { RequestTransaction } from '@qlover/fe-corekit';
7
11
 
8
- export type UserApiLoginTransaction = AppApiTransaction<
9
- { email: string; password: string },
10
- {
11
- token: string;
12
- }
13
- >;
14
-
15
- export type UserApiRegisterTransaction = AppApiTransaction<
16
- {
17
- email: string;
18
- password: string;
19
- },
20
- AppApiTransaction['response']['data']
21
- >;
22
-
23
12
  /**
24
13
  * UserApi
25
14
  *
@@ -36,7 +25,7 @@ export class AppUserApi implements AppUserApiInterface {
36
25
 
37
26
  async login(
38
27
  params: UserApiLoginTransaction['data']
39
- ): Promise<AppApiResult<unknown>> {
28
+ ): Promise<UserApiLoginTransaction['response']> {
40
29
  const response = await this.client.request<UserApiLoginTransaction>({
41
30
  url: '/user/login',
42
31
  method: 'POST',
@@ -44,12 +33,12 @@ export class AppUserApi implements AppUserApiInterface {
44
33
  encryptProps: 'password'
45
34
  });
46
35
 
47
- return response.data;
36
+ return response;
48
37
  }
49
38
 
50
39
  async register(
51
40
  params: UserApiRegisterTransaction['data']
52
- ): Promise<AppApiResult<unknown>> {
41
+ ): Promise<UserApiRegisterTransaction['response']> {
53
42
  const response = await this.client.request<UserApiRegisterTransaction>({
54
43
  url: '/user/register',
55
44
  method: 'POST',
@@ -57,15 +46,15 @@ export class AppUserApi implements AppUserApiInterface {
57
46
  encryptProps: 'password'
58
47
  });
59
48
 
60
- return response.data;
49
+ return response;
61
50
  }
62
51
 
63
- async logout(): Promise<AppApiResult<unknown>> {
64
- const response = await this.client.request({
52
+ async logout(
53
+ _params?: unknown
54
+ ): Promise<UserApiLogoutTransaction['response']> {
55
+ return await this.client.request<UserApiLogoutTransaction>({
65
56
  url: '/user/logout',
66
57
  method: 'POST'
67
58
  });
68
-
69
- return response.data as AppApiResult<unknown>;
70
59
  }
71
60
  }
@@ -9,9 +9,10 @@ import type { IOCIdentifierMap } from '@config/IOCIdentifier';
9
9
  import { ClientIOCRegister } from './ClientIOCRegister';
10
10
  import { appConfig } from '../globals';
11
11
 
12
- export class ClientIOC
13
- implements IOCInterface<IOCIdentifierMap, IOCContainerInterface>
14
- {
12
+ export class ClientIOC implements IOCInterface<
13
+ IOCIdentifierMap,
14
+ IOCContainerInterface
15
+ > {
15
16
  protected ioc: IOCFunctionInterface<
16
17
  IOCIdentifierMap,
17
18
  IOCContainerInterface
@@ -10,9 +10,10 @@ import type {
10
10
  IOCRegisterInterface
11
11
  } from '@qlover/corekit-bridge';
12
12
 
13
- export class ClientIOCRegister
14
- implements IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>
15
- {
13
+ export class ClientIOCRegister implements IOCRegisterInterface<
14
+ IOCContainerInterface,
15
+ IocRegisterOptions
16
+ > {
16
17
  constructor(protected options: IocRegisterOptions) {}
17
18
 
18
19
  /**
@@ -1,8 +1,9 @@
1
1
  import 'reflect-metadata';
2
2
 
3
3
  // ! global variables, don't import any dependencies and don't have side effects
4
- import { ColorFormatter, ConsoleHandler, Logger } from '@qlover/corekit-bridge';
4
+ import { ColorFormatter } from '@qlover/corekit-bridge';
5
5
  import { JSONSerializer } from '@qlover/fe-corekit';
6
+ import { Logger, ConsoleHandler } from '@qlover/logger';
6
7
  import { AppConfig } from '@/base/cases/AppConfig';
7
8
  import { DialogHandler } from '@/base/cases/DialogHandler';
8
9
  import { loggerStyles } from '@config/common';
@@ -9,9 +9,10 @@ import type { IOCInterface } from '@/base/port/IOCInterface';
9
9
  import type { IOCIdentifierMapServer } from '@config/IOCIdentifier';
10
10
  import { ServerIOCRegister } from './ServerIOCRegister';
11
11
 
12
- export class ServerIOC
13
- implements IOCInterface<IOCIdentifierMapServer, IOCContainerInterface>
14
- {
12
+ export class ServerIOC implements IOCInterface<
13
+ IOCIdentifierMapServer,
14
+ IOCContainerInterface
15
+ > {
15
16
  static instance: ServerIOC | null = null;
16
17
 
17
18
  protected ioc: IOCFunctionInterface<
@@ -1,18 +1,17 @@
1
1
  import {
2
- ConsoleHandler,
3
- Logger,
4
- TimestampFormatter,
5
2
  type IOCContainerInterface,
6
3
  type IOCManagerInterface,
7
4
  type IOCRegisterInterface
8
5
  } from '@qlover/corekit-bridge';
6
+ import { Logger, ConsoleHandler, TimestampFormatter } from '@qlover/logger';
9
7
  import type { IocRegisterOptions } from '@/base/port/IOCInterface';
10
8
  import { SupabaseBridge } from '@/server/sqlBridges/SupabaseBridge';
11
9
  import { IOCIdentifier as I } from '@config/IOCIdentifier';
12
10
 
13
- export class ServerIOCRegister
14
- implements IOCRegisterInterface<IOCContainerInterface, IocRegisterOptions>
15
- {
11
+ export class ServerIOCRegister implements IOCRegisterInterface<
12
+ IOCContainerInterface,
13
+ IocRegisterOptions
14
+ > {
16
15
  constructor(protected options: IocRegisterOptions) {}
17
16
 
18
17
  /**
@@ -27,7 +27,7 @@ export default getRequestConfig(async ({ requestLocale }) => {
27
27
  onError: (error) => {
28
28
  if (error.message.includes('MISSING_MESSAGE')) {
29
29
  console.warn(`[i18n] Missing translation: ${error.message}`);
30
- return error.key; // 返回 key 作为 fallback 文本
30
+ return error.message; // 返回 key 作为 fallback 文本
31
31
  }
32
32
  throw error; // 其他错误仍然抛出
33
33
  }
@@ -42,7 +42,7 @@ export default getRequestConfig(async ({ requestLocale }) => {
42
42
  onError: (error) => {
43
43
  if (error.message.includes('MISSING_MESSAGE')) {
44
44
  console.warn(`[i18n] Missing translation: ${error.message}`);
45
- return error.key; // 返回 key 作为 fallback 文本
45
+ return error.message; // 返回 key 作为 fallback 文本
46
46
  }
47
47
  throw error; // 其他错误仍然抛出
48
48
  }
@@ -7,9 +7,7 @@ import type { CrentialTokenInterface } from './port/CrentialTokenInterface';
7
7
  export type UserCredentialTokenValue = Pick<UserSchema, 'id' | 'email'>;
8
8
 
9
9
  @injectable()
10
- export class UserCredentialToken
11
- implements CrentialTokenInterface<UserCredentialTokenValue>
12
- {
10
+ export class UserCredentialToken implements CrentialTokenInterface<UserCredentialTokenValue> {
13
11
  protected jwtSecret: string;
14
12
  protected jwtExpiresIn: string;
15
13
 
@@ -5,8 +5,7 @@ import type { LinkProps } from 'next/link';
5
5
  import type { ReactNode } from 'react';
6
6
 
7
7
  interface LocaleLinkProps
8
- extends Omit<LinkProps, 'href'>,
9
- React.HTMLAttributes<HTMLAnchorElement> {
8
+ extends Omit<LinkProps, 'href'>, React.HTMLAttributes<HTMLAnchorElement> {
10
9
  href:
11
10
  | string
12
11
  | {
@@ -6,7 +6,7 @@ export class MockAppConfig implements EnvConfigInterface {
6
6
 
7
7
  appVersion = version;
8
8
 
9
- env: string = import.meta.env.MODE;
9
+ env: string = 'test';
10
10
 
11
11
  userTokenStorageKey = '__fe_user_token__';
12
12
 
@@ -1,18 +1,7 @@
1
- import { TestBootstrapsProvider } from './TestBootstrapsProvider';
2
-
3
- interface TestAppProps {
4
- children: React.ReactNode;
5
- /**
6
- * Initial URL path for the router
7
- * @default ['/en/']
8
- */
9
- routerInitialEntries?: string[];
10
- /**
11
- * Initial index of the entries array
12
- * @default 0
13
- */
14
- routerInitialIndex?: number;
15
- }
1
+ import {
2
+ TestBootstrapsProvider,
3
+ type TestBootstrapsProviderProps
4
+ } from './TestBootstrapsProvider';
16
5
 
17
6
  /**
18
7
  * TestApp - Complete test wrapper with IOC and Router
@@ -31,13 +20,17 @@ interface TestAppProps {
31
20
  export function TestApp({
32
21
  children,
33
22
  routerInitialEntries,
34
- routerInitialIndex
35
- }: TestAppProps) {
23
+ routerInitialIndex,
24
+ bootHref,
25
+ appConfig
26
+ }: TestBootstrapsProviderProps) {
36
27
  return (
37
28
  <TestBootstrapsProvider
38
29
  data-testid="TestApp"
39
30
  routerInitialEntries={routerInitialEntries}
40
31
  routerInitialIndex={routerInitialIndex}
32
+ bootHref={bootHref}
33
+ appConfig={appConfig}
41
34
  >
42
35
  {children}
43
36
  </TestBootstrapsProvider>
@@ -1,12 +1,9 @@
1
+ import { appConfig as globalsAppConfig } from '@/core/globals';
1
2
  import { IOCContext } from '@/uikit/contexts/IOCContext';
2
3
  import { TestRouter } from './TestRouter';
3
4
  import { testIOC } from '../testIOC/TestIOC';
4
-
5
- export function TestBootstrapsProvider({
6
- children,
7
- routerInitialEntries,
8
- routerInitialIndex
9
- }: {
5
+ import type { EnvConfigInterface } from '@qlover/corekit-bridge';
6
+ export interface TestBootstrapsProviderProps {
10
7
  children: React.ReactNode;
11
8
  /**
12
9
  * Initial URL path for the router
@@ -18,8 +15,30 @@ export function TestBootstrapsProvider({
18
15
  * @default 0
19
16
  */
20
17
  routerInitialIndex?: number;
21
- }) {
22
- const IOC = testIOC.create();
18
+
19
+ /**
20
+ * The boot href
21
+ *
22
+ * @default `https://localhost.test:3000/en/`
23
+ */
24
+ bootHref?: string;
25
+
26
+ appConfig?: EnvConfigInterface;
27
+ }
28
+
29
+ const defaultBootHref = 'https://localhost.test:3000/en/';
30
+
31
+ export function TestBootstrapsProvider({
32
+ children,
33
+ routerInitialEntries,
34
+ routerInitialIndex,
35
+ bootHref,
36
+ appConfig
37
+ }: TestBootstrapsProviderProps) {
38
+ const IOC = testIOC.create({
39
+ pathname: bootHref ?? defaultBootHref,
40
+ appConfig: appConfig ?? globalsAppConfig
41
+ });
23
42
 
24
43
  return (
25
44
  <IOCContext.Provider data-testid="TestBootstrapsProvider" value={IOC}>
@@ -1,5 +1,5 @@
1
1
  import { vi } from 'vitest';
2
- import { MockAppConfig } from './MockAppConfit';
2
+ import { MockAppConfig } from './MockAppConfig';
3
3
  import { MockDialogHandler } from './MockDialogHandler';
4
4
  import { MockLogger } from './MockLogger';
5
5
 
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Mock i18next-http-backend for testing
3
+ *
4
+ * This mock prevents network requests while still allowing i18next to work properly.
5
+ * It loads translation data directly from JSON files, enabling translation testing.
6
+ */
7
+
8
+ import enCommon from '../../public/locales/en/common.json';
9
+ import zhCommon from '../../public/locales/zh/common.json';
10
+ import type { i18n as I18nType } from 'i18next';
11
+
12
+ // Translation resources loaded from JSON files
13
+ const resources: Record<string, Record<string, Record<string, string>>> = {
14
+ en: {
15
+ common: enCommon as Record<string, string>
16
+ },
17
+ zh: {
18
+ common: zhCommon as Record<string, string>
19
+ }
20
+ };
21
+
22
+ /**
23
+ * Mock HttpApi backend class for i18next
24
+ * Implements the backend interface to avoid network requests
25
+ */
26
+ export class MockHttpBackend {
27
+ type = 'backend';
28
+
29
+ /**
30
+ * Initialize the backend
31
+ * This is called by i18next when .use() is called
32
+ */
33
+ init(
34
+ _services: unknown,
35
+ _backendOptions: unknown,
36
+ _i18nextOptions: unknown,
37
+ i18nextInstance: I18nType
38
+ ): void {
39
+ // Preload resources directly into i18next
40
+ if (
41
+ i18nextInstance &&
42
+ typeof i18nextInstance.addResourceBundle === 'function'
43
+ ) {
44
+ // Add resources for all languages and namespaces
45
+ Object.keys(resources).forEach((lng) => {
46
+ Object.keys(resources[lng]).forEach((ns) => {
47
+ i18nextInstance.addResourceBundle(
48
+ lng,
49
+ ns,
50
+ resources[lng][ns],
51
+ true,
52
+ true
53
+ );
54
+ });
55
+ });
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Read translation data for a language and namespace
61
+ * This method is called by i18next to load translations
62
+ */
63
+ read(
64
+ language: string,
65
+ namespace: string,
66
+ callback: (error: Error | null, data?: Record<string, string>) => void
67
+ ): void {
68
+ try {
69
+ const data = resources[language]?.[namespace];
70
+ if (data) {
71
+ // Return immediately with the data
72
+ callback(null, data);
73
+ } else {
74
+ callback(new Error(`Translation not found: ${language}/${namespace}`));
75
+ }
76
+ } catch (error) {
77
+ callback(error as Error);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Load URL - called by i18next-http-backend to load from URL
83
+ * We intercept this and return data from memory instead
84
+ */
85
+ loadUrl(
86
+ _url: string,
87
+ callback: (error: Error | null, data?: Record<string, string>) => void
88
+ ): void {
89
+ // Extract language and namespace from URL if possible
90
+ // Format: /locales/{{lng}}/{{ns}}.json
91
+ const match = _url.match(/locales\/([^/]+)\/([^/]+)\.json/);
92
+ if (match) {
93
+ const [, lng, ns] = match;
94
+ const data = resources[lng]?.[ns];
95
+ if (data) {
96
+ callback(null, data);
97
+ return;
98
+ }
99
+ }
100
+ // Fallback: return empty object
101
+ callback(null, {});
102
+ }
103
+
104
+ /**
105
+ * Create - not used in our mock
106
+ */
107
+ create(): void {
108
+ // No-op
109
+ }
110
+ }
@@ -1,5 +1,5 @@
1
1
  import { baseNoLocaleRoutes, baseRoutes } from '@config/app.router';
2
- import { useLocaleRoutes } from '@config/common';
2
+ import { routerPrefix, useLocaleRoutes } from '@config/common';
3
3
  import { I } from '@config/IOCIdentifier';
4
4
  import { themeConfig } from '@config/theme';
5
5
  import { ThemeService } from '@qlover/corekit-bridge';
@@ -54,7 +54,8 @@ export class TestIOCRegister
54
54
  {
55
55
  routes: useLocaleRoutes ? baseRoutes : baseNoLocaleRoutes,
56
56
  logger: ioc.get(I.Logger),
57
- hasLocalRoutes: useLocaleRoutes
57
+ hasLocalRoutes: useLocaleRoutes,
58
+ routerPrefix: routerPrefix
58
59
  }
59
60
  )
60
61
  );
@@ -49,3 +49,16 @@ global.IntersectionObserver = vi.fn().mockImplementation(() => ({
49
49
 
50
50
  // Mock globals
51
51
  vi.mock('@/core/globals', () => createMockGlobals());
52
+
53
+ // Mock i18next-http-backend to avoid network requests in tests
54
+ // This prevents timeout issues when I18nService tries to load language files
55
+ // The mock loads translations directly from JSON files, enabling translation testing
56
+ vi.mock('i18next-http-backend', async () => {
57
+ const { MockHttpBackend } = await import('@__mocks__/i18nextHttpBackend');
58
+ return { default: MockHttpBackend };
59
+ });
60
+
61
+ // Mock i18next-browser-languagedetector to avoid browser API calls
62
+ vi.mock('i18next-browser-languagedetector', () => ({
63
+ default: {}
64
+ }));
@@ -205,7 +205,8 @@ describe('I18nService', () => {
205
205
  const result = service.t(key);
206
206
  expect(result).toBe('translated_test.key');
207
207
  expect(i18n.t).toHaveBeenCalledWith(key, {
208
- lng: 'en'
208
+ lng: 'en',
209
+ nsSeparator: false
209
210
  });
210
211
  });
211
212
 
@@ -216,6 +217,7 @@ describe('I18nService', () => {
216
217
  expect(result).toBe('translated_test.key');
217
218
  expect(i18n.t).toHaveBeenCalledWith(key, {
218
219
  lng: 'en',
220
+ nsSeparator: false,
219
221
  ...params
220
222
  });
221
223
  });
@@ -6,8 +6,6 @@ import type { ExecutorPageBridgeInterface } from '@/base/port/ExecutorPageBridge
6
6
  import type { JSONStoragePageBridgeInterface } from '@/base/port/JSONStoragePageBridgeInterface';
7
7
  import type { RequestPageBridgeInterface } from '@/base/port/RequestPageBridgeInterface';
8
8
  import type { I18nService } from '@/base/services/I18nService';
9
- import type { MessageService } from '@/base/services/MessageService';
10
- import type { ProcesserExecutor } from '@/base/services/ProcesserExecutor';
11
9
  import type { RouteService } from '@/base/services/RouteService';
12
10
  import type { UserService } from '@/base/services/UserService';
13
11
  import type * as CorekitBridge from '@qlover/corekit-bridge';
@@ -30,10 +28,17 @@ export const IOCIdentifier = Object.freeze({
30
28
  AntdStaticApiInterface: 'AntdStaticApiInterface',
31
29
  RequestCatcherInterface: 'RequestCatcherInterface',
32
30
  I18nServiceInterface: 'I18nServiceInterface',
33
- ProcesserExecutorInterface: 'ProcesserExecutorInterface',
34
31
  RouteServiceInterface: 'RouteServiceInterface',
32
+ /**
33
+ * User service interface
34
+ *
35
+ * This interface is implemented by the corekit-bridge UserServiceInterface interface, used to manage user information and authentication.
36
+ *
37
+ * @example ```
38
+ * const userService = useIOC(IOCIdentifier.UserServiceInterface);
39
+ * ```
40
+ */
35
41
  UserServiceInterface: 'UserServiceInterface',
36
- MessageServiceInterface: 'MessageServiceInterface',
37
42
  I18nKeyErrorPlugin: 'I18nKeyErrorPlugin',
38
43
  FeApiCommonPlugin: 'FeApiCommonPlugin',
39
44
  ApiMockPlugin: 'ApiMockPlugin',
@@ -73,10 +78,8 @@ export interface IOCIdentifierMap {
73
78
  [IOCIdentifier.AntdStaticApiInterface]: DialogHandler;
74
79
  [IOCIdentifier.RequestCatcherInterface]: RequestStatusCatcher;
75
80
  [IOCIdentifier.I18nServiceInterface]: I18nService;
76
- [IOCIdentifier.ProcesserExecutorInterface]: ProcesserExecutor;
77
81
  [IOCIdentifier.RouteServiceInterface]: RouteService;
78
82
  [IOCIdentifier.UserServiceInterface]: UserService;
79
- [IOCIdentifier.MessageServiceInterface]: MessageService;
80
83
  [IOCIdentifier.I18nKeyErrorPlugin]: I18nKeyErrorPlugin;
81
84
  [IOCIdentifier.FeApiCommonPlugin]: CorekitBridge.RequestCommonPlugin;
82
85
  [IOCIdentifier.ApiMockPlugin]: CorekitBridge.ApiMockPlugin;
@@ -35,6 +35,8 @@ export const loggerStyles = {
35
35
  *
36
36
  * - 需要以 / 开头
37
37
  * - 但是不能只有 /
38
+ *
39
+ * **TODO: 未来可能需要修改为支持 vercel 环境使用前缀**
38
40
  */
39
41
  export const routerPrefix = '/router-root';
40
42
 
@@ -45,3 +47,39 @@ export const routerPrefix = '/router-root';
45
47
  * - false: 不使用本地化路由,直接使用路径 (例如: /home)
46
48
  */
47
49
  export const useLocaleRoutes = true;
50
+
51
+ /**
52
+ * 注入到浏览器全局变量中需要忽略的变量
53
+ *
54
+ * 应用 `@/core/globals.ts` 中的变量
55
+ *
56
+ * 可能 appConfig 有敏感信息,需要忽略
57
+ *
58
+ * - 可以直接忽略整个 appConfig 对象, 例如: 'appConfig'
59
+ * - 也可以忽略单个属性, 例如: 'appConfig.openAiTokenPrefix', 'appConfig.openAiToken'
60
+ *
61
+ * @example 忽略 appConfig 对象
62
+ * ```typescript
63
+ * export const omitInjectedGlobals = [
64
+ * 'appConfig'
65
+ * ];
66
+ * ```
67
+ *
68
+ * @example 忽略 appConfig 中的 openAiTokenPrefix 和 openAiToken 属性
69
+ * ```typescript
70
+ * export const omitInjectedGlobals = [
71
+ * 'appConfig.openAiTokenPrefix',
72
+ * 'appConfig.openAiToken'
73
+ * ];
74
+ * ```
75
+ */
76
+ export const omitInjectedGlobals = [
77
+ 'appConfig.openAiTokenPrefix',
78
+ 'appConfig.openAiToken',
79
+ 'appConfig.loginPassword',
80
+ 'appConfig.loginUser',
81
+ 'appConfig.aiApiTokenPrefix',
82
+ 'appConfig.openAiBaseUrl',
83
+ 'appConfig.aiApiBaseUrl',
84
+ 'appConfig.aiApiToken'
85
+ ];
@@ -3,29 +3,19 @@
3
3
  "mock": true,
4
4
  "noUrl": true
5
5
  },
6
- "GET https://feapi.example.com/api/userinfo": {
7
- "name": "John Doe",
8
- "email": "john.doe@example.com",
9
- "picture": "https://randomuser.me/api/portraits/men/1.jpg"
10
- },
6
+
11
7
  "GET /api/userinfo": {
12
8
  "name": "John Doe",
13
9
  "email": "john.doe@example.com",
14
10
  "picture": "https://randomuser.me/api/portraits/men/1.jpg"
15
11
  },
16
- "POST https://feapi.example.com/api/login": {
17
- "token": "asdfasdf123123asdfasdf"
18
- },
19
12
  "POST /api/login": {
20
13
  "token": "/api/login-token-adfasdfasdf"
21
14
  },
22
- "POST https://feapi.example.com/api/register": {
23
- "token": "asdfasdf123123asdfasdf"
24
- },
25
15
  "POST /api/register": {
26
16
  "token": "asdfasdf123123asdfasdf"
27
17
  },
28
- "POST https://api.openai.com/v1/chat/completions": {
18
+ "POST /chat/completions": {
29
19
  "id": "chatcmpl-1234567890",
30
20
  "object": "chat.completion",
31
21
  "created": 1721702400,
@@ -37,5 +27,8 @@
37
27
  }
38
28
  }
39
29
  ]
30
+ },
31
+ "POST /api/logout": {
32
+ "redirect": "/login"
40
33
  }
41
34
  }
@@ -7,10 +7,7 @@ import unusedImports from 'eslint-plugin-unused-imports';
7
7
  import qloverEslint from '@qlover/eslint-plugin';
8
8
  import tseslint from 'typescript-eslint';
9
9
  import globals from 'globals';
10
- import {
11
- disableGlobals,
12
- restrictSpecificGlobals
13
- } from './makes/eslint-utils.mjs';
10
+ import { restrictSpecificGlobals } from './makes/eslint-utils.mjs';
14
11
 
15
12
  const __filename = fileURLToPath(import.meta.url);
16
13
  const __dirname = dirname(__filename);
@@ -186,10 +183,14 @@ const eslintConfig = [
186
183
  'prettier/prettier': [
187
184
  'error',
188
185
  {
189
- semi: true,
190
186
  singleQuote: true,
191
187
  trailingComma: 'none',
192
188
  endOfLine: 'lf'
189
+ },
190
+ {
191
+ // 仅用于单独部署时对 eslint prettier 插件自动查找 prettierrc 时报错
192
+ // 注意: vscode 等编辑器会失效, 作为单独项目开发时可以去掉
193
+ usePrettierrc: false
193
194
  }
194
195
  ],
195
196
  // 默认禁用 export default
@@ -32,7 +32,7 @@
32
32
  "dev:force": "vite --mode localhost --force",
33
33
  "dev:staging": "vite --mode staging",
34
34
  "dev:prod": "vite --mode production",
35
- "build": "npm run lint && vite build",
35
+ "build": "vite build",
36
36
  "build:staging": "npm run lint && vite build --mode staging",
37
37
  "build:prod": "npm run lint && vite build --mode production",
38
38
  "build:analyze": "vite build --mode production && start dist/stats.html",