@oj-module/cli 0.2.29 → 0.2.30

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@oj-module/cli",
3
3
  "type": "module",
4
- "version": "0.2.29",
4
+ "version": "0.2.30",
5
5
  "description": "React Antd 模块工程命令行工具(含预构建宿主 shell-dist)",
6
6
  "license": "MIT",
7
7
  "exports": {
@@ -49,7 +49,7 @@
49
49
  ],
50
50
  "dependencies": {
51
51
  "@faker-js/faker": "^10.4.0",
52
- "@oj-module/runtime": "0.2.29",
52
+ "@oj-module/runtime": "0.2.30",
53
53
  "@redocly/cli": "^2.51.0",
54
54
  "@tailwindcss/vite": "^4.3.0",
55
55
  "cac": "^6.7.14",
@@ -235,9 +235,54 @@ async function hydrateUser() {
235
235
  useAuthStore.subscribe((state, prev) => {
236
236
  if (state.token !== prev.token) hydrateUser().then(() => {
237
237
  useAccessStore.getState().setAccessStore(getRoutes());
238
+ repatchShellRoutes();
238
239
  });
239
240
  });
240
241
  /**
242
+ * 宿主路由根 id:patchRoutes 重登记的 parentId(与 runtime 的 ROOT_ROUTE_ID
243
+ * 同名无碍——各自作用于各自 router 实例)。
244
+ */
245
+ var SHELL_ROOT_ID = "shell-root";
246
+ /** Boot 建表后持有的宿主 router 实例;token 变化时按新角色快照重登记其路由表。 */
247
+ var shellRouter = null;
248
+ /**
249
+ * 宿主路由表 children:index 重定向 + 模块路由(getHostRoutes 同源重写)+
250
+ * catch-all 兜底。Boot 建表与登录后重登记共用同一构建口,保证两处同源。
251
+ */
252
+ function buildShellChildren() {
253
+ const hostRoutes = getHostRoutes();
254
+ return [
255
+ {
256
+ index: true,
257
+ element: /* @__PURE__ */ jsx(Navigate, {
258
+ to: hostRoutes[0]?.path ?? "/",
259
+ replace: true
260
+ })
261
+ },
262
+ ...hostRoutes,
263
+ {
264
+ path: "*",
265
+ element: /* @__PURE__ */ jsx(CatchAll, {})
266
+ }
267
+ ];
268
+ }
269
+ /**
270
+ * 按当前角色快照重登记宿主路由表(B16 回归,2026-10-24)。
271
+ *
272
+ * setAccessStore 内部的 router.patchRoutes 打的是 runtime 单例 router,而宿主
273
+ * 渲染的是本文件 Boot 里自建的 router——仅调 setAccessStore 只会更新菜单
274
+ * (access store),路由表保持 Boot 期快照。后果:Boot 期 user-info 失败
275
+ * (后端闪断/过期 token 刷新失败)→ 角色空 → requiredRoles 门禁模块被过滤出
276
+ * 路由表;随后 SPA 登录(不刷新页面)菜单回来了,但 /system/* 仍不在路由表里,
277
+ * 直达恒落 catch-all 404「返回首页」,须硬刷新才恢复。
278
+ */
279
+ function repatchShellRoutes() {
280
+ if (!shellRouter) return;
281
+ shellRouter.patchRoutes(SHELL_ROOT_ID, buildShellChildren());
282
+ const { pathname, search } = shellRouter.state.location;
283
+ shellRouter.navigate(`${pathname}${search}`, { replace: true }).catch(() => {});
284
+ }
285
+ /**
241
286
  * 直链兜底页(catch-all 路由元素)。无匹配路由时渲染框架 `PageError`,
242
287
  * 把 title 设为框架 404 文案(`exception.notFoundTitle` = "404"),
243
288
  * 观感与无权限面板同构,替代 React Router 默认的「Unexpected Application Error!」。
@@ -299,27 +344,14 @@ function Boot() {
299
344
  await hydrateUser();
300
345
  await loadAll(manifest);
301
346
  if (cancelled) return;
302
- const hostRoutes = getHostRoutes();
303
- setRouter(createBrowserRouter([{
347
+ shellRouter = createBrowserRouter([{
304
348
  path: "/",
349
+ id: SHELL_ROOT_ID,
305
350
  element: /* @__PURE__ */ jsxs(ErrorBoundary, {
306
351
  FallbackComponent: PageError,
307
352
  children: [/* @__PURE__ */ jsx(LayoutEffects, {}), /* @__PURE__ */ jsx(Outlet, {})]
308
353
  }),
309
- children: [
310
- {
311
- index: true,
312
- element: /* @__PURE__ */ jsx(Navigate, {
313
- to: hostRoutes[0]?.path ?? "/",
314
- replace: true
315
- })
316
- },
317
- ...hostRoutes,
318
- {
319
- path: "*",
320
- element: /* @__PURE__ */ jsx(CatchAll, {})
321
- }
322
- ]
354
+ children: buildShellChildren()
323
355
  }], {
324
356
  /**
325
357
  * 子路径部署(oj `mounts: [{ prefix: /oj, web: dist, spa: true }]`):
@@ -330,7 +362,8 @@ function Boot() {
330
362
  *
331
363
  * 浏览器 URL 不受影响:react-router 只是在匹配/生成时剥/补前缀。
332
364
  */
333
- basename: base }));
365
+ basename: base });
366
+ setRouter(shellRouter);
334
367
  } catch (e) {
335
368
  if (!cancelled) setError(e instanceof Error ? e.message : String(e));
336
369
  }
@@ -304,10 +304,10 @@ function getAppInfo() {
304
304
  return {
305
305
  "pkg": {
306
306
  "name": "@oj-module/runtime",
307
- "version": "0.2.29",
307
+ "version": "0.2.30",
308
308
  "license": "MIT"
309
309
  },
310
- "lastBuildTime": "2026-10-24 20:31:54"
310
+ "lastBuildTime": "2026-10-24 23:41:26"
311
311
  };
312
312
  }
313
313
  var init_get_app_info = __esmMin((() => {}));
@@ -5,9 +5,9 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" href="/favicon.ico" />
7
7
  <title>React Antd Admin · Shell</title>
8
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-5zLHUGQHePNQzqOPB7RBQA=='; script-src-attr 'none'; connect-src 'self' data: https://modules.cdn.example.com; style-src 'self' https://modules.cdn.example.com 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://ant.design https://react.dev https://condorheroblog.github.io; form-action 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests" />
8
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-6NpMECPEhWQb/Ob12W+5HQ=='; script-src-attr 'none'; connect-src 'self' data: https://modules.cdn.example.com; style-src 'self' https://modules.cdn.example.com 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://ant.design https://react.dev https://condorheroblog.github.io; form-action 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests" />
9
9
 
10
- <script type="importmap" nonce="5zLHUGQHePNQzqOPB7RBQA==">{
10
+ <script type="importmap" nonce="6NpMECPEhWQb/Ob12W+5HQ==">{
11
11
  "imports": {
12
12
  "react": "/assets/react.js",
13
13
  "react/jsx-runtime": "/assets/jsx-runtime.js",
@@ -133,7 +133,7 @@
133
133
  "dayjs/locale/zh-cn": "/assets/ojm-dayjs-locale-zh-cn.js"
134
134
  }
135
135
  }</script>
136
- <script type="module" crossorigin src="/assets/index-CnJsHOe-.js"></script>
136
+ <script type="module" crossorigin src="/assets/index-FYt9CjFd.js"></script>
137
137
  </head>
138
138
  <body>
139
139
  <div id="root"></div>
@@ -7,9 +7,9 @@
7
7
  "react-router": "7.18.2",
8
8
  "react-router/dom": "7.18.2",
9
9
  "@tanstack/react-query": "5.102.8",
10
- "@oj-module/runtime": "0.2.29",
11
- "@oj-module/runtime/contract": "0.2.29",
12
- "@oj-module/runtime/contract/errors": "0.2.29",
10
+ "@oj-module/runtime": "0.2.30",
11
+ "@oj-module/runtime/contract": "0.2.30",
12
+ "@oj-module/runtime/contract/errors": "0.2.30",
13
13
  "@plane/kit": "0.1.20",
14
14
  "@plane/editor": "0.1.20",
15
15
  "antd": "6.6.1",
@@ -73,8 +73,11 @@ export default defineModule({
73
73
  peerRuntime: ">=0.0.0",
74
74
  // 路由级门禁(B16):非系统管理员不注入 /system 路由、菜单不可见;
75
75
  // API 级 403 由 assertSystemAdmin + service 层硬 enforcement 兜底。
76
- // 角色口径对齐后端 assertSystemAdmin(access.ts:90)——superadmin **或**
77
- // admin 均可;seed 的 admin 账号只有 superadmin,写 ["admin"] 会把它自己挡在门外。
76
+ // 角色口径对齐后端 assertSystemAdmin(access.ts:87)——superadmin **或**
77
+ // admin 均可,两者必须同时保留:D12(2026-10-22)起账号名=角色名,admin 与
78
+ // superadmin 是两个独立账号(seed.sql),只留 ["admin"] 会把 superadmin 挡在
79
+ // 门外(注册期过滤 → 路由消失 → 登录后 404 兜底)。改这两个值前先跑
80
+ // tests/system-guard.regression.spec.ts(只留 ["admin"] 会立刻变红)。
78
81
  config: { requiredRoles: ["admin", "superadmin"] },
79
82
  routes,
80
83
  i18n: {
@@ -8,9 +8,9 @@
8
8
  "react-router": "7.18.2",
9
9
  "react-router/dom": "7.18.2",
10
10
  "@tanstack/react-query": "5.102.8",
11
- "@oj-module/runtime": "0.2.29",
12
- "@oj-module/runtime/contract": "0.2.29",
13
- "@oj-module/runtime/contract/errors": "0.2.29",
11
+ "@oj-module/runtime": "0.2.30",
12
+ "@oj-module/runtime/contract": "0.2.30",
13
+ "@oj-module/runtime/contract/errors": "0.2.30",
14
14
  "@plane/kit": "0.1.20",
15
15
  "@plane/editor": "0.1.20",
16
16
  "antd": "6.6.1",