@robsun/create-keystone-app 0.2.13 → 0.4.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 (88) hide show
  1. package/README.md +46 -43
  2. package/dist/create-keystone-app.js +347 -10
  3. package/dist/create-module.js +1219 -607
  4. package/package.json +22 -23
  5. package/template/.claude/skills/keystone-implement/SKILL.md +113 -0
  6. package/template/.claude/skills/keystone-implement/references/CHECKLIST.md +91 -0
  7. package/template/.claude/skills/keystone-implement/references/PATTERNS.md +1088 -0
  8. package/template/.claude/skills/keystone-implement/references/SCHEMA.md +135 -0
  9. package/template/.claude/skills/keystone-implement/references/TESTING.md +231 -0
  10. package/template/.claude/skills/keystone-requirements/SKILL.md +296 -0
  11. package/template/.claude/skills/keystone-requirements/references/CONFIRM_TEMPLATE.md +170 -0
  12. package/template/.claude/skills/keystone-requirements/references/SCHEMA.md +135 -0
  13. package/template/.eslintrc.js +3 -0
  14. package/template/.github/workflows/ci.yml +30 -0
  15. package/template/.github/workflows/release.yml +32 -0
  16. package/template/.golangci.yml +11 -0
  17. package/template/README.md +81 -73
  18. package/template/apps/server/README.md +8 -0
  19. package/template/apps/server/cmd/server/main.go +27 -185
  20. package/template/apps/server/config.example.yaml +31 -1
  21. package/template/apps/server/config.yaml +31 -1
  22. package/template/apps/server/go.mod +60 -18
  23. package/template/apps/server/go.sum +183 -31
  24. package/template/apps/server/internal/frontend/embed.go +3 -8
  25. package/template/apps/server/internal/modules/example/README.md +18 -0
  26. package/template/apps/server/internal/modules/example/api/handler/handler_test.go +9 -0
  27. package/template/apps/server/internal/modules/example/api/handler/item_handler.go +468 -165
  28. package/template/apps/server/internal/modules/example/bootstrap/seeds/item.go +217 -8
  29. package/template/apps/server/internal/modules/example/domain/models/item.go +40 -7
  30. package/template/apps/server/internal/modules/example/domain/service/approval_callback.go +68 -0
  31. package/template/apps/server/internal/modules/example/domain/service/approval_schema.go +41 -0
  32. package/template/apps/server/internal/modules/example/domain/service/errors.go +20 -22
  33. package/template/apps/server/internal/modules/example/domain/service/item_service.go +267 -7
  34. package/template/apps/server/internal/modules/example/domain/service/item_service_test.go +281 -0
  35. package/template/apps/server/internal/modules/example/i18n/keys.go +32 -20
  36. package/template/apps/server/internal/modules/example/i18n/locales/en-US.json +30 -18
  37. package/template/apps/server/internal/modules/example/i18n/locales/zh-CN.json +30 -18
  38. package/template/apps/server/internal/modules/example/infra/exporter/item_exporter.go +119 -0
  39. package/template/apps/server/internal/modules/example/infra/importer/item_importer.go +77 -0
  40. package/template/apps/server/internal/modules/example/infra/repository/item_repository.go +99 -49
  41. package/template/apps/server/internal/modules/example/module.go +171 -97
  42. package/template/apps/server/internal/modules/example/tests/integration_test.go +7 -0
  43. package/template/apps/server/internal/modules/manifest.go +7 -7
  44. package/template/apps/web/README.md +4 -2
  45. package/template/apps/web/package.json +1 -1
  46. package/template/apps/web/src/app.config.ts +8 -6
  47. package/template/apps/web/src/index.css +7 -3
  48. package/template/apps/web/src/main.tsx +2 -5
  49. package/template/apps/web/src/modules/example/help/en-US/faq.md +27 -0
  50. package/template/apps/web/src/modules/example/help/en-US/items.md +30 -0
  51. package/template/apps/web/src/modules/example/help/en-US/overview.md +31 -0
  52. package/template/apps/web/src/modules/example/help/zh-CN/faq.md +27 -0
  53. package/template/apps/web/src/modules/example/help/zh-CN/items.md +31 -0
  54. package/template/apps/web/src/modules/example/help/zh-CN/overview.md +32 -0
  55. package/template/apps/web/src/modules/example/locales/en-US/example.json +99 -32
  56. package/template/apps/web/src/modules/example/locales/zh-CN/example.json +85 -18
  57. package/template/apps/web/src/modules/example/pages/ExampleItemsPage.tsx +840 -237
  58. package/template/apps/web/src/modules/example/services/exampleItems.ts +79 -8
  59. package/template/apps/web/src/modules/example/types.ts +14 -1
  60. package/template/apps/web/src/modules/index.ts +1 -0
  61. package/template/apps/web/vite.config.ts +9 -3
  62. package/template/docs/CONVENTIONS.md +10 -7
  63. package/template/package.json +4 -5
  64. package/template/pnpm-lock.yaml +76 -5
  65. package/template/scripts/build.bat +15 -3
  66. package/template/scripts/build.sh +9 -3
  67. package/template/scripts/check-help.js +249 -0
  68. package/template/scripts/compress-assets.js +89 -0
  69. package/template/scripts/test.bat +23 -0
  70. package/template/scripts/test.sh +16 -0
  71. package/template/.claude/skills/keystone-dev/SKILL.md +0 -103
  72. package/template/.claude/skills/keystone-dev/references/APPROVAL.md +0 -121
  73. package/template/.claude/skills/keystone-dev/references/CAPABILITIES.md +0 -261
  74. package/template/.claude/skills/keystone-dev/references/TEMPLATES.md +0 -532
  75. package/template/.claude/skills/keystone-dev/references/TESTING.md +0 -44
  76. package/template/.codex/skills/keystone-dev/SKILL.md +0 -103
  77. package/template/.codex/skills/keystone-dev/references/APPROVAL.md +0 -121
  78. package/template/.codex/skills/keystone-dev/references/CAPABILITIES.md +0 -261
  79. package/template/.codex/skills/keystone-dev/references/TEMPLATES.md +0 -532
  80. package/template/.codex/skills/keystone-dev/references/TESTING.md +0 -44
  81. package/template/apps/server/internal/app/routes/module_routes.go +0 -16
  82. package/template/apps/server/internal/app/routes/routes.go +0 -226
  83. package/template/apps/server/internal/app/startup/startup.go +0 -74
  84. package/template/apps/server/internal/frontend/handler.go +0 -122
  85. package/template/apps/server/internal/modules/registry.go +0 -145
  86. package/template/apps/web/src/modules/example/help/faq.md +0 -23
  87. package/template/apps/web/src/modules/example/help/items.md +0 -26
  88. package/template/apps/web/src/modules/example/help/overview.md +0 -25
@@ -1,19 +1,71 @@
1
- import { api, type ApiResponse } from '@robsun/keystone-web-core'
2
- import type { ExampleItem, ExampleItemStatus } from '../types'
1
+ import { api, type ApiResponse, type PaginatedData } from '@robsun/keystone-web-core'
2
+ import type {
3
+ ExampleApprovalStatus,
4
+ ExampleItem,
5
+ ExampleItemCategory,
6
+ ExampleItemStatus,
7
+ } from '../types'
3
8
 
4
- type ItemListResponse = {
5
- items: ExampleItem[]
9
+ export type ExampleItemListParams = {
10
+ page?: number
11
+ pageSize?: number
12
+ keyword?: string
13
+ status?: ExampleItemStatus
14
+ approvalStatus?: ExampleApprovalStatus
15
+ category?: ExampleItemCategory
16
+ startDate?: string
17
+ endDate?: string
18
+ }
19
+
20
+ export type ExampleItemExportParams = Omit<ExampleItemListParams, 'page' | 'pageSize'>
21
+
22
+ export const listExampleItems = async (
23
+ params: ExampleItemListParams = {}
24
+ ): Promise<PaginatedData<ExampleItem>> => {
25
+ const { data } = await api.get<ApiResponse<PaginatedData<ExampleItem>>>('/example/items', {
26
+ params: {
27
+ page: params.page,
28
+ page_size: params.pageSize,
29
+ keyword: params.keyword,
30
+ status: params.status,
31
+ approval_status: params.approvalStatus,
32
+ category: params.category,
33
+ start_date: params.startDate,
34
+ end_date: params.endDate,
35
+ },
36
+ })
37
+ return data.data
38
+ }
39
+
40
+ export const exportExampleItemsPdf = async (
41
+ params: ExampleItemExportParams = {}
42
+ ): Promise<Blob> => {
43
+ const { data } = await api.get<Blob>('/example/items/export/pdf', {
44
+ params: {
45
+ keyword: params.keyword,
46
+ status: params.status,
47
+ approval_status: params.approvalStatus,
48
+ category: params.category,
49
+ start_date: params.startDate,
50
+ end_date: params.endDate,
51
+ },
52
+ responseType: 'blob',
53
+ })
54
+ return data
6
55
  }
7
56
 
8
- export const listExampleItems = async () => {
9
- const { data } = await api.get<ApiResponse<ItemListResponse>>('/example/items')
10
- return data.data.items
57
+ export const getExampleItem = async (id: number): Promise<ExampleItem> => {
58
+ const { data } = await api.get<ApiResponse<ExampleItem>>(`/example/items/${id}`)
59
+ return data.data
11
60
  }
12
61
 
13
62
  export const createExampleItem = async (payload: {
14
63
  title: string
15
64
  description?: string
65
+ category?: ExampleItemCategory
66
+ amount?: number
16
67
  status?: ExampleItemStatus
68
+ attachment?: Record<string, unknown> | null
17
69
  }) => {
18
70
  const { data } = await api.post<ApiResponse<ExampleItem>>('/example/items', payload)
19
71
  return data.data
@@ -21,7 +73,14 @@ export const createExampleItem = async (payload: {
21
73
 
22
74
  export const updateExampleItem = async (
23
75
  id: number,
24
- payload: { title?: string; description?: string; status?: ExampleItemStatus }
76
+ payload: {
77
+ title?: string
78
+ description?: string
79
+ category?: ExampleItemCategory
80
+ amount?: number
81
+ status?: ExampleItemStatus
82
+ attachment?: Record<string, unknown> | null
83
+ }
25
84
  ) => {
26
85
  const { data } = await api.patch<ApiResponse<ExampleItem>>(`/example/items/${id}`, payload)
27
86
  return data.data
@@ -30,3 +89,15 @@ export const updateExampleItem = async (
30
89
  export const deleteExampleItem = async (id: number) => {
31
90
  await api.delete<ApiResponse<{ id: number }>>(`/example/items/${id}`)
32
91
  }
92
+
93
+ export const submitExampleItem = async (id: number) => {
94
+ const { data } = await api.post<ApiResponse<ExampleItem>>(`/example/items/${id}/submit`)
95
+ return data.data
96
+ }
97
+
98
+ export const cancelExampleItem = async (id: number, reason?: string) => {
99
+ const { data } = await api.post<ApiResponse<ExampleItem>>(`/example/items/${id}/cancel`, {
100
+ reason,
101
+ })
102
+ return data.data
103
+ }
@@ -1,10 +1,23 @@
1
- export type ExampleItemStatus = 'active' | 'inactive'
1
+ import type { UploadedFile } from '@robsun/keystone-web-core'
2
+
3
+ export type ExampleItemStatus = 'draft' | 'pending' | 'approved' | 'rejected' | 'cancelled'
4
+
5
+ export type ExampleItemCategory = 'general' | 'operations' | 'finance' | 'growth'
6
+
7
+ export type ExampleApprovalStatus = 'pending' | 'approved' | 'rejected' | 'cancelled'
2
8
 
3
9
  export interface ExampleItem {
4
10
  id: number
11
+ number: string
5
12
  title: string
6
13
  description: string
14
+ category: ExampleItemCategory
15
+ amount: number
7
16
  status: ExampleItemStatus
17
+ approval_instance_id?: number
18
+ approval_status?: ExampleApprovalStatus
19
+ reject_reason?: string
20
+ attachment?: UploadedFile | null
8
21
  created_at: string
9
22
  updated_at: string
10
23
  }
@@ -0,0 +1 @@
1
+ // Register application modules here.
@@ -1,12 +1,18 @@
1
1
  /// <reference types="vitest" />
2
2
  import { createKeystoneViteConfig } from '@robsun/keystone-web-core/vite'
3
3
  import path from 'path'
4
+ import { fileURLToPath } from 'url'
5
+
6
+ const rootDir = path.dirname(fileURLToPath(import.meta.url))
4
7
 
5
8
  export default createKeystoneViteConfig({
6
- helpDir: path.resolve(__dirname, './src/modules'),
9
+ root: rootDir,
10
+ helpExtraDirs: [path.resolve(rootDir, './src/modules')],
11
+ helpLocales: ['zh-CN', 'en-US'],
12
+ helpDefaultLocale: 'zh-CN',
7
13
  resolve: {
8
14
  alias: {
9
- '@app': path.resolve(__dirname, './src'),
15
+ '@app': path.resolve(rootDir, './src'),
10
16
  },
11
17
  },
12
18
  server: {
@@ -19,7 +25,7 @@ export default createKeystoneViteConfig({
19
25
  },
20
26
  },
21
27
  build: {
22
- outDir: path.resolve(__dirname, '../server/internal/frontend/dist'),
28
+ outDir: path.resolve(rootDir, '../server/internal/frontend/dist'),
23
29
  emptyOutDir: false,
24
30
  sourcemap: true,
25
31
  rollupOptions: {
@@ -16,9 +16,11 @@
16
16
  提示:更细节的入口说明见 `apps/web/README.md` 与 `apps/server/README.md`。
17
17
 
18
18
  ## 开发流程
19
+ - 先写/补测试:后端用 `*_test.go`,前端用 `*.test.tsx`/`*.spec.tsx`,覆盖核心逻辑与关键路径。
19
20
  - `pnpm dev`:执行 `scripts/dev.*`,检查环境并启动 Air + Vite。
20
21
  - `apps/server/.air.toml`:后端热重载配置。
21
- - `pnpm server:dev` 仅启动后端;`pnpm web:dev` 仅启动前端。
22
+ - `pnpm server:dev` 仅启动后端;`pnpm -C apps/web dev` 仅启动前端。
23
+ - 开发完成运行测试:`pnpm test` 或 `pnpm -C apps/web test` + `go -C apps/server test ./...`。
22
24
 
23
25
  ## 配置与环境
24
26
  - `apps/server/config.yaml` 为默认配置;`apps/server/config.example.yaml` 用作模板。
@@ -52,10 +54,10 @@ registerModule({ name: 'orders', routes: orderRoutes })
52
54
  ```
53
55
  - 页面放 `pages/`,组件放 `components/`,API 放 `services/`。
54
56
 
55
- ## Help 文档
56
- - 文档放在 `apps/web/src/modules/<module>/help/**`。
57
- - `helpKey` 与路由 `handle.helpKey` 保持一致。
58
- - Vite 已配置 `helpDir` 扫描 `src/modules`。
57
+ ## Help 文档
58
+ - 文档放在 `apps/web/src/modules/<module>/help/<locale>/**`。
59
+ - `helpKey` 与路由 `handle.helpKey` 保持一致。
60
+ - Vite 默认合并 core 帮助与 `src/modules`(`helpExtraDirs`),并按语言目录加载。
59
61
 
60
62
  ## 后端模块规范
61
63
  推荐结构:
@@ -77,7 +79,7 @@ type Module interface {
77
79
  RegisterRoutes(rg *gin.RouterGroup)
78
80
  RegisterModels() []interface{}
79
81
  RegisterPermissions(reg *permissions.Registry) error
80
- RegisterJobs(reg *jobs.Registry) error
82
+ RegisterTasks(reg *scheduler.Registry) error
81
83
  Migrate(db *gorm.DB) error
82
84
  Seed(db *gorm.DB) error
83
85
  }
@@ -138,7 +140,7 @@ RegisterPermissions 负责定义权限种子与后台管理项
138
140
 
139
141
  ## 任务与队列
140
142
  - Worker 在 `cmd/server/main.go` 启动。
141
- - 模块通过 `RegisterJobs` 注册任务处理器。
143
+ - 模块通过 `RegisterTasks` 注册任务处理器。
142
144
  - 队列配置在 `apps/server/config.yaml` 的 `queue` 部分。
143
145
 
144
146
  ## 嵌入式前端
@@ -222,3 +224,4 @@ return &i18n.I18nError{Key: "order.notFound"}
222
224
  - Web:`pnpm -C apps/web test`(Vitest)。
223
225
  - Server:`go -C apps/server test ./...`。
224
226
  - 全量:`pnpm test`。
227
+ - 测试目录:`tests/unit`、`tests/integration`、`tests/perf`(或包内 `*_test.go`)。
@@ -2,18 +2,17 @@
2
2
  "name": "__APP_NAME__",
3
3
  "private": true,
4
4
  "version": "0.1.0",
5
- "scripts": {
6
- "dev": "node scripts/dev.js",
5
+ "scripts": {
6
+ "dev": "node scripts/dev.js",
7
+ "server:dev": "go -C apps/server run ./cmd/server",
7
8
  "build": "node scripts/build.js",
8
9
  "build:web": "pnpm --filter web build",
9
- "check-modules": "node scripts/check-modules.js",
10
+ "check-modules": "node scripts/check-modules.js && node scripts/check-help.js",
10
11
  "create:module": "pnpm dlx --package @robsun/create-keystone-app create-keystone-module",
11
12
  "test": "pnpm check-modules && node scripts/test.js",
12
13
  "lint": "pnpm --filter web lint",
13
14
  "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
14
15
  "clean": "node scripts/clean.js",
15
- "web:dev": "pnpm -C apps/web dev",
16
- "server:dev": "go -C apps/server run ./cmd/server",
17
16
  "generate:i18n-types": "node scripts/generate-i18n-types.js",
18
17
  "prepare": "husky"
19
18
  },
@@ -27,20 +27,26 @@ importers:
27
27
  specifier: ^6.1.0
28
28
  version: 6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
29
29
  '@robsun/keystone-web-core':
30
- specifier: 0.1.9
31
- version: 0.1.9(@ant-design/icons@6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router-dom@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))
30
+ specifier: ^0.3.0
31
+ version: 0.3.0(@ant-design/icons@6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router-dom@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))
32
32
  antd:
33
33
  specifier: ^6.0.1
34
34
  version: 6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
35
35
  dayjs:
36
36
  specifier: ^1.11.19
37
37
  version: 1.11.19
38
+ i18next:
39
+ specifier: ^24.2.3
40
+ version: 24.2.3(typescript@5.9.3)
38
41
  react:
39
42
  specifier: ^19.2.0
40
43
  version: 19.2.3
41
44
  react-dom:
42
45
  specifier: ^19.2.0
43
46
  version: 19.2.3(react@19.2.3)
47
+ react-i18next:
48
+ specifier: ^15.5.1
49
+ version: 15.7.4(i18next@24.2.3(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)
44
50
  react-router-dom:
45
51
  specifier: ^7.10.1
46
52
  version: 7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -924,8 +930,8 @@ packages:
924
930
  react: '>=16.9.0'
925
931
  react-dom: '>=16.9.0'
926
932
 
927
- '@robsun/keystone-web-core@0.1.9':
928
- resolution: {integrity: sha512-zbN6yiq3p/wwVOZvGj5YI6lT+xXCyWx87QIsM3IXnNyMxJl4OLRvv1xXN3u6K4uQpp2q+no4CeYLAfRwFIKNNw==}
933
+ '@robsun/keystone-web-core@0.3.0':
934
+ resolution: {integrity: sha512-BFJehFDaAv62gtoBEw1BhgeYRbyz/LIOKM5tlIZSR8IxlwxagNBiGywwP+gX+93HQRbM6+3aT4EZkDzi4T7r4g==}
929
935
  peerDependencies:
930
936
  '@ant-design/icons': ^6.1.0
931
937
  antd: ^6.0.1
@@ -1912,6 +1918,9 @@ packages:
1912
1918
  resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
1913
1919
  engines: {node: '>=18'}
1914
1920
 
1921
+ html-parse-stringify@3.0.1:
1922
+ resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
1923
+
1915
1924
  html-url-attributes@3.0.1:
1916
1925
  resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
1917
1926
 
@@ -1928,6 +1937,17 @@ packages:
1928
1937
  engines: {node: '>=18'}
1929
1938
  hasBin: true
1930
1939
 
1940
+ i18next-browser-languagedetector@8.2.0:
1941
+ resolution: {integrity: sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==}
1942
+
1943
+ i18next@24.2.3:
1944
+ resolution: {integrity: sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==}
1945
+ peerDependencies:
1946
+ typescript: ^5
1947
+ peerDependenciesMeta:
1948
+ typescript:
1949
+ optional: true
1950
+
1931
1951
  iconv-lite@0.6.3:
1932
1952
  resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
1933
1953
  engines: {node: '>=0.10.0'}
@@ -2448,6 +2468,22 @@ packages:
2448
2468
  peerDependencies:
2449
2469
  react: ^19.2.3
2450
2470
 
2471
+ react-i18next@15.7.4:
2472
+ resolution: {integrity: sha512-nyU8iKNrI5uDJch0z9+Y5XEr34b0wkyYj3Rp+tfbahxtlswxSCjcUL9H0nqXo9IR3/t5Y5PKIA3fx3MfUyR9Xw==}
2473
+ peerDependencies:
2474
+ i18next: '>= 23.4.0'
2475
+ react: '>= 16.8.0'
2476
+ react-dom: '*'
2477
+ react-native: '*'
2478
+ typescript: ^5
2479
+ peerDependenciesMeta:
2480
+ react-dom:
2481
+ optional: true
2482
+ react-native:
2483
+ optional: true
2484
+ typescript:
2485
+ optional: true
2486
+
2451
2487
  react-is@17.0.2:
2452
2488
  resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
2453
2489
 
@@ -2862,6 +2898,10 @@ packages:
2862
2898
  jsdom:
2863
2899
  optional: true
2864
2900
 
2901
+ void-elements@3.1.0:
2902
+ resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
2903
+ engines: {node: '>=0.10.0'}
2904
+
2865
2905
  w3c-xmlserializer@5.0.0:
2866
2906
  resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
2867
2907
  engines: {node: '>=18'}
@@ -3744,7 +3784,7 @@ snapshots:
3744
3784
  react: 19.2.3
3745
3785
  react-dom: 19.2.3(react@19.2.3)
3746
3786
 
3747
- '@robsun/keystone-web-core@0.1.9(@ant-design/icons@6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router-dom@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))':
3787
+ '@robsun/keystone-web-core@0.3.0(@ant-design/icons@6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/react@19.2.7)(antd@6.1.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react-router-dom@7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))':
3748
3788
  dependencies:
3749
3789
  '@ant-design/icons': 6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
3750
3790
  '@vitejs/plugin-react': 5.1.2(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))
@@ -3754,8 +3794,11 @@ snapshots:
3754
3794
  dayjs: 1.11.19
3755
3795
  fuse.js: 7.1.0
3756
3796
  gray-matter: 4.0.3
3797
+ i18next: 24.2.3(typescript@5.9.3)
3798
+ i18next-browser-languagedetector: 8.2.0
3757
3799
  react: 19.2.3
3758
3800
  react-dom: 19.2.3(react@19.2.3)
3801
+ react-i18next: 15.7.4(i18next@24.2.3(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)
3759
3802
  react-markdown: 10.1.0(@types/react@19.2.7)(react@19.2.3)
3760
3803
  react-router-dom: 7.11.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
3761
3804
  remark-gfm: 4.0.1
@@ -3764,7 +3807,9 @@ snapshots:
3764
3807
  - '@types/react'
3765
3808
  - debug
3766
3809
  - immer
3810
+ - react-native
3767
3811
  - supports-color
3812
+ - typescript
3768
3813
  - use-sync-external-store
3769
3814
  - vite
3770
3815
 
@@ -4830,6 +4875,10 @@ snapshots:
4830
4875
  dependencies:
4831
4876
  whatwg-encoding: 3.1.1
4832
4877
 
4878
+ html-parse-stringify@3.0.1:
4879
+ dependencies:
4880
+ void-elements: 3.1.0
4881
+
4833
4882
  html-url-attributes@3.0.1: {}
4834
4883
 
4835
4884
  http-proxy-agent@7.0.2:
@@ -4848,6 +4897,16 @@ snapshots:
4848
4897
 
4849
4898
  husky@9.1.7: {}
4850
4899
 
4900
+ i18next-browser-languagedetector@8.2.0:
4901
+ dependencies:
4902
+ '@babel/runtime': 7.28.4
4903
+
4904
+ i18next@24.2.3(typescript@5.9.3):
4905
+ dependencies:
4906
+ '@babel/runtime': 7.28.4
4907
+ optionalDependencies:
4908
+ typescript: 5.9.3
4909
+
4851
4910
  iconv-lite@0.6.3:
4852
4911
  dependencies:
4853
4912
  safer-buffer: 2.1.2
@@ -5537,6 +5596,16 @@ snapshots:
5537
5596
  react: 19.2.3
5538
5597
  scheduler: 0.27.0
5539
5598
 
5599
+ react-i18next@15.7.4(i18next@24.2.3(typescript@5.9.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3):
5600
+ dependencies:
5601
+ '@babel/runtime': 7.28.4
5602
+ html-parse-stringify: 3.0.1
5603
+ i18next: 24.2.3(typescript@5.9.3)
5604
+ react: 19.2.3
5605
+ optionalDependencies:
5606
+ react-dom: 19.2.3(react@19.2.3)
5607
+ typescript: 5.9.3
5608
+
5540
5609
  react-is@17.0.2: {}
5541
5610
 
5542
5611
  react-is@18.3.1: {}
@@ -5961,6 +6030,8 @@ snapshots:
5961
6030
  - supports-color
5962
6031
  - terser
5963
6032
 
6033
+ void-elements@3.1.0: {}
6034
+
5964
6035
  w3c-xmlserializer@5.0.0:
5965
6036
  dependencies:
5966
6037
  xml-name-validator: 5.0.0
@@ -53,7 +53,7 @@ for /d %%i in ("%EMBED_DIR%\*") do rd /s /q "%%i" 2>nul
53
53
  echo [OK] Embed directory cleaned
54
54
 
55
55
  echo ----------------------------------------
56
- echo [2/4] Building Frontend
56
+ echo [2/5] Building Frontend
57
57
  echo ----------------------------------------
58
58
  cd /d "%FRONTEND_DIR%"
59
59
  call pnpm build
@@ -67,7 +67,19 @@ echo [OK] Frontend build completed
67
67
  cd /d "%PROJECT_ROOT%"
68
68
 
69
69
  echo ----------------------------------------
70
- echo [3/4] Building Backend (with embedded frontend)
70
+ echo [3/5] Precompressing Frontend Assets
71
+ echo ----------------------------------------
72
+ call node "%PROJECT_ROOT%\scripts\compress-assets.js"
73
+ if errorlevel 1 (
74
+ echo [FAIL] Precompression failed
75
+ set FAILED=1
76
+ cd /d "%PROJECT_ROOT%"
77
+ exit /b 1
78
+ )
79
+ echo [OK] Frontend assets precompressed
80
+
81
+ echo ----------------------------------------
82
+ echo [4/5] Building Backend (with embedded frontend)
71
83
  echo ----------------------------------------
72
84
  cd /d "%BACKEND_DIR%"
73
85
 
@@ -100,7 +112,7 @@ echo [OK] Linux build completed
100
112
  cd /d "%PROJECT_ROOT%"
101
113
 
102
114
  echo ----------------------------------------
103
- echo [4/4] Copying configuration template
115
+ echo [5/5] Copying configuration template
104
116
  echo ----------------------------------------
105
117
  if exist "%BACKEND_DIR%\config.example.yaml" (
106
118
  copy /Y "%BACKEND_DIR%\config.example.yaml" "%OUTPUT_DIR%\config.example.yaml" >nul
@@ -52,7 +52,7 @@ find "$EMBED_DIR" -mindepth 1 ! -name '.gitkeep' -delete 2>/dev/null || true
52
52
  echo -e "${GREEN}[OK]${NC} Embed directory cleaned"
53
53
 
54
54
  echo "----------------------------------------"
55
- echo "[2/4] Building Frontend"
55
+ echo "[2/5] Building Frontend"
56
56
  echo "----------------------------------------"
57
57
  cd "$FRONTEND_DIR"
58
58
  pnpm build
@@ -60,7 +60,13 @@ echo -e "${GREEN}[OK]${NC} Frontend build completed"
60
60
  cd "$PROJECT_ROOT"
61
61
 
62
62
  echo "----------------------------------------"
63
- echo "[3/4] Building Backend (with embedded frontend)"
63
+ echo "[3/5] Precompressing Frontend Assets"
64
+ echo "----------------------------------------"
65
+ node "$PROJECT_ROOT/scripts/compress-assets.js"
66
+ echo -e "${GREEN}[OK]${NC} Frontend assets precompressed"
67
+
68
+ echo "----------------------------------------"
69
+ echo "[4/5] Building Backend (with embedded frontend)"
64
70
  echo "----------------------------------------"
65
71
  cd "$BACKEND_DIR"
66
72
 
@@ -75,7 +81,7 @@ echo -e "${GREEN}[OK]${NC} Linux build completed"
75
81
  cd "$PROJECT_ROOT"
76
82
 
77
83
  echo "----------------------------------------"
78
- echo "[4/4] Copying configuration template"
84
+ echo "[5/5] Copying configuration template"
79
85
  echo "----------------------------------------"
80
86
  if [ -f "$BACKEND_DIR/config.example.yaml" ]; then
81
87
  cp "$BACKEND_DIR/config.example.yaml" "$OUTPUT_DIR/config.example.yaml"