create-windy 0.3.19 → 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 (213) hide show
  1. package/README.md +28 -13
  2. package/dist/cli.js +783 -280
  3. package/package.json +1 -1
  4. package/template/.agents/skills/windy-business-module/SKILL.md +34 -10
  5. package/template/.windy-template.json +2 -2
  6. package/template/AGENTS.md +18 -8
  7. package/template/README.md +32 -22
  8. package/template/apps/server/Dockerfile +1 -0
  9. package/template/apps/server/package.json +1 -0
  10. package/template/apps/server/scripts/validate-module-composition-once.ts +4 -0
  11. package/template/apps/server/scripts/validate-module-composition.ts +28 -0
  12. package/template/apps/server/src/agent/data-scope-routes.test.ts +20 -5
  13. package/template/apps/server/src/agent/routes.test.ts +20 -7
  14. package/template/apps/server/src/agent/routes.ts +14 -0
  15. package/template/apps/server/src/agent/runtime-bootstrap.ts +22 -6
  16. package/template/apps/server/src/agent/sandbox-worker.ts +2 -2
  17. package/template/apps/server/src/agent/unavailable-routes.ts +33 -0
  18. package/template/apps/server/src/app/server-composition.ts +56 -0
  19. package/template/apps/server/src/application-services.ts +2 -0
  20. package/template/apps/server/src/bulk-data/service.ts +14 -0
  21. package/template/apps/server/src/data-governance/export/authorization-port.ts +1 -1
  22. package/template/apps/server/src/data-governance/export/postgres-api-fixture.ts +18 -2
  23. package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +10 -9
  24. package/template/apps/server/src/data-governance/export/routes-test-fixture.ts +6 -8
  25. package/template/apps/server/src/data-governance/user-plaintext-routes.test.ts +4 -2
  26. package/template/apps/server/src/feature/routes.authorization.test.ts +26 -1
  27. package/template/apps/server/src/feature/routes.ts +33 -15
  28. package/template/apps/server/src/foundation-activation.test.ts +60 -0
  29. package/template/apps/server/src/foundation.ts +64 -5
  30. package/template/apps/server/src/guards.test.ts +23 -0
  31. package/template/apps/server/src/guards.ts +12 -0
  32. package/template/apps/server/src/index.ts +28 -34
  33. package/template/apps/server/src/installed-business-modules.ts +13 -24
  34. package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +1 -0
  35. package/template/apps/server/src/jobs/legacy-adapter.test.ts +106 -0
  36. package/template/apps/server/src/jobs/legacy-service-runtime.ts +40 -0
  37. package/template/apps/server/src/license/activation.test.ts +30 -0
  38. package/template/apps/server/src/license/activation.ts +26 -0
  39. package/template/apps/server/src/license/restriction.test.ts +16 -0
  40. package/template/apps/server/src/license/routes-offline-code.test.ts +6 -2
  41. package/template/apps/server/src/license/routes.ts +20 -4
  42. package/template/apps/server/src/license/runtime-service.test.ts +10 -2
  43. package/template/apps/server/src/module-bootstrap.ts +14 -23
  44. package/template/apps/server/src/module-composition.test.ts +43 -2
  45. package/template/apps/server/src/module-composition.ts +29 -0
  46. package/template/apps/server/src/module-host/durable-context-guard.test.ts +172 -0
  47. package/template/apps/server/src/module-host/durable-context-unavailable.test.ts +50 -1
  48. package/template/apps/server/src/module-host/durable-context.test.ts +42 -25
  49. package/template/apps/server/src/module-host/durable-context.ts +42 -2
  50. package/template/apps/server/src/module-host/durable-handler-registration.ts +96 -5
  51. package/template/apps/server/src/module-host/implementation-completeness.test.ts +185 -0
  52. package/template/apps/server/src/module-host/implementation-completeness.ts +132 -0
  53. package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
  54. package/template/apps/server/src/module-host/initialize.test.ts +78 -2
  55. package/template/apps/server/src/module-host/initialize.ts +15 -4
  56. package/template/apps/server/src/module-host/project-attachments.test.ts +92 -0
  57. package/template/apps/server/src/module-host/project-attachments.ts +58 -0
  58. package/template/apps/server/src/module-host/project-composition-package.test.ts +186 -0
  59. package/template/apps/server/src/module-host/project-composition.test.ts +260 -0
  60. package/template/apps/server/src/module-host/project-composition.ts +311 -0
  61. package/template/apps/server/src/module-host/request-context.ts +3 -0
  62. package/template/apps/server/src/module-host/route-declaration.ts +15 -6
  63. package/template/apps/server/src/module-host/route-installer.test.ts +15 -2
  64. package/template/apps/server/src/module-host/route-installer.ts +12 -2
  65. package/template/apps/server/src/module-host/route-registration.test.ts +13 -4
  66. package/template/apps/server/src/module-host/runtime-composition-compatibility.test.ts +63 -0
  67. package/template/apps/server/src/module-host/runtime-composition-compatibility.ts +46 -0
  68. package/template/apps/server/src/module-host/runtime-composition.ts +34 -0
  69. package/template/apps/server/src/module-host/search-provider-registration.test.ts +64 -0
  70. package/template/apps/server/src/module-host/search-provider-registration.ts +35 -0
  71. package/template/apps/server/src/module-host/static-route-authorization.test.ts +99 -0
  72. package/template/apps/server/src/module-host/static-route-authorization.ts +90 -0
  73. package/template/apps/server/src/module-host/task-definitions.ts +1 -0
  74. package/template/apps/server/src/module-host/user-directory-composition.test.ts +4 -1
  75. package/template/apps/server/src/module-host.test.ts +2 -0
  76. package/template/apps/server/src/module-preflight.test.ts +289 -0
  77. package/template/apps/server/src/module-preflight.ts +96 -0
  78. package/template/apps/server/src/module-runtime-validation.ts +1 -0
  79. package/template/apps/server/src/platform-app-bootstrap.ts +50 -0
  80. package/template/apps/server/src/platform-http-runtime.ts +32 -7
  81. package/template/apps/server/src/route-guards.test.ts +22 -0
  82. package/template/apps/server/src/route-guards.ts +10 -3
  83. package/template/apps/server/src/runtime-audit-query.ts +50 -0
  84. package/template/apps/server/src/runtime-feature.test.ts +46 -0
  85. package/template/apps/server/src/runtime-feature.ts +5 -0
  86. package/template/apps/server/src/runtime.ts +10 -49
  87. package/template/apps/server/src/scheduler/audit-action.test.ts +49 -0
  88. package/template/apps/server/src/scheduler/audit.ts +1 -1
  89. package/template/apps/server/src/scheduler/execution-recorder.ts +10 -5
  90. package/template/apps/server/src/scheduler/recovery-service.test.ts +4 -3
  91. package/template/apps/server/src/scheduler/service-idle.test.ts +4 -3
  92. package/template/apps/server/src/scheduler/service.test.ts +1 -0
  93. package/template/apps/server/src/scheduler/service.ts +18 -21
  94. package/template/apps/server/src/scheduler/types.ts +5 -1
  95. package/template/apps/server/src/startup-composition-validation.test.ts +35 -0
  96. package/template/apps/server/src/startup-composition-validation.ts +54 -0
  97. package/template/apps/server/src/startup-preflight.test.ts +36 -0
  98. package/template/apps/server/src/startup-preflight.ts +37 -0
  99. package/template/apps/server/src/system/audit-routes.test.ts +9 -3
  100. package/template/apps/server/src/system/department-routes.test.ts +5 -3
  101. package/template/apps/server/src/system/feature-routes.test.ts +4 -3
  102. package/template/apps/server/src/system/interface-catalog.test.ts +5 -3
  103. package/template/apps/server/src/system/interface-catalog.ts +3 -0
  104. package/template/apps/server/src/system/menu-routes.test.ts +5 -3
  105. package/template/apps/server/src/system/route-test-runtime.ts +24 -0
  106. package/template/apps/server/src/system/routes-validation.test.ts +7 -3
  107. package/template/apps/server/src/system/routes.test.ts +21 -11
  108. package/template/apps/server/src/system/routes.ts +11 -2
  109. package/template/apps/server/src/work-order/foundation-modules.test.ts +6 -4
  110. package/template/apps/server/src/work-order/routes.test.ts +8 -0
  111. package/template/apps/server/src/work-order/task.test.ts +6 -1
  112. package/template/apps/web/Dockerfile +5 -0
  113. package/template/apps/web/package.json +1 -0
  114. package/template/apps/web/scripts/validate-admin-ui-composition.ts +106 -0
  115. package/template/apps/web/src/app/web-composition.ts +24 -0
  116. package/template/apps/web/src/app/web-composition.webtest.ts +15 -0
  117. package/template/apps/web/src/layout/AppSidebar.vue +8 -1
  118. package/template/apps/web/src/layout/NavigationSearchDialog.vue +8 -1
  119. package/template/apps/web/src/layout/navigation.ts +29 -4
  120. package/template/apps/web/src/layout/navigation.webtest.ts +51 -0
  121. package/template/apps/web/src/pages/dashboard/dashboard-presenter.ts +6 -1
  122. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +202 -0
  123. package/template/apps/web/src/pages/work-orders/WorkOrderListPage.webtest.ts +51 -0
  124. package/template/apps/web/src/router/admin-extension-loader.ts +44 -0
  125. package/template/apps/web/src/router/admin-extension-loader.webtest.ts +49 -0
  126. package/template/apps/web/src/router/admin-ui-composition-plugin.ts +33 -0
  127. package/template/apps/web/src/router/admin-ui-composition-plugin.webtest.ts +20 -0
  128. package/template/apps/web/src/router/admin-ui-composition.ts +313 -0
  129. package/template/apps/web/src/router/admin-ui-composition.webtest.ts +225 -0
  130. package/template/apps/web/src/router/admin-ui-module-registry-plugin.ts +55 -0
  131. package/template/apps/web/src/router/admin-ui-module-registry-plugin.webtest.ts +46 -0
  132. package/template/apps/web/src/router/business-routes.ts +32 -45
  133. package/template/apps/web/src/router/business-routes.webtest.ts +40 -44
  134. package/template/apps/web/src/router/index.ts +64 -54
  135. package/template/apps/web/src/router/manifest-routes.ts +27 -7
  136. package/template/apps/web/src/router/manifest-routes.webtest.ts +10 -2
  137. package/template/apps/web/src/router/platform-admin-route-bindings.ts +58 -0
  138. package/template/apps/web/src/router/reserved-route-paths.ts +14 -0
  139. package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +10 -4
  140. package/template/apps/web/src/router/web-composition-host.ts +20 -0
  141. package/template/apps/web/src/services/work-order-api.ts +16 -0
  142. package/template/apps/web/src/services/work-order-api.webtest.ts +21 -0
  143. package/template/apps/web/src/virtual-admin-ui-modules.d.ts +5 -0
  144. package/template/apps/web/vite.config.ts +22 -1
  145. package/template/docs/architecture/migration-bundle.md +10 -7
  146. package/template/docs/architecture/module-manifest-composition.md +85 -3
  147. package/template/docs/architecture/search-provider-protocol.md +6 -1
  148. package/template/docs/architecture/server-module-interface.md +70 -7
  149. package/template/docs/development/business-web-routes.md +53 -50
  150. package/template/docs/development/module-lifecycle.md +48 -54
  151. package/template/docs/platform/navigation-registry.md +5 -4
  152. package/template/package.json +1 -1
  153. package/template/packages/config/package.json +1 -1
  154. package/template/packages/database/package.json +1 -1
  155. package/template/packages/example-work-order/package.json +6 -0
  156. package/template/packages/example-work-order/src/admin-page.ts +78 -0
  157. package/template/packages/example-work-order/src/admin.ts +35 -0
  158. package/template/packages/example-work-order/src/manifest.test.ts +62 -5
  159. package/template/packages/example-work-order/src/manifest.ts +54 -7
  160. package/template/packages/example-work-order/src/migration-bundle.ts +8 -0
  161. package/template/packages/example-work-order/src/server-module.ts +2 -2
  162. package/template/packages/jobs/package.json +1 -1
  163. package/template/packages/modules/index.ts +4 -0
  164. package/template/packages/modules/package.json +3 -2
  165. package/template/packages/modules/src/admin-ui-entry.ts +8 -0
  166. package/template/packages/modules/src/admin-ui-manifest.test.ts +52 -0
  167. package/template/packages/modules/src/admin-ui-manifest.ts +53 -0
  168. package/template/packages/modules/src/capability-audit-validation.test.ts +127 -0
  169. package/template/packages/modules/src/capability-audit-validation.ts +38 -0
  170. package/template/packages/modules/src/capability-guard-validation.ts +148 -0
  171. package/template/packages/modules/src/capability-key-validation.test.ts +150 -0
  172. package/template/packages/modules/src/composition-fixtures.test-support.ts +7 -5
  173. package/template/packages/modules/src/composition.test.ts +74 -9
  174. package/template/packages/modules/src/composition.ts +8 -0
  175. package/template/packages/modules/src/manifest.ts +1 -0
  176. package/template/packages/modules/src/migration-bundle-validation.test.ts +23 -0
  177. package/template/packages/modules/src/module-host-range.test.ts +57 -0
  178. package/template/packages/modules/src/module-host-range.ts +125 -0
  179. package/template/packages/modules/src/project-module-assembly.test.ts +133 -0
  180. package/template/packages/modules/src/project-module-assembly.ts +126 -0
  181. package/template/packages/modules/src/system-admin-routes.ts +6 -5
  182. package/template/packages/modules/src/system-admin-ui-catalog.ts +72 -0
  183. package/template/packages/modules/src/system-agent-tools.ts +2 -2
  184. package/template/packages/modules/src/system-api-binding.ts +43 -9
  185. package/template/packages/modules/src/system-api-control-plane.ts +202 -0
  186. package/template/packages/modules/src/system-api-data-governance.ts +4 -2
  187. package/template/packages/modules/src/system-api-operational.ts +57 -8
  188. package/template/packages/modules/src/system-api-permissions.ts +115 -21
  189. package/template/packages/modules/src/system-api-watermark.ts +3 -0
  190. package/template/packages/modules/src/system-audit-actions.ts +7 -0
  191. package/template/packages/modules/src/system-data-governance.ts +5 -2
  192. package/template/packages/modules/src/system-features.ts +31 -0
  193. package/template/packages/modules/src/system-governed-export-bindings.ts +8 -4
  194. package/template/packages/modules/src/system-module-catalog.test.ts +39 -4
  195. package/template/packages/modules/src/system-module-catalog.ts +68 -7
  196. package/template/packages/modules/src/system-module-tasks.ts +8 -1
  197. package/template/packages/modules/src/system-modules-access.test.ts +4 -0
  198. package/template/packages/modules/src/system-modules.test.ts +10 -2
  199. package/template/packages/modules/src/system-modules.ts +6 -5
  200. package/template/packages/modules/src/system-permissions.ts +4 -0
  201. package/template/packages/modules/user/module.ts +19 -2
  202. package/template/packages/server-sdk/index.ts +8 -0
  203. package/template/packages/server-sdk/package.json +2 -1
  204. package/template/packages/server-sdk/src/durable-handler.ts +16 -1
  205. package/template/packages/server-sdk/src/module-adapter.test.ts +44 -0
  206. package/template/packages/server-sdk/src/module-adapter.ts +59 -0
  207. package/template/packages/server-sdk/src/route-definition.ts +3 -2
  208. package/template/packages/shared/package.json +1 -1
  209. package/template/packages/shared/src/audit.test.ts +7 -0
  210. package/template/packages/shared/src/audit.ts +16 -0
  211. package/template/packages/shared/src/migration-bundle-validation.ts +9 -11
  212. package/template/packages/shared/src/rbac.ts +2 -0
  213. package/template/apps/web/src/app/business-routes.ts +0 -22
@@ -2,6 +2,7 @@ import { describe, expect, test } from "vitest";
2
2
  import type { RouteRecordRaw } from "vue-router";
3
3
  import routerSource from "./index.ts?raw";
4
4
  import fixtureSource from "./router-test-fixtures.ts?raw";
5
+ import compositionSource from "../app/web-composition.ts?raw";
5
6
  import { collectRouteAccess } from "./router-test-fixtures";
6
7
 
7
8
  describe("Router shared-scaffold 中性契约", () => {
@@ -25,16 +26,21 @@ describe("Router shared-scaffold 中性契约", () => {
25
26
  expect(routerSource).toContain("component: errorPageComponents.network");
26
27
  });
27
28
 
28
- test("平台路由组合 project-owned 业务注册表且保持在兜底页之前", () => {
29
+ test("平台路由组合 project-owned Admin UI 声明且保持在兜底页之前", () => {
29
30
  expect(routerSource).toContain(
30
- 'import { installedBusinessAdminRoutes } from "@/app/business-routes";',
31
+ 'import { webAdminUiComposition } from "./web-composition-host";',
31
32
  );
32
33
  const businessIndex = routerSource.indexOf(
33
- "...materializeBusinessAdminRoutes(installedBusinessAdminRoutes)",
34
+ "...materializeBusinessAdminRoutes(webAdminUiComposition.modules)",
35
+ );
36
+ const fallbackIndex = routerSource.indexOf(
37
+ "path: reservedWebRoutePaths.notFound",
34
38
  );
35
- const fallbackIndex = routerSource.indexOf('path: "/:pathMatch(.*)*"');
36
39
  expect(businessIndex).toBeGreaterThan(0);
37
40
  expect(businessIndex).toBeLessThan(fallbackIndex);
41
+ expect(compositionSource).toContain("defineWebComposition");
42
+ expect(compositionSource).not.toContain("featureKey");
43
+ expect(compositionSource).not.toContain("permissionKey");
38
44
  });
39
45
 
40
46
  test("测试快照只从项目注入路由递归派生访问键", () => {
@@ -0,0 +1,20 @@
1
+ import {
2
+ systemAdminUiManifests,
3
+ validateProjectModuleAssembly,
4
+ } from "@southwind-ai/modules/admin-ui";
5
+ import { installedAdminUiModules } from "virtual:windy-admin-ui-modules";
6
+ import moduleAssembly from "../../../../windy.modules.json";
7
+ import { webComposition } from "../app/web-composition";
8
+ import { resolveAdminUiComposition } from "./admin-ui-composition";
9
+
10
+ /**
11
+ * Host 对项目组合声明的唯一解析入口。
12
+ *
13
+ * Router、Navigation 和 Vite 构建门禁必须消费同一结果,避免运行时与构建时规则漂移。
14
+ */
15
+ export const webAdminUiComposition = resolveAdminUiComposition({
16
+ declaration: webComposition,
17
+ assembly: validateProjectModuleAssembly(moduleAssembly),
18
+ platformModules: systemAdminUiManifests(),
19
+ installedModules: installedAdminUiModules,
20
+ });
@@ -0,0 +1,16 @@
1
+ import type {
2
+ WorkOrderListQuery,
3
+ WorkOrderPage,
4
+ } from "@southwind-ai/example-work-order/contract";
5
+ import { apiRequest } from "./http";
6
+
7
+ export function listWorkOrders(query: WorkOrderListQuery = {}) {
8
+ const params = new URLSearchParams();
9
+ if (query.page !== undefined) params.set("page", String(query.page));
10
+ if (query.pageSize !== undefined) {
11
+ params.set("pageSize", String(query.pageSize));
12
+ }
13
+ if (query.q?.trim()) params.set("q", query.q.trim());
14
+ const search = params.toString();
15
+ return apiRequest<WorkOrderPage>(`/work-orders${search ? `?${search}` : ""}`);
16
+ }
@@ -0,0 +1,21 @@
1
+ import { beforeEach, describe, expect, test, vi } from "vitest";
2
+
3
+ const mocks = vi.hoisted(() => ({
4
+ request: vi.fn(),
5
+ }));
6
+
7
+ vi.mock("./http", () => ({ apiRequest: mocks.request }));
8
+
9
+ import { listWorkOrders } from "./work-order-api";
10
+
11
+ describe("示例工单 API", () => {
12
+ beforeEach(() => mocks.request.mockReset());
13
+
14
+ test("列表查询保留分页与搜索条件", () => {
15
+ listWorkOrders({ page: 2, pageSize: 20, q: "登录" });
16
+
17
+ expect(mocks.request).toHaveBeenCalledWith(
18
+ "/work-orders?page=2&pageSize=20&q=%E7%99%BB%E5%BD%95",
19
+ );
20
+ });
21
+ });
@@ -0,0 +1,5 @@
1
+ declare module "virtual:windy-admin-ui-modules" {
2
+ import type { AdminUiModule } from "./router/admin-ui-composition";
3
+
4
+ export const installedAdminUiModules: readonly AdminUiModule[];
5
+ }
@@ -1,16 +1,37 @@
1
+ import { spawnSync } from "node:child_process";
1
2
  import { fileURLToPath, URL } from "node:url";
2
3
  import tailwindcss from "@tailwindcss/vite";
3
4
  import vue from "@vitejs/plugin-vue";
4
5
  import { defineConfig } from "vite";
6
+ import moduleAssembly from "../../windy.modules.json";
7
+ import { validateProjectModuleAssembly } from "../../packages/modules/src/project-module-assembly";
5
8
  import { projectFingerprint, windyDevIdentityPlugin } from "./dev/dev-identity";
9
+ import { adminUiCompositionValidationPlugin } from "./src/router/admin-ui-composition-plugin";
10
+ import { adminUiModuleRegistryPlugin } from "./src/router/admin-ui-module-registry-plugin";
6
11
 
7
12
  const fingerprint =
8
13
  process.env.WINDY_DEV_FINGERPRINT || projectFingerprint(process.cwd());
9
14
  const devPort = Number(process.env.WINDY_DEV_PORT || 8_746);
10
15
  const usePolling = process.env.WINDY_DEV_USE_POLLING === "1";
16
+ const adminUiValidatorEntry = fileURLToPath(
17
+ new URL("./scripts/validate-admin-ui-composition.ts", import.meta.url),
18
+ );
11
19
 
12
20
  export default defineConfig({
13
- plugins: [vue(), tailwindcss(), windyDevIdentityPlugin(fingerprint)],
21
+ plugins: [
22
+ adminUiModuleRegistryPlugin(validateProjectModuleAssembly(moduleAssembly)),
23
+ adminUiCompositionValidationPlugin({
24
+ validatorEntry: adminUiValidatorEntry,
25
+ runValidator: (entry) => {
26
+ const result = spawnSync("bun", [entry], { stdio: "inherit" });
27
+ if (result.error) throw result.error;
28
+ return result.status ?? 1;
29
+ },
30
+ }),
31
+ vue(),
32
+ tailwindcss(),
33
+ windyDevIdentityPlugin(fingerprint),
34
+ ],
14
35
  resolve: {
15
36
  alias: {
16
37
  "@": fileURLToPath(new URL("./src", import.meta.url)),
@@ -1,6 +1,6 @@
1
1
  # Migration Bundle 契约
2
2
 
3
- 更新时间:2026-07-25。
3
+ 更新时间:2026-07-29。
4
4
 
5
5
  ## 目标与边界
6
6
 
@@ -22,8 +22,9 @@ Migration Bundle 是模块级的数据库迁移机制:业务模块把迁移作
22
22
  - `formatVersion`:固定为 `"1"`,其它版本直接拒绝;
23
23
  - `module` / `version`:必须与 Manifest 的 `name` / `version` 完全一致,
24
24
  `version` 必须是 semver(`x.y.z`,允许预发布后缀);
25
- - `dependencies`:可选,声明本模块依赖的其它模块,必须与 Manifest `dependencies`
26
- 一致;被依赖模块当前也必须携带 Bundle,否则按“依赖未安装模块”拒绝;
25
+ - `dependencies`:可选,只声明 DDL 排序所需的其它 Migration Bundle,必须是
26
+ Manifest `dependencies` 的子集;Manifest 可以额外声明没有数据库迁移关系的运行时
27
+ 依赖。Bundle 中的每个依赖当前都必须携带 Bundle,否则按“依赖未安装模块”拒绝;
27
28
  - `migrations`:`BundleMigrationDefinition` 列表,字段为 `id`、`module`、
28
29
  `description`、`direction`、`checksum`、可选 `dependsOn` 和 `run(context)`。
29
30
 
@@ -43,9 +44,9 @@ Migration Bundle 是模块级的数据库迁移机制:业务模块把迁移作
43
44
  (不依赖 drizzle),`@southwind-ai/database` 的 runner 与
44
45
  `@southwind-ai/modules` 的启动校验共用同一份实现:
45
46
 
46
- - `normalizeManifestBundles(modules)`:校验 Bundle 与 Manifest 的
47
- module/version/dependencies/migrations 完全一致,且 `schemaExports` 只引用
48
- Bundle 中存在的 Migration;
47
+ - `normalizeManifestBundles(modules)`:校验 Bundle 与 Manifest 的 module/version
48
+ 一致、Bundle dependencies 是 Manifest runtime dependencies 的子集、migrations
49
+ 一致,且 `schemaExports` 只引用 Bundle 中存在的 Migration;
49
50
  - `validateAndOrderBundles(bundles)`:拒绝格式版本不支持、非 semver 版本、
50
51
  重复模块、重复 Migration ID、未声明的跨模块 `dependsOn`,并对模块依赖做
51
52
  拓扑排序,循环依赖即抛错;
@@ -64,7 +65,9 @@ Migration Bundle 是模块级的数据库迁移机制:业务模块把迁移作
64
65
  才把显式 Bundle 交给 PostgreSQL runner。
65
66
 
66
67
  示例:`packages/example-work-order` 的 `workOrderMigrationBundle` 是唯一随
67
- 仓库分发的业务 Bundle,其合法性由启动链路的回归测试覆盖。
68
+ 仓库分发的业务 Bundle。它的 Manifest 依赖身份、搜索、调度和存储等运行时模块,但
69
+ Bundle 没有跨模块 DDL 排序要求,因此不复制这些运行时依赖;其合法性由启动链路的
70
+ 回归测试覆盖。
68
71
 
69
72
  ## PostgreSQL 启动执行
70
73
 
@@ -1,10 +1,13 @@
1
1
  # Module Manifest 组合契约
2
2
 
3
- 更新时间:2026-07-23。
3
+ 更新时间:2026-07-29。
4
4
 
5
5
  ## 目标与边界
6
6
 
7
- Module Manifest 是模块公开能力的唯一声明 Interface;Server、Web 等宿主只提供具体 Implementation,并在 Composition Root 通过同一个 `validateModuleComposition` Seam 完成 fail-fast 校验。本批不实现业务搜索,只提供通用 Provider 注册位。
7
+ Module Manifest 是模块公开能力的唯一声明 Interface;Server、Web 等宿主只提供具体
8
+ Implementation,并在 Composition Root 通过同一个 `validateModuleComposition` Seam
9
+ 完成 fail-fast 校验。搜索等业务实现由模块 Provider 提供,平台只拥有通用注册、治理和
10
+ 执行契约。
8
11
 
9
12
  Manifest 当前覆盖:
10
13
 
@@ -42,12 +45,48 @@ License 配置必须引用同一 Manifest 中已注册的 Feature/Permission。
42
45
  项目构建时和服务监听前。这样权限目录与 License/Feature 目录不会依赖第二份手写 Key
43
46
  列表,也不会出现只注册页面或 API 的“幽灵功能”。
44
47
 
48
+ ### 当前执行状态
49
+
50
+ 第一批运行时组合门禁已经落地:非目录菜单、Admin Route、非匿名 API、定时任务、
51
+ Agent Tool、AI Operation 与 Provider 必须同时声明 Permission 和 Feature;缺少任一
52
+ Guard 会由 `validateModuleComposition` 在宿主开始监听前拒绝。匿名基础设施 API 是
53
+ 显式例外,不通过字段可选性隐式放行。
54
+
55
+ Feature 管理页面和 API 由 `system.feature` Feature 与 `system.feature.*` Permission
56
+ 共同保护。为避免控制面自锁,`system.feature` 是不可关闭、隐藏或附加 License 限制的
57
+ 核心恢复能力;关闭 `system.rbac` 后,具备管理权限的开发或超级管理员仍可重新开启它。
58
+
59
+ 第二批同基础 Key 门禁已经启用:每个 Permission 必须存在去掉末段动作后的 Feature,
60
+ 所有受 Guard 保护的入口和数据治理策略也必须引用该 Feature。用户、角色、部门、菜单和
61
+ 字典分别使用 `system.user`、`system.role`、`system.department`、`system.menu` 与
62
+ `system.dict`,并显式依赖总开关 `system.rbac`;关闭总开关会让资源能力以
63
+ `dependency-missing` fail closed,但不会锁死独立的 Feature 恢复控制面。
64
+
65
+ API、Task、Tool、AI Operation、Provider 与受治理资源的 Audit Action 已进入 Manifest
66
+ 引用校验。分发目录会交叉校验运行时依赖,并在生命周期命令写文件前检查 Windy range、
67
+ 依赖、冲突、runtime activation 与 deployment install 能力;当前内置模块都需要
68
+ project-owned Composition,因此部署后的 add/update/remove 会在计划阶段 fail closed。
69
+
70
+ 普通 Web build 会加载每个启用 package 的 `./admin` 和 canonical `./manifest`,核对窄
71
+ 投影、Host range、页面实现与路由碰撞。Server build 和普通启动会通过无数据库预检子
72
+ 进程执行完整 Composition、实现、依赖闭包和 attachment 校验,再允许审计恢复、
73
+ Migration、Worker 与监听。不存在“构建通过、启动时才第一次发现未知能力”的宽松路径。
74
+
75
+ 平台静态 HTTP Route 通过 `createManifestAuthorizedRouteApp()` 包装,并由
76
+ `createStaticRouteAuthorizer()` 使用同一 Manifest Feature、Permission、License 与
77
+ 模块启用状态完成请求时 Guard;实际 Elysia Route 也必须反向存在于 Manifest。只有显式
78
+ 标记 `immutableCoreAccess` 的健康探针和认证恢复入口可以跳过运行态 Guard,但它们仍须
79
+ 具有 Manifest 声明,不能用匿名字段隐式绕过。
80
+
45
81
  ## 模块与 Adapter
46
82
 
47
83
  - `@southwind-ai/modules` 是深 Module:拥有声明类型、依赖排序、引用规则和组合验证,不依赖 Elysia 或 Vue。
48
84
  - `systemModuleFactories` 是 15 个平台模块的运行时目录;`systemFoundationModule()` 仅保留为兼容聚合视图,不再作为 Composition Root 中唯一的巨型平台模块。
49
85
  - 多个模块可以向同一菜单目录贡献入口;`composeModuleMenus()` 合并一致的目录壳,并在标题、类型、顺序或 Guard 漂移时 fail fast。
50
- - Server Adapter 提供 API Route、Task、Tool、Provider Handler Key;启动监听前验证。
86
+ - Server Adapter 提供 API Route、Task、Tool、Provider 与 Durable Handler Key;启动
87
+ 监听前验证。Durable Handler 还必须绑定 Manifest 中同一能力的 Feature、Permission
88
+ 与 Audit Action;Host 冻结该绑定,并在每次首次执行、重试和恢复领取时按当前
89
+ Actor、License、Feature 与 RBAC 状态重新 Guard。
51
90
  - 业务模块可在 `auditActions` 中直接注册包含 Key、名称和说明的动作定义;组合后的
52
91
  Audit Action Catalog 与 License Catalog 会通过 Foundation / Admin Bootstrap 契约开放,
53
92
  管理端筛选与展示不维护第二份静态业务 Key。
@@ -59,6 +98,45 @@ License 配置必须引用同一 Manifest 中已注册的 Feature/Permission。
59
98
 
60
99
  这种边界允许宿主替换实现,同时保持 Manifest 的能力语义稳定。
61
100
 
101
+ ## 项目装配与 Host 协议
102
+
103
+ `windy.modules.json` 是 Web 与 Server 共用的 project-owned 装配声明:
104
+
105
+ - `package=null` 表示由 Host 提供实现的平台模块;其它项使用稳定 npm package 名称;
106
+ - `enabled` 控制模块是否进入运行组合,`admin.visible` 只控制入口呈现;
107
+ - `order`、`config` 属于项目选择;版本仍由 `package.json` 与 `bun.lock` 决定;
108
+ - 两个核心模块必须启用,依赖必须形成已启用闭包,重复名称/顺序或非法 package 名称
109
+ 直接拒绝。
110
+
111
+ `system.settings` 的 Admin Route 引用外部 `system.admin`,`system.storage` 承载
112
+ Settings 声明的公共品牌文件 Route,`system.data-governance` 也向其它 Feature
113
+ 贡献治理能力;Host 会拒绝只通过 registry 停用这三类跨模块贡献者。需要排除时必须在
114
+ 创建期物理裁剪,或由已部署项目执行人工迁移。
115
+
116
+ Web 的 Vite virtual registry 只静态 import 启用 package 的 `${package}/admin`。该导出
117
+ 只含 `AdminUiManifest`、Host range 和页面 loader;构建时再与
118
+ `${package}/manifest` 的精确窄投影比对。项目在
119
+ `apps/web/src/app/web-composition.ts` 只覆盖页面 loader 和呈现,不复制治理字段。
120
+ 整个 `apps/web/src` 都是 project-owned;Host 源码可替换,但失去协议后构建只诊断,
121
+ 不会覆盖项目实现。
122
+
123
+ Server package 的 canonical 声明来自 `${package}/manifest`。根
124
+ `windyServerModule`、本地 Adapter 返回的 `module.manifest`,以及根可选的
125
+ `windyModuleManifest` 都必须复用该同一对象。`self-contained` Adapter 可以由 Host
126
+ 动态加载;`project-attachment` 只能在 project-owned
127
+ `apps/server/src/app/server-composition.ts` 中声明,并通过同名 attachment 接收项目
128
+ Repository 等 runtime。整组 attachment 会先于任何 `setup()` 完成校验,避免部分注册。
129
+
130
+ ## License 激活边界
131
+
132
+ 未选择 `system.license` 或没有启用 License 配置时,Server 使用明确的 free/no-license
133
+ 路径:License 版本、Entitlement 与限制态 Guard 不阻止产品功能,Feature 与 RBAC 仍
134
+ 照常执行。模块已生成但在 registry 中禁用时,已声明 License runtime API Route 仍保留,
135
+ 统一 `StaticRouteAuthorizer` 会以 `feature-disabled` 返回 403,不能因省略 Route
136
+ 破坏声明/实现完整性,也不能绕过 Guard。只有模块与配置都启用后,后端 License Provider
137
+ 才执行最终授权;限制态下只有显式 License 恢复入口拥有窄
138
+ `admin-admission` 例外,Admin 的普通开关和页面不能自行放行。
139
+
62
140
  ## Capability Key 重命名边界
63
141
 
64
142
  平台当前没有 Module、Feature、Permission、Task、Tool、Operation 或 API Key 的通用
@@ -75,5 +153,9 @@ rename/alias seam。Composition、Guard、history 和审计都按 Manifest 中
75
153
  - 第一批:类型、声明级验证和失败回归。
76
154
  - 第二批(已完成):Server 直接读取 Elysia 实际路由表、Scheduler definitions 和 Agent Tool Handler Registry,在 `listen` 与 Worker 启动前执行 fail-fast。无数据库开发模式下不会注册的密码管理与个人资料路由,由其实际注册函数导出 Implementation 描述,避免把条件装配误判成缺少代码实现。
77
155
  - 第三批(已完成):Web 只注册页面组件、name 和 props,path、标题、Permission 与 Feature 由 Manifest Admin Route Adapter 生成;模块加载时校验所有 Admin Route 都有实现。License 恢复页的“已认证即可直访”作为 `directAccess: authenticated` 显式声明,Sidebar 可见性仍使用 Manifest Permission/Feature。
156
+ - 第四批(已完成):Web/Server 共用 `windy.modules.json`,package 使用
157
+ `./admin`、`./manifest` 与 `windyServerModule` 标准导出;Web build、Server build
158
+ 和普通启动执行 canonical projection/identity、Host range、依赖闭包、实现完整性与
159
+ runtime attachment 门禁。
78
160
 
79
161
  每批都必须更新本文件与产品任务状态后再提交。
@@ -92,4 +92,9 @@ Composition Root 把 Server 实际注册的搜索 API 与 Provider Handler Key
92
92
 
93
93
  示例 Work Order 已注册 `work-order.ticket.search`,Provider 只调用 `WorkOrderService.list`,后者把查询、count、分页与服务端 dataScope 一起交给 scoped Repository;测试会让裸 `Repository.list` 主动抛错,并确认本人范围不会返回其他负责人的匹配工单。另一个实现相同 Key/版本的测试 Provider 可以在不修改 Core 的情况下替换工单 Adapter。
94
94
 
95
- 生成器默认产物保留 Work Order Provider;`--no-example` 完整移除业务 Provider、schema 与 migration 引用,但保留搜索 Core、Guarded API 和类型化空 Registry。`bun run test:create-windy` 对两种生成物执行真实安装、typecheck、test、build 和 Server 启动,并显式断言该移除边界;`bun run test:db:data-scope` 在真实 PostgreSQL 中分别验证核心用户资源与可删除工单资源的 Repository 级范围约束。
95
+ 首次创建显式选择 `--module work-order` 时才包含 Work Order Provider;默认选择和
96
+ `--no-example` 都会完整移除业务 Provider、schema 与 migration 引用,但保留搜索 Core、
97
+ Guarded API 和类型化空 Registry。`bun run test:create-windy` 对包含/排除示例的生成物
98
+ 执行真实安装、typecheck、test、build 和 Server 启动,并显式断言该移除边界;
99
+ `bun run test:db:data-scope` 在真实 PostgreSQL 中分别验证核心用户资源与可删除工单资源
100
+ 的 Repository 级范围约束。
@@ -2,29 +2,88 @@
2
2
 
3
3
  ## 定位
4
4
 
5
- Server Module Interface 是可安装业务模块与平台宿主之间的唯一契约:下游业务包(独立仓库)以一个 `InstallableServerModule` 同时贡献 `manifest`(声明)与 `setup(host)`(实现注册),宿主按统一顺序装配 Route、Task、Tool、Upload Policy、Role Preset、Search Provider 与 Migration Bundle,不再为每个模块手工接线。
5
+ Server Module Interface 是业务模块与平台宿主之间的唯一契约:业务 package 以
6
+ `InstallableServerModule` 同时贡献 `manifest`(声明)与 `setup(host)`(实现注册),
7
+ 宿主按统一顺序装配 Route、Task、Tool、Upload Policy、Role Preset、Search Provider
8
+ 与 Migration Bundle,不再为每个模块手工维护能力 Key。
6
9
 
7
10
  分层:
8
11
 
9
12
  - `packages/server-sdk`:公开 Interface。纯类型加少量校验辅助,不依赖 Elysia / Drizzle / 宿主内部类型;
10
13
  - `apps/server/src/module-host/`:宿主实现。注册收集、Manifest 一致性校验、Guarded Route 安装、受限 Port 构造与各子系统适配;
11
- - `packages/example-work-order`:拥有 Manifest 与 `setup(host)` 注册过程;
12
- - `apps/server/src/example-modules.ts`:只注入 Repository、Handler 等宿主适配器,并提供统一的运行时 attach;
13
- - `apps/server/src/installed-business-modules.ts`:唯一安装清单,Foundation、Host 初始化和运行时 attach 都从该清单派生。
14
+ - `packages/example-work-order`:拥有 canonical Manifest、`setup(host)` 注册过程和
15
+ package Adapter;
16
+ - `apps/server/src/app/server-composition.ts`:project-owned 组合声明,读取
17
+ `windy.modules.json`,选择本地 Adapter 与 runtime attachment;
18
+ - `apps/server/src/module-host/project-composition.ts`:解析 package、Host range、
19
+ Manifest identity、启用依赖闭包和运行模式。
20
+
21
+ ## package 与项目组合
22
+
23
+ 标准 package 始终使用 canonical Manifest,并按运行模式选择 Adapter:
24
+
25
+ - `${package}/manifest` 导出 `windyModuleManifest`,是 Host 的 canonical 声明;
26
+ - self-contained package 根导出 `windyServerModule`;
27
+ - 需要项目 runtime 的 package 在 project Composition 注册本地 Adapter,不要求动态
28
+ 根 Adapter;
29
+ - package 根若同时导出 `windyModuleManifest`,必须与 `./manifest` 是同一对象;
30
+ - Adapter `create()` 返回的 `module.manifest` 也必须是该 canonical 对象。
31
+
32
+ `WindyServerModuleAdapter.runtimeMode` 有两个值:
33
+
34
+ - `self-contained`:Host 可从 package 根动态加载;不能获得数据库或项目内部
35
+ Repository;
36
+ - `project-attachment`:只允许项目 Composition Root 显式注册,并由同名 attachment
37
+ 在预校验成功后接收项目 runtime。
38
+
39
+ 动态 package 宣称 `project-attachment` 会被拒绝。本地 Adapter 也必须加载并复用
40
+ `${package}/manifest`,不能用一份内容相同但独立创建的 Manifest 绕过身份校验。
41
+
42
+ ### 0.3.17 project-owned 装配兼容
43
+
44
+ 最低支持基线只有 `apps/server/src/installed-business-modules.ts`,没有后来的
45
+ `app/server-composition.ts`。普通更新必须保留这份客户文件,因此受管 Host 通过
46
+ `module-host/runtime-composition.ts` 同时读取旧 `installedBusinessModules` 契约和
47
+ 新版声明式 Composition,而不要求旧文件新增导出。
48
+
49
+ 同名模块优先使用旧 project-owned 实例,Manifest 内容不一致时 fail closed;只有新版
50
+ Composition 独有的模块才由新版 attachment 挂载。这样旧实例的 `setup()` 与
51
+ `attach()` 仍成对执行,也不会因为目标模板出现同名 Adapter 而重复挂载。新生成项目的
52
+ 旧兼容列表为空,全部走声明式 Composition。
14
53
 
15
54
  ## 声明与实现的一致性
16
55
 
17
56
  Manifest 是能力的唯一事实源,双向校验保证不漂移:
18
57
 
19
- - 注册时(`initializeModuleHosts`):安装单元的 Manifest 必须与平台能力目录是同一声明;实现必须与 Manifest 声明一致——路由绑定、任务、工具、Provider 未声明即抛错;角色预设的权限必须属于本模块声明;
58
+ - 组合时:每个启用模块必须存在于 `windy.modules.json`,平台模块必须使用
59
+ `package=null`,两个核心模块必须启用;Manifest dependencies 必须形成已启用闭包,
60
+ Host range 必须兼容;
61
+ - 注册时(`initializeModuleHosts`):安装单元的 Manifest 必须与平台能力目录是同一声明;实现必须与 Manifest 声明一致——路由绑定、任务、工具、Provider 未声明即抛错;Durable Handler 必须精确绑定本模块已声明的 Feature、Permission 与 Audit Action 组合;角色预设的权限必须属于本模块声明;
20
62
  - 装配后(`validateServerModuleComposition`):声明了没实现(缺 Handler)与实现了没声明(`moduleRegistrations` 比对)都在启动前报错;
21
63
  - 所有 Key 强制模块命名空间(等于模块名或以 `<moduleName>.` 开头),跨模块重复注册立即失败。
22
64
 
65
+ attachment 在任一模块 `setup()` 前完成整组预校验,避免前半组已经产生注册副作用后才
66
+ 发现缺失或重复 attachment。`setup()` 后再次校验 Manifest identity,模块若在注册期间
67
+ 替换声明会被拒绝。
68
+
69
+ 同一套完整组合门禁会在 Server build 和普通启动的无数据库子进程中执行;只有通过后
70
+ 才允许审计恢复、Migration、Worker 和 HTTP listener。安装命令尚不能接入
71
+ self-contained package 时,也必须在写项目之前 fail closed。
72
+
73
+ 平台自身的静态 Route 也不在 Host 契约之外:`createManifestAuthorizedRouteApp()` 将
74
+ 实际 Handler 交给 `StaticRouteAuthorizer`,按 Manifest 的模块启用状态、License、
75
+ Feature 与 Permission 执行请求时 Guard。只有显式 `immutableCoreAccess` 的健康探针与
76
+ 认证恢复入口可以跳过运行态 Guard,但仍必须在 Manifest 注册;其它实际 Elysia Route
77
+ 找不到声明时,build/start 预检直接失败。
78
+
23
79
  ## 受限 Port 的取舍
24
80
 
25
81
  - **Actor 只读白名单视图**:宿主为 `ModuleActor` 构造冻结的最小对象,不把凭据状态与数据范围令牌带入模块运行时。宿主内部通过不可伪造的映射恢复原始 `RequestActor`,外部模块无法扩权或伪造该关联。
26
82
  - **初始化两阶段**:`setup(host)` 在 Storage / Durable Worker 创建之前运行,只能登记定义;需要宿主服务的实现(路由 Handler、任务执行体、Provider)通过延迟绑定(`requireService` 闭包 + 安装清单统一 `attach()`)解析,与平台既有 `DeferredLegacyExecutor` 模式一致。
27
83
  - **Queue 与提交人不可伪造**:模块 Jobs Port 只能提交本模块命名空间的 Handler,Queue 强制为模块名,提交人由宿主以当前 Actor 补齐;Worker 与 Repository 始终由宿主持有。
84
+ - **持久任务每次领取都重新授权**:Host 冻结注册时通过校验的 Durable Handler
85
+ 能力绑定;首次执行、重试和进程恢复后的领取都重新解析当前 Actor,并按当前
86
+ License、Feature 与 RBAC 状态执行 Guard。提交时曾获授权不能成为永久通行证。
28
87
  - **审计白名单**:模块只能 emit 自己 Manifest 注册的审计动作;Guard 拒绝(`security.denied`)由宿主统一记录,模块无法关闭。
29
88
  - **存储最小授权**:上传 purpose 强制属于本模块已注册 Upload Policy,Owner 由宿主
30
89
  以当前 Actor 补齐。Owner 读取保持用途约束;跨上传者读取的 purpose 只可来自当前
@@ -34,6 +93,10 @@ Manifest 是能力的唯一事实源,双向校验保证不漂移:
34
93
  Repository 或任意 manage 能力。
35
94
  - **角色预设非破坏性合并**:预设随内置角色同步,手工追加的授权保留;与既有角色 Code 冲突直接启动失败,不做静默覆盖。
36
95
 
37
- ## 暂不迁移的部分
96
+ ## 当前边界
38
97
 
39
- 系统模块(auth / license / storage / scheduler 等平台能力)仍保持原有的宿主内部接线,不经过 ModuleHost 路由注册;Module Interface 面向的是业务模块。平台任务的硬编码 Handler 映射(`createPlatformTaskDefinitions`)同样保留,业务任务一律改走 `registerTaskHandlers`。
98
+ 平台系统模块仍有部分 Host 内部接线;业务模块使用 ModuleHost。当前目录里的内置模块
99
+ 是创建期 source/template distribution,没有一个已取得部署后 add/update/remove 的
100
+ self-contained H3 证据。整个 `apps/server/src/app` 由项目拥有,默认装配入口是
101
+ `server-composition.ts`;普通更新不会覆盖。若客户移除协议实现,平台只能诊断,不能
102
+ 重写这些文件。
@@ -1,66 +1,69 @@
1
- # project-owned 业务 Web 路由
1
+ # project-owned Web 与 Admin UI 组合
2
2
 
3
- Windy Starter 把平台 Router 和访问快照 Guard 保留在
4
- `apps/web/src/router/`,客户业务路由只注册到
5
- `apps/web/src/app/business-routes.ts`。整个 `apps/web/src/app/` 在
6
- `.windy/files.json` 中属于 `project-owned`:更新器不会覆盖客户新增、修改或删除的
7
- 路由注册与页面源码。
3
+ 更新时间:2026-07-29。
8
4
 
9
- ## 注册一个页面
5
+ 整个 `apps/web/src` 都是 project-owned 应用源码。布局、页面、组件库封装、Router、
6
+ 导航呈现与 Host 实现均可由项目修改、替换或删除;普通 `windy update` 和模块命令不能
7
+ 为了接入入口而重写这些文件。
10
8
 
11
- 先在业务 Module Manifest 中用同一个能力基础 Key 声明 Feature、Permission、菜单和
12
- Admin Route,再在 `installedBusinessAdminRoutes` 中注册页面实现:
9
+ ## package 默认贡献
13
10
 
14
- ```ts
15
- import CaseRecordsPage from "./case-records/CaseRecordsPage.vue";
11
+ 带管理面的业务 package 必须同时提供:
16
12
 
17
- export const installedBusinessAdminRoutes = [
18
- {
19
- key: "case.record",
20
- path: "/cases",
21
- name: "case-records",
22
- title: "案件",
23
- component: CaseRecordsPage,
24
- featureKey: "case.record",
25
- permissionKey: "case.record.read",
26
- },
27
- ] satisfies readonly BusinessAdminRouteRegistration[];
28
- ```
13
+ - `${package}/manifest`:导出 canonical `windyModuleManifest`;
14
+ - `${package}/admin`:只导出 browser-safe 的 `windyAdminUiModule`,其中
15
+ `AdminUiManifest` 仅投影模块身份、版本和 `adminRoutes`;
16
+ - 每个 Admin Route 的 `routeKey -> lazy loader` 页面绑定及 `hostRange`。
29
17
 
30
- 注册表必须与 Module Manifest 使用相同的 `key`、`path`、`featureKey` 和
31
- `permissionKey`。服务端仍是最终授权边界;Web Router 会把注册项统一转换为
32
- `layout: "admin"` 路由,并在每次导航时使用访问快照检查 Feature 与 Permission。
33
- 空 Guard、重复业务 Route Key、相对路径和业务兜底路径会在启动组合时被拒绝。
18
+ Vite 在构建时读取 `windy.modules.json`,为其中 `enabled=true` 且 `package` 非空的
19
+ 项生成静态 virtual registry。禁用模块不会被 import 或进入 Web bundle。构建脚本还会
20
+ 独立加载 `./manifest`,要求 `./admin` 的窄 Manifest 与 canonical 投影完全一致,并
21
+ 检查 Host range、缺失页面、重复 route key/path 和保留路径碰撞。
34
22
 
35
- 页面组件及其 composable、表单和状态放在 `apps/web/src/app/` 下。不要为了增加业务
36
- 页面修改以下 shared-scaffold:
23
+ Migration、Task、Provider、Audit、Schema 与持久化元数据不得通过 `./admin` 进入浏览器
24
+ bundle。页面 loader 失败或没有合法 `default` 导出时,只显示 Admin shell 内的模块失败
25
+ 页,不卸载整个应用。
37
26
 
38
- - `apps/web/src/router/index.ts`
39
- - `apps/web/src/router/business-routes.ts`
40
- - `apps/web/src/layout/AppSidebar.vue`
41
- - `apps/web/src/layout/navigation.ts`
27
+ ## 项目覆盖
42
28
 
43
- ## 导航来源
29
+ 项目在 `apps/web/src/app/web-composition.ts` 只覆盖自己接管的页面 loader 和呈现偏好,
30
+ 不重复 path、title、Feature 或 Permission:
44
31
 
45
- 侧栏和导航搜索不会读取 Web 注册表来复制一份菜单。服务端启动时从已安装业务 Module
46
- Manifest 同步菜单,`/api/foundation` 返回经过 License、Feature 和 RBAC 裁剪的访问
47
- 快照,Web 外壳据此渲染导航。这样路径、权限、Feature 和菜单身份仍由 Manifest
48
- 统一治理,客户只在 project-owned 注册表绑定 Vue 页面实现。
32
+ ```ts
33
+ export const webComposition = defineWebComposition({
34
+ overrides: [
35
+ {
36
+ moduleName: "case",
37
+ pages: [
38
+ {
39
+ routeKey: "case.review.queue",
40
+ load: () => import("../pages/case/CaseReviewQueuePage.vue"),
41
+ },
42
+ ],
43
+ },
44
+ ],
45
+ presentation: [
46
+ {
47
+ routeKey: "case.review.queue",
48
+ title: "材料审核",
49
+ order: 120,
50
+ },
51
+ ],
52
+ });
53
+ ```
49
54
 
50
- 如果页面可直达但侧栏没有入口,优先检查服务端
51
- `apps/server/src/installed-business-modules.ts` 是否已经安装对应业务 Module、Manifest
52
- 是否声明菜单,以及当前账号是否具有 Permission;不要在 AppSidebar 手写菜单。
55
+ 路径与 Guard 始终来自同一完整 Manifest。服务端是最终授权边界;Web Router 只使用
56
+ Foundation 访问快照改善导航和直达体验。`admin.visible=false` 只隐藏该模块的菜单入口,
57
+ 不删除路由,也不替代 Server Guard。
53
58
 
54
59
  ## 升级行为
55
60
 
56
- 首次生成时,`business-routes.ts` 会记为 `project-owned`。后续 target 即使提供了新的
57
- 默认内容,也不能把 source manifest 中的该路径降级成 `shared-scaffold` 或
58
- `windy-managed`:
61
+ `apps/web/src` 在 `.windy/files.json` 中整体标为 `project-owned`:
59
62
 
60
- - 客户修改继续保留;
61
- - 客户删除会以 tombstone 跨版本保留;
62
- - 客户重新创建后,清单刷新采纳新正文 hash,但仍保持 `project-owned`;
63
- - 平台只升级 Router Adapter 与 Guard,实现变更通过 shared-scaffold 三方合并进入。
63
+ - 客户保留、修改与删除均由项目决定;
64
+ - 删除以 tombstone 跨版本保留;
65
+ - 模块 package 更新只改变依赖与自身导出,不改客户 layout、Router 或页面;
66
+ - Host 不再实现协议时,CLI/构建只诊断并 fail closed,不能覆盖客户实现。
64
67
 
65
- 升级前可运行 `bun run windy update --dry-run` 查看所有权和冲突计划。纯业务页面与
66
- 注册表变更不应产生平台侧源码覆盖。
68
+ 未来若提供整套 Admin 预设更新,它必须是独立、显式选择的三方合并能力;当前没有
69
+ `windy admin update` 命令,不能把该目标写成已交付的日常路径。