@oj-module/cli 0.2.28 → 0.2.29

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.28",
4
+ "version": "0.2.29",
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.28",
52
+ "@oj-module/runtime": "0.2.29",
53
53
  "@redocly/cli": "^2.51.0",
54
54
  "@tailwindcss/vite": "^4.3.0",
55
55
  "cac": "^6.7.14",
@@ -1,5 +1,5 @@
1
1
  import { StyleProvider } from "@ant-design/cssinjs";
2
- import { AntdApp, ErrorBoundary, LayoutEffects, PageError, getHostRoutes, loadAll, setupI18n, useAuthStore, usePreferences, useUserStore } from "@oj-module/runtime";
2
+ import { AntdApp, ErrorBoundary, LayoutEffects, PageError, getHostRoutes, getRoutes, loadAll, setupI18n, useAccessStore, useAuthStore, usePreferences, useUserStore } from "@oj-module/runtime";
3
3
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
4
  import { ConfigProvider, theme } from "antd";
5
5
  import { useEffect, useState } from "react";
@@ -231,12 +231,11 @@ async function hydrateUser() {
231
231
  /**
232
232
  * 登录/登出都会经 auth store reset() 清空 user store(宿主链无 AuthGuard 重新
233
233
  * 拉取),token 变化即重新定角色。
234
- *
235
- * 已知偏差:角色变化只影响渲染期判断,侧栏菜单是 Boot 期按当时角色登记的,
236
- * 不登录直接进站再登录的场景菜单需刷新一次才对(后端 403 硬兜底,不是权限泄漏)。
237
234
  */
238
235
  useAuthStore.subscribe((state, prev) => {
239
- if (state.token !== prev.token) hydrateUser();
236
+ if (state.token !== prev.token) hydrateUser().then(() => {
237
+ useAccessStore.getState().setAccessStore(getRoutes());
238
+ });
240
239
  });
241
240
  /**
242
241
  * 直链兜底页(catch-all 路由元素)。无匹配路由时渲染框架 `PageError`,
@@ -304,10 +304,10 @@ function getAppInfo() {
304
304
  return {
305
305
  "pkg": {
306
306
  "name": "@oj-module/runtime",
307
- "version": "0.2.28",
307
+ "version": "0.2.29",
308
308
  "license": "MIT"
309
309
  },
310
- "lastBuildTime": "2026-10-24 18:30:15"
310
+ "lastBuildTime": "2026-10-24 20:31:54"
311
311
  };
312
312
  }
313
313
  var init_get_app_info = __esmMin((() => {}));
@@ -28190,6 +28190,7 @@ init_layout_effects();
28190
28190
  init_locales();
28191
28191
  init_module_loader();
28192
28192
  init_slots();
28193
+ init_access();
28193
28194
  init_auth();
28194
28195
  init_tabs();
28195
28196
  init_user();
@@ -28203,4 +28204,4 @@ init_ri();
28203
28204
  init_menu_icons();
28204
28205
  init_tree();
28205
28206
  //#endregion
28206
- export { AccessControl, AccessControlRoles, AntdApp, BasicButton, BasicContent, BasicTable, EmbeddedIcon, ErrorBoundary, ExternalIcon, FormAvatarItem, FormTreeItem, Iframe, LayoutCenterIcon, LayoutEffects, LayoutLeftIcon, LayoutRightIcon, MixedNavigationIcon, OutsidePageIcon, PageError, ProfileCardIcon, RiAccountCircleLine, RiContrastFill, RiFullscreenExitLine, RiFullscreenLine, RiMailCheckLine, RiMoonIcon, RiReactjsLine, RiSunIcon, RiUserSettingsLine, ServerErrorIcon, SideNavigationIcon, TopNavigationIcon, TwoColumnNavigationIcon, accessControlCodes, bindingDiff, canAssignRole, canDeleteRole, canEditRoleBindings, defineModule, fetchAddMenuItem, fetchAddRoleItem, fetchAsyncRoutes, fetchChangePassword, fetchDeleteMenuItem, fetchDeleteRoleItem, fetchLdapLogin, fetchLogin, fetchLoginMethods, fetchLogout, fetchMenuByRoleId, fetchMenuList, fetchRbacAdminResetPassword, fetchRbacContactDetail, fetchRbacContacts, fetchRbacCreateRole, fetchRbacCreateUser, fetchRbacDeleteRole, fetchRbacOplog, fetchRbacOplogPost, fetchRbacPermissionList, fetchRbacRoleList, fetchRbacSetForbidden, fetchRbacSettings, fetchRbacUpdateBindings, fetchRbacUpdateSetting, fetchRbacUpdateUserRoles, fetchRbacUserList, fetchRefreshToken, fetchRoleList, fetchRoleMenu, fetchSolutionRoutes, fetchUpdateMenuItem, fetchUpdateRoleItem, fetchUserInfo, filterTree, getAllExpandedKeys, getAppInfo, getBooleanOptions, getHostRoutes, getModule, getModuleRoutePrefix, getModules, getRedirectPath, getRegisteredApiPrefix, getRegisteredStore, getRoutes, getYesNoOptions, groupByModule, groupSettingsByModule, handleTree, loadAll, mapTree, menuIcons, notify, permissionPrefix, request, resolveSiteBase, roleLabel, setupI18n, traverseTreeValues, unloadModule, useAccess, useAuthStore, usePreferences, useSlotNodes, useSolutionRoutes, useTabKey, useTabsStore, useUserStore, withSiteBase, external_exports as z };
28207
+ export { AccessControl, AccessControlRoles, AntdApp, BasicButton, BasicContent, BasicTable, EmbeddedIcon, ErrorBoundary, ExternalIcon, FormAvatarItem, FormTreeItem, Iframe, LayoutCenterIcon, LayoutEffects, LayoutLeftIcon, LayoutRightIcon, MixedNavigationIcon, OutsidePageIcon, PageError, ProfileCardIcon, RiAccountCircleLine, RiContrastFill, RiFullscreenExitLine, RiFullscreenLine, RiMailCheckLine, RiMoonIcon, RiReactjsLine, RiSunIcon, RiUserSettingsLine, ServerErrorIcon, SideNavigationIcon, TopNavigationIcon, TwoColumnNavigationIcon, accessControlCodes, bindingDiff, canAssignRole, canDeleteRole, canEditRoleBindings, defineModule, fetchAddMenuItem, fetchAddRoleItem, fetchAsyncRoutes, fetchChangePassword, fetchDeleteMenuItem, fetchDeleteRoleItem, fetchLdapLogin, fetchLogin, fetchLoginMethods, fetchLogout, fetchMenuByRoleId, fetchMenuList, fetchRbacAdminResetPassword, fetchRbacContactDetail, fetchRbacContacts, fetchRbacCreateRole, fetchRbacCreateUser, fetchRbacDeleteRole, fetchRbacOplog, fetchRbacOplogPost, fetchRbacPermissionList, fetchRbacRoleList, fetchRbacSetForbidden, fetchRbacSettings, fetchRbacUpdateBindings, fetchRbacUpdateSetting, fetchRbacUpdateUserRoles, fetchRbacUserList, fetchRefreshToken, fetchRoleList, fetchRoleMenu, fetchSolutionRoutes, fetchUpdateMenuItem, fetchUpdateRoleItem, fetchUserInfo, filterTree, getAllExpandedKeys, getAppInfo, getBooleanOptions, getHostRoutes, getModule, getModuleRoutePrefix, getModules, getRedirectPath, getRegisteredApiPrefix, getRegisteredStore, getRoutes, getYesNoOptions, groupByModule, groupSettingsByModule, handleTree, loadAll, mapTree, menuIcons, notify, permissionPrefix, request, resolveSiteBase, roleLabel, setupI18n, traverseTreeValues, unloadModule, useAccess, useAccessStore, useAuthStore, usePreferences, useSlotNodes, useSolutionRoutes, useTabKey, useTabsStore, useUserStore, withSiteBase, external_exports as z };
@@ -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-mh43xEkINEUP2sZNPLqVdA=='; 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-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" />
9
9
 
10
- <script type="importmap" nonce="mh43xEkINEUP2sZNPLqVdA==">{
10
+ <script type="importmap" nonce="5zLHUGQHePNQzqOPB7RBQA==">{
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-CtTVbohp.js"></script>
136
+ <script type="module" crossorigin src="/assets/index-CnJsHOe-.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.28",
11
- "@oj-module/runtime/contract": "0.2.28",
12
- "@oj-module/runtime/contract/errors": "0.2.28",
10
+ "@oj-module/runtime": "0.2.29",
11
+ "@oj-module/runtime/contract": "0.2.29",
12
+ "@oj-module/runtime/contract/errors": "0.2.29",
13
13
  "@plane/kit": "0.1.20",
14
14
  "@plane/editor": "0.1.20",
15
15
  "antd": "6.6.1",
@@ -0,0 +1,39 @@
1
+ import type { Server } from "node:http";
2
+ import type { AddressInfo } from "node:net";
3
+
4
+ import http from "node:http";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+
7
+ import { listenOnFreePort } from "../static-handler";
8
+
9
+ // 回归:listen(port, cb) 的 cb 在重试成功后以旧端口先 resolve——
10
+ // 日志「改用 5175」与「已启动 5174」并存且实际监听在 5175(2026-10-24)。
11
+ describe("listenOnFreePort", () => {
12
+ const cleanup: Server[] = [];
13
+
14
+ afterEach(() => {
15
+ for (const s of cleanup.splice(0))
16
+ s.close();
17
+ });
18
+
19
+ it("占用首端口时顺延,返回端口与实际监听端口一致", async () => {
20
+ const occupier = http.createServer(() => {}).listen(0);
21
+ cleanup.push(occupier);
22
+ await new Promise<void>(r => occupier.on("listening", r));
23
+ const busy = (occupier.address() as AddressInfo).port;
24
+
25
+ const server = http.createServer(() => {});
26
+ cleanup.push(server);
27
+ const port = await listenOnFreePort(server, busy);
28
+ expect(port).toBe(busy + 1);
29
+ // 旧 bug 在此暴露:resolve 的是 busy,实际却监听在 busy+1
30
+ expect((server.address() as AddressInfo).port).toBe(port);
31
+ });
32
+
33
+ it("首端口空闲时原样监听", async () => {
34
+ const server = http.createServer(() => {});
35
+ cleanup.push(server);
36
+ const port = await listenOnFreePort(server, 0);
37
+ expect((server.address() as AddressInfo).port).toBe(port);
38
+ });
39
+ });
package/src/build.ts CHANGED
@@ -106,6 +106,7 @@ export const accessControlCodes = _obj;
106
106
  export const AccessControlRoles = _obj;
107
107
  export const permissionPrefix = "permission:button";
108
108
  // store
109
+ export const useAccessStore = _fn;
109
110
  export const useAuthStore = _fn;
110
111
  // 方案路由归并 store(token 就绪自动重拉,R21)
111
112
  export const useSolutionRoutes = _fn;
@@ -229,7 +229,14 @@ export function createStaticHandler(opts: StaticHandlerOptions): (req: http.Inco
229
229
  export function listenOnFreePort(server: http.Server, startPort: number, maxTries = 10): Promise<number> {
230
230
  return new Promise((resolve, reject) => {
231
231
  const attempt = (port: number) => {
232
- server.once("error", (err: NodeJS.ErrnoException) => {
232
+ // 回调手动挂/摘(listen(port, cb) 的 cb 摘不掉前次失败的滞留闭包,
233
+ // 重试成功时会以旧端口先 resolve——日志与实listen端口漂移,2026-10-24 实测)
234
+ function cleanup() {
235
+ server.off("error", onError);
236
+ server.off("listening", onListen);
237
+ }
238
+ function onError(err: NodeJS.ErrnoException) {
239
+ cleanup();
233
240
  if (err.code === "EADDRINUSE" && port - startPort < maxTries) {
234
241
  console.warn(`[ojm] 端口 ${port} 已被占用,改用 ${port + 1}`);
235
242
  attempt(port + 1);
@@ -237,8 +244,16 @@ export function listenOnFreePort(server: http.Server, startPort: number, maxTrie
237
244
  else {
238
245
  reject(err);
239
246
  }
240
- });
241
- server.listen(port, () => resolve(port));
247
+ }
248
+ function onListen() {
249
+ cleanup();
250
+ // 取实绑端口(startPort=0 时 OS 自分随机端口,闭包 port 不可信)
251
+ const addr = server.address();
252
+ resolve(typeof addr === "object" && addr ? addr.port : port);
253
+ }
254
+ server.once("error", onError);
255
+ server.once("listening", onListen);
256
+ server.listen(port);
242
257
  };
243
258
  attempt(startPort);
244
259
  });
@@ -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.28",
12
- "@oj-module/runtime/contract": "0.2.28",
13
- "@oj-module/runtime/contract/errors": "0.2.28",
11
+ "@oj-module/runtime": "0.2.29",
12
+ "@oj-module/runtime/contract": "0.2.29",
13
+ "@oj-module/runtime/contract/errors": "0.2.29",
14
14
  "@plane/kit": "0.1.20",
15
15
  "@plane/editor": "0.1.20",
16
16
  "antd": "6.6.1",