create-windy 0.3.18 → 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.
- package/README.md +28 -13
- package/dist/cli.js +788 -280
- package/package.json +1 -1
- package/template/.agents/skills/windy-business-module/SKILL.md +34 -10
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +18 -8
- package/template/README.md +32 -22
- package/template/apps/agent-server/src/southwind_agent_server/adk_debug.py +38 -5
- package/template/apps/agent-server/tests/test_adk_debug_result_polling.py +139 -0
- package/template/apps/server/Dockerfile +1 -0
- package/template/apps/server/package.json +1 -0
- package/template/apps/server/scripts/validate-module-composition-once.ts +4 -0
- package/template/apps/server/scripts/validate-module-composition.ts +28 -0
- package/template/apps/server/src/agent/data-scope-routes.test.ts +20 -5
- package/template/apps/server/src/agent/routes.test.ts +20 -7
- package/template/apps/server/src/agent/routes.ts +14 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +22 -6
- package/template/apps/server/src/agent/sandbox-worker.ts +2 -2
- package/template/apps/server/src/agent/unavailable-routes.ts +33 -0
- package/template/apps/server/src/app/server-composition.ts +56 -0
- package/template/apps/server/src/application-services.ts +2 -0
- package/template/apps/server/src/bulk-data/service.ts +14 -0
- package/template/apps/server/src/data-governance/export/authorization-port.ts +1 -1
- package/template/apps/server/src/data-governance/export/postgres-api-fixture.ts +18 -2
- package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +10 -9
- package/template/apps/server/src/data-governance/export/routes-test-fixture.ts +6 -8
- package/template/apps/server/src/data-governance/user-plaintext-routes.test.ts +4 -2
- package/template/apps/server/src/feature/routes.authorization.test.ts +26 -1
- package/template/apps/server/src/feature/routes.ts +33 -15
- package/template/apps/server/src/foundation-activation.test.ts +60 -0
- package/template/apps/server/src/foundation.ts +64 -5
- package/template/apps/server/src/guards.test.ts +23 -0
- package/template/apps/server/src/guards.ts +12 -0
- package/template/apps/server/src/index.ts +28 -34
- package/template/apps/server/src/installed-business-modules.ts +13 -24
- package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +1 -0
- package/template/apps/server/src/jobs/legacy-adapter.test.ts +106 -0
- package/template/apps/server/src/jobs/legacy-service-runtime.ts +40 -0
- package/template/apps/server/src/license/activation.test.ts +30 -0
- package/template/apps/server/src/license/activation.ts +26 -0
- package/template/apps/server/src/license/restriction.test.ts +16 -0
- package/template/apps/server/src/license/routes-offline-code.test.ts +6 -2
- package/template/apps/server/src/license/routes.ts +20 -4
- package/template/apps/server/src/license/runtime-service.test.ts +10 -2
- package/template/apps/server/src/module-bootstrap.ts +14 -23
- package/template/apps/server/src/module-composition.test.ts +43 -2
- package/template/apps/server/src/module-composition.ts +29 -0
- package/template/apps/server/src/module-host/durable-context-guard.test.ts +172 -0
- package/template/apps/server/src/module-host/durable-context-unavailable.test.ts +50 -1
- package/template/apps/server/src/module-host/durable-context.test.ts +42 -25
- package/template/apps/server/src/module-host/durable-context.ts +42 -2
- package/template/apps/server/src/module-host/durable-handler-registration.ts +96 -5
- package/template/apps/server/src/module-host/implementation-completeness.test.ts +185 -0
- package/template/apps/server/src/module-host/implementation-completeness.ts +132 -0
- package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
- package/template/apps/server/src/module-host/initialize.test.ts +78 -2
- package/template/apps/server/src/module-host/initialize.ts +15 -4
- package/template/apps/server/src/module-host/project-attachments.test.ts +92 -0
- package/template/apps/server/src/module-host/project-attachments.ts +58 -0
- package/template/apps/server/src/module-host/project-composition-package.test.ts +186 -0
- package/template/apps/server/src/module-host/project-composition.test.ts +260 -0
- package/template/apps/server/src/module-host/project-composition.ts +311 -0
- package/template/apps/server/src/module-host/request-context.ts +3 -0
- package/template/apps/server/src/module-host/route-declaration.ts +15 -6
- package/template/apps/server/src/module-host/route-installer.test.ts +15 -2
- package/template/apps/server/src/module-host/route-installer.ts +12 -2
- package/template/apps/server/src/module-host/route-registration.test.ts +13 -4
- package/template/apps/server/src/module-host/runtime-composition-compatibility.test.ts +63 -0
- package/template/apps/server/src/module-host/runtime-composition-compatibility.ts +46 -0
- package/template/apps/server/src/module-host/runtime-composition.ts +34 -0
- package/template/apps/server/src/module-host/search-provider-registration.test.ts +64 -0
- package/template/apps/server/src/module-host/search-provider-registration.ts +35 -0
- package/template/apps/server/src/module-host/static-route-authorization.test.ts +99 -0
- package/template/apps/server/src/module-host/static-route-authorization.ts +90 -0
- package/template/apps/server/src/module-host/task-definitions.ts +1 -0
- package/template/apps/server/src/module-host/user-directory-composition.test.ts +4 -1
- package/template/apps/server/src/module-host.test.ts +2 -0
- package/template/apps/server/src/module-preflight.test.ts +289 -0
- package/template/apps/server/src/module-preflight.ts +96 -0
- package/template/apps/server/src/module-runtime-validation.ts +1 -0
- package/template/apps/server/src/platform-app-bootstrap.ts +50 -0
- package/template/apps/server/src/platform-http-runtime.ts +32 -7
- package/template/apps/server/src/route-guards.test.ts +22 -0
- package/template/apps/server/src/route-guards.ts +10 -3
- package/template/apps/server/src/runtime-audit-query.ts +50 -0
- package/template/apps/server/src/runtime-feature.test.ts +46 -0
- package/template/apps/server/src/runtime-feature.ts +5 -0
- package/template/apps/server/src/runtime.ts +10 -49
- package/template/apps/server/src/scheduler/audit-action.test.ts +49 -0
- package/template/apps/server/src/scheduler/audit.ts +1 -1
- package/template/apps/server/src/scheduler/execution-recorder.ts +10 -5
- package/template/apps/server/src/scheduler/recovery-service.test.ts +4 -3
- package/template/apps/server/src/scheduler/service-idle.test.ts +4 -3
- package/template/apps/server/src/scheduler/service.test.ts +1 -0
- package/template/apps/server/src/scheduler/service.ts +18 -21
- package/template/apps/server/src/scheduler/types.ts +5 -1
- package/template/apps/server/src/startup-composition-validation.test.ts +35 -0
- package/template/apps/server/src/startup-composition-validation.ts +54 -0
- package/template/apps/server/src/startup-preflight.test.ts +36 -0
- package/template/apps/server/src/startup-preflight.ts +37 -0
- package/template/apps/server/src/system/audit-routes.test.ts +9 -3
- package/template/apps/server/src/system/department-routes.test.ts +5 -3
- package/template/apps/server/src/system/feature-routes.test.ts +4 -3
- package/template/apps/server/src/system/interface-catalog.test.ts +5 -3
- package/template/apps/server/src/system/interface-catalog.ts +3 -0
- package/template/apps/server/src/system/menu-routes.test.ts +5 -3
- package/template/apps/server/src/system/route-test-runtime.ts +24 -0
- package/template/apps/server/src/system/routes-validation.test.ts +7 -3
- package/template/apps/server/src/system/routes.test.ts +21 -11
- package/template/apps/server/src/system/routes.ts +11 -2
- package/template/apps/server/src/work-order/foundation-modules.test.ts +6 -4
- package/template/apps/server/src/work-order/routes.test.ts +8 -0
- package/template/apps/server/src/work-order/task.test.ts +6 -1
- package/template/apps/web/Dockerfile +5 -0
- package/template/apps/web/package.json +1 -0
- package/template/apps/web/scripts/validate-admin-ui-composition.ts +106 -0
- package/template/apps/web/src/app/web-composition.ts +24 -0
- package/template/apps/web/src/app/web-composition.webtest.ts +15 -0
- package/template/apps/web/src/layout/AppSidebar.vue +8 -1
- package/template/apps/web/src/layout/NavigationSearchDialog.vue +8 -1
- package/template/apps/web/src/layout/navigation.ts +29 -4
- package/template/apps/web/src/layout/navigation.webtest.ts +51 -0
- package/template/apps/web/src/pages/dashboard/dashboard-presenter.ts +6 -1
- package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +202 -0
- package/template/apps/web/src/pages/work-orders/WorkOrderListPage.webtest.ts +51 -0
- package/template/apps/web/src/router/admin-extension-loader.ts +44 -0
- package/template/apps/web/src/router/admin-extension-loader.webtest.ts +49 -0
- package/template/apps/web/src/router/admin-ui-composition-plugin.ts +33 -0
- package/template/apps/web/src/router/admin-ui-composition-plugin.webtest.ts +20 -0
- package/template/apps/web/src/router/admin-ui-composition.ts +313 -0
- package/template/apps/web/src/router/admin-ui-composition.webtest.ts +225 -0
- package/template/apps/web/src/router/admin-ui-module-registry-plugin.ts +55 -0
- package/template/apps/web/src/router/admin-ui-module-registry-plugin.webtest.ts +46 -0
- package/template/apps/web/src/router/business-routes.ts +32 -45
- package/template/apps/web/src/router/business-routes.webtest.ts +40 -44
- package/template/apps/web/src/router/index.ts +64 -54
- package/template/apps/web/src/router/manifest-routes.ts +27 -7
- package/template/apps/web/src/router/manifest-routes.webtest.ts +10 -2
- package/template/apps/web/src/router/platform-admin-route-bindings.ts +58 -0
- package/template/apps/web/src/router/reserved-route-paths.ts +14 -0
- package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +10 -4
- package/template/apps/web/src/router/web-composition-host.ts +20 -0
- package/template/apps/web/src/services/work-order-api.ts +16 -0
- package/template/apps/web/src/services/work-order-api.webtest.ts +21 -0
- package/template/apps/web/src/virtual-admin-ui-modules.d.ts +5 -0
- package/template/apps/web/vite.config.ts +22 -1
- package/template/docs/architecture/migration-bundle.md +10 -7
- package/template/docs/architecture/module-manifest-composition.md +85 -3
- package/template/docs/architecture/search-provider-protocol.md +6 -1
- package/template/docs/architecture/server-module-interface.md +70 -7
- package/template/docs/development/business-web-routes.md +53 -50
- package/template/docs/development/module-lifecycle.md +48 -54
- package/template/docs/platform/agent-runtime.md +2 -0
- package/template/docs/platform/navigation-registry.md +5 -4
- package/template/package.json +1 -1
- package/template/packages/config/package.json +1 -1
- package/template/packages/database/package.json +1 -1
- package/template/packages/example-work-order/package.json +6 -0
- package/template/packages/example-work-order/src/admin-page.ts +78 -0
- package/template/packages/example-work-order/src/admin.ts +35 -0
- package/template/packages/example-work-order/src/manifest.test.ts +62 -5
- package/template/packages/example-work-order/src/manifest.ts +54 -7
- package/template/packages/example-work-order/src/migration-bundle.ts +8 -0
- package/template/packages/example-work-order/src/server-module.ts +2 -2
- package/template/packages/jobs/package.json +1 -1
- package/template/packages/modules/index.ts +4 -0
- package/template/packages/modules/package.json +3 -2
- package/template/packages/modules/src/admin-ui-entry.ts +8 -0
- package/template/packages/modules/src/admin-ui-manifest.test.ts +52 -0
- package/template/packages/modules/src/admin-ui-manifest.ts +53 -0
- package/template/packages/modules/src/capability-audit-validation.test.ts +127 -0
- package/template/packages/modules/src/capability-audit-validation.ts +38 -0
- package/template/packages/modules/src/capability-guard-validation.ts +148 -0
- package/template/packages/modules/src/capability-key-validation.test.ts +150 -0
- package/template/packages/modules/src/composition-fixtures.test-support.ts +7 -5
- package/template/packages/modules/src/composition.test.ts +74 -9
- package/template/packages/modules/src/composition.ts +8 -0
- package/template/packages/modules/src/manifest.ts +1 -0
- package/template/packages/modules/src/migration-bundle-validation.test.ts +23 -0
- package/template/packages/modules/src/module-host-range.test.ts +57 -0
- package/template/packages/modules/src/module-host-range.ts +125 -0
- package/template/packages/modules/src/project-module-assembly.test.ts +133 -0
- package/template/packages/modules/src/project-module-assembly.ts +126 -0
- package/template/packages/modules/src/system-admin-routes.ts +6 -5
- package/template/packages/modules/src/system-admin-ui-catalog.ts +72 -0
- package/template/packages/modules/src/system-agent-tools.ts +2 -2
- package/template/packages/modules/src/system-api-binding.ts +43 -9
- package/template/packages/modules/src/system-api-control-plane.ts +202 -0
- package/template/packages/modules/src/system-api-data-governance.ts +4 -2
- package/template/packages/modules/src/system-api-operational.ts +57 -8
- package/template/packages/modules/src/system-api-permissions.ts +115 -21
- package/template/packages/modules/src/system-api-watermark.ts +3 -0
- package/template/packages/modules/src/system-audit-actions.ts +7 -0
- package/template/packages/modules/src/system-data-governance.ts +5 -2
- package/template/packages/modules/src/system-features.ts +31 -0
- package/template/packages/modules/src/system-governed-export-bindings.ts +8 -4
- package/template/packages/modules/src/system-module-catalog.test.ts +39 -4
- package/template/packages/modules/src/system-module-catalog.ts +68 -7
- package/template/packages/modules/src/system-module-tasks.ts +8 -1
- package/template/packages/modules/src/system-modules-access.test.ts +4 -0
- package/template/packages/modules/src/system-modules.test.ts +10 -2
- package/template/packages/modules/src/system-modules.ts +6 -5
- package/template/packages/modules/src/system-permissions.ts +4 -0
- package/template/packages/modules/user/module.ts +19 -2
- package/template/packages/server-sdk/index.ts +8 -0
- package/template/packages/server-sdk/package.json +2 -1
- package/template/packages/server-sdk/src/durable-handler.ts +16 -1
- package/template/packages/server-sdk/src/module-adapter.test.ts +44 -0
- package/template/packages/server-sdk/src/module-adapter.ts +59 -0
- package/template/packages/server-sdk/src/route-definition.ts +3 -2
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/audit.test.ts +7 -0
- package/template/packages/shared/src/audit.ts +16 -0
- package/template/packages/shared/src/migration-bundle-validation.ts +9 -11
- package/template/packages/shared/src/rbac.ts +2 -0
- package/template/apps/web/src/app/business-routes.ts +0 -22
|
@@ -1,72 +1,66 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 项目模块装配与生命周期
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
更新时间:2026-07-29。
|
|
4
|
+
|
|
5
|
+
## 当前可用命令
|
|
6
|
+
|
|
7
|
+
已部署项目通过项目锁定版本的 `create-windy` 管理装配选择:
|
|
5
8
|
|
|
6
9
|
```bash
|
|
7
10
|
bun run windy module list
|
|
8
|
-
bun run windy module
|
|
9
|
-
bun run windy module
|
|
10
|
-
bun run windy module update work-order
|
|
11
|
-
bun run windy module remove work-order --dry-run
|
|
12
|
-
bun run windy module remove work-order --confirm-data-disposition
|
|
11
|
+
bun run windy module enable <module>
|
|
12
|
+
bun run windy module disable <module>
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
`
|
|
19
|
-
Schema、migration 与卸载策略。目标组合仍由 immutable template 生成,之后进入与
|
|
20
|
-
`windy update` 相同的 Base/Ours/Theirs 计划、文件 expectation、workspace 检查、
|
|
21
|
-
CAS 写入、验证、Project Lock/受管清单提交和 `doctor --repair` 恢复流程。
|
|
15
|
+
`windy.modules.json` 是 project-owned 装配声明,记录 Module 名称、无版本 package
|
|
16
|
+
入口、`enabled`、顺序、Admin 可见性和配置;实际 package 版本只来自
|
|
17
|
+
`package.json` 与 `bun.lock`。`enable/disable` 使用 CAS 事务只修改这份声明,不改
|
|
18
|
+
`apps/web/src`、Server Composition、Router、layout 或 package 内容。
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
启用会递归启用已经安装的依赖;依赖缺失、版本不兼容、冲突、核心模块停用或仍有启用
|
|
21
|
+
模块依赖待停用模块时,在写入前拒绝。`system.settings` 的 Admin Route 引用
|
|
22
|
+
`system.admin`,`system.storage` 承载 Settings 声明的公共品牌文件 Route,
|
|
23
|
+
`system.data-governance` 也向其它 Feature 贡献治理能力;三者都不能
|
|
24
|
+
只靠运行时开关安全停用,因此 `disable` 会 fail closed。部署后需要排除它们时必须由
|
|
25
|
+
项目执行人工物理迁移,不能把修改 registry 当作已卸载。
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
CLI 重建所选模块并应用目录声明的更新 recipe。正式操作只处理计划涉及的文件,不检查
|
|
29
|
-
无关 Git 工作区改动;客户修改过的受管文件或模块 project-owned 扩展点会形成冲突,
|
|
30
|
-
不会被静默覆盖。
|
|
27
|
+
## 创建期模块与部署期安装的边界
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
当前目录中的 15 个平台模块和示例 `work-order` 都是 source/template distribution:
|
|
30
|
+
它们在首次创建时通过 `--module`、`--all-modules`、`--no-optional-modules` 或
|
|
31
|
+
`--no-example` 物理组合。其文件仍包含 project-owned Web、Server Composition、
|
|
32
|
+
Repository 或业务源码,不是可以在已部署项目中独立落盘的 self-contained package。
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
`--confirm-data-disposition`。
|
|
34
|
+
因此对这些内置模块执行下列命令时,CLI 会在生成更新计划和写项目之前 fail closed:
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
CLI 不连接生产数据库,确认参数表示部署操作人已完成并审阅相应动作,不代表 Windy
|
|
44
|
-
自动执行 SQL 或数据库归档。即使策略名为 `archive-data`,CLI 对数据库本身仍是
|
|
45
|
-
retain:数据删除或归档由处置文档约束的外部运维流程负责。目录策略只能被提高,不能
|
|
46
|
-
从 `archive-data` 或 `manual` 降为 `retain-data`。
|
|
36
|
+
```bash
|
|
37
|
+
bun run windy module add <built-in-module>
|
|
38
|
+
bun run windy module update <built-in-module>
|
|
39
|
+
bun run windy module remove <built-in-module>
|
|
40
|
+
```
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`windy_migration_history` 的 migration id 与 checksum 判定,不会因为代码重装而
|
|
51
|
-
重复执行已应用迁移。历史目录受清单 hash 保护,缺失登记、符号链接或内容修改都会
|
|
52
|
-
fail closed。
|
|
42
|
+
重复 add 已安装模块、重复 remove 未安装模块仍按幂等 no-op 处理;只有实际集合会变化
|
|
43
|
+
时才检查部署期能力。这不能作为安装、更新或卸载已经交付的证据。
|
|
53
44
|
|
|
54
|
-
|
|
45
|
+
`ModuleDistributionManifest.deploymentInstall` 只有在模块完成以下闭环后才能标为
|
|
46
|
+
`supported`:
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
- package 提供 browser-safe `./admin`、canonical `./manifest` 与
|
|
49
|
+
self-contained 根 `windyServerModule`;
|
|
50
|
+
- 安装只更新依赖图和 `windy.modules.json`,不改写 project-owned Web/Server 源码;
|
|
51
|
+
- Manifest、Host range、依赖闭包和实现完整性在安装、构建与监听前均 fail closed;
|
|
52
|
+
- 数据处置、不可改写 migration、失败恢复和重复操作有真实部署回归证据。
|
|
59
53
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
当前目录没有 `deploymentInstall: "supported"` 的模块,所以没有 H3 模块,也不支持
|
|
55
|
+
任意 npm package 名称的 H3 安装。需要客户 Repository 或项目运行时 attachment 的模块
|
|
56
|
+
只能在 `apps/server/src/app/server-composition.ts` 由项目显式装配。
|
|
63
57
|
|
|
64
|
-
|
|
65
|
-
`system.identity` 是 profile 核心,不支持卸载。
|
|
58
|
+
## 数据与升级原则
|
|
66
59
|
|
|
67
|
-
|
|
60
|
+
部署期物理卸载将来仍必须默认保留数据,不执行 down migration;`archive-data` 或
|
|
61
|
+
`manual` 只能绑定已审阅的外部运维计划,CLI 不连接生产数据库,也不能声称已自动
|
|
62
|
+
归档。已发布 Migration Bundle 的 ID 与 checksum 必须保留。
|
|
68
63
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
安装、数据库、Server、卸载残留和恢复门禁。
|
|
64
|
+
普通 `windy update` 只升级平台依赖、受管 Wrapper 和兼容运行内核,不重写
|
|
65
|
+
`apps/web/src` 或 `apps/server/src/app`。模块协议不兼容时应
|
|
66
|
+
诊断并停止,不能用模板覆盖客户 Composition 来制造成功。
|
|
@@ -240,6 +240,8 @@ Operation Key 与真实开发数据用户注入 Windy-owned `docker-compose.agen
|
|
|
240
240
|
不连接业务 Repository 或 Tool Host;
|
|
241
241
|
- 正式 Server 仍签发 Actor、Scope 和短时 Execution Grant,Agent Server 仍按
|
|
242
242
|
`allowedToolKeys` 执行并审计;
|
|
243
|
+
- SSE 先于结果持久化结束时,Adapter 最多再等待 60 秒并每 500ms 读取同一个 Run
|
|
244
|
+
的结果;只重试明确标记为可重试的 `RUN_CONFLICT`,不会重新创建 Run;
|
|
243
245
|
- 事件摘要最多 64 条,单个 SSE frame 最大 64 KiB;流式 `output.delta` 只显示计数,
|
|
244
246
|
客户端边读边压缩,不先把完整事件流保存在内存。
|
|
245
247
|
|
|
@@ -35,7 +35,8 @@ PUT /api/system/menus/:id/placement
|
|
|
35
35
|
|
|
36
36
|
新增页面时先在所属 Module Manifest 用同一能力 Key 注册 Feature、权限、菜单和 API 绑定,再新增 Router 页面。Router 契约测试会比较所有 `type=menu` 的 Manifest 路径与 Web 路由,避免出现“菜单有入口但页面不存在”或“页面绕过注册表”的漂移。
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
project-owned
|
|
40
|
-
Feature/RBAC Guard
|
|
41
|
-
|
|
38
|
+
业务 package 的 browser-safe `./admin` 提供 Manifest 窄投影与默认页面,项目可在
|
|
39
|
+
project-owned `apps/web/src/app/web-composition.ts` 按 route key 覆盖 loader 和呈现;
|
|
40
|
+
路径与 Feature/RBAC Guard 不在 Web 重复声明。侧栏仍从服务端 Manifest 快照生成。完整
|
|
41
|
+
接入与升级边界见
|
|
42
|
+
[project-owned Web 与 Admin UI 组合](../development/business-web-routes.md)。
|
package/template/package.json
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"generate:crud": "bun run packages/crud-generator/src/cli.ts",
|
|
28
28
|
"create:windy": "bun run packages/create-windy/src/cli.ts",
|
|
29
29
|
"test:create-windy": "bun run packages/create-windy/src/regression.ts",
|
|
30
|
-
"build": "bun run --cwd apps/web build",
|
|
30
|
+
"build": "bun run --cwd apps/server build && bun run --cwd apps/web build",
|
|
31
31
|
"build:license": "bun run --cwd apps/license build:web",
|
|
32
32
|
"db:generate": "drizzle-kit generate --config drizzle.config.ts",
|
|
33
33
|
"db:migrate": "drizzle-kit migrate --config drizzle.config.ts",
|
|
@@ -13,8 +13,14 @@
|
|
|
13
13
|
"drizzle-orm": "^0.45.2",
|
|
14
14
|
"zod": "^4.4.3"
|
|
15
15
|
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"vue": "^3.5.0"
|
|
18
|
+
},
|
|
16
19
|
"exports": {
|
|
17
20
|
".": "./index.ts",
|
|
21
|
+
"./admin": "./src/admin.ts",
|
|
22
|
+
"./contract": "./src/contract.ts",
|
|
23
|
+
"./manifest": "./src/manifest.ts",
|
|
18
24
|
"./migration-bundle": "./src/migration-bundle.ts"
|
|
19
25
|
}
|
|
20
26
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { defineComponent, h, onMounted, ref } from "vue";
|
|
2
|
+
import type { WorkOrder, WorkOrderPage } from "./contract.js";
|
|
3
|
+
|
|
4
|
+
export default defineComponent({
|
|
5
|
+
name: "DefaultWorkOrderAdminPage",
|
|
6
|
+
setup() {
|
|
7
|
+
const items = ref<WorkOrder[]>([]);
|
|
8
|
+
const loading = ref(true);
|
|
9
|
+
const error = ref("");
|
|
10
|
+
|
|
11
|
+
onMounted(async () => {
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch("/api/work-orders?page=1&pageSize=20");
|
|
14
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
15
|
+
const payload: unknown = await response.json();
|
|
16
|
+
if (!isWorkOrderPage(payload)) throw new Error("响应结构无效");
|
|
17
|
+
items.value = payload.items;
|
|
18
|
+
} catch {
|
|
19
|
+
error.value = "工单加载失败,请稍后重试。";
|
|
20
|
+
} finally {
|
|
21
|
+
loading.value = false;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return () =>
|
|
26
|
+
h("main", { class: "space-y-4 p-6" }, [
|
|
27
|
+
h("header", { class: "space-y-1" }, [
|
|
28
|
+
h("h1", { class: "text-2xl font-semibold" }, "工单管理"),
|
|
29
|
+
h(
|
|
30
|
+
"p",
|
|
31
|
+
{ class: "text-sm text-muted-foreground" },
|
|
32
|
+
"模块 package 提供的默认页面;项目可在 Web Composition 中替换。",
|
|
33
|
+
),
|
|
34
|
+
]),
|
|
35
|
+
loading.value
|
|
36
|
+
? h("p", "正在加载工单…")
|
|
37
|
+
: error.value
|
|
38
|
+
? h("p", { role: "alert" }, error.value)
|
|
39
|
+
: h(
|
|
40
|
+
"ul",
|
|
41
|
+
{ class: "divide-y rounded-md border" },
|
|
42
|
+
items.value.map((item) =>
|
|
43
|
+
h("li", { class: "space-y-1 p-4", key: item.id }, [
|
|
44
|
+
h("p", { class: "font-medium" }, item.title),
|
|
45
|
+
h(
|
|
46
|
+
"p",
|
|
47
|
+
{ class: "text-sm text-muted-foreground" },
|
|
48
|
+
`${item.workflowStatus} · ${item.priority}`,
|
|
49
|
+
),
|
|
50
|
+
]),
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
]);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
function isWorkOrderPage(value: unknown): value is WorkOrderPage {
|
|
58
|
+
return (
|
|
59
|
+
isRecord(value) &&
|
|
60
|
+
Array.isArray(value.items) &&
|
|
61
|
+
value.items.every(
|
|
62
|
+
(item) =>
|
|
63
|
+
isRecord(item) &&
|
|
64
|
+
typeof item.id === "string" &&
|
|
65
|
+
typeof item.title === "string" &&
|
|
66
|
+
typeof item.description === "string" &&
|
|
67
|
+
typeof item.priority === "string" &&
|
|
68
|
+
typeof item.workflowStatus === "string",
|
|
69
|
+
) &&
|
|
70
|
+
typeof value.page === "number" &&
|
|
71
|
+
typeof value.pageSize === "number" &&
|
|
72
|
+
typeof value.total === "number"
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
77
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
78
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineAdminUiManifest,
|
|
3
|
+
defineWindyAdminUiModule,
|
|
4
|
+
} from "@southwind-ai/modules/admin-ui";
|
|
5
|
+
|
|
6
|
+
export const workOrderAdminUiManifest = defineAdminUiManifest({
|
|
7
|
+
name: "work-order",
|
|
8
|
+
version: "0.1.0",
|
|
9
|
+
adminRoutes: [
|
|
10
|
+
{
|
|
11
|
+
key: "work-order.ticket.list",
|
|
12
|
+
path: "/admin/work-orders",
|
|
13
|
+
title: "工单管理",
|
|
14
|
+
surface: "sidebar",
|
|
15
|
+
directAccess: "guarded",
|
|
16
|
+
permissionKey: "work-order.ticket.read",
|
|
17
|
+
featureKey: "work-order.ticket",
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* package 标准 Admin 导出,提供可独立装配的默认页面。
|
|
24
|
+
* 项目可以通过 project-owned Web Composition 覆盖页面 loader。
|
|
25
|
+
*/
|
|
26
|
+
export const windyAdminUiModule = defineWindyAdminUiModule({
|
|
27
|
+
manifest: workOrderAdminUiManifest,
|
|
28
|
+
hostRange: "^0.1.0",
|
|
29
|
+
pages: [
|
|
30
|
+
{
|
|
31
|
+
routeKey: "work-order.ticket.list",
|
|
32
|
+
load: () => import("./admin-page.js"),
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
});
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
windyAdminUiModule,
|
|
4
|
+
workOrderAdminUiManifest,
|
|
5
|
+
} from "@southwind-ai/example-work-order/admin";
|
|
6
|
+
import {
|
|
7
|
+
windyModuleManifest,
|
|
8
|
+
workOrderModule,
|
|
9
|
+
} from "@southwind-ai/example-work-order/manifest";
|
|
3
10
|
|
|
4
11
|
describe("示例工单 Module Manifest", () => {
|
|
5
|
-
test("
|
|
12
|
+
test("业务能力注册 API、任务与 Windy Admin Surface", () => {
|
|
6
13
|
const manifest = workOrderModule();
|
|
7
14
|
|
|
8
15
|
expect(manifest.name).not.toBe("system");
|
|
@@ -16,16 +23,54 @@ describe("示例工单 Module Manifest", () => {
|
|
|
16
23
|
"work-order.ticket.manage",
|
|
17
24
|
]);
|
|
18
25
|
expect(manifest.apiPermissions).toHaveLength(5);
|
|
19
|
-
expect(
|
|
20
|
-
|
|
26
|
+
expect(
|
|
27
|
+
manifest.apiPermissions.map(({ auditAction }) => auditAction),
|
|
28
|
+
).toEqual([
|
|
29
|
+
"data.read",
|
|
30
|
+
"data.read",
|
|
31
|
+
"crud.create",
|
|
32
|
+
"crud.update",
|
|
33
|
+
"crud.delete",
|
|
34
|
+
]);
|
|
35
|
+
expect(manifest.menus).toEqual([
|
|
36
|
+
{
|
|
37
|
+
key: "work-order.ticket.list",
|
|
38
|
+
title: "工单管理",
|
|
39
|
+
type: "menu",
|
|
40
|
+
order: 100,
|
|
41
|
+
visible: true,
|
|
42
|
+
path: "/admin/work-orders",
|
|
43
|
+
icon: "clipboard-list",
|
|
44
|
+
featureKey: "work-order.ticket",
|
|
45
|
+
permissionKey: "work-order.ticket.read",
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
expect(manifest.adminRoutes).toEqual([
|
|
49
|
+
{
|
|
50
|
+
key: "work-order.ticket.list",
|
|
51
|
+
path: "/admin/work-orders",
|
|
52
|
+
title: "工单管理",
|
|
53
|
+
surface: "sidebar",
|
|
54
|
+
directAccess: "guarded",
|
|
55
|
+
featureKey: "work-order.ticket",
|
|
56
|
+
permissionKey: "work-order.ticket.read",
|
|
57
|
+
},
|
|
58
|
+
]);
|
|
21
59
|
expect(manifest.auditActions).toEqual(
|
|
22
|
-
expect.arrayContaining([
|
|
60
|
+
expect.arrayContaining([
|
|
61
|
+
"crud.create",
|
|
62
|
+
"crud.update",
|
|
63
|
+
"crud.delete",
|
|
64
|
+
"data.read",
|
|
65
|
+
"task.execute",
|
|
66
|
+
]),
|
|
23
67
|
);
|
|
24
68
|
expect(manifest.tasks).toContainEqual(
|
|
25
69
|
expect.objectContaining({
|
|
26
70
|
key: "work-order.ticket.snapshot",
|
|
27
71
|
featureKey: "work-order.ticket",
|
|
28
72
|
permissionKey: "work-order.ticket.manage",
|
|
73
|
+
auditAction: "task.execute",
|
|
29
74
|
}),
|
|
30
75
|
);
|
|
31
76
|
expect(manifest.providers).toEqual([
|
|
@@ -50,5 +95,17 @@ describe("示例工单 Module Manifest", () => {
|
|
|
50
95
|
persistence: "postgresql-with-memory-fallback",
|
|
51
96
|
uiComposition: "external-business-app",
|
|
52
97
|
});
|
|
98
|
+
expect(workOrderAdminUiManifest.adminRoutes).toEqual(manifest.adminRoutes);
|
|
99
|
+
expect(windyModuleManifest).toEqual(manifest);
|
|
100
|
+
expect(windyAdminUiModule).toMatchObject({
|
|
101
|
+
manifest: workOrderAdminUiManifest,
|
|
102
|
+
hostRange: "^0.1.0",
|
|
103
|
+
pages: [
|
|
104
|
+
expect.objectContaining({
|
|
105
|
+
routeKey: "work-order.ticket.list",
|
|
106
|
+
load: expect.any(Function),
|
|
107
|
+
}),
|
|
108
|
+
],
|
|
109
|
+
});
|
|
53
110
|
});
|
|
54
111
|
});
|
|
@@ -2,11 +2,14 @@ import {
|
|
|
2
2
|
defineSearchProvider,
|
|
3
3
|
featureKey,
|
|
4
4
|
licenseVersionKey,
|
|
5
|
+
type AuditActionType,
|
|
5
6
|
} from "@southwind-ai/shared";
|
|
6
7
|
import {
|
|
7
8
|
WORK_ORDER_INITIAL_MIGRATION_ID,
|
|
9
|
+
WORK_ORDER_MODULE_DEPENDENCIES,
|
|
8
10
|
workOrderMigrationBundle,
|
|
9
11
|
} from "./migration-bundle.js";
|
|
12
|
+
import { workOrderAdminUiManifest } from "./admin.js";
|
|
10
13
|
|
|
11
14
|
const FEATURE_KEY = featureKey("work-order.ticket");
|
|
12
15
|
|
|
@@ -16,19 +19,52 @@ export function workOrderModule() {
|
|
|
16
19
|
version: "0.1.0",
|
|
17
20
|
label: "示例工单",
|
|
18
21
|
description: "可删除的业务模块示例,演示完整平台能力接入。",
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
dependencies: [...WORK_ORDER_MODULE_DEPENDENCIES],
|
|
23
|
+
menus: [
|
|
24
|
+
{
|
|
25
|
+
key: "work-order.ticket.list",
|
|
26
|
+
title: "工单管理",
|
|
27
|
+
type: "menu" as const,
|
|
28
|
+
order: 100,
|
|
29
|
+
visible: true,
|
|
30
|
+
path: "/admin/work-orders",
|
|
31
|
+
icon: "clipboard-list",
|
|
32
|
+
permissionKey: "work-order.ticket.read",
|
|
33
|
+
featureKey: FEATURE_KEY,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
adminRoutes: [...workOrderAdminUiManifest.adminRoutes],
|
|
21
37
|
permissions: [
|
|
22
38
|
permission("read", "查看工单"),
|
|
23
39
|
permission("create", "创建工单"),
|
|
24
40
|
permission("manage", "管理工单"),
|
|
25
41
|
],
|
|
26
42
|
apiPermissions: [
|
|
27
|
-
binding("GET", "/api/work-orders", "work-order.ticket.read"),
|
|
28
|
-
binding(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
43
|
+
binding("GET", "/api/work-orders", "work-order.ticket.read", "data.read"),
|
|
44
|
+
binding(
|
|
45
|
+
"GET",
|
|
46
|
+
"/api/work-orders/:id",
|
|
47
|
+
"work-order.ticket.read",
|
|
48
|
+
"data.read",
|
|
49
|
+
),
|
|
50
|
+
binding(
|
|
51
|
+
"POST",
|
|
52
|
+
"/api/work-orders",
|
|
53
|
+
"work-order.ticket.create",
|
|
54
|
+
"crud.create",
|
|
55
|
+
),
|
|
56
|
+
binding(
|
|
57
|
+
"PUT",
|
|
58
|
+
"/api/work-orders/:id",
|
|
59
|
+
"work-order.ticket.manage",
|
|
60
|
+
"crud.update",
|
|
61
|
+
),
|
|
62
|
+
binding(
|
|
63
|
+
"DELETE",
|
|
64
|
+
"/api/work-orders/:id",
|
|
65
|
+
"work-order.ticket.manage",
|
|
66
|
+
"crud.delete",
|
|
67
|
+
),
|
|
32
68
|
],
|
|
33
69
|
features: [
|
|
34
70
|
{
|
|
@@ -61,6 +97,7 @@ export function workOrderModule() {
|
|
|
61
97
|
description: "统计当前工单待办和高优先级积压,写入调度执行结果。",
|
|
62
98
|
permissionKey: "work-order.ticket.manage",
|
|
63
99
|
featureKey: FEATURE_KEY,
|
|
100
|
+
auditAction: "task.execute",
|
|
64
101
|
intervalSeconds: 900,
|
|
65
102
|
maxAttempts: 3,
|
|
66
103
|
retryDelaySeconds: 60,
|
|
@@ -88,7 +125,9 @@ export function workOrderModule() {
|
|
|
88
125
|
"crud.create" as const,
|
|
89
126
|
"crud.update" as const,
|
|
90
127
|
"crud.delete" as const,
|
|
128
|
+
"data.read" as const,
|
|
91
129
|
"data.search" as const,
|
|
130
|
+
"task.execute" as const,
|
|
92
131
|
],
|
|
93
132
|
metadata: {
|
|
94
133
|
removableExample: true,
|
|
@@ -99,6 +138,12 @@ export function workOrderModule() {
|
|
|
99
138
|
};
|
|
100
139
|
}
|
|
101
140
|
|
|
141
|
+
/**
|
|
142
|
+
* package 标准 Manifest 导出。构建与 Server 可用它核对窄 Admin 投影,
|
|
143
|
+
* 浏览器只能从 `./admin` 子路径加载。
|
|
144
|
+
*/
|
|
145
|
+
export const windyModuleManifest = workOrderModule();
|
|
146
|
+
|
|
102
147
|
function permission(action: "read" | "create" | "manage", label: string) {
|
|
103
148
|
return {
|
|
104
149
|
key: `work-order.ticket.${action}`,
|
|
@@ -115,11 +160,13 @@ function binding(
|
|
|
115
160
|
method: "GET" | "POST" | "PUT" | "DELETE",
|
|
116
161
|
path: string,
|
|
117
162
|
permissionKey: string,
|
|
163
|
+
auditAction: AuditActionType,
|
|
118
164
|
) {
|
|
119
165
|
return {
|
|
120
166
|
method,
|
|
121
167
|
path,
|
|
122
168
|
permissionKey,
|
|
123
169
|
featureKey: "work-order.ticket",
|
|
170
|
+
auditAction,
|
|
124
171
|
};
|
|
125
172
|
}
|
|
@@ -7,6 +7,14 @@ import {
|
|
|
7
7
|
export const WORK_ORDER_INITIAL_MIGRATION_ID =
|
|
8
8
|
"work-order.0001.create-work-orders";
|
|
9
9
|
|
|
10
|
+
export const WORK_ORDER_MODULE_DEPENDENCIES = [
|
|
11
|
+
"system",
|
|
12
|
+
"system.identity",
|
|
13
|
+
"system.search",
|
|
14
|
+
"system.scheduler",
|
|
15
|
+
"system.storage",
|
|
16
|
+
] as const;
|
|
17
|
+
|
|
10
18
|
export const workOrderMigrationBundle: MigrationBundle = {
|
|
11
19
|
formatVersion: MIGRATION_BUNDLE_FORMAT_VERSION,
|
|
12
20
|
module: "work-order",
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
ModuleSearchProvider,
|
|
6
6
|
ModuleTaskHandler,
|
|
7
7
|
} from "@southwind-ai/server-sdk";
|
|
8
|
-
import {
|
|
8
|
+
import { windyModuleManifest } from "./manifest.js";
|
|
9
9
|
|
|
10
10
|
export interface WorkOrderServerBindings {
|
|
11
11
|
readonly routes: readonly ModuleRouteDefinition[];
|
|
@@ -20,7 +20,7 @@ export function createWorkOrderServerModule(
|
|
|
20
20
|
bindings: WorkOrderServerBindings,
|
|
21
21
|
): InstallableServerModule<ModuleManifest> {
|
|
22
22
|
return {
|
|
23
|
-
manifest:
|
|
23
|
+
manifest: windyModuleManifest,
|
|
24
24
|
setup(host) {
|
|
25
25
|
host.registerUploadPolicy({
|
|
26
26
|
purpose: "work-order.ticket.attachment",
|
|
@@ -8,3 +8,7 @@ export * from "./src/system-module-catalog.js";
|
|
|
8
8
|
export * from "./src/system-api-permissions.js";
|
|
9
9
|
export * from "./src/tool-composition.js";
|
|
10
10
|
export * from "./src/ai-network-policy.js";
|
|
11
|
+
export * from "./src/project-module-assembly.js";
|
|
12
|
+
export * from "./src/module-host-range.js";
|
|
13
|
+
export * from "./src/admin-ui-manifest.js";
|
|
14
|
+
export * from "./src/system-admin-ui-catalog.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@southwind-ai/modules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"@southwind-ai/shared": "workspace:^"
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
|
-
".": "./index.ts"
|
|
18
|
+
".": "./index.ts",
|
|
19
|
+
"./admin-ui": "./src/admin-ui-entry.ts"
|
|
19
20
|
},
|
|
20
21
|
"publishConfig": {
|
|
21
22
|
"access": "public"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./admin-ui-manifest.js";
|
|
2
|
+
export * from "./module-host-range.js";
|
|
3
|
+
export * from "./project-module-assembly.js";
|
|
4
|
+
export * from "./system-admin-ui-catalog.js";
|
|
5
|
+
export type {
|
|
6
|
+
AdminRouteSurface,
|
|
7
|
+
ModuleAdminRouteDefinition,
|
|
8
|
+
} from "./manifest.js";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
defineAdminUiManifest,
|
|
4
|
+
defineWindyAdminUiModule,
|
|
5
|
+
projectAdminUiManifest,
|
|
6
|
+
} from "./admin-ui-manifest.js";
|
|
7
|
+
import { systemModuleManifests } from "./system-module-catalog.js";
|
|
8
|
+
import { systemAdminUiManifests } from "./system-admin-ui-catalog.js";
|
|
9
|
+
|
|
10
|
+
describe("Admin UI 窄 Manifest", () => {
|
|
11
|
+
test("从完整 Manifest 只投影身份与 Admin Route", () => {
|
|
12
|
+
const full = systemModuleManifests().find(
|
|
13
|
+
({ name }) => name === "system.identity",
|
|
14
|
+
)!;
|
|
15
|
+
const projected = projectAdminUiManifest(full);
|
|
16
|
+
|
|
17
|
+
expect(projected).toEqual({
|
|
18
|
+
name: full.name,
|
|
19
|
+
version: full.version,
|
|
20
|
+
adminRoutes: full.adminRoutes,
|
|
21
|
+
});
|
|
22
|
+
expect(projected).not.toHaveProperty("permissions");
|
|
23
|
+
expect(projected).not.toHaveProperty("migrations");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("系统窄目录与完整 Manifest 的 Admin Route 保持同源", () => {
|
|
27
|
+
const full = new Map(
|
|
28
|
+
systemModuleManifests().map((manifest) => [
|
|
29
|
+
manifest.name,
|
|
30
|
+
projectAdminUiManifest(manifest),
|
|
31
|
+
]),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(systemAdminUiManifests()).toEqual([...full.values()]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("标准 Admin UI Module 保留 Host range 与页面绑定", () => {
|
|
38
|
+
const manifest = defineAdminUiManifest({
|
|
39
|
+
name: "case",
|
|
40
|
+
version: "1.0.0",
|
|
41
|
+
adminRoutes: [],
|
|
42
|
+
});
|
|
43
|
+
const module = defineWindyAdminUiModule({
|
|
44
|
+
manifest,
|
|
45
|
+
hostRange: "^0.1.0",
|
|
46
|
+
pages: [],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(module.manifest).toBe(manifest);
|
|
50
|
+
expect(module.hostRange).toBe("^0.1.0");
|
|
51
|
+
});
|
|
52
|
+
});
|