@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/new.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ojm new <name>` —— 脚手架内快速生成用户方案骨架(多方案 phase 3,设计 v2 §C)。
|
|
3
|
+
*
|
|
4
|
+
* 生成 module-root 布局的 `src/<name>/{api,web}` 最小骨架:solution.yaml(方案契约,
|
|
5
|
+
* `ojm build --bundle` 入口)+ web(entry.ts 示例路由 / 占位页 / 双语 locales)+
|
|
6
|
+
* 可选 api(oj 七键 manifest + ping 示例 handler)。生成物补产物后即过打包校验
|
|
7
|
+
* (round-trip 承诺,N1 用例钉死)。
|
|
8
|
+
*
|
|
9
|
+
* 前置守卫(全 fail-fast,顺序即排查顺序):
|
|
10
|
+
* 1. N0 非 ojm 工程(无 web.config.ts / src/)→ 指向 `ojm init`;
|
|
11
|
+
* 2. N6 历史布局(new/flat)→ 拒绝 + 迁移指引(生成 module-root 骨架会触发
|
|
12
|
+
* 双源码树互斥,把 build/dev 打挂——宁可拒绝不留半套);
|
|
13
|
+
* 3. N3 名字四查:kebab-case 正则(与契约 C2-01 同条)→ 保留名 → 框架自带名
|
|
14
|
+
* (`src/.ojm-builtin.json`)→ 目录已存在;
|
|
15
|
+
* 4. N7 原子性:任一步失败 best-effort 删除已建 `src/<name>/`,不留半截目录
|
|
16
|
+
* (半截目录会触发 scanModuleRoot 的坏模块报错)。
|
|
17
|
+
*
|
|
18
|
+
* 刻意不做:web.config.ts 登记——module-root 布局以 `src/<name>/web/entry.ts`
|
|
19
|
+
* 扫描为真源(0.2.20 起登记只是可选排序覆盖),生成即被扫描收录,向用户 TS 源文件
|
|
20
|
+
* 做文本插入既脆弱又零收益(六方评审一致结论)。
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import fs from "node:fs";
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
import { readBuiltinModules } from "./builtin";
|
|
26
|
+
import { tryResolveLayout } from "./layout";
|
|
27
|
+
import { assertValidSolutionName, RESERVED_NAMES } from "./manifest-contract";
|
|
28
|
+
|
|
29
|
+
export interface NewSolutionOptions {
|
|
30
|
+
/** 示例路由布局(默认 container;none = 无外壳裸页面) */
|
|
31
|
+
layout?: "container" | "none"
|
|
32
|
+
/** 同时生成 api 骨架(oj 七键 manifest + ping 示例 handler) */
|
|
33
|
+
api?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface NewSolutionResult {
|
|
37
|
+
/** 生成的方案目录(src/<name>) */
|
|
38
|
+
dir: string
|
|
39
|
+
/** 相对工程根的文件清单 */
|
|
40
|
+
files: string[]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createSolution(projectRoot: string, name: string, opts: NewSolutionOptions = {}): NewSolutionResult {
|
|
44
|
+
// N0:工程判定(web.config.ts 与 src/ 至少要有一个——空目录跑 new 是最常见误用)
|
|
45
|
+
const hasConfig = fs.existsSync(path.join(projectRoot, "web.config.ts"));
|
|
46
|
+
const hasSrc = fs.existsSync(path.join(projectRoot, "src"));
|
|
47
|
+
if (!hasConfig && !hasSrc) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`[ojm] ${projectRoot} 不是 ojm 工程(没有 web.config.ts / src/)。`
|
|
50
|
+
+ "先 `ojm init` 创建脚手架,再在里面 `ojm new <name>`。",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// N6:布局守卫。tryResolveLayout 自带双树互斥/坏目录的人话报错,直接传播;
|
|
55
|
+
// 历史布局(new/flat)拒绝——module-root 骨架落进去会触发互斥,把 build/dev 打挂
|
|
56
|
+
const layout = tryResolveLayout(projectRoot);
|
|
57
|
+
if (layout !== null && layout.kind !== "module-root") {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`[ojm] ojm new 只支持 module-root 布局工程(src/<name>/{api,web}),当前为 ${layout.kind} 历史布局。`
|
|
60
|
+
+ "在历史布局工程里生成骨架会触发两套源码树互斥、把 build/dev 打挂,故拒绝。"
|
|
61
|
+
+ "请先迁移布局,或在新脚手架工程(ojm init)里使用。",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// N3:名字四查
|
|
66
|
+
assertValidSolutionName(name, "ojm new <name>");
|
|
67
|
+
if (RESERVED_NAMES.has(name)) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`[ojm] 「${name}」是保留名(框架功能命名空间,如 system/auth/market),不能用作方案名。`
|
|
70
|
+
+ "换一个不与框架能力撞名的名字。",
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (readBuiltinModules(projectRoot).has(name)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`[ojm] 「${name}」是框架自带方案(见 src/.ojm-builtin.json;ojm info --all 可看全名单)。`
|
|
76
|
+
+ "想改造自带模块:`ojm new <新名>` 生成骨架后把代码复制过去(自带身份按目录名判定,复制即脱离)。",
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
const dir = path.join(projectRoot, "src", name);
|
|
80
|
+
if (fs.existsSync(dir))
|
|
81
|
+
throw new Error(`[ojm] src/${name}/ 已存在。换个名字,或删掉该目录后重试。`);
|
|
82
|
+
|
|
83
|
+
const shell = opts.layout ?? "container";
|
|
84
|
+
const files: string[] = [];
|
|
85
|
+
try {
|
|
86
|
+
put(projectRoot, files, path.join(dir, "solution.yaml"), solutionYaml(name, opts.api));
|
|
87
|
+
put(projectRoot, files, path.join(dir, "web", "entry.ts"), entryTs(name, shell));
|
|
88
|
+
put(projectRoot, files, path.join(dir, "web", "pages", "index.tsx"), pageTsx(name));
|
|
89
|
+
put(projectRoot, files, path.join(dir, "web", "locales", "zh-CN.json"), `${JSON.stringify({ menu: { main: `${name} 方案` } }, null, "\t")}\n`);
|
|
90
|
+
put(projectRoot, files, path.join(dir, "web", "locales", "en-US.json"), `${JSON.stringify({ menu: { main: name } }, null, "\t")}\n`);
|
|
91
|
+
if (opts.api) {
|
|
92
|
+
put(projectRoot, files, path.join(dir, "api", "manifest.yaml"), apiManifest(name));
|
|
93
|
+
put(projectRoot, files, path.join(dir, "api", "api.ts"), apiTs(name));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
// N7:不留半截目录(scanModuleRoot 会把「有 api/ 或 web/ 但缺标记」的目录判成坏模块)
|
|
98
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
return { dir, files };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function put(projectRoot: string, files: string[], p: string, content: string): void {
|
|
105
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
106
|
+
fs.writeFileSync(p, content);
|
|
107
|
+
files.push(path.relative(projectRoot, p));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function solutionYaml(name: string, withApi: boolean | undefined): string {
|
|
111
|
+
const api = withApi
|
|
112
|
+
? [
|
|
113
|
+
"api:",
|
|
114
|
+
` module: ${name}`,
|
|
115
|
+
` prefix: /api/${name}`,
|
|
116
|
+
"",
|
|
117
|
+
].join("\n")
|
|
118
|
+
: "";
|
|
119
|
+
return `# 方案契约:ojm build --bundle 的入口(字段规范见 devkit 手册 §11.1;
|
|
120
|
+
# 优先级:本文件 > 工程根 solution.yaml > 同名 manifest.yaml 回退)。
|
|
121
|
+
name: ${name}
|
|
122
|
+
version: 0.1.0
|
|
123
|
+
title: ${name}
|
|
124
|
+
summary: ${name} 方案(ojm new 生成的骨架,替换为你的描述)
|
|
125
|
+
changelog:
|
|
126
|
+
- 0.1.0 首个版本(ojm new 生成)
|
|
127
|
+
${api}web:
|
|
128
|
+
route_prefix: /${name}
|
|
129
|
+
entry: modules/${name}/0.1.0/entry.js
|
|
130
|
+
`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function entryTs(name: string, shell: "container" | "none"): string {
|
|
134
|
+
return `import { defineModule } from "@oj-module/runtime";
|
|
135
|
+
import { lazy } from "react";
|
|
136
|
+
|
|
137
|
+
const Index = lazy(() => import("./pages/index"));
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* ${name} 方案:\`ojm new\` 生成的最小骨架。
|
|
141
|
+
* 菜单顺序由 handle.order 决定(≥0 主菜单区升序;<0 底部固定区);
|
|
142
|
+
* 标题键对应 locales/<lang>.json 的 menu.main。
|
|
143
|
+
*/
|
|
144
|
+
export default defineModule({
|
|
145
|
+
name: "${name}",
|
|
146
|
+
description: "${name} 方案",
|
|
147
|
+
version: "0.1.0",
|
|
148
|
+
peerRuntime: ">=0.0.0",
|
|
149
|
+
routes: [
|
|
150
|
+
{
|
|
151
|
+
path: "/${name}",
|
|
152
|
+
handle: { layout: "${shell}", order: 200, title: "${name}:menu.main" },
|
|
153
|
+
children: [
|
|
154
|
+
{
|
|
155
|
+
index: true,
|
|
156
|
+
Component: Index,
|
|
157
|
+
handle: { title: "${name}:menu.main" },
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
i18n: {
|
|
163
|
+
"zh-CN": () => import("./locales/zh-CN.json"),
|
|
164
|
+
"en-US": () => import("./locales/en-US.json"),
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function pageTsx(name: string): string {
|
|
171
|
+
return `import { BasicContent } from "@oj-module/runtime";
|
|
172
|
+
import { Card, Typography } from "antd";
|
|
173
|
+
|
|
174
|
+
/** ${name} 方案首页:\`ojm new\` 生成的占位页,替换为你的实现。 */
|
|
175
|
+
export default function IndexPage() {
|
|
176
|
+
return (
|
|
177
|
+
<BasicContent>
|
|
178
|
+
<Card>
|
|
179
|
+
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
|
180
|
+
${name}
|
|
181
|
+
</Typography.Title>
|
|
182
|
+
<Typography.Text type="secondary">
|
|
183
|
+
由 ojm new 生成的方案骨架——从这里开始你的实现。
|
|
184
|
+
</Typography.Text>
|
|
185
|
+
</Card>
|
|
186
|
+
</BasicContent>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function apiManifest(name: string): string {
|
|
193
|
+
return `# oj 原生七键清单:oj 按 src/<name>/api/manifest.yaml 枚举发现后端模块,
|
|
194
|
+
# 与方案契约(上级 solution.yaml)是两条互不干扰的枚举(devkit 手册 §1.2)。
|
|
195
|
+
name: ${name}
|
|
196
|
+
desc: ${name} 方案后端(ojm new 生成的骨架)
|
|
197
|
+
version: 0.1.0
|
|
198
|
+
tables: []
|
|
199
|
+
`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function apiTs(name: string): string {
|
|
203
|
+
return `// GET /api/${name}/ping —— 健康探针示例(保存即热更;出参挂平级 .response 即入 openapi.json)。
|
|
204
|
+
export async function ping() {
|
|
205
|
+
json.ok({ pong: true, module: "${name}" });
|
|
206
|
+
}
|
|
207
|
+
`;
|
|
208
|
+
}
|
package/src/oj-caps.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* oj 二进制**能力**探针(设计 202610220010 §7「双探」)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不能只看版本号:`bin/oj` 允许手改/手替(vendor.ts:143 的逃生门),
|
|
5
|
+
* `bin/.oj-version` 标记会漂移;且 vendor.ts:102-110 已实证「`oj -V` 正常但
|
|
6
|
+
* JsRuntime 能力缺失」真实存在。版本号是声明,能跑通才是事实。
|
|
7
|
+
*
|
|
8
|
+
* 为什么单独探 `--module-root`:module-root 布局(`src/<name>/{api,web}`)依赖
|
|
9
|
+
* oj 的模块根可配置。能力缺失时必须 fail-fast 并给处置命令——回落旧布局会让
|
|
10
|
+
* 「源码搬了、构建产物还是旧的」这种半坏状态长期存在,比直接失败难排查得多。
|
|
11
|
+
*
|
|
12
|
+
* 缓存:`bin/.oj-caps`,以二进制的 size+mtime 为指纹。指纹变了就重探,
|
|
13
|
+
* 所以手替 `bin/oj` 不会吃到旧缓存。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { execFileSync } from "node:child_process";
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { resolveOjBin } from "./vendor";
|
|
20
|
+
|
|
21
|
+
export interface OjCaps {
|
|
22
|
+
/** `oj build --help` 是否接受 `--module-root <DIR>`(源码树根,与 `-d` 互斥) */
|
|
23
|
+
moduleRoot: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 探针执行器(测试注入):执行 oj 子命令并返回合并输出 */
|
|
27
|
+
export type OjProbeRun = (ojBin: string, args: string[]) => string;
|
|
28
|
+
|
|
29
|
+
const defaultRun: OjProbeRun = (ojBin, args) =>
|
|
30
|
+
execFileSync(ojBin, args, { encoding: "utf-8", timeout: 10_000, stdio: ["ignore", "pipe", "pipe"] });
|
|
31
|
+
|
|
32
|
+
export const CAPS_FILE = ".oj-caps";
|
|
33
|
+
|
|
34
|
+
/** 二进制指纹:size + mtime 足以识别「换了文件」,比哈希便宜且无 IO 放大 */
|
|
35
|
+
function fingerprint(ojBin: string): string {
|
|
36
|
+
const st = fs.statSync(ojBin);
|
|
37
|
+
return `${st.size}:${Math.floor(st.mtimeMs)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function capsPath(binDir: string): string {
|
|
41
|
+
return path.join(binDir, CAPS_FILE);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 缓存文件形状:指纹 + 能力位 */
|
|
45
|
+
interface CapsCache {
|
|
46
|
+
fingerprint?: string
|
|
47
|
+
caps?: Partial<OjCaps>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 读缓存;指纹不匹配或结构不对 → null(重探) */
|
|
51
|
+
export function readCapsCache(ojBin: string): OjCaps | null {
|
|
52
|
+
try {
|
|
53
|
+
const raw = JSON.parse(fs.readFileSync(capsPath(path.dirname(ojBin)), "utf-8")) as CapsCache | null;
|
|
54
|
+
if (!raw || raw.fingerprint !== fingerprint(ojBin) || typeof raw.caps?.moduleRoot !== "boolean")
|
|
55
|
+
return null;
|
|
56
|
+
return { moduleRoot: raw.caps.moduleRoot };
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 写缓存(失败不影响主流程:缓存只是加速,不是正确性来源) */
|
|
64
|
+
export function writeCapsCache(ojBin: string, caps: OjCaps): void {
|
|
65
|
+
try {
|
|
66
|
+
fs.writeFileSync(
|
|
67
|
+
capsPath(path.dirname(ojBin)),
|
|
68
|
+
`${JSON.stringify({ fingerprint: fingerprint(ojBin), caps }, null, 2)}\n`,
|
|
69
|
+
"utf-8",
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// 只读目录 / 权限不足:忽略
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 现场探测:解析 `oj build --help` 的旗标列表(不缓存) */
|
|
78
|
+
export function probeOjCaps(ojBin: string, run: OjProbeRun = defaultRun): OjCaps {
|
|
79
|
+
let help = "";
|
|
80
|
+
try {
|
|
81
|
+
help = run(ojBin, ["build", "--help"]);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`[ojm] 无法执行 ${ojBin} build --help:${(error as Error).message}\n`
|
|
86
|
+
+ "处置:`ojm vendor` 重装 oj 二进制。",
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
// 只认长旗标整词:`--module-root <DIR>`;避免帮助文本里的说明文字误命中
|
|
90
|
+
return { moduleRoot: /--module-root\b/.test(help) };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* module-root 布局的能力闸门:缺失即抛,文案给处置命令。
|
|
95
|
+
* 有缓存走缓存(热路径每次 build/dev 都会经过这里)。
|
|
96
|
+
*/
|
|
97
|
+
export function requireModuleRootCapability(
|
|
98
|
+
projectRoot: string,
|
|
99
|
+
run: OjProbeRun = defaultRun,
|
|
100
|
+
): OjCaps {
|
|
101
|
+
const ojBin = resolveOjBin(projectRoot);
|
|
102
|
+
if (!fs.existsSync(ojBin)) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
`[ojm] 缺少 ${ojBin}:module-root 布局需要 oj 提供 --module-root。\n`
|
|
105
|
+
+ "处置:`ojm vendor` 安装 oj 二进制。",
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const caps = readCapsCache(ojBin) ?? (() => {
|
|
110
|
+
const probed = probeOjCaps(ojBin, run);
|
|
111
|
+
writeCapsCache(ojBin, probed);
|
|
112
|
+
return probed;
|
|
113
|
+
})();
|
|
114
|
+
|
|
115
|
+
if (caps.moduleRoot)
|
|
116
|
+
return caps;
|
|
117
|
+
|
|
118
|
+
const version = (() => {
|
|
119
|
+
try {
|
|
120
|
+
return run(ojBin, ["-V"]).trim() || "(未知)";
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return "(不可探测)";
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
126
|
+
throw new Error(
|
|
127
|
+
`[ojm] 当前 oj 不支持 --module-root(版本 ${version}),无法构建 src/<name>/{api,web} 布局。\n`
|
|
128
|
+
+ "原因:module-root 布局要求 oj build / serve 的模块根可配置;本版 oj 只认 `-d` 首层子目录为模块。\n"
|
|
129
|
+
+ "处置:升级 oj 后重试——`ojm vendor <tag>`(需 >= 支持 --module-root 的版本),"
|
|
130
|
+
+ "或把工程改回 web/src/<name> 布局。\n"
|
|
131
|
+
+ `缓存:${capsPath(path.dirname(ojBin))}(换二进制后指纹变化会自动重探)。`,
|
|
132
|
+
);
|
|
133
|
+
}
|
package/src/oj.ts
CHANGED
|
@@ -40,17 +40,40 @@ export interface StartOjPoll {
|
|
|
40
40
|
timeoutMs?: number
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* 工程根:从 config 所在目录向上找最近的含 `bin/` 的目录(ojm 的 vendor 布局)。
|
|
45
|
+
*
|
|
46
|
+
* 两种布局:
|
|
47
|
+
* <root>/api/config.yaml → <root>/bin/oj (历史布局,命中第二层)
|
|
48
|
+
* <root>/config.yaml → <root>/bin/oj (module-root,命中第一层)
|
|
49
|
+
* 找不到 `bin/` 时退回旧的「往上两级」,保持既有报错行为(缺 bin/oj 的文案不变)。
|
|
50
|
+
*/
|
|
51
|
+
function resolveProjectRoot(configPath: string): string {
|
|
52
|
+
const configDir = path.dirname(configPath);
|
|
53
|
+
for (const dir of [configDir, path.dirname(configDir)]) {
|
|
54
|
+
if (fs.existsSync(path.join(dir, "bin")))
|
|
55
|
+
return dir;
|
|
56
|
+
}
|
|
57
|
+
return path.dirname(configDir);
|
|
58
|
+
}
|
|
59
|
+
|
|
43
60
|
export function startOj(
|
|
44
61
|
configPath: string,
|
|
45
62
|
base: string,
|
|
46
|
-
|
|
63
|
+
apiSource: string | string[],
|
|
47
64
|
poll: StartOjPoll = {},
|
|
48
65
|
extraArgs: string[] = [],
|
|
49
66
|
): OjProcess {
|
|
50
67
|
const port = readOjPort(configPath);
|
|
51
|
-
// 约定:
|
|
52
|
-
|
|
68
|
+
// 约定:vendor 二进制在 <project>/bin/oj。工程根不能按「config 往上两级」算:
|
|
69
|
+
// module-root 布局的 config 在仓库根(<project>/config.yaml),往上两级会越过工程,
|
|
70
|
+
// 落到父目录找 bin/oj。改为「向上找最近的含 bin/ 的目录」,两种布局都成立。
|
|
71
|
+
const projectRoot = resolveProjectRoot(configPath);
|
|
53
72
|
const binPath = resolveOjBin(projectRoot);
|
|
73
|
+
// 后端源码参数二选一(oj v0.1.85:`--api-path` 与 `--module-root` 互斥,同时给会被
|
|
74
|
+
// clap 拒掉):历史布局传目录 → `--api-path <dir>`;module-root 布局传**已拼好的
|
|
75
|
+
// 旗标数组** → `["--module-root", "<project>/src"]`。
|
|
76
|
+
const apiArgs = Array.isArray(apiSource) ? apiSource : ["--api-path", apiSource];
|
|
54
77
|
const healthUrl = `http://127.0.0.1:${port}${base}/health`;
|
|
55
78
|
|
|
56
79
|
// 插件目录发现显式化:oj 的 resolve_plugins_dir 依赖 current_exe() 启发式,而
|
|
@@ -69,7 +92,7 @@ export function startOj(
|
|
|
69
92
|
let stderrTail = "";
|
|
70
93
|
// --console-log:oj 新版终端默认静默(console_log 缺省 false,只落 logs/),
|
|
71
94
|
// ojm dev/preview 的 [oj] 透传管道必须显式打开终端输出才能看到日志(手册 §10)。
|
|
72
|
-
const child = spawn(binPath, ["serve", "-c", configPath, "-b", base,
|
|
95
|
+
const child = spawn(binPath, ["serve", "-c", configPath, "-b", base, ...apiArgs, "--console-log", ...extraArgs], {
|
|
73
96
|
stdio: ["ignore", "pipe", "pipe"],
|
|
74
97
|
...(env ? { env } : {}),
|
|
75
98
|
});
|
package/src/preview-config.ts
CHANGED
|
@@ -44,7 +44,7 @@ export interface DerivePreviewConfigOptions {
|
|
|
44
44
|
apiRoot?: string
|
|
45
45
|
/** 显式端口时覆盖 server.port;缺省沿用原 config(oj 代码默认 9778) */
|
|
46
46
|
port?: number
|
|
47
|
-
/** 原 config 文本(测试注入);缺省读
|
|
47
|
+
/** 原 config 文本(测试注入);缺省读 config.yaml */
|
|
48
48
|
configText?: string
|
|
49
49
|
}
|
|
50
50
|
|
package/src/preview.ts
CHANGED
|
@@ -59,7 +59,8 @@ export async function runPreview(projectRoot: string, opts: PreviewOptions = {})
|
|
|
59
59
|
const siteDir = opts.siteDir ?? layout.distDir;
|
|
60
60
|
const ojBin = resolveOjBin(projectRoot);
|
|
61
61
|
const configPath = resolveConfigPath(projectRoot);
|
|
62
|
-
|
|
62
|
+
// release 根恒为扁平 api/dist(module-root 只改源码布局,不改产物形状——设计 C-3/C-7)
|
|
63
|
+
const apiDist = layout.apiDist;
|
|
63
64
|
|
|
64
65
|
// fail-fast 四查:缺一即提示对应补救(init 幂等补缺 / ojm build 产物)
|
|
65
66
|
if (!existsSync(ojBin))
|
package/src/site.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import process from "node:process";
|
|
8
8
|
|
|
9
|
-
/** 任务记录(线形状镜像;真源 templates/
|
|
9
|
+
/** 任务记录(线形状镜像;真源 templates/src/_platform/api/_lifecycle/types.ts:65,客户端不 import 模板内部) */
|
|
10
10
|
export interface TaskRecord {
|
|
11
11
|
/**
|
|
12
12
|
* 记录 schema 版本(P2-10 线形状镜像):缺省(undefined)=旧记录=v1 语义照常渲染;
|
|
@@ -30,7 +30,7 @@ export interface TaskRecord {
|
|
|
30
30
|
finishedAt?: number
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/** 模板 serve 缺省端口(templates/
|
|
33
|
+
/** 模板 serve 缺省端口(templates/config.yaml server.port: 9778;host 显式钉 127.0.0.1) */
|
|
34
34
|
export const DEFAULT_SITE_URL = "http://127.0.0.1:9778";
|
|
35
35
|
|
|
36
36
|
/** 本 CLI 已知的任务记录 schema 版本(P2-10;与服务端 tasks.ts TASK_SCHEMA_VERSION 手工同步,更高版本触发降级提示) */
|
package/src/usage.ts
CHANGED
|
@@ -16,18 +16,22 @@ export function usageText(): string {
|
|
|
16
16
|
开发模式快捷键(仅 TTY 终端,类 Vite):
|
|
17
17
|
r 强制重建并热更新 h 显示帮助 c 清屏 o 浏览器打开
|
|
18
18
|
q 退出(后端 oj 一并终止)
|
|
19
|
-
ojm build [--bundle] [--out <dir>]
|
|
19
|
+
ojm build [--bundle [name]] [--out <dir>]
|
|
20
20
|
构建后端(oj build)与模块产物(含全站合并)
|
|
21
|
-
--bundle
|
|
21
|
+
--bundle 方案包构建:契约校验(fail-fast)→ 确定性 tgz
|
|
22
22
|
→ dist/<name>-<version>.tgz + sha256(市场安装器的上游产物)
|
|
23
|
+
--bundle <name> 只打这一份方案(无参 = 全部用户方案,框架自带自动排除)
|
|
23
24
|
--out <dir> --bundle 输出目录(缺省 dist/)
|
|
25
|
+
ojm new <name> [--layout container|none] [--api]
|
|
26
|
+
脚手架内生成用户方案骨架(src/<name>/{api,web} + solution.yaml;
|
|
27
|
+
module-root 扫描自动收录,无需登记 web.config.ts)
|
|
24
28
|
ojm preview [port]
|
|
25
29
|
生产形态预览(migrate → oj serve 单服务:API 与静态 SPA 同进程,深链回落)
|
|
26
30
|
静态由 oj web 挂载直出;ojm 不起第二个服务、不做反代
|
|
27
31
|
ojm api [dir] [--check] [--docs] [--exempt <path>]
|
|
28
32
|
契约代码生成(client / routes / openapi / stub)
|
|
29
33
|
--check 四重对账(生成物同步 / route 双向 / routes.js / WS 路由鉴权)
|
|
30
|
-
--exempt <path> 指定豁免清单,覆盖默认
|
|
34
|
+
--exempt <path> 指定豁免清单,覆盖默认 .ojm-api-exempt.json(工程根;历史布局在 api/ 下)
|
|
31
35
|
--docs 出自包含文档站(单文件离线可看)
|
|
32
36
|
ojm info
|
|
33
37
|
输出版本矩阵与模块清单(报障用,US-7)
|
|
@@ -68,7 +72,7 @@ export function usageText(): string {
|
|
|
68
72
|
--check 离线自检两份索引签名
|
|
69
73
|
ojm market setup [--url <站点地址>] [--user <u> --pass <p>] [--new-pass <p>]
|
|
70
74
|
把系统设置 framework.market_cdn_url 指向本地模拟 CDN(工程根 cdn/,由
|
|
71
|
-
|
|
75
|
+
config.yaml 的 /cdn 挂载直出)。首跑若账号处于首次改密期(seed admin
|
|
72
76
|
must_change=1),会自动改密换全权 token 后再写入。
|
|
73
77
|
ojm market publish [--fixture <name>@<version>] [--real <方案工程>] [--id <key>] [--key <pem>] [--rebuild-index] [--check]
|
|
74
78
|
把方案包落进 cdn/:合成 fixture(或 --real 走 ojm build --bundle)→ 确定性 tgz
|
|
@@ -86,7 +90,7 @@ export function usageText(): string {
|
|
|
86
90
|
在一次性隔离站点(随机端口 + 一次性 DB/密钥/凭据/信任库/CDN)里跑完整安装,
|
|
87
91
|
跑完即毁;--keep 保留现场供排查。用于确认「这个包到底能不能装」,不污染你的工程。
|
|
88
92
|
ojm test|exec|openapi|migrate|schema [args...]
|
|
89
|
-
透传给 oj 同名子命令(自动补 -c <project>/
|
|
93
|
+
透传给 oj 同名子命令(自动补 -c <project>/config.yaml;其余参数原样转发)
|
|
90
94
|
例:ojm test --anonymous / ojm migrate --db report / ojm openapi --check
|
|
91
95
|
`;
|
|
92
96
|
}
|
|
@@ -104,12 +108,19 @@ const COMMAND_HELP: Record<string, string> = {
|
|
|
104
108
|
开发模式快捷键(仅 TTY 终端,类 Vite):
|
|
105
109
|
r 强制重建并热更新 h 显示帮助 c 清屏 o 浏览器打开
|
|
106
110
|
q 退出(后端 oj 一并终止)
|
|
107
|
-
port 缺省 5174(oj 后端端口以
|
|
108
|
-
"build": `ojm build [--bundle] [--out <dir>]
|
|
111
|
+
port 缺省 5174(oj 后端端口以 config.yaml 的 server.port 为准)`,
|
|
112
|
+
"build": `ojm build [--bundle [name]] [--out <dir>]
|
|
109
113
|
构建后端(oj build)与模块产物(含全站合并)
|
|
110
|
-
--bundle
|
|
114
|
+
--bundle 方案包构建:契约校验(fail-fast,两段式全绿才写盘)→ 确定性 tgz
|
|
111
115
|
→ dist/<name>-<version>.tgz + sha256(市场安装器的上游产物)
|
|
116
|
+
--bundle <name> / --bundle=<name> 只打这一份方案;无参 = 全部用户方案
|
|
117
|
+
(src/<name>/solution.yaml;框架自带方案自动排除并逐条日志)
|
|
112
118
|
--out <dir> --bundle 输出目录(缺省 dist/)`,
|
|
119
|
+
"new": `ojm new <name> [--layout container|none] [--api]
|
|
120
|
+
脚手架内生成用户方案骨架:src/<name>/solution.yaml(契约,build --bundle 入口)
|
|
121
|
+
+ web(entry.ts 示例路由/占位页/双语 locales)+ 可选 api(oj 七键 manifest + ping 示例)
|
|
122
|
+
守卫:非 ojm 工程 / 历史布局 / 非法名 / 保留名 / 框架自带名 / 重名一律拒绝(零写入)
|
|
123
|
+
module-root 布局以 src/<name>/web/entry.ts 扫描收录,无需登记 web.config.ts`,
|
|
113
124
|
"preview": `ojm preview [port]
|
|
114
125
|
生产形态预览(migrate → oj serve 单服务:API 与静态 SPA 同进程,深链回落)
|
|
115
126
|
静态由 oj web 挂载直出;ojm 不起第二个服务、不做反代
|
|
@@ -117,11 +128,12 @@ const COMMAND_HELP: Record<string, string> = {
|
|
|
117
128
|
"api": `ojm api [dir] [--check] [--docs] [--exempt <path>]
|
|
118
129
|
契约代码生成(client / routes / openapi / stub)
|
|
119
130
|
--check 四重对账(生成物同步 / route 双向 / routes.js / WS 路由鉴权)
|
|
120
|
-
--exempt <path> 指定豁免清单,覆盖默认
|
|
131
|
+
--exempt <path> 指定豁免清单,覆盖默认 .ojm-api-exempt.json(工程根;历史布局在 api/ 下)
|
|
121
132
|
--docs 出自包含文档站(单文件离线可看)
|
|
122
133
|
dir 缺省当前目录;显式目录须先存在`,
|
|
123
|
-
"info": `ojm info
|
|
124
|
-
|
|
134
|
+
"info": `ojm info [--all]
|
|
135
|
+
输出版本矩阵与模块清单(报障用)
|
|
136
|
+
--all 展开框架自带模块(默认折叠为一行计数;名单见 src/.ojm-builtin.json)`,
|
|
125
137
|
"vendor": `ojm vendor [tag] [--force]
|
|
126
138
|
下载/重装 oj vendor(经 npm 包 @oj-bin/oj;tag 缺省取最新,形如 v0.1.13)
|
|
127
139
|
--force 即使已存在也重新下载`,
|
|
@@ -168,7 +180,7 @@ const COMMAND_HELP: Record<string, string> = {
|
|
|
168
180
|
本地模拟 CDN 与市场链路子命令:setup / publish / build-index / verify / trial
|
|
169
181
|
详见各子命令帮助:ojm market setup --help 等`,
|
|
170
182
|
"market setup": `ojm market setup [--url <站点地址>] [--user <u> --pass <p>] [--new-pass <p>]
|
|
171
|
-
把系统设置 framework.market_cdn_url 指向本地模拟 CDN(工程根 cdn/,由
|
|
183
|
+
把系统设置 framework.market_cdn_url 指向本地模拟 CDN(工程根 cdn/,由 config.yaml 的 /cdn 挂载直出)。首跑若账号处于首次改密期(seed admin must_change=1),会自动改密换全权 token 后再写入`,
|
|
172
184
|
"market publish": `ojm market publish [--fixture <name>@<version>] [--real <方案工程>] [--id <key>] [--key <pem>] [--rebuild-index] [--check]
|
|
173
185
|
把方案包落进 cdn/:合成 fixture(或 --real 走 ojm build --bundle)→ 确定性 tgz → 包级 detached 签名 <name>-<version>.tgz.sig
|
|
174
186
|
不碰索引(索引由 build-index 扫描重建,多人发布才不会互相覆盖)`,
|
package/src/vendor.ts
CHANGED
|
@@ -35,8 +35,12 @@ export const NPM_PKG = "@oj-bin/oj";
|
|
|
35
35
|
* (见 CHANGELOG 0.1.20 / docs/guides/api-codegen-openapi.md)。**0.2.13 抬到 0.1.81**:
|
|
36
36
|
* 模板侧 `solution-perms.ts` 权限级联清理用 `startswith` 操作键(v0.1.81 补齐,
|
|
37
37
|
* 构造器迁移收尾),oj < 0.1.81 时卸载方案报 `unknown variant startswith` fail-closed。
|
|
38
|
+
* **本版抬到 0.1.85**:`src/<name>/{web,api}` 布局依赖 `--module-root <DIR>`(源码树根,
|
|
39
|
+
* 与 `-d`/`--api-path` 互斥);旧版收到该旗标即报错。0.1.85 同时落地了
|
|
40
|
+
* `manifest.name` 与目录名脱钩、`#/m/x` 按 name 解析(见
|
|
41
|
+
* docs/prd/202610220010-layout-module-root-design.md §5)。
|
|
38
42
|
*/
|
|
39
|
-
export const MIN_OJ_VERSION = "0.1.
|
|
43
|
+
export const MIN_OJ_VERSION = "0.1.85";
|
|
40
44
|
|
|
41
45
|
/** 按段数值比较(字符串比较会把 0.1.9 判成大于 0.1.69);非数字段按 0 处理 */
|
|
42
46
|
export function isSupportedOjVersion(version: string): boolean {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# 本文件由 ojm init 生成;config 内相对路径相对本文件所在目录。
|
|
3
3
|
# 更多能力见手册 §10:vars / broker / tasks / oidc / ws / html_meta / secrets。
|
|
4
4
|
#
|
|
5
|
-
# ---- 证书与密钥的唯一落点:`./config/`(=
|
|
5
|
+
# ---- 证书与密钥的唯一落点:`./config/`(= 工程根/config/,本文件同级的子目录)----
|
|
6
6
|
# 三件套由 `ojm init` 现场签发(dev 缺证时 `ojm dev` 也会补签),逐把的作用与后果:
|
|
7
7
|
#
|
|
8
8
|
# ① private.pem —— 证书签发私钥(RSA 2048)。只用来给 ③ 签名,**oj 不读它**,
|
|
@@ -25,19 +25,24 @@
|
|
|
25
25
|
# 站点挂载(oj v0.1.58 起顶层扁平表:一行=一个 URL 前缀+一个目录,api/web 恰好其一)。
|
|
26
26
|
# 被删除的 server.api_prefix / app_spa_fallback 已由 mounts 取代;
|
|
27
27
|
# ojm dev 的 SPA 深链回落由 ojm 自建静态层负责;ojm preview 为单服务形态,
|
|
28
|
-
# 现场生成派生 config
|
|
28
|
+
# 现场生成派生 config(.ojm-preview.yaml,注入下面的 web 挂载 + spa: true),无需手改。
|
|
29
|
+
#
|
|
30
|
+
# **这里没有 api 挂载是有意的**:本工程是 `src/<name>/{api,web}` 布局,模块分散在
|
|
31
|
+
# `src/<name>/api` 下,而 mounts 的 api 项按 `<目录>/<模块>/manifest.yaml` 单层解析、
|
|
32
|
+
# 不会递归(实测报 `module 'x' missing manifest.yaml`)。源码形态由 `ojm dev` 用
|
|
33
|
+
# `--module-root src` 供给,产物形态由 `ojm preview` 用 `--api-path api/dist` 供给;
|
|
34
|
+
# 两者与 mounts 的 api 项同时出现还会报「module declared in multiple places」。
|
|
35
|
+
# 手写部署(不经 ojm)时才需要自己加回一条 `- prefix: /api` + `api: dist`。
|
|
29
36
|
mounts:
|
|
30
|
-
- prefix: /api # api 挂载:/api/<module>/...(目录相对 config 所在目录 → api/src)
|
|
31
|
-
api: src
|
|
32
37
|
# 本地模拟 CDN:直出工程根 cdn/(内容见 cdn/README.md)。
|
|
33
38
|
# 红线:生产请删掉这一条挂载,并把 system 设置 framework.market_cdn_url 指向真 CDN。
|
|
34
39
|
# 不得给此挂载设 spa——缺文件必须返回 404,回落 index.html 会让市场解析崩。
|
|
35
40
|
- prefix: /cdn
|
|
36
|
-
web:
|
|
41
|
+
web: ./cdn
|
|
37
42
|
# 生产若由 oj 直出前端(前后端同进程,即 ojm preview 的生产形态),
|
|
38
43
|
# 取消下一行注释并先 build 出 web/dist(preview 会自动注入等价挂载,无需手改):
|
|
39
44
|
# - prefix: "/"
|
|
40
|
-
# web: "
|
|
45
|
+
# web: "./web/dist" # 相对 config.yaml → 工程根/web/dist
|
|
41
46
|
# spa: true
|
|
42
47
|
# 嵌套 handler 组(oj v0.1.64)无需配置开关,组织方式见 api.ts 顶部注释。
|
|
43
48
|
|
|
@@ -84,8 +89,8 @@ platform:
|
|
|
84
89
|
# 平台特权模块名单:方案安装器的 reloading 段调 server.reloadConfig 触发热更。
|
|
85
90
|
# 缺这条 → 装在最后一步报 NotCapable: module '_platform' is not platform-privileged。
|
|
86
91
|
modules: [_platform]
|
|
87
|
-
#
|
|
88
|
-
#
|
|
92
|
+
# 受控根 = 后端模块源码根(`src/<name>/api` 的父目录,即 src)。ojm preview 派生 config
|
|
93
|
+
# 时会改写为 dist,保持与 `--api-path` 同源;手写部署请按实际模块根填写。
|
|
89
94
|
root: src
|
|
90
95
|
# ---- U6 信任库加固(P3-1;P2-2 红线 spike 结论)----
|
|
91
96
|
# 威胁:devkit 0.1.75 语义下 fs: 段对全部模块生效(jail = fs.root),且 trusted_paths 只增不减——
|
package/templates/global.d.ts
CHANGED
|
@@ -840,11 +840,12 @@ interface OjRabbitClient {
|
|
|
840
840
|
metadata: () => Promise<any>
|
|
841
841
|
}
|
|
842
842
|
|
|
843
|
-
// oj 的导入别名 `#x`(本模块根)/
|
|
844
|
-
// tsconfig 的 `paths`
|
|
845
|
-
//
|
|
846
|
-
//
|
|
847
|
-
//
|
|
848
|
-
//
|
|
843
|
+
// oj 的导入别名 `#x`(本模块根)/ `#/<模块名>/x`(目标模块根)是「引用方所在模块」
|
|
844
|
+
// 相对的,tsconfig 的 `paths` 表达不了模块相对解析,只能按模块枚举
|
|
845
|
+
// (`tsconfig.paths.json`,由 `ojm init` / `ojm build` / `ojm dev` 按 `src/*/api/manifest.yaml`
|
|
846
|
+
// 生成,被 `src/tsconfig.json` extends),因此仅能命中**已存在**的文件。无法静态定位
|
|
847
|
+
// 的别名(如尚未创建的 `#_shared/view`)由 `types/oj-modules.d.ts`(非模块,真 ambient
|
|
848
|
+
// `declare module "#*"`)兜底为 `any` 而非报 TS2307;已存在、能被 `paths` 命中的别名
|
|
849
|
+
// 仍走真实文件类型。运行时由 oj 的 loader 解析,本文件与该兜底只影响编辑器 / tsc。
|
|
849
850
|
|
|
850
851
|
export {};
|