openxiangda-cli 2.0.0-alpha.119 → 2.0.0-alpha.120

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda-cli",
3
- "version": "2.0.0-alpha.119",
3
+ "version": "2.0.0-alpha.120",
4
4
  "description": "Thin application-level CLI for OpenXiangda 2.0.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,9 +22,9 @@
22
22
  "dependencies": {
23
23
  "@oclif/core": "4.13.3",
24
24
  "openxiangda-contracts": "2.0.0-alpha.58",
25
- "openxiangda-devkit-core": "2.0.0-alpha.75",
26
- "openxiangda-mcp": "2.0.0-alpha.75",
27
- "openxiangda-skill-kit": "2.0.0-alpha.94"
25
+ "openxiangda-devkit-core": "2.0.0-alpha.76",
26
+ "openxiangda-mcp": "2.0.0-alpha.76",
27
+ "openxiangda-skill-kit": "2.0.0-alpha.95"
28
28
  },
29
29
  "devDependencies": {
30
30
  "tsx": "4.23.12",
@@ -3,7 +3,7 @@
3
3
  - Use only the workspace-pinned CLI: `pnpm openxiangda <command>`. Never invoke a bare global `openxiangda` inside a 2.0 task.
4
4
  - `appspec/` is the optional OpenXiangda 2.0 business-intent layer. Before changing observable behavior, read the bounded index with `pnpm openxiangda spec context --json`, then select only the relevant stable ID; use no ChangeSpec for L0 work, one short ChangeSpec for L1, and add permission/rollback/concurrency detail only for L2/L3. Before close, the user—not AI—confirms `currentSpec=merged` or `not-applicable`. AppSpec is advisory and never a release gate. Never read, import or migrate 1.x `openspec/` or SDD.
5
5
  - Use Vite, React Router, Refine Core and Ant Design. Do not add Umi, ProComponents or another admin shell.
6
- - Bind every generated `appRoutes` entry to its local page with `defineApplicationContributions`; desktop `admin` routes stay inside the platform Shell, while `user` routes render without an admin Shell for independent mobile/user experiences. Use only its typed `toolbar`, `row` and `detail` resource slots for generated resource actions. Declare the complete editable admin menu with `defineAdminNavigation` and its page/group helpers; the Shell renders only generated `adminNavigation` references and permissions only filter them. Do not create another router, menu store, layout, identity provider, permission store or copied CRUD page; route/action access uses capability or `allOf`/`anyOf`, while Data/App API and Workflow authorization remain server-owned.
6
+ - Bind every generated `appRoutes` entry to its local page with `defineApplicationContributions`; desktop `admin` routes stay inside the platform Shell, while `user` routes render without an admin Shell for independent mobile/user experiences. Generated resource CRUD routes are compiler-owned under `/admin/resources/<resourceCode>...` and `/m/admin/resources/<resourceCode>...`; never recreate root resource paths, aliases or redirects. Explicit routes that have the same canonical shape as another explicit or generated route fail compilation, even when dynamic parameter names differ. Use only the typed `toolbar`, `row` and `detail` resource slots for generated resource actions. Declare the complete editable admin menu with `defineAdminNavigation` and its page/group helpers; the Shell renders only generated `adminNavigation` references and permissions only filter them. Do not create another router, menu store, layout, identity provider, permission store or copied CRUD page; route/action access uses capability or `allOf`/`anyOf`, while Data/App API and Workflow authorization remain server-owned.
7
7
  - Declare each resource once in `openxiangda.config.ts` with only `code`, `name`, `fields` and optional `mutationOwner`, generated/list/layout/data-policy settings. Each field owns type, label, required state, Surface flags, reference/file metadata and access. Resource codes are lower kebab-case. Use `native` for direct Data API mutations, `action`, `readonly` or `workflow` for non-Native ownership; never grant or generate Native mutation for a non-Native owner.
8
8
  - Never write resource-level `schemaVersion`, `appCode`, `schema`, `surface`, `capabilities`, `fieldPolicies` or `platform/data` modules. The compiler derives the strict DataResource, CRUD capabilities, Surface and AI Schema. The application manifest still starts with its one top-level `schemaVersion: 3`.
9
9
  - Ordinary list/get/create/update/delete, filters, export and batch operations use the platform Native Data API. Do not create Function CRUD or NestJS wrappers.
@@ -26,6 +26,11 @@ BrowserRouter、RuntimeBoundary、Refine、generated routes 和后台 Shell
26
26
  在显式后台导航组中加入 `adminApplicationTodoCenterPage()`;平台生成
27
27
  `/admin/todos` 与 `/m/todos`,只读取当前用户的消息/待办投影并统一解析目标页面。
28
28
 
29
+ 生成式资源后台路径固定为 `/admin/resources/<resourceCode>...`,移动后台面为
30
+ `/m/admin/resources/<resourceCode>...`;普通根路径和 `/m/...` 产品路径可由显式
31
+ `user` route 使用。生成页内部跳转来自编译后的 `adminPages`,不要手写旧根路径、别名或
32
+ 重定向。显式 route 与任一生成 route 的动态路径形状重复时,配置编译会直接失败。
33
+
29
34
  前端一次读取当前登录用户在当前环境的完整应用角色并集和 capability。应用可在 `perspectives` 中声明工作视角;视角只投影页面、行和字段的读取范围,不改变真实身份,也不收窄新增、编辑、删除、流程与自定义动作权限。标准 Data API 自动识别视角;只有绕过标准 Data API 的自定义 Nest 查询才需要通过 `@CurrentPerspective()` 显式实现同等读取投影。字段默认继承资源 read/create/update;`access` 数组为 all-of,`false` 为明确拒绝,无权字段在提交前从 payload 删除。
30
35
 
31
36
  ```bash
@@ -15,7 +15,7 @@
15
15
  "@nestjs/common": "11.1.29",
16
16
  "@nestjs/core": "11.1.29",
17
17
  "@nestjs/platform-fastify": "11.1.29",
18
- "openxiangda": "2.0.0-alpha.33",
18
+ "openxiangda": "2.0.0-alpha.34",
19
19
  "reflect-metadata": "0.2.2",
20
20
  "rxjs": "7.8.2"
21
21
  },
@@ -99,14 +99,23 @@ export const liveDefinition: GeneratedResourceDefinition = {
99
99
 
100
100
  const definitions = { [code]: liveDefinition };
101
101
  const provider = createApplicationProvider(definitions);
102
+ const routeBase = `/admin/resources/${code}`;
103
+ const routePaths = {
104
+ fallback: '/admin',
105
+ list: routeBase,
106
+ create: `${routeBase}/new`,
107
+ detail: `${routeBase}/:id`,
108
+ edit: `${routeBase}/:id/edit`,
109
+ };
102
110
  const initialPath =
103
- new URLSearchParams(window.location.search).get('initial') || `/${code}`;
111
+ new URLSearchParams(window.location.search).get('initial') || routeBase;
104
112
 
105
113
  function resourcePage(mode: 'list' | 'create' | 'edit' | 'detail') {
106
114
  return (
107
115
  <GeneratedResourcePage
108
116
  definition={liveDefinition}
109
117
  mode={mode}
118
+ paths={routePaths}
110
119
  resourceCode={code}
111
120
  />
112
121
  );
@@ -124,22 +133,22 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
124
133
  resources={[
125
134
  {
126
135
  name: code,
127
- list: `/${code}`,
128
- create: `/${code}/new`,
129
- edit: `/${code}/:id/edit`,
130
- show: `/${code}/:id`,
136
+ list: routePaths.list,
137
+ create: routePaths.create,
138
+ edit: routePaths.edit,
139
+ show: routePaths.detail,
131
140
  },
132
141
  ]}
133
142
  >
134
143
  <Routes>
135
- <Route path={`/${code}`} element={resourcePage('list')} />
136
- <Route path={`/${code}/new`} element={resourcePage('create')} />
144
+ <Route path={routePaths.list} element={resourcePage('list')} />
145
+ <Route path={routePaths.create} element={resourcePage('create')} />
137
146
  <Route
138
- path={`/${code}/:id`}
147
+ path={routePaths.detail}
139
148
  element={resourcePage('detail')}
140
149
  />
141
150
  <Route
142
- path={`/${code}/:id/edit`}
151
+ path={routePaths.edit}
143
152
  element={resourcePage('edit')}
144
153
  />
145
154
  </Routes>
@@ -31,7 +31,9 @@ test.describe('@live-data-api standard resource UI', () => {
31
31
  latitude: 30.234567,
32
32
  accuracy: 4,
33
33
  });
34
- await page.goto('/data-api-live.e2e.html?initial=/field-records');
34
+ await page.goto(
35
+ '/data-api-live.e2e.html?initial=/admin/resources/field-records',
36
+ );
35
37
  await expect(page.locator('.oxa-list-surface')).toBeVisible();
36
38
 
37
39
  await page.getByRole('button', { name: '新增字段协议记录' }).click();
@@ -102,7 +104,9 @@ test.describe('@live-data-api standard resource UI', () => {
102
104
  path: '/',
103
105
  },
104
106
  ]);
105
- await page.goto('/data-api-live.e2e.html?initial=/field-records');
107
+ await page.goto(
108
+ '/data-api-live.e2e.html?initial=/admin/resources/field-records',
109
+ );
106
110
  await expect(page.locator('.oxa-list-surface')).toBeVisible();
107
111
  await page.locator('.oxa-search-panel .ant-select').first().click();
108
112
  await page.getByText('标题', { exact: true }).last().click();
@@ -129,7 +133,9 @@ test.describe('@live-data-api standard resource UI', () => {
129
133
  path: '/',
130
134
  },
131
135
  ]);
132
- await page.goto('/data-api-live.e2e.html?initial=/field-records');
136
+ await page.goto(
137
+ '/data-api-live.e2e.html?initial=/admin/resources/field-records',
138
+ );
133
139
  await expect(
134
140
  page.getByText('当前平台用户无字段协议记录页面权限', { exact: true })
135
141
  ).toBeVisible();
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import ReactDOM from 'react-dom/client';
3
3
  import { Button, Typography } from 'antd';
4
4
  import {
5
- defineAdminContributions,
5
+ defineApplicationContributions,
6
6
  OpenXiangdaApplication,
7
7
  } from 'openxiangda/react';
8
8
  import type { DataFieldSurface, DataResourceSurface } from 'openxiangda/core';
@@ -145,7 +145,11 @@ function ContentOperationsPage() {
145
145
  return <Typography.Title level={2}>内容业务操作</Typography.Title>;
146
146
  }
147
147
 
148
- const adminContributions = defineAdminContributions(
148
+ function ResourcePortalPage() {
149
+ return <Typography.Title level={2}>用户端内容门户</Typography.Title>;
150
+ }
151
+
152
+ const applicationContributions = defineApplicationContributions(
149
153
  {
150
154
  contentOperations: {
151
155
  code: 'content-operations',
@@ -156,9 +160,20 @@ const adminContributions = defineAdminContributions(
156
160
  tabPersistence: 'session',
157
161
  keepAlive: 'none',
158
162
  },
163
+ resourcePortal: {
164
+ code: 'resource-portal',
165
+ path: '/resource-01',
166
+ label: '用户端内容门户',
167
+ surface: 'user',
168
+ tabPersistence: 'session',
169
+ keepAlive: 'none',
170
+ },
159
171
  } as const,
160
172
  {
161
- pages: { contentOperations: ContentOperationsPage },
173
+ pages: {
174
+ contentOperations: ContentOperationsPage,
175
+ resourcePortal: ResourcePortalPage,
176
+ },
162
177
  resources: {
163
178
  'resource-01': {
164
179
  toolbar: [
@@ -177,7 +192,7 @@ const adminContributions = defineAdminContributions(
177
192
  );
178
193
 
179
194
  if (location.pathname.endsWith('/resource-experience.e2e.html')) {
180
- history.replaceState({}, '', '/resource-01');
195
+ history.replaceState({}, '', '/admin/resources/resource-01');
181
196
  }
182
197
 
183
198
  ReactDOM.createRoot(document.getElementById('root')!).render(
@@ -216,7 +231,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
216
231
  {
217
232
  code: 'resource:resource-01:list',
218
233
  kind: 'resource-list',
219
- path: '/resource-01',
234
+ path: '/admin/resources/resource-01',
220
235
  label: '资源 01',
221
236
  navigationEligible: true,
222
237
  capability: resourceDefinitions['resource-01'].capabilities.read,
@@ -225,7 +240,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
225
240
  {
226
241
  code: 'resource:resource-01:detail',
227
242
  kind: 'resource-detail',
228
- path: '/resource-01/:id',
243
+ path: '/admin/resources/resource-01/:id',
229
244
  label: '资源 01 详情',
230
245
  navigationEligible: false,
231
246
  capability: resourceDefinitions['resource-01'].capabilities.read,
@@ -234,7 +249,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
234
249
  {
235
250
  code: 'resource:resource-01:create',
236
251
  kind: 'resource-create',
237
- path: '/resource-01/new',
252
+ path: '/admin/resources/resource-01/new',
238
253
  label: '新增资源 01',
239
254
  navigationEligible: false,
240
255
  capability: resourceDefinitions['resource-01'].capabilities.create,
@@ -243,7 +258,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
243
258
  {
244
259
  code: 'resource:resource-01:update',
245
260
  kind: 'resource-update',
246
- path: '/resource-01/:id/edit',
261
+ path: '/admin/resources/resource-01/:id/edit',
247
262
  label: '编辑资源 01',
248
263
  navigationEligible: false,
249
264
  capability: resourceDefinitions['resource-01'].capabilities.update,
@@ -252,7 +267,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
252
267
  {
253
268
  code: 'resource:resource-02:list',
254
269
  kind: 'resource-list',
255
- path: '/resource-02',
270
+ path: '/admin/resources/resource-02',
256
271
  label: '内容文章',
257
272
  navigationEligible: true,
258
273
  capability: resourceDefinitions['resource-02'].capabilities.read,
@@ -261,7 +276,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
261
276
  {
262
277
  code: 'resource:resource-03:list',
263
278
  kind: 'resource-list',
264
- path: '/resource-03',
279
+ path: '/admin/resources/resource-03',
265
280
  label: '内容专题',
266
281
  navigationEligible: true,
267
282
  capability: resourceDefinitions['resource-03'].capabilities.read,
@@ -270,7 +285,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
270
285
  {
271
286
  code: 'resource:resource-04:list',
272
287
  kind: 'resource-list',
273
- path: '/resource-04',
288
+ path: '/admin/resources/resource-04',
274
289
  label: '会员档案',
275
290
  navigationEligible: true,
276
291
  capability: resourceDefinitions['resource-04'].capabilities.read,
@@ -279,7 +294,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
279
294
  {
280
295
  code: 'resource:resource-05:list',
281
296
  kind: 'resource-list',
282
- path: '/resource-05',
297
+ path: '/admin/resources/resource-05',
283
298
  label: '门户轮播',
284
299
  navigationEligible: true,
285
300
  capability: resourceDefinitions['resource-05'].capabilities.read,
@@ -288,7 +303,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
288
303
  {
289
304
  code: 'resource:resource-06:list',
290
305
  kind: 'resource-list',
291
- path: '/resource-06',
306
+ path: '/admin/resources/resource-06',
292
307
  label: '工会小组',
293
308
  navigationEligible: true,
294
309
  capability: resourceDefinitions['resource-06'].capabilities.read,
@@ -297,7 +312,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
297
312
  {
298
313
  code: 'resource:resource-07:list',
299
314
  kind: 'resource-list',
300
- path: '/resource-07',
315
+ path: '/admin/resources/resource-07',
301
316
  label: '场地预约',
302
317
  navigationEligible: true,
303
318
  capability: resourceDefinitions['resource-07'].capabilities.read,
@@ -306,7 +321,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
306
321
  {
307
322
  code: 'resource:resource-08:list',
308
323
  kind: 'resource-list',
309
- path: '/resource-08',
324
+ path: '/admin/resources/resource-08',
310
325
  label: '场地不可用时段',
311
326
  navigationEligible: true,
312
327
  capability: resourceDefinitions['resource-08'].capabilities.read,
@@ -315,7 +330,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
315
330
  {
316
331
  code: 'resource:resource-09:list',
317
332
  kind: 'resource-list',
318
- path: '/resource-09',
333
+ path: '/admin/resources/resource-09',
319
334
  label: '场地档案',
320
335
  navigationEligible: true,
321
336
  capability: resourceDefinitions['resource-09'].capabilities.read,
@@ -324,7 +339,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
324
339
  {
325
340
  code: 'resource:resource-10:list',
326
341
  kind: 'resource-list',
327
- path: '/resource-10',
342
+ path: '/admin/resources/resource-10',
328
343
  label: '通知公告',
329
344
  navigationEligible: true,
330
345
  capability: resourceDefinitions['resource-10'].capabilities.read,
@@ -333,7 +348,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
333
348
  {
334
349
  code: 'resource:resource-11:list',
335
350
  kind: 'resource-list',
336
- path: '/resource-11',
351
+ path: '/admin/resources/resource-11',
337
352
  label: '活动报名',
338
353
  navigationEligible: true,
339
354
  capability: resourceDefinitions['resource-11'].capabilities.read,
@@ -342,7 +357,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
342
357
  {
343
358
  code: 'resource:resource-12:list',
344
359
  kind: 'resource-list',
345
- path: '/resource-12',
360
+ path: '/admin/resources/resource-12',
346
361
  label: '资料下载',
347
362
  navigationEligible: true,
348
363
  capability: resourceDefinitions['resource-12'].capabilities.read,
@@ -358,7 +373,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
358
373
  routeCode: 'content-operations',
359
374
  },
360
375
  ]}
361
- contributions={adminContributions}
376
+ contributions={applicationContributions}
362
377
  resourceDefinitions={resourceDefinitions}
363
378
  />
364
379
  </React.StrictMode>
@@ -221,7 +221,7 @@ test('renders the compiled desktop resource contract and shared workbench', asyn
221
221
  const definition = definitions[code];
222
222
  await page.goto(`${runtimeBase}/admin`);
223
223
  await expect(page.locator('.oxa-list-surface')).toBeVisible();
224
- await expect(page).toHaveURL(`${runtimeBase}/${code}`);
224
+ await expect(page).toHaveURL(`${runtimeBase}/admin/resources/${code}`);
225
225
  await expect(page.locator('.oxa-sider').getByText(definition.name, { exact: true })).toBeVisible();
226
226
  await expect(page.getByRole('button', { name: /导出$/ })).toBeVisible();
227
227
  await expect(page.getByRole('button', { name: /列设置$/ })).toBeVisible();
@@ -263,7 +263,9 @@ test('opens the stable admin entry under the preproduction gateway mount', async
263
263
  await mockPlatform(page, true, devRuntimeBase);
264
264
  await page.goto(`${devRuntimeBase}/admin`);
265
265
  await expect(page.locator('.oxa-list-surface')).toBeVisible();
266
- await expect(page).toHaveURL(`${devRuntimeBase}/${codes[0]}`);
266
+ await expect(page).toHaveURL(
267
+ `${devRuntimeBase}/admin/resources/${codes[0]}`,
268
+ );
267
269
  });
268
270
 
269
271
  test('uses the complete current-user role union when no Perspective is selected', async ({ page }) => {
@@ -339,7 +341,7 @@ test('preserves the left menu scroll position when switching resource pages', as
339
341
  .locator('.oxa-sider')
340
342
  .getByText('资料下载', { exact: true })
341
343
  .evaluate(node => (node as HTMLElement).click());
342
- await expect(page).toHaveURL('/resource-12');
344
+ await expect(page).toHaveURL('/admin/resources/resource-12');
343
345
  await expect.poll(() => scroller.evaluate(node => node.scrollTop)).toBe(240);
344
346
  });
345
347
 
@@ -357,6 +359,26 @@ test('mounts generated operation routes and typed actions in the unified Shell',
357
359
  await expect(page.locator('.oxa-sider')).toBeVisible();
358
360
  });
359
361
 
362
+ test('keeps the user portal and namespaced generated admin resource in one router', async ({ page }) => {
363
+ await mockPlatform(page, true, runtimeBase, true);
364
+ await page.goto('/resource-experience.e2e.html');
365
+ await page.evaluate(() => {
366
+ history.pushState({}, '', '/resource-01');
367
+ window.dispatchEvent(new PopStateEvent('popstate'));
368
+ });
369
+ await expect(
370
+ page.getByRole('heading', { name: '用户端内容门户' }),
371
+ ).toBeVisible();
372
+ await expect(page.locator('.oxa-sider')).toHaveCount(0);
373
+
374
+ await page.evaluate(() => {
375
+ history.pushState({}, '', '/admin/resources/resource-01');
376
+ window.dispatchEvent(new PopStateEvent('popstate'));
377
+ });
378
+ await expect(page.locator('.oxa-list-surface')).toBeVisible();
379
+ await expect(page.locator('.oxa-sider')).toHaveCount(1);
380
+ });
381
+
360
382
  test('fails closed on a direct operation route without its capability', async ({ page }) => {
361
383
  await mockPlatform(page, false, runtimeBase, false);
362
384
  await page.goto('/resource-experience.e2e.html');
@@ -374,7 +396,7 @@ test('keeps platform implementation guidance out of generated forms', async ({ p
374
396
  await mockPlatform(page, true, runtimeBase, true);
375
397
  await page.goto('/resource-experience.e2e.html');
376
398
  await page.getByRole('button', { name: '新增内容栏目' }).click();
377
- await expect(page).toHaveURL('/resource-01/new');
399
+ await expect(page).toHaveURL('/admin/resources/resource-01/new');
378
400
  const formLabels = page.locator('.ant-form-item-label label');
379
401
  await expect(formLabels.first()).toBeVisible();
380
402
  const labels = await formLabels.allTextContents();
@@ -419,7 +441,9 @@ test('renders platform mobile controls from declared field widgets', async ({ pa
419
441
  test.skip(!candidate, 'No mobile platform field is declared.');
420
442
  await mockPlatform(page);
421
443
  await page.setViewportSize({ width: 390, height: 844 });
422
- await page.goto(`${runtimeBase}/m/${candidate!.code}/new`);
444
+ await page.goto(
445
+ `${runtimeBase}/m/admin/resources/${candidate!.code}/new`,
446
+ );
423
447
  await expect(page.locator('.oxa-mobile-form')).toBeVisible();
424
448
 
425
449
  const widgets = new Set(Object.values(candidate!.definition.surface.fields).map(field => field.widget));
@@ -448,7 +472,7 @@ test('fails closed when the current user lacks the declared read capability', as
448
472
  test.skip(!codes.length, 'No resource permission exists in an empty application.');
449
473
  await mockPlatform(page, false);
450
474
  const code = codes[0];
451
- await page.goto(`${runtimeBase}/${code}`);
475
+ await page.goto(`${runtimeBase}/admin/resources/${code}`);
452
476
  await expect(page.getByText(new RegExp(`当前平台用户无${definitions[code].name}页面权限`))).toBeVisible();
453
477
  await expect(page.locator('.oxa-list-surface')).toHaveCount(0);
454
478
  });
@@ -14,7 +14,7 @@
14
14
  "@ant-design/icons": "6.2.3",
15
15
  "@refinedev/core": "5.0.12",
16
16
  "antd": "6.4.3",
17
- "openxiangda": "2.0.0-alpha.33",
17
+ "openxiangda": "2.0.0-alpha.34",
18
18
  "react": "19.2.8",
19
19
  "react-dom": "19.2.8",
20
20
  "react-router-dom": "7.8.2"
@@ -22,7 +22,7 @@
22
22
  "build": "pnpm --recursive build"
23
23
  },
24
24
  "devDependencies": {
25
- "openxiangda": "2.0.0-alpha.33",
25
+ "openxiangda": "2.0.0-alpha.34",
26
26
  "typescript": "5.9.3"
27
27
  }
28
28
  }