@robsun/create-keystone-app 0.2.11 → 0.2.13

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.
@@ -1,47 +1,47 @@
1
- import { lazy, Suspense, type ComponentType, type ReactElement } from 'react'
2
- import type { RouteObject } from 'react-router-dom'
3
- import { AppstoreOutlined } from '@ant-design/icons'
4
- import { Spin } from 'antd'
5
-
6
- const lazyNamed = <T extends Record<string, ComponentType>, K extends keyof T>(
7
- factory: () => Promise<T>,
8
- name: K
9
- ) =>
10
- lazy(async () => {
11
- const module = await factory()
12
- return { default: module[name] }
13
- })
14
-
15
- const withSuspense = (element: ReactElement) => (
16
- <Suspense
17
- fallback={
18
- <div style={{ padding: 24, display: 'flex', justifyContent: 'center' }}>
19
- <Spin />
20
- </div>
21
- }
22
- >
23
- {element}
24
- </Suspense>
25
- )
26
-
27
- const ExampleItemsPage = lazyNamed(() => import('./pages/ExampleItemsPage'), 'ExampleItemsPage')
28
-
29
- export const exampleRoutes: RouteObject[] = [
30
- {
31
- path: 'example',
32
- element: <ExampleItemsPage />,
33
- handle: {
34
- menu: {
35
- labelKey: 'example:menu.items',
36
- icon: <AppstoreOutlined />,
37
- permission: 'example:item:view',
38
- },
39
- breadcrumbKey: 'example:menu.items',
40
- permission: 'example:item:view',
41
- helpKey: 'example/items',
42
- },
43
- },
44
- ].map((route) => ({
45
- ...route,
46
- element: route.element ? withSuspense(route.element) : route.element,
47
- }))
1
+ import { lazy, Suspense, type ComponentType, type ReactElement } from 'react'
2
+ import type { RouteObject } from 'react-router-dom'
3
+ import { AppstoreOutlined } from '@ant-design/icons'
4
+ import { Spin } from 'antd'
5
+
6
+ const lazyNamed = <T extends Record<string, ComponentType>, K extends keyof T>(
7
+ factory: () => Promise<T>,
8
+ name: K
9
+ ) =>
10
+ lazy(async () => {
11
+ const module = await factory()
12
+ return { default: module[name] }
13
+ })
14
+
15
+ const withSuspense = (element: ReactElement) => (
16
+ <Suspense
17
+ fallback={
18
+ <div style={{ padding: 24, display: 'flex', justifyContent: 'center' }}>
19
+ <Spin />
20
+ </div>
21
+ }
22
+ >
23
+ {element}
24
+ </Suspense>
25
+ )
26
+
27
+ const ExampleItemsPage = lazyNamed(() => import('./pages/ExampleItemsPage'), 'ExampleItemsPage')
28
+
29
+ export const exampleRoutes: RouteObject[] = [
30
+ {
31
+ path: 'example',
32
+ element: <ExampleItemsPage />,
33
+ handle: {
34
+ menu: {
35
+ labelKey: 'example:menu.items',
36
+ icon: <AppstoreOutlined />,
37
+ permission: 'example:item:view',
38
+ },
39
+ breadcrumbKey: 'example:menu.items',
40
+ permission: 'example:item:view',
41
+ helpKey: 'example/items',
42
+ },
43
+ },
44
+ ].map((route) => ({
45
+ ...route,
46
+ element: route.element ? withSuspense(route.element) : route.element,
47
+ }))
@@ -1,224 +1,224 @@
1
- # Keystone App Conventions
2
-
3
- ## 目标
4
- - Web/Server 模块结构统一,便于复用与维护。
5
- - 路由、菜单、权限、帮助文档集中管理。
6
- - 约定优于配置,降低上手成本。
7
- 编码规范请参考 `docs/CODE_STYLE.md`。
8
-
9
- ## 项目结构
10
- - `apps/web/`:React + Vite 壳与业务模块。
11
- - `apps/server/`:Go 服务端(`cmd/server` 启动,`internal/app` 编排)。
12
- - `apps/server/internal/modules/`:后端模块注册与实现。
13
- - `apps/server/internal/frontend/`:嵌入式前端输出目录(`dist/`)。
14
- - `scripts/`:跨平台 dev/build/test/clean。
15
- - `apps/server/config.yaml`:服务端配置(支持环境变量覆盖,`docker-compose.yml` 提供依赖)。
16
- 提示:更细节的入口说明见 `apps/web/README.md` 与 `apps/server/README.md`。
17
-
18
- ## 开发流程
19
- - `pnpm dev`:执行 `scripts/dev.*`,检查环境并启动 Air + Vite。
20
- - `apps/server/.air.toml`:后端热重载配置。
21
- - `pnpm server:dev` 仅启动后端;`pnpm web:dev` 仅启动前端。
22
-
23
- ## 配置与环境
24
- - `apps/server/config.yaml` 为默认配置;`apps/server/config.example.yaml` 用作模板。
25
- - `apps/server/config.yaml` 供 `go run`/Air 使用。
26
- - 前端环境变量参考 `apps/web/.env.example`(Vite 会读取)。
27
- - 后端环境变量需通过 Shell/工具注入(服务端不读取 `.env`)。
28
- - 本地数据库默认 SQLite:`apps/server/data/keystone-local.db`。
29
- - 本地存储目录:`apps/server/storage/`(当 `storage.driver=local`)。
30
-
31
- ## 前端模块规范
32
- - 模块目录:`apps/web/src/modules/<module>`。
33
- - 在 `index.ts` 注册模块:
34
- ```ts
35
- import { registerModule } from '@robsun/keystone-web-core'
36
- import { orderRoutes } from './routes'
37
-
38
- registerModule({ name: 'orders', routes: orderRoutes })
39
- ```
40
- - 在 `routes.tsx` 配置菜单/权限/帮助:
41
- ```tsx
42
- {
43
- path: 'orders',
44
- element: <Outlet />,
45
- handle: {
46
- menu: { label: 'Orders', icon: <FileTextOutlined />, permission: 'sales:order:view' },
47
- breadcrumb: 'Orders',
48
- permission: 'sales:order:view',
49
- helpKey: 'sales/orders',
50
- },
51
- }
52
- ```
53
- - 页面放 `pages/`,组件放 `components/`,API 放 `services/`。
54
-
55
- ## Help 文档
56
- - 文档放在 `apps/web/src/modules/<module>/help/**`。
57
- - `helpKey` 与路由 `handle.helpKey` 保持一致。
58
- - Vite 已配置 `helpDir` 扫描 `src/modules`。
59
-
60
- ## 后端模块规范
61
- 推荐结构:
62
- ```
63
- apps/server/internal/modules/<module>/
64
- module.go
65
- api/routes/
66
- api/handler/
67
- domain/service/
68
- infra/repository/
69
- bootstrap/migrations/
70
- bootstrap/seeds/
71
- ```
72
-
73
- 模块接口:
74
- ```go
75
- type Module interface {
76
- Name() string
77
- RegisterRoutes(rg *gin.RouterGroup)
78
- RegisterModels() []interface{}
79
- RegisterPermissions(reg *permissions.Registry) error
80
- RegisterJobs(reg *jobs.Registry) error
81
- Migrate(db *gorm.DB) error
82
- Seed(db *gorm.DB) error
83
- }
84
- ```
85
-
86
- ## 模块注册与启用
87
- - 在 `apps/server/internal/modules/manifest.go` 注册模块。
88
- - 在 `apps/server/config.yaml` 的 `modules.enabled` 启用模块。
89
- - Web 端 `app.config.ts` 保持同名启用列表。
90
-
91
- ## 权限命名与同步
92
- - 命名规范:`{module}:{resource}:{action}`,例如:`example:item:view`。
93
- - 菜单权限通常使用 `{module}:{resource}`,用于菜单与分组,例如:`example:item`。
94
- - 前端使用 `routes.tsx` 的 `handle.permission` 与 `handle.menu.permission` 控制路由/菜单访问。
95
- - 后端使用 `RegisterPermissions` 注册同名权限,确保权限种子与管理后台一致。
96
-
97
- 示例(后端注册):
98
- ```go
99
- if err := reg.CreateMenu("example:item", "Example Items", "example", 10); err != nil {
100
- return err
101
- }
102
- if err := reg.CreateAction("example:item:view", "View Items", "example", "example:item"); err != nil {
103
- return err
104
- }
105
- if err := reg.CreateAction("example:item:manage", "Manage Items", "example", "example:item"); err != nil {
106
- return err
107
- }
108
- ```
109
-
110
- 示例(前端路由):
111
- ```tsx
112
- {
113
- path: 'example',
114
- element: <ExampleItemsPage />,
115
- handle: {
116
- menu: { label: 'Example Items', icon: <AppstoreOutlined />, permission: 'example:item:view' },
117
- breadcrumb: 'Example Items',
118
- permission: 'example:item:view',
119
- helpKey: 'example/items',
120
- },
121
- }
122
- ```
123
-
124
- 权限同步流程(简化):
125
- ```
126
- routes.tsx handle.permission
127
-
128
- 登录接口返回权限列表(permissions)
129
-
130
- 前端菜单/路由/按钮显示与否
131
-
132
- RegisterPermissions 负责定义权限种子与后台管理项
133
- ```
134
-
135
- ## 迁移 / 种子 / 权限
136
- - `startup.InitializeDatabase` 统一执行:核心迁移 + 模块迁移 + 种子 + 权限注册。
137
- - 模块权限通过 `RegisterPermissions` 注入。
138
-
139
- ## 任务与队列
140
- - Worker 在 `cmd/server/main.go` 启动。
141
- - 模块通过 `RegisterJobs` 注册任务处理器。
142
- - 队列配置在 `apps/server/config.yaml` 的 `queue` 部分。
143
-
144
- ## 嵌入式前端
145
- - Vite `build.outDir` 指向 `apps/server/internal/frontend/dist`。
146
- - Go 通过 `//go:embed` 嵌入前端产物。
147
- - 保留 `apps/server/internal/frontend/dist/.gitkeep`,避免 embed 找不到目录。
148
-
149
- ## 多语言支持 (i18n)
150
-
151
- > 详细指南请参考 [I18N.md](./I18N.md)
152
-
153
- ### 启用多语言
154
- 在 `apps/web/src/app.config.ts` 中配置:
155
- ```ts
156
- export const appConfig: Partial<KeystoneWebConfig> = {
157
- // ...
158
- ui: {
159
- i18n: {
160
- enabled: true,
161
- defaultLocale: 'zh-CN',
162
- supportedLocales: ['zh-CN', 'en-US'],
163
- },
164
- },
165
- }
166
- ```
167
-
168
- ### 前端模块 i18n
169
- 1. 创建语言包目录 `modules/<module>/locales/zh-CN/*.json` 和 `en-US/*.json`
170
- 2. 在 `modules/<module>/index.ts` 注册:
171
- ```ts
172
- import { registerModule, loadModuleLocales } from '@robsun/keystone-web-core'
173
-
174
- loadModuleLocales('myModule', {
175
- 'zh-CN': () => import('./locales/zh-CN/myModule.json'),
176
- 'en-US': () => import('./locales/en-US/myModule.json'),
177
- })
178
-
179
- registerModule({ name: 'myModule', routes: myRoutes })
180
- ```
181
-
182
- 3. 路由配置使用 `labelKey` 和 `breadcrumbKey`:
183
- ```tsx
184
- {
185
- path: 'items',
186
- handle: {
187
- menu: {
188
- label: '项目列表', // 回退文案
189
- labelKey: 'myModule:menu.items', // i18n 键(优先)
190
- icon: <AppstoreOutlined />,
191
- },
192
- breadcrumb: '项目列表',
193
- breadcrumbKey: 'myModule:menu.items',
194
- },
195
- }
196
- ```
197
-
198
- 4. 在组件中使用翻译:
199
- ```tsx
200
- import { useTranslation } from 'react-i18next'
201
-
202
- function MyComponent() {
203
- const { t } = useTranslation('myModule')
204
- return <Button>{t('actions.save')}</Button>
205
- }
206
- ```
207
-
208
- ### 后端 i18n
209
- 后端已内置 i18n 支持,使用 `Accept-Language` 请求头自动选择语言:
210
- ```go
211
- import "your-project/infra/i18n"
212
-
213
- // 响应
214
- response.SuccessI18n(c, i18n.MsgCreated, data)
215
- response.BadRequestI18n(c, i18n.MsgInvalidRequest)
216
-
217
- // 自定义错误
218
- return &i18n.I18nError{Key: "order.notFound"}
219
- ```
220
-
221
- ## Testing
222
- - Web:`pnpm -C apps/web test`(Vitest)。
223
- - Server:`go -C apps/server test ./...`。
224
- - 全量:`pnpm test`。
1
+ # Keystone App Conventions
2
+
3
+ ## 目标
4
+ - Web/Server 模块结构统一,便于复用与维护。
5
+ - 路由、菜单、权限、帮助文档集中管理。
6
+ - 约定优于配置,降低上手成本。
7
+ 编码规范请参考 `docs/CODE_STYLE.md`。
8
+
9
+ ## 项目结构
10
+ - `apps/web/`:React + Vite 壳与业务模块。
11
+ - `apps/server/`:Go 服务端(`cmd/server` 启动,`internal/app` 编排)。
12
+ - `apps/server/internal/modules/`:后端模块注册与实现。
13
+ - `apps/server/internal/frontend/`:嵌入式前端输出目录(`dist/`)。
14
+ - `scripts/`:跨平台 dev/build/test/clean。
15
+ - `apps/server/config.yaml`:服务端配置(支持环境变量覆盖,`docker-compose.yml` 提供依赖)。
16
+ 提示:更细节的入口说明见 `apps/web/README.md` 与 `apps/server/README.md`。
17
+
18
+ ## 开发流程
19
+ - `pnpm dev`:执行 `scripts/dev.*`,检查环境并启动 Air + Vite。
20
+ - `apps/server/.air.toml`:后端热重载配置。
21
+ - `pnpm server:dev` 仅启动后端;`pnpm web:dev` 仅启动前端。
22
+
23
+ ## 配置与环境
24
+ - `apps/server/config.yaml` 为默认配置;`apps/server/config.example.yaml` 用作模板。
25
+ - `apps/server/config.yaml` 供 `go run`/Air 使用。
26
+ - 前端环境变量参考 `apps/web/.env.example`(Vite 会读取)。
27
+ - 后端环境变量需通过 Shell/工具注入(服务端不读取 `.env`)。
28
+ - 本地数据库默认 SQLite:`apps/server/data/keystone-local.db`。
29
+ - 本地存储目录:`apps/server/storage/`(当 `storage.driver=local`)。
30
+
31
+ ## 前端模块规范
32
+ - 模块目录:`apps/web/src/modules/<module>`。
33
+ - 在 `index.ts` 注册模块:
34
+ ```ts
35
+ import { registerModule } from '@robsun/keystone-web-core'
36
+ import { orderRoutes } from './routes'
37
+
38
+ registerModule({ name: 'orders', routes: orderRoutes })
39
+ ```
40
+ - 在 `routes.tsx` 配置菜单/权限/帮助:
41
+ ```tsx
42
+ {
43
+ path: 'orders',
44
+ element: <Outlet />,
45
+ handle: {
46
+ menu: { label: 'Orders', icon: <FileTextOutlined />, permission: 'sales:order:view' },
47
+ breadcrumb: 'Orders',
48
+ permission: 'sales:order:view',
49
+ helpKey: 'sales/orders',
50
+ },
51
+ }
52
+ ```
53
+ - 页面放 `pages/`,组件放 `components/`,API 放 `services/`。
54
+
55
+ ## Help 文档
56
+ - 文档放在 `apps/web/src/modules/<module>/help/**`。
57
+ - `helpKey` 与路由 `handle.helpKey` 保持一致。
58
+ - Vite 已配置 `helpDir` 扫描 `src/modules`。
59
+
60
+ ## 后端模块规范
61
+ 推荐结构:
62
+ ```
63
+ apps/server/internal/modules/<module>/
64
+ module.go
65
+ api/routes/
66
+ api/handler/
67
+ domain/service/
68
+ infra/repository/
69
+ bootstrap/migrations/
70
+ bootstrap/seeds/
71
+ ```
72
+
73
+ 模块接口:
74
+ ```go
75
+ type Module interface {
76
+ Name() string
77
+ RegisterRoutes(rg *gin.RouterGroup)
78
+ RegisterModels() []interface{}
79
+ RegisterPermissions(reg *permissions.Registry) error
80
+ RegisterJobs(reg *jobs.Registry) error
81
+ Migrate(db *gorm.DB) error
82
+ Seed(db *gorm.DB) error
83
+ }
84
+ ```
85
+
86
+ ## 模块注册与启用
87
+ - 在 `apps/server/internal/modules/manifest.go` 注册模块。
88
+ - 在 `apps/server/config.yaml` 的 `modules.enabled` 启用模块。
89
+ - Web 端 `app.config.ts` 保持同名启用列表。
90
+
91
+ ## 权限命名与同步
92
+ - 命名规范:`{module}:{resource}:{action}`,例如:`example:item:view`。
93
+ - 菜单权限通常使用 `{module}:{resource}`,用于菜单与分组,例如:`example:item`。
94
+ - 前端使用 `routes.tsx` 的 `handle.permission` 与 `handle.menu.permission` 控制路由/菜单访问。
95
+ - 后端使用 `RegisterPermissions` 注册同名权限,确保权限种子与管理后台一致。
96
+
97
+ 示例(后端注册):
98
+ ```go
99
+ if err := reg.CreateMenu("example:item", "Example Items", "example", 10); err != nil {
100
+ return err
101
+ }
102
+ if err := reg.CreateAction("example:item:view", "View Items", "example", "example:item"); err != nil {
103
+ return err
104
+ }
105
+ if err := reg.CreateAction("example:item:manage", "Manage Items", "example", "example:item"); err != nil {
106
+ return err
107
+ }
108
+ ```
109
+
110
+ 示例(前端路由):
111
+ ```tsx
112
+ {
113
+ path: 'example',
114
+ element: <ExampleItemsPage />,
115
+ handle: {
116
+ menu: { label: 'Example Items', icon: <AppstoreOutlined />, permission: 'example:item:view' },
117
+ breadcrumb: 'Example Items',
118
+ permission: 'example:item:view',
119
+ helpKey: 'example/items',
120
+ },
121
+ }
122
+ ```
123
+
124
+ 权限同步流程(简化):
125
+ ```
126
+ routes.tsx handle.permission
127
+
128
+ 登录接口返回权限列表(permissions)
129
+
130
+ 前端菜单/路由/按钮显示与否
131
+
132
+ RegisterPermissions 负责定义权限种子与后台管理项
133
+ ```
134
+
135
+ ## 迁移 / 种子 / 权限
136
+ - `startup.InitializeDatabase` 统一执行:核心迁移 + 模块迁移 + 种子 + 权限注册。
137
+ - 模块权限通过 `RegisterPermissions` 注入。
138
+
139
+ ## 任务与队列
140
+ - Worker 在 `cmd/server/main.go` 启动。
141
+ - 模块通过 `RegisterJobs` 注册任务处理器。
142
+ - 队列配置在 `apps/server/config.yaml` 的 `queue` 部分。
143
+
144
+ ## 嵌入式前端
145
+ - Vite `build.outDir` 指向 `apps/server/internal/frontend/dist`。
146
+ - Go 通过 `//go:embed` 嵌入前端产物。
147
+ - 保留 `apps/server/internal/frontend/dist/.gitkeep`,避免 embed 找不到目录。
148
+
149
+ ## 多语言支持 (i18n)
150
+
151
+ > 详细指南请参考 [I18N.md](./I18N.md)
152
+
153
+ ### 启用多语言
154
+ 在 `apps/web/src/app.config.ts` 中配置:
155
+ ```ts
156
+ export const appConfig: Partial<KeystoneWebConfig> = {
157
+ // ...
158
+ ui: {
159
+ i18n: {
160
+ enabled: true,
161
+ defaultLocale: 'zh-CN',
162
+ supportedLocales: ['zh-CN', 'en-US'],
163
+ },
164
+ },
165
+ }
166
+ ```
167
+
168
+ ### 前端模块 i18n
169
+ 1. 创建语言包目录 `modules/<module>/locales/zh-CN/*.json` 和 `en-US/*.json`
170
+ 2. 在 `modules/<module>/index.ts` 注册:
171
+ ```ts
172
+ import { registerModule, loadModuleLocales } from '@robsun/keystone-web-core'
173
+
174
+ loadModuleLocales('myModule', {
175
+ 'zh-CN': () => import('./locales/zh-CN/myModule.json'),
176
+ 'en-US': () => import('./locales/en-US/myModule.json'),
177
+ })
178
+
179
+ registerModule({ name: 'myModule', routes: myRoutes })
180
+ ```
181
+
182
+ 3. 路由配置使用 `labelKey` 和 `breadcrumbKey`:
183
+ ```tsx
184
+ {
185
+ path: 'items',
186
+ handle: {
187
+ menu: {
188
+ label: '项目列表', // 回退文案
189
+ labelKey: 'myModule:menu.items', // i18n 键(优先)
190
+ icon: <AppstoreOutlined />,
191
+ },
192
+ breadcrumb: '项目列表',
193
+ breadcrumbKey: 'myModule:menu.items',
194
+ },
195
+ }
196
+ ```
197
+
198
+ 4. 在组件中使用翻译:
199
+ ```tsx
200
+ import { useTranslation } from 'react-i18next'
201
+
202
+ function MyComponent() {
203
+ const { t } = useTranslation('myModule')
204
+ return <Button>{t('actions.save')}</Button>
205
+ }
206
+ ```
207
+
208
+ ### 后端 i18n
209
+ 后端已内置 i18n 支持,使用 `Accept-Language` 请求头自动选择语言:
210
+ ```go
211
+ import "your-project/infra/i18n"
212
+
213
+ // 响应
214
+ response.SuccessI18n(c, i18n.MsgCreated, data)
215
+ response.BadRequestI18n(c, i18n.MsgInvalidRequest)
216
+
217
+ // 自定义错误
218
+ return &i18n.I18nError{Key: "order.notFound"}
219
+ ```
220
+
221
+ ## Testing
222
+ - Web:`pnpm -C apps/web test`(Vitest)。
223
+ - Server:`go -C apps/server test ./...`。
224
+ - 全量:`pnpm test`。
@@ -1,27 +1,27 @@
1
- {
2
- "name": "__APP_NAME__",
3
- "private": true,
4
- "version": "0.1.0",
5
- "scripts": {
6
- "dev": "node scripts/dev.js",
7
- "build": "node scripts/build.js",
8
- "build:web": "pnpm --filter web build",
9
- "check-modules": "node scripts/check-modules.js",
10
- "create:module": "pnpm dlx --package @robsun/create-keystone-app create-keystone-module",
11
- "test": "pnpm check-modules && node scripts/test.js",
12
- "lint": "pnpm --filter web lint",
13
- "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
14
- "clean": "node scripts/clean.js",
15
- "web:dev": "pnpm -C apps/web dev",
16
- "server:dev": "go -C apps/server run ./cmd/server",
17
- "generate:i18n-types": "node scripts/generate-i18n-types.js",
18
- "prepare": "husky"
19
- },
20
- "devDependencies": {
21
- "husky": "^9.1.7",
22
- "lint-staged": "^16.2.7",
23
- "prettier": "^3.7.4",
24
- "yaml": "^2.4.5"
25
- },
26
- "packageManager": "pnpm@9.15.0"
27
- }
1
+ {
2
+ "name": "__APP_NAME__",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "scripts": {
6
+ "dev": "node scripts/dev.js",
7
+ "build": "node scripts/build.js",
8
+ "build:web": "pnpm --filter web build",
9
+ "check-modules": "node scripts/check-modules.js",
10
+ "create:module": "pnpm dlx --package @robsun/create-keystone-app create-keystone-module",
11
+ "test": "pnpm check-modules && node scripts/test.js",
12
+ "lint": "pnpm --filter web lint",
13
+ "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
14
+ "clean": "node scripts/clean.js",
15
+ "web:dev": "pnpm -C apps/web dev",
16
+ "server:dev": "go -C apps/server run ./cmd/server",
17
+ "generate:i18n-types": "node scripts/generate-i18n-types.js",
18
+ "prepare": "husky"
19
+ },
20
+ "devDependencies": {
21
+ "husky": "^9.1.7",
22
+ "lint-staged": "^16.2.7",
23
+ "prettier": "^3.7.4",
24
+ "yaml": "^2.4.5"
25
+ },
26
+ "packageManager": "pnpm@9.15.0"
27
+ }