@oj-module/cli 0.2.18 → 0.2.23
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/devkit/README.md +4 -4
- package/devkit/SKILL.md +41 -37
- package/devkit/framework-dev.md +6 -5
- package/devkit/manual.md +352 -168
- package/devkit/menu-rules.md +3 -3
- package/devkit/module-dev-and-cdn-playground.md +31 -22
- package/devkit/sql-builder-missing-capabilities-pr.md +11 -6
- package/package.json +2 -2
- package/shell-dist/assets/runtime.js +59 -34
- package/shell-dist/index.html +2 -2
- package/shell-dist/versions.json +3 -3
- package/src/__tests__/build-backend.test.ts +54 -0
- package/src/__tests__/init-packagejson.test.ts +29 -0
- package/src/__tests__/layout.test.ts +176 -0
- package/src/__tests__/module-list.test.ts +66 -0
- package/src/__tests__/oj-caps.test.ts +114 -0
- package/src/alias-paths.ts +102 -0
- package/src/args.ts +54 -4
- package/src/build.ts +41 -9
- package/src/builtin.ts +93 -0
- package/src/bundle.ts +254 -38
- package/src/config.ts +82 -9
- package/src/contract/__tests__/market-client.integration.test.ts +14 -14
- package/src/contract/check.ts +56 -28
- package/src/contract/emit-stub.ts +21 -9
- package/src/contract/handler-scan.ts +19 -10
- package/src/contract/run.ts +14 -4
- package/src/dev.ts +80 -9
- package/src/index.ts +27 -7
- package/src/info.ts +69 -10
- package/src/init.ts +114 -28
- package/src/layout.ts +282 -24
- package/src/lifecycle/__tests__/args.test.ts +20 -0
- package/src/lifecycle/__tests__/builtin.test.ts +97 -0
- package/src/lifecycle/__tests__/bundle.test.ts +184 -6
- package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
- package/src/lifecycle/__tests__/new.test.ts +129 -0
- package/src/lifecycle/__tests__/smoke.test.ts +2 -1
- package/src/manifest-contract.ts +16 -3
- package/src/market/__tests__/bootstrap.test.ts +17 -9
- package/src/market/bootstrap.ts +30 -17
- package/src/market/publish.ts +10 -2
- package/src/new.ts +208 -0
- package/src/oj-caps.ts +133 -0
- package/src/oj.ts +27 -4
- package/src/preview-config.ts +1 -1
- package/src/preview.ts +2 -1
- package/src/site.ts +2 -2
- package/src/usage.ts +24 -12
- package/src/vendor.ts +5 -1
- package/templates/{api/config.yaml → config.yaml} +13 -8
- package/templates/global.d.ts +7 -6
- package/templates/init.include.yaml +41 -36
- package/templates/package.json +3 -1
- package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/installer.ts +4 -0
- package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/smoke.ts +21 -2
- package/templates/{api/src/_platform → src/_platform/api}/fixtures/l1-test-user.sql +4 -5
- package/templates/{api/src/_platform → src/_platform/api}/market/api.ts +25 -15
- package/templates/src/_platform/api/seed.mysql.sql +16 -0
- package/templates/src/_platform/api/seed.pg.sql +16 -0
- package/templates/{api/src/_platform → src/_platform/api}/seed.sql +16 -5
- package/templates/{api/src/_platform → src/_platform/api}/solutions/api.ts +7 -3
- package/templates/{api/src/auth → src/auth/api}/_shared/login-throttle.ts +2 -2
- package/templates/{api/src/auth → src/auth/api}/_shared/session.ts +2 -2
- package/templates/{api/src/auth → src/auth/api}/api.ts +1 -1
- package/templates/{api/src/auth → src/auth/api}/ldap-login/api.ts +1 -1
- package/templates/src/auth/api/manifest.yaml +6 -0
- package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/InstallDialog.test.tsx +53 -1
- package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/marketClient.fake.ts +6 -0
- package/templates/{web/src/oj-market → src/oj-market/web}/api/client.ts +5 -0
- package/templates/{web/src/oj-market → src/oj-market/web}/api/context.ts +2 -2
- package/templates/{web/src/oj-market → src/oj-market/web}/api/defaultClient.ts +7 -1
- package/templates/src/oj-market/web/components/InstallDialog.tsx +194 -0
- package/templates/{web/src/oj-market → src/oj-market/web}/components/RollbackDialog.tsx +7 -3
- package/templates/{web/src/oj-market → src/oj-market/web}/components/UninstallConfirm.tsx +18 -7
- package/templates/{web/src/oj-market → src/oj-market/web}/hooks/useMarket.ts +29 -12
- package/templates/{web/src/oj-market → src/oj-market/web}/locales/en-US.json +10 -1
- package/templates/{web/src/oj-market → src/oj-market/web}/locales/zh-CN.json +10 -1
- package/templates/{web/src/oj-market → src/oj-market/web}/types.ts +15 -2
- package/templates/{api/src/system → src/system/api}/login-methods/api.ts +2 -2
- package/templates/src/system/api/manifest.yaml +6 -0
- package/templates/{api/src/system → src/system/api}/oplog/api.ts +4 -4
- package/templates/{api/src/system → src/system/api}/permissions/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/roles/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/roles/bindings/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/settings/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/users/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/users/forbidden/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/users/password/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/users/roles/api.ts +3 -3
- package/templates/{web/src/system → src/system/web}/locales/en-US.json +1 -1
- package/templates/{web/src/system → src/system/web}/locales/zh-CN.json +1 -1
- package/templates/src/tsconfig.json +16 -0
- package/templates/{api/tests → tests}/auth-ldap.spec.ts +2 -2
- package/templates/{api/tests → tests}/auth-session.spec.ts +3 -3
- package/templates/{api/tests → tests}/auth-throttle.spec.ts +1 -1
- package/templates/tests/fixture.ts +434 -0
- package/templates/{api/tests → tests}/guard.spec.ts +1 -1
- package/templates/{api/tests → tests}/l1-builder.test.ts +2 -2
- package/templates/{api/tests → tests}/lifecycle/atomic-state.spec.ts +3 -3
- package/templates/{api/tests → tests}/lifecycle/bootstrap.spec.ts +8 -4
- package/templates/{api/tests → tests}/lifecycle/deploy-smoke.spec.ts +31 -8
- package/templates/{api/tests → tests}/lifecycle/installer.spec.ts +15 -15
- package/templates/{api/tests → tests}/lifecycle/market-api.spec.ts +30 -29
- package/templates/{api/tests → tests}/lifecycle/migrate.spec.ts +6 -6
- package/templates/{api/tests → tests}/lifecycle/preflight.spec.ts +4 -4
- package/templates/{api/tests → tests}/lifecycle/register.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/semver.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/sha384.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/sig-fixtures.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/solutions-api.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/steps-fs.spec.ts +12 -12
- package/templates/{api/tests → tests}/lifecycle/text-edit.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/tgz-guard.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/trust.spec.ts +5 -5
- package/templates/{api/tests → tests}/lifecycle/uninstall.spec.ts +7 -7
- package/templates/{api/tests → tests}/lifecycle/verify-checks.spec.ts +9 -6
- package/templates/{api/tests → tests}/lifecycle/verify-report.spec.ts +7 -5
- package/templates/{api/tests → tests}/query-utils.spec.ts +1 -1
- package/templates/{api/tests → tests}/rbac-audit.spec.ts +1 -1
- package/templates/{api/tests → tests}/rbac-contacts.spec.ts +2 -2
- package/templates/{api/tests → tests}/rbac-guard.spec.ts +1 -1
- package/templates/{api/tests → tests}/rbac-roles.spec.ts +2 -2
- package/templates/{api/tests → tests}/rbac-seed.spec.ts +13 -11
- package/templates/{api/tests → tests}/rbac-settings.spec.ts +2 -2
- package/templates/{api/tests → tests}/rbac-solution-perms.spec.ts +1 -1
- package/templates/{api/tests → tests}/rbac-users.spec.ts +3 -3
- package/templates/tests/seed-role-accounts.spec.ts +87 -0
- package/templates/tsconfig.json +5 -1
- package/templates/tsconfig.paths.json +19 -0
- package/templates/types/oj-modules.d.ts +17 -0
- package/templates/vitest.config.ts +22 -0
- package/templates/web.config.ts +23 -13
- package/vendor/host-versions.json +3 -3
- package/templates/api/src/_platform/seed.mysql.sql +0 -8
- package/templates/api/src/_platform/seed.pg.sql +0 -8
- package/templates/api/src/auth/manifest.yaml +0 -5
- package/templates/api/src/system/manifest.yaml +0 -5
- package/templates/api/tests/fixture.ts +0 -58
- package/templates/api/tsconfig.json +0 -8
- package/templates/api/vitest.config.ts +0 -12
- package/templates/logs/task-events.jsonl +0 -0
- package/templates/web/src/oj-market/components/InstallDialog.tsx +0 -112
- /package/templates/{api/.ojm-api-exempt.json → .ojm-api-exempt.json} +0 -0
- /package/templates/{api/seed.mysql.sql → seed.mysql.sql} +0 -0
- /package/templates/{api/seed.pg.sql → seed.pg.sql} +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/README.md +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/atomic.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/history.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/lock.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/semver.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/sha384.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/state.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/deploy-web.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/download.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/extract.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/migrate.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/preflight.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/register.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/reload.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/verify.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/tasks.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/text-edit.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/tgz-guard.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/trust.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/types.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/uninstall.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/verify-checks.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/verify-report.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/respond.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/contacts.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/roles.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/settings.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/solution-perms.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/users.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/access.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/audit.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/guard.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/oplog.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/query.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/rate.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/site-facade.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/contacts/api.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/contacts/detail/api.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/manifest.yaml +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/market/bootstrap.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0002__add_users_avatar_base64.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0003__users_source_display_name.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0003__users_source_display_name.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0005__users_must_change.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0005__users_must_change.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0006__users_is_forbidden.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0007__users_email.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0008__sys_refresh_token.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0008__sys_refresh_token.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0009__sys_throttle.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0009__sys_throttle.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0009__sys_throttle.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0010__sys_solution_route.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0010__sys_solution_route.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0010__sys_solution_route.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0011__sys_setting_group_label.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0012__market_history.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0012__market_history.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0012__market_history.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0013__users_profile.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/schema.yaml +0 -0
- /package/templates/{web/src/_platform → src/_platform/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/_platform → src/_platform/web}/__client/api.ts +0 -0
- /package/templates/{web/src/about → src/about/web}/entry.ts +0 -0
- /package/templates/{web/src/about → src/about/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/about → src/about/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/about → src/about/web}/pages/constants.ts +0 -0
- /package/templates/{web/src/about → src/about/web}/pages/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/entry.ts +0 -0
- /package/templates/{web/src/access → src/access/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/access → src/access/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/access-mode/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/admin-visible/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/button-control/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/common-visible/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/page-control/index.tsx +0 -0
- /package/templates/{api/src/auth → src/auth/api}/_shared/ldap-login.ts +0 -0
- /package/templates/{api/src/auth → src/auth/api}/_shared/ldap.ts +0 -0
- /package/templates/{web/src/auth → src/auth/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/auth → src/auth/web}/__client/api.ts +0 -0
- /package/templates/{api/src/demo → src/demo/api}/manifest.yaml +0 -0
- /package/templates/{api/src/demo → src/demo/api}/migrations/0001__create_todos.sql +0 -0
- /package/templates/{api/src/demo → src/demo/api}/schema.yaml +0 -0
- /package/templates/{api/src/demo → src/demo/api}/seed.sql +0 -0
- /package/templates/{api/src/demo → src/demo/api}/todos/api.ts +0 -0
- /package/templates/{web/src/demo → src/demo/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/demo → src/demo/web}/__client/api.ts +0 -0
- /package/templates/{web/src/demo → src/demo/web}/entry.ts +0 -0
- /package/templates/{web/src/demo → src/demo/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/demo → src/demo/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/demo → src/demo/web}/pages/index.tsx +0 -0
- /package/templates/{web/src/exception → src/exception/web}/entry.ts +0 -0
- /package/templates/{web/src/exception → src/exception/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/exception → src/exception/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/exception → src/exception/web}/pages/403/index.tsx +0 -0
- /package/templates/{web/src/exception → src/exception/web}/pages/404/index.tsx +0 -0
- /package/templates/{web/src/exception → src/exception/web}/pages/500/index.tsx +0 -0
- /package/templates/{web/src/exception → src/exception/web}/pages/unknown-component/index.tsx +0 -0
- /package/templates/{api/src/home → src/home/api}/line/api.ts +0 -0
- /package/templates/{api/src/home → src/home/api}/manifest.yaml +0 -0
- /package/templates/{api/src/home → src/home/api}/migrations/0001__create_home_tables.sql +0 -0
- /package/templates/{api/src/home → src/home/api}/pie/api.ts +0 -0
- /package/templates/{api/src/home → src/home/api}/schema.yaml +0 -0
- /package/templates/{api/src/home → src/home/api}/seed.sql +0 -0
- /package/templates/{web/src/home → src/home/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/home → src/home/web}/__client/api.ts +0 -0
- /package/templates/{web/src/home → src/home/web}/entry.ts +0 -0
- /package/templates/{web/src/home → src/home/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/home → src/home/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/components/bar-chart.tsx +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/components/card-list.tsx +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/components/line-chart.tsx +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/components/pie-chart.tsx +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/index.tsx +0 -0
- /package/templates/{web/src/login → src/login/web}/entry.ts +0 -0
- /package/templates/{web/src/login → src/login/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/login → src/login/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/login → src/login/web}/pages/change-password.tsx +0 -0
- /package/templates/{web/src/login → src/login/web}/pages/login.tsx +0 -0
- /package/templates/{api/src/notification → src/notification/api}/_shared/notify.ts +0 -0
- /package/templates/{api/src/notification → src/notification/api}/api.ts +0 -0
- /package/templates/{api/src/notification → src/notification/api}/del/api.ts +0 -0
- /package/templates/{api/src/notification → src/notification/api}/manifest.yaml +0 -0
- /package/templates/{api/src/notification → src/notification/api}/migrations/0001__create_notifications.sql +0 -0
- /package/templates/{api/src/notification → src/notification/api}/migrations/0002__notifications_user.sql +0 -0
- /package/templates/{api/src/notification → src/notification/api}/migrations/0003__notifications_broadcast.sql +0 -0
- /package/templates/{api/src/notification → src/notification/api}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
- /package/templates/{api/src/notification → src/notification/api}/schema.yaml +0 -0
- /package/templates/{api/src/notification → src/notification/api}/seed.sql +0 -0
- /package/templates/{web/src/notification → src/notification/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/notification → src/notification/web}/__client/api.ts +0 -0
- /package/templates/{web/src/notification → src/notification/web}/entry.ts +0 -0
- /package/templates/{web/src/notification → src/notification/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/notification → src/notification/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/notification → src/notification/web}/pages/center/index.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/CardGrid.test.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/HistoryDrawer.test.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/RollbackDialog.test.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/UninstallConfirm.test.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/poll.test.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/render.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/runtime-stub.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/semver.test.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/api/MarketClientProvider.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/CardGrid.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/CardItem.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/DetailDrawer.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/HistoryDrawer.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/entry.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/pages/MarketApp.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/utils/poll.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/utils/semver.ts +0 -0
- /package/templates/{web/src/outside → src/outside/web}/entry.ts +0 -0
- /package/templates/{web/src/outside → src/outside/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/outside → src/outside/web}/locales/zh-CN.json +0 -0
- /package/templates/{api/src/personal-center → src/personal-center/api}/manifest.yaml +0 -0
- /package/templates/{api/src/personal-center → src/personal-center/api}/profile/api.ts +0 -0
- /package/templates/{api/src/personal-center → src/personal-center/api}/upload/api.ts +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/__client/api.ts +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/entry.ts +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/pages/my-profile/index.tsx +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/entry.ts +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/pages/menu1/menu1-1/index.tsx +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/pages/menu1/menu1-2/index.tsx +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/pages/menu2/index.tsx +0 -0
- /package/templates/{web/src → src}/styles.css +0 -0
- /package/templates/{web/src/system → src/system/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/system → src/system/web}/__client/api.ts +0 -0
- /package/templates/{web/src/system → src/system/web}/entry.ts +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/errors.ts +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/forbidden.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/login-methods/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/oplog/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/roles/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/settings/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/users/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/users/roles.tsx +0 -0
- /package/templates/{api/src/web → src/web/api}/get-async-routes/api.ts +0 -0
- /package/templates/{api/src/web → src/web/api}/hello/api.ts +0 -0
- /package/templates/{api/src/web → src/web/api}/manifest.yaml +0 -0
- /package/templates/{api/src/web → src/web/api}/user-info/api.ts +0 -0
- /package/templates/{web/src → src/web}/web/__client/api.schemas.ts +0 -0
- /package/templates/{web/src → src/web}/web/__client/api.ts +0 -0
- /package/templates/{api/tests → tests}/rbac-tables.spec.ts +0 -0
package/src/index.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
|
-
import { parseApiArgs, parseBuildArgs, parseInitArgs, parseLifecycleArgs, parseVendorArgs } from "./args";
|
|
4
|
+
import { parseApiArgs, parseBuildArgs, parseInitArgs, parseLifecycleArgs, parseNewArgs, parseVendorArgs } from "./args";
|
|
5
5
|
import { buildBackend, buildModules } from "./build";
|
|
6
6
|
import { buildBundle } from "./bundle";
|
|
7
7
|
import { devServer } from "./dev";
|
|
8
8
|
import { mergeManifests, printInfo } from "./info";
|
|
9
9
|
import { initProject } from "./init";
|
|
10
|
+
import { createSolution } from "./new";
|
|
10
11
|
import { runPreview } from "./preview";
|
|
11
12
|
import { getCommandHelp, resolveHelpRequest, usageText } from "./usage";
|
|
12
13
|
import { vendorCommand } from "./vendor";
|
|
@@ -50,10 +51,13 @@ async function main() {
|
|
|
50
51
|
|
|
51
52
|
switch (command) {
|
|
52
53
|
case "build": {
|
|
53
|
-
const { bundle, out } = parseBuildArgs(process.argv.slice(3));
|
|
54
|
+
const { bundle, bundleName, out } = parseBuildArgs(process.argv.slice(3));
|
|
54
55
|
if (bundle) {
|
|
55
56
|
// 方案包构建(P1-11):契约校验 fail-fast → 确定性 tgz + sha256(消费方:市场安装器)
|
|
56
|
-
|
|
57
|
+
// 多方案:无 bundleName 打全部用户方案(自带排除),有则只打 name 那一份
|
|
58
|
+
const results = await buildBundle(projectRoot, { out, name: bundleName });
|
|
59
|
+
if (results.length > 1)
|
|
60
|
+
console.log(`[ojm] 共打包 ${results.length} 份方案包(逐包签名:ojm sign dist/<name>-<version>.tgz --id <publisher>.<key> --check)`);
|
|
57
61
|
break;
|
|
58
62
|
}
|
|
59
63
|
// 设计 §5:后端 oj build(零 DB 副作用)→ 前端全站合并(仅 build 清场合并)
|
|
@@ -61,6 +65,19 @@ async function main() {
|
|
|
61
65
|
await buildModules(projectRoot, { mergeSite: true });
|
|
62
66
|
break;
|
|
63
67
|
}
|
|
68
|
+
case "new": {
|
|
69
|
+
const { name, layout, api } = parseNewArgs(process.argv.slice(3));
|
|
70
|
+
if (!name) {
|
|
71
|
+
console.error("[ojm] 用法:ojm new <name> [--layout container|none] [--api](非交互环境必须显式给 name)");
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
const created = createSolution(projectRoot, name, { layout, api });
|
|
75
|
+
console.log(`[ojm] 已生成方案骨架:${path.relative(process.cwd(), created.dir)}`);
|
|
76
|
+
for (const f of created.files)
|
|
77
|
+
console.log(`[ojm] + ${f}`);
|
|
78
|
+
console.log(`[ojm] 下一步:pnpm dev 看效果 → ojm build && ojm build --bundle ${name} 打包`);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
64
81
|
case "init": {
|
|
65
82
|
const { dest, yes } = parseInitArgs(process.argv.slice(3));
|
|
66
83
|
await initProject(dest ? path.resolve(dest) : projectRoot, { yes });
|
|
@@ -79,7 +96,7 @@ async function main() {
|
|
|
79
96
|
break;
|
|
80
97
|
}
|
|
81
98
|
case "info":
|
|
82
|
-
await printInfo(projectRoot);
|
|
99
|
+
await printInfo(projectRoot, undefined, { all: process.argv.slice(3).includes("--all") });
|
|
83
100
|
break;
|
|
84
101
|
case "vendor": {
|
|
85
102
|
const { force, tag } = parseVendorArgs(process.argv.slice(3));
|
|
@@ -100,7 +117,7 @@ async function main() {
|
|
|
100
117
|
}
|
|
101
118
|
if (check) {
|
|
102
119
|
const { checkApi } = await import("./contract/check");
|
|
103
|
-
// --exempt <path
|
|
120
|
+
// --exempt <path>:相对路径按项目根解析;缺省按布局找 .ojm-api-exempt.json(工程根 → api/)
|
|
104
121
|
// (旧名 api/.ram-api-exempt.json 仍被读取,R4)
|
|
105
122
|
const { violations, hints } = await checkApi({ cwd: apiRoot, exempt: exempt ? path.resolve(projectRoot, exempt) : undefined });
|
|
106
123
|
for (const v of violations)
|
|
@@ -197,7 +214,10 @@ async function main() {
|
|
|
197
214
|
|
|
198
215
|
main().catch((error: unknown) => {
|
|
199
216
|
// Error 的 message 已是人话(含修复指引);堆栈默认是噪音,OJM_DEBUG=1 才输出
|
|
200
|
-
// (旧名 RAM_DEBUG
|
|
201
|
-
|
|
217
|
+
// (旧名 RAM_DEBUG 一并读,兼容既有排障习惯)。
|
|
218
|
+
// 文案常自带 [ojm] 前缀(bundle/publish/sign 等抛出的错误),顶层不再叠一个,否则变 [ojm] [ojm]。
|
|
219
|
+
const isDebug = !!(process.env.OJM_DEBUG ?? process.env.RAM_DEBUG);
|
|
220
|
+
const body = error instanceof Error ? (isDebug ? error.stack ?? error.message : error.message) : String(error);
|
|
221
|
+
console.error(/^\[ojm/.test(body) ? body : `[ojm] ${body}`);
|
|
202
222
|
process.exit(1);
|
|
203
223
|
});
|
package/src/info.ts
CHANGED
|
@@ -12,9 +12,11 @@ import fs from "node:fs";
|
|
|
12
12
|
|
|
13
13
|
import http from "node:http";
|
|
14
14
|
import path from "node:path";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { readBuiltinModules } from "./builtin";
|
|
16
|
+
import { loadModulesConfig, loadSiteConfig, mergeModuleList } from "./config";
|
|
17
|
+
import { resolveConfigPath, scanModules, tryResolveLayout } from "./layout";
|
|
17
18
|
import { mergeModuleManifests } from "./manifest";
|
|
19
|
+
import { probeOjCaps } from "./oj-caps";
|
|
18
20
|
import { readOjApiPrefix, readOjServerField } from "./oj-config";
|
|
19
21
|
import { isSupportedOjVersion, MIN_OJ_VERSION, readLocalVersion, resolveOjBin } from "./vendor";
|
|
20
22
|
import { readHostVersions, resolveShellDist } from "./versions";
|
|
@@ -90,7 +92,50 @@ export function realOjObservability(projectRoot: string): OjObservability {
|
|
|
90
92
|
};
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
export
|
|
95
|
+
export interface PrintInfoOptions {
|
|
96
|
+
/** 展开框架自带模块(默认折叠为一行计数) */
|
|
97
|
+
all?: boolean
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 模块清单条目解析:module-root 用「扫描 + 登记覆盖」并集(与 build 同源,
|
|
102
|
+
* 未登记的用户模块也可见——旧实现只读 web.config.ts 会漏掉它们);历史布局
|
|
103
|
+
* 维持登记清单(loadModulesConfig)。
|
|
104
|
+
*/
|
|
105
|
+
async function resolveModuleEntries(projectRoot: string): Promise<{ name: string, entry: string, enabled: boolean }[]> {
|
|
106
|
+
const layout = tryResolveLayout(projectRoot);
|
|
107
|
+
if (layout?.kind === "module-root") {
|
|
108
|
+
const config = await loadSiteConfig(projectRoot);
|
|
109
|
+
return mergeModuleList(scanModules(layout), config.modules, projectRoot);
|
|
110
|
+
}
|
|
111
|
+
const config = await loadModulesConfig(projectRoot);
|
|
112
|
+
return config.modules.map(m => ({ name: m.name, entry: m.entry, enabled: m.enabled !== false }));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** 清单渲染:自带模块默认折叠(--all 展开);坏名单文件告警降级为平铺,info 不失败 */
|
|
116
|
+
function renderModuleLines(entries: { name: string, entry: string, enabled: boolean }[], projectRoot: string, all: boolean): string {
|
|
117
|
+
let builtin = new Set<string>();
|
|
118
|
+
try {
|
|
119
|
+
builtin = readBuiltinModules(projectRoot);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.warn(`[ojm] ⚠️ ${error instanceof Error ? error.message : String(error)}——本次按无内置名单渲染。`);
|
|
123
|
+
}
|
|
124
|
+
const fmt = (m: { name: string, entry: string, enabled: boolean }) => ` · ${m.name}(${m.entry})${m.enabled ? "" : " [disabled]"}`;
|
|
125
|
+
const own = entries.filter(m => builtin.has(m.name));
|
|
126
|
+
const user = entries.filter(m => !builtin.has(m.name));
|
|
127
|
+
const lines = user.map(fmt);
|
|
128
|
+
if (own.length > 0) {
|
|
129
|
+
if (all)
|
|
130
|
+
lines.push(...own.map(fmt));
|
|
131
|
+
else
|
|
132
|
+
lines.push(` 框架自带:${own.length} 个(ojm info --all 展开)`);
|
|
133
|
+
}
|
|
134
|
+
// 零模块时留占位行,避免清单段整段空白像渲染故障
|
|
135
|
+
return lines.length > 0 ? lines.join("\n") : "(无 web 模块)";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function printInfo(projectRoot: string, oj: OjObservability = realOjObservability(projectRoot), opts: PrintInfoOptions = {}): Promise<void> {
|
|
94
139
|
const cliVersion = readPkgVersion(path.join(projectRoot, "node_modules/@oj-module/cli/package.json"));
|
|
95
140
|
const runtimeVersion = readPkgVersion(path.join(projectRoot, "node_modules/@oj-module/runtime/package.json"));
|
|
96
141
|
|
|
@@ -106,10 +151,8 @@ export async function printInfo(projectRoot: string, oj: OjObservability = realO
|
|
|
106
151
|
|
|
107
152
|
let moduleLines = "(web.config.ts 加载失败或无模块)";
|
|
108
153
|
try {
|
|
109
|
-
const
|
|
110
|
-
moduleLines =
|
|
111
|
-
.map(m => ` · ${m.name}(${m.entry})${m.enabled === false ? " [disabled]" : ""}`)
|
|
112
|
-
.join("\n");
|
|
154
|
+
const entries = await resolveModuleEntries(projectRoot);
|
|
155
|
+
moduleLines = renderModuleLines(entries, projectRoot, opts.all ?? false);
|
|
113
156
|
}
|
|
114
157
|
catch (error) {
|
|
115
158
|
moduleLines = `(加载失败:${(error as Error).message})`;
|
|
@@ -150,21 +193,37 @@ export async function printInfo(projectRoot: string, oj: OjObservability = realO
|
|
|
150
193
|
: "证书状态: 无法探测(服务未运行时属正常;ojm dev/preview 起服后可测)";
|
|
151
194
|
|
|
152
195
|
const markerShort = markerVersion?.replace(/^v/, "");
|
|
153
|
-
|
|
196
|
+
// 版本门禁以**现场版本**为准,标记只用来报漂移:`bin/oj` 允许手替(vendor.ts 逃生门),
|
|
197
|
+
// 只看标记会把「标记新、二进制旧」判成健康(设计 §7「双探」第 1 条)。
|
|
198
|
+
const liveShort = /(\d+\.\d+\.\d+)/.exec(siteVersion)?.[1];
|
|
199
|
+
const drift = markerShort && (liveShort ?? siteVersion).includes(markerShort)
|
|
154
200
|
? ""
|
|
155
201
|
: markerShort
|
|
156
202
|
? `\n [!] 现场版本与安装标记(${markerVersion})不一致,请核对(ojm vendor --force 重装)`
|
|
157
203
|
: "";
|
|
204
|
+
const gateShort = liveShort ?? markerShort;
|
|
158
205
|
// 最低版本门禁:ojm 只与 >= MIN_OJ_VERSION 的 oj 组合可用(子命令 serve 等契约)
|
|
159
|
-
const outdated =
|
|
206
|
+
const outdated = gateShort && !isSupportedOjVersion(gateShort)
|
|
160
207
|
? `\n [!] 低于 ojm 要求的最低版本 v${MIN_OJ_VERSION}(oj v0.1.32 起启动子命令 server → serve)——请升级:ojm vendor v${MIN_OJ_VERSION}`
|
|
161
208
|
: "";
|
|
209
|
+
// 能力探针(§7 第 2 条):版本号是声明,能跑通才是事实
|
|
210
|
+
const capsLine = fs.existsSync(ojBin)
|
|
211
|
+
? (() => {
|
|
212
|
+
try {
|
|
213
|
+
return `--module-root ${probeOjCaps(ojBin).moduleRoot ? "可用" : `不可用(src/<name>/{api,web} 布局需 oj >= v0.1.85;ojm vendor v${MIN_OJ_VERSION})`}`;
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
return `探测失败(${(error as Error).message})`;
|
|
217
|
+
}
|
|
218
|
+
})()
|
|
219
|
+
: "未知(bin/oj 不存在,ojm vendor 安装)";
|
|
162
220
|
backendBlock = `
|
|
163
221
|
后端(oj):
|
|
164
222
|
安装标记: ${markerVersion ?? "未知(缺 bin/.oj-version,ojm vendor 重装)"}
|
|
165
223
|
现场版本: ${siteVersion}${drift}${outdated}
|
|
166
224
|
版本要求: >= v${MIN_OJ_VERSION}(ojm 以最新 oj 为准,不做兼容分支)
|
|
167
|
-
|
|
225
|
+
能力探针: ${capsLine}
|
|
226
|
+
端口/base: ${port} / ${base}(config.yaml)
|
|
168
227
|
${certLine}
|
|
169
228
|
`;
|
|
170
229
|
}
|
package/src/init.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `ojm init` —— 前后端一体化工程脚手架(设计 §3,D1/D3/D6/D10)。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* 产物(布局 `src/<name>/{api,web}`):src/(模块源码单树)、config.yaml + config/(现场签发证书)、
|
|
5
|
+
* seed.*.sql / tests / vitest.config.ts(后端工程面,与 config.yaml 同级)、
|
|
6
|
+
* bin/(从 everpan/only-js 最新 release 联网下载:oj + plugins + devkit)、工程配置文件。
|
|
7
7
|
*
|
|
8
|
-
* 幂等补缺:对既有工程重跑只补缺失文件(bin/oj
|
|
9
|
-
*
|
|
8
|
+
* 幂等补缺:对既有工程重跑只补缺失文件(bin/oj 已在则跳过下载,但先过 §7 能力闸门),
|
|
9
|
+
* config.yaml 与用户文件永不覆盖——「重新 init 覆盖脚手架」是审阅记录二明令禁止的行为。
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { execFileSync } from "node:child_process";
|
|
@@ -16,9 +16,12 @@ import path from "node:path";
|
|
|
16
16
|
import process from "node:process";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
import yaml from "yaml";
|
|
19
|
+
import { ALIAS_PATHS_FILE, syncAliasPaths } from "./alias-paths";
|
|
20
|
+
import { BUILTIN_LIST_FILE, writeBuiltinList } from "./builtin";
|
|
19
21
|
import { resolveCertDir, resolveConfigPath } from "./layout";
|
|
22
|
+
import { probeOjCaps } from "./oj-caps";
|
|
20
23
|
import { mintDevCert } from "./oj-cert";
|
|
21
|
-
import { resolveOjBin, vendorCommand } from "./vendor";
|
|
24
|
+
import { isSupportedOjVersion, MIN_OJ_VERSION, resolveOjBin, vendorCommand } from "./vendor";
|
|
22
25
|
import { readHostVersions } from "./versions";
|
|
23
26
|
|
|
24
27
|
export interface InitOptions {
|
|
@@ -33,6 +36,7 @@ export interface InitOptions {
|
|
|
33
36
|
interface InitReport {
|
|
34
37
|
created: string[]
|
|
35
38
|
skipped: string[]
|
|
39
|
+
warnings: string[]
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/**
|
|
@@ -41,6 +45,59 @@ interface InitReport {
|
|
|
41
45
|
*/
|
|
42
46
|
export type Sealer = (destDir: string, plainSecret: string) => Promise<string | null>;
|
|
43
47
|
|
|
48
|
+
/**
|
|
49
|
+
* 既有 `bin/oj` 的能力预检(设计 §7「双探」;评审 BLOCKING #8)。
|
|
50
|
+
*
|
|
51
|
+
* 只查**已存在**的二进制:刚由 `vendorCommand` 装下的那份已被 `MIN_OJ_VERSION` 挡过一遍,
|
|
52
|
+
* 而「从别处拷来的旧二进制」正是这条闸门要拦的形态。
|
|
53
|
+
*
|
|
54
|
+
* 三档处置:
|
|
55
|
+
* - `-V` 读不出版本(桩/损坏二进制)→ 告警放行,不阻断幂等补缺;
|
|
56
|
+
* - 版本 < MIN_OJ_VERSION → 抛错(`ojm vendor --force` 可覆盖标记);
|
|
57
|
+
* - 版本够但 `--module-root` 缺失 → 抛错,这是「声明新、能力旧」的真实形态。
|
|
58
|
+
*/
|
|
59
|
+
function assertExistingOjUsable(destDir: string, report: InitReport): void {
|
|
60
|
+
const ojBin = resolveOjBin(destDir);
|
|
61
|
+
if (!fs.existsSync(ojBin))
|
|
62
|
+
return;
|
|
63
|
+
|
|
64
|
+
let printed: string;
|
|
65
|
+
try {
|
|
66
|
+
printed = execFileSync(ojBin, ["-V"], { encoding: "utf-8", timeout: 10_000 }).trim();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
report.warnings.push(`无法执行 ${ojBin} -V(${(error as Error).message}),跳过版本校验`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const version = /(\d+\.\d+\.\d+)/.exec(printed)?.[1];
|
|
74
|
+
if (!version) {
|
|
75
|
+
report.warnings.push(`无法解析 ${ojBin} -V 的输出(${printed || "空"}),跳过版本校验`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!isSupportedOjVersion(version)) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`[ojm] ${ojBin} 是 v${version},低于 ojm 要求的最低版本 v${MIN_OJ_VERSION}。\n`
|
|
81
|
+
+ "原因:init 不再对已存在的 bin/oj 静默跳过——旧二进制生成的新布局工程会在 `ojm build` 时才失败。\n"
|
|
82
|
+
+ `处置:\`ojm vendor --force v${MIN_OJ_VERSION}\` 重装,或删除 ${ojBin} 后重跑 init。`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
if (probeOjCaps(ojBin).moduleRoot)
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
report.warnings.push(`能力探针失败(${(error as Error).message}),跳过 --module-root 校验`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
throw new Error(
|
|
95
|
+
`[ojm] ${ojBin}(v${version})不接受 --module-root,无法支撑 src/<name>/{api,web} 布局。\n`
|
|
96
|
+
+ "原因:版本号是声明,能跑通才是事实——`oj build --help` 里没有该旗标。\n"
|
|
97
|
+
+ `处置:\`ojm vendor --force v${MIN_OJ_VERSION}\` 重装。`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
44
101
|
export async function initProject(destDir: string, opts: InitOptions = {}): Promise<void> {
|
|
45
102
|
fs.mkdirSync(destDir, { recursive: true });
|
|
46
103
|
const existing = fs.readdirSync(destDir);
|
|
@@ -51,9 +108,13 @@ export async function initProject(destDir: string, opts: InitOptions = {}): Prom
|
|
|
51
108
|
);
|
|
52
109
|
}
|
|
53
110
|
|
|
54
|
-
const report: InitReport = { created: [], skipped: [] };
|
|
111
|
+
const report: InitReport = { created: [], skipped: [], warnings: [] };
|
|
55
112
|
const cliRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
56
113
|
|
|
114
|
+
// 0) 已有 bin/oj 先过能力闸门(评审 BLOCKING #8):早前「bin/oj 存在即跳过」
|
|
115
|
+
// 会让从旧工程带来的 0.1.84 静默过关,等 `ojm build` 才死在 --module-root 不认。
|
|
116
|
+
assertExistingOjUsable(destDir, report);
|
|
117
|
+
|
|
57
118
|
// 1) 模板(占位符替换 + 幂等跳过)
|
|
58
119
|
const templatesDir = path.join(cliRoot, "templates");
|
|
59
120
|
const jwtSecret = crypto.randomBytes(32).toString("hex");
|
|
@@ -65,14 +126,29 @@ export async function initProject(destDir: string, opts: InitOptions = {}): Prom
|
|
|
65
126
|
}, report, include, templatesDir);
|
|
66
127
|
pruneUnincludedApiPaths(destDir, include, report);
|
|
67
128
|
|
|
68
|
-
//
|
|
129
|
+
// 1a) src/.ojm-builtin.json:脚手架自带模块身份(名单派生自上面的白名单,单一真源)。
|
|
130
|
+
// copyIfAbsent:旧工程重跑 init 时缺失才补写,已存在不覆盖用户手改。
|
|
131
|
+
const builtinStatus = writeBuiltinList(destDir, include);
|
|
132
|
+
if (builtinStatus === "created")
|
|
133
|
+
report.created.push(`src/${BUILTIN_LIST_FILE}`);
|
|
134
|
+
else if (builtinStatus === "skipped")
|
|
135
|
+
report.skipped.push(`${BUILTIN_LIST_FILE}(已存在,不覆盖用户手改)`);
|
|
136
|
+
|
|
137
|
+
// 1b) tsconfig.paths.json:把 oj 别名 `#/<模块名>/…` 投影成 TS 的 paths。
|
|
138
|
+
// 必须**现场生成**而不是随模板拷贝——模板兼作 demo 工程(多出 demo/home 两个
|
|
139
|
+
// 模块),而用户工程只有白名单里的那些;照拷会让 paths 指向不存在的目录。
|
|
140
|
+
if (syncAliasPaths(destDir) === "created")
|
|
141
|
+
report.created.push(ALIAS_PATHS_FILE);
|
|
142
|
+
|
|
143
|
+
// 2) 证书三件套(现场签发,D6)——落点即 resolveCertDir()(config.yaml 同级的 config/;
|
|
144
|
+
// 新布局 config.yaml 在工程根,故为 <工程根>/config/)
|
|
69
145
|
const certDir = resolveCertDir(destDir);
|
|
70
146
|
if (fs.existsSync(path.join(certDir, "cert.jws"))) {
|
|
71
|
-
report.skipped.push("
|
|
147
|
+
report.skipped.push("config/cert.jws");
|
|
72
148
|
}
|
|
73
149
|
else {
|
|
74
150
|
await mintDevCert(certDir);
|
|
75
|
-
report.created.push("
|
|
151
|
+
report.created.push("config/{private.pem,public.pem,cert.jws}");
|
|
76
152
|
}
|
|
77
153
|
|
|
78
154
|
// 3) oj 二进制:从最新 release 联网下载(sha256 校验 → tar 解包 → chmod),
|
|
@@ -164,7 +240,9 @@ export async function initProject(destDir: string, opts: InitOptions = {}): Prom
|
|
|
164
240
|
console.log(`[ojm] 新增 ${report.created.length} 项,跳过(已存在)${report.skipped.length} 项`);
|
|
165
241
|
for (const item of report.skipped)
|
|
166
242
|
console.log(`[ojm] · 跳过 ${item}`);
|
|
167
|
-
|
|
243
|
+
for (const item of report.warnings)
|
|
244
|
+
console.warn(`[ojm] ! ${item}`);
|
|
245
|
+
console.log("[ojm] 下一步:pnpm install && pnpm dev(登录 superadmin / 123456,角色账号见 devkit 手册 §1.1)");
|
|
168
246
|
}
|
|
169
247
|
|
|
170
248
|
/** 默认密封器:bin/oj secret keygen + seal(失败返回 null,不抛) */
|
|
@@ -213,7 +291,7 @@ async function sealJwtSecret(destDir: string, report: InitReport, seal: Sealer =
|
|
|
213
291
|
// secrets 段:私钥只放部署机,路径相对 config 目录(config.yaml 同级)
|
|
214
292
|
const withSecrets = `${next.trimEnd()}\nsecrets:\n private_key_path: ./config/secrets-private.pem\n public_key_path: ./config/secrets-public.pem\n`;
|
|
215
293
|
fs.writeFileSync(configPath, withSecrets);
|
|
216
|
-
report.created.push("
|
|
294
|
+
report.created.push("config/{secrets-private.pem,secrets-public.pem} + auth.jwt_secret 已密封(ENC[...])");
|
|
217
295
|
}
|
|
218
296
|
|
|
219
297
|
/**
|
|
@@ -318,9 +396,10 @@ function pruneUnincludedApiPaths(destDir: string, include: Set<string>, report:
|
|
|
318
396
|
// 只裁本次新建的 openapi.json:init 是幂等补缺,不改动既有工程文件
|
|
319
397
|
if (!report.created.includes("openapi.json"))
|
|
320
398
|
return;
|
|
399
|
+
// 白名单里 `src/<name>/api` 这条(整模块收录)决定「脚手架有哪些后端模块」
|
|
321
400
|
const mods = new Set([...include]
|
|
322
|
-
.map(p => (
|
|
323
|
-
.filter((m): m is string => !!m
|
|
401
|
+
.map(p => /^src\/([^/]+)\/api$/.exec(p)?.[1])
|
|
402
|
+
.filter((m): m is string => !!m));
|
|
324
403
|
if (mods.size === 0)
|
|
325
404
|
return;
|
|
326
405
|
const openapiPath = path.join(destDir, "openapi.json");
|
|
@@ -401,15 +480,15 @@ node_modules/
|
|
|
401
480
|
bin/
|
|
402
481
|
.ojm-tmp-*
|
|
403
482
|
# ojm preview 的派生 config(临时产物,进程退出即删;见 preview-config.ts)
|
|
404
|
-
|
|
405
|
-
# 本地 sqlite
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
483
|
+
.ojm-preview.yaml
|
|
484
|
+
# 本地 sqlite 与运行日志(config.yaml 在工程根,路径都相对工程根)
|
|
485
|
+
db.sqlite
|
|
486
|
+
db.sqlite-shm
|
|
487
|
+
db.sqlite-wal
|
|
488
|
+
logs/
|
|
410
489
|
# 现场签发的开发证书与凭据私钥(ojm dev 缺证时自动签发,见 oj-cert.ts);私钥绝不入仓
|
|
411
|
-
|
|
412
|
-
|
|
490
|
+
config/*.pem
|
|
491
|
+
config/cert.jws
|
|
413
492
|
# 构建产物
|
|
414
493
|
api/dist/
|
|
415
494
|
web/dist/
|
|
@@ -506,7 +585,8 @@ function mergePackageJson(existing: Record<string, any>, generated: Record<strin
|
|
|
506
585
|
return merged;
|
|
507
586
|
}
|
|
508
587
|
|
|
509
|
-
|
|
588
|
+
/** 生成脚手架 package.json(导出供守卫测试:scripts ⊆ 模板 scripts,防双写漂移) */
|
|
589
|
+
export function generatePackageJson(cliRoot: string, projectName: string) {
|
|
510
590
|
const { matrix: hostVersions } = resolveVersionMatrix(cliRoot);
|
|
511
591
|
const cliPkg = JSON.parse(fs.readFileSync(path.join(cliRoot, "package.json"), "utf-8"));
|
|
512
592
|
|
|
@@ -556,11 +636,17 @@ function generatePackageJson(cliRoot: string, projectName: string) {
|
|
|
556
636
|
type: "module",
|
|
557
637
|
private: true,
|
|
558
638
|
scripts: {
|
|
559
|
-
dev: "ojm dev",
|
|
560
|
-
build: "ojm build",
|
|
561
|
-
preview: "ojm preview",
|
|
562
|
-
info: "ojm info",
|
|
563
|
-
typecheck: "tsc --noEmit -p tsconfig.json",
|
|
639
|
+
"dev": "ojm dev",
|
|
640
|
+
"build": "ojm build",
|
|
641
|
+
"preview": "ojm preview",
|
|
642
|
+
"info": "ojm info",
|
|
643
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
644
|
+
// typecheck:api 指向 src/tsconfig.json(后端工程面,白名单收录)——
|
|
645
|
+
// 早期生成器漏发这条(与模板双写漂移),补上并钉守卫测试
|
|
646
|
+
"typecheck:api": "tsc --noEmit -p src/tsconfig.json",
|
|
647
|
+
// 多方案(phase 2/3):打包全部用户方案(自带自动排除)/ 生成新方案骨架
|
|
648
|
+
"bundle": "ojm build --bundle",
|
|
649
|
+
"new": "ojm new",
|
|
564
650
|
},
|
|
565
651
|
devDependencies: devDeps,
|
|
566
652
|
};
|