@oj-module/cli 0.2.64 → 0.2.67
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/bin/.oj-version +1 -0
- package/bin/oj +4 -0
- package/devkit/README.md +18 -4
- package/devkit/SKILL.md +35 -3
- package/devkit/framework-dev.md +22 -11
- package/devkit/manual.md +161 -68
- package/devkit/menu-rules.md +4 -1
- package/devkit/publish-walkthrough.md +110 -37
- package/devkit/wecom-internal-app.md +7 -2
- package/devkit/wecom-multi-corp-links.md +163 -0
- package/package.json +3 -2
- package/shell-dist/assets/ahooks.2l850ymmh0.js +18 -0
- package/shell-dist/assets/antd-es-locale-zh_CN.1gjjsvxbf0.js +1 -0
- package/shell-dist/assets/antd-img-crop.196rkty480.js +99 -0
- package/shell-dist/assets/antd-locale-en_US.561tozx860.js +1 -0
- package/shell-dist/assets/antd-locale-zh_CN.1cmpmgzh70.js +1 -0
- package/shell-dist/assets/antd.uw1coq15l0.js +348 -0
- package/shell-dist/assets/clsx.jotilrt2e0.js +1 -0
- package/shell-dist/assets/contract-errors.2o17vz9u50.js +1 -0
- package/shell-dist/assets/contract.3h327o3mj0.js +57 -0
- package/shell-dist/assets/cssinjs.atgmhan7m0.js +3 -0
- package/shell-dist/assets/dayjs-plugin-advancedFormat.3vsyd2eis0.js +1 -0
- package/shell-dist/assets/dayjs-plugin-customParseFormat.13xhghv1d0.js +1 -0
- package/shell-dist/assets/dayjs-plugin-isoWeek.1fn1tw82p0.js +1 -0
- package/shell-dist/assets/dayjs-plugin-localeData.2rwbjp9nk0.js +1 -0
- package/shell-dist/assets/dayjs-plugin-quarterOfYear.2oqo1lcx80.js +1 -0
- package/shell-dist/assets/dayjs-plugin-relativeTime.2pqs3et4y0.js +1 -0
- package/shell-dist/assets/dayjs-plugin-weekOfYear.gvn9iph080.js +1 -0
- package/shell-dist/assets/dayjs-plugin-weekYear.2u78l3ex10.js +1 -0
- package/shell-dist/assets/dayjs-plugin-weekday.2d56a4tml0.js +1 -0
- package/shell-dist/assets/dayjs.pa60l6wjo0.js +1 -0
- package/shell-dist/assets/dnd-kit-core.1xyreig6r0.js +5 -0
- package/shell-dist/assets/dnd-kit-sortable.s94qciuft0.js +1 -0
- package/shell-dist/assets/dnd-kit-utilities.1umw4xphb0.js +1 -0
- package/shell-dist/assets/i18next.33zqofapj0.js +1 -0
- package/shell-dist/assets/icons-es-components-Context.365qmflwg0.js +1 -0
- package/shell-dist/assets/icons.159wjwals0.js +57 -0
- package/shell-dist/assets/{index-xZhmujjM.js → index.6alqyx6ae0.js} +21 -1
- package/shell-dist/assets/jsx-dev-runtime.3uibuc82f0.js +14 -0
- package/shell-dist/assets/jsx-runtime.h40xu4o5b0.js +14 -0
- package/shell-dist/assets/keepalive-for-react.xenu5vam70.js +1 -0
- package/shell-dist/assets/kit.37kr8ui390.js +1 -0
- package/shell-dist/assets/ky.1cgch7gu60.js +6 -0
- package/shell-dist/assets/motion-react.pjsxytvjs0.js +12 -0
- package/shell-dist/assets/motion.21tciqutg0.js +4 -0
- package/shell-dist/assets/nprogress.2nz76n9ny0.js +7 -0
- package/shell-dist/assets/ojm-dayjs-locale-zh-cn.3vr3beiyp0.js +9 -0
- package/shell-dist/assets/pinyin-pro.3v0mnxczn0.js +1 -0
- package/shell-dist/assets/pro-components.2kyot1vcl0.js +83 -0
- package/shell-dist/assets/react-countup.2x8idnr8y0.js +10 -0
- package/shell-dist/assets/react-dom-client.ick0v05ob0.js +43 -0
- package/shell-dist/assets/react-dom.2so9uxg3f0.js +22 -0
- package/shell-dist/assets/react-error-boundary.2dncdfj3b0.js +1 -0
- package/shell-dist/assets/react-i18next.auhvjhqv00.js +1 -0
- package/shell-dist/assets/react-jss.1ei6dr9e00.js +29 -0
- package/shell-dist/assets/react-query.3rln45ow00.js +1 -0
- package/shell-dist/assets/react-router-dom.3c6zld8h40.js +44 -0
- package/shell-dist/assets/react-router.2h9rebcv50.js +56 -0
- package/shell-dist/assets/react.1ew63mwts0.js +14 -0
- package/shell-dist/assets/runtime.2dgstpjsu0.js +21702 -0
- package/shell-dist/assets/simplebar-react.2g739uvai0.js +1 -0
- package/shell-dist/assets/spin-delay.yjeyj6iwp0.js +9 -0
- package/shell-dist/assets/tailwind-merge.125ehiqjw0.js +1 -0
- package/shell-dist/assets/use-sync-external-store-shim.1nz1pi9bb0.js +22 -0
- package/shell-dist/assets/zustand-middleware.1xaxjueme0.js +6 -0
- package/shell-dist/assets/zustand-react-shallow.pybayv1hx0.js +1 -0
- package/shell-dist/assets/zustand-react.9fsl9brxy0.js +1 -0
- package/shell-dist/assets/zustand-shallow.6cz0yyv9x0.js +1 -0
- package/shell-dist/assets/zustand-vanilla-shallow.3424monv50.js +1 -0
- package/shell-dist/assets/zustand-vanilla.1mo3ka3g30.js +1 -0
- package/shell-dist/assets/zustand.3gjgzlnqo0.js +1 -0
- package/shell-dist/index.html +67 -118
- package/shell-dist/versions.json +3 -3
- package/src/__tests__/publish-keygen.test.ts +157 -0
- package/src/__tests__/templates-migration-schema-parity.test.ts +540 -0
- package/src/__tests__/templates-schema-columns.test.ts +15 -2
- package/src/__tests__/templates-tenant-scope.test.ts +6 -2
- package/src/bundle.ts +8 -3
- package/src/cli.ts +17 -5
- package/src/dev.ts +15 -10
- package/src/esm-exports.ts +152 -70
- package/src/install.ts +11 -4
- package/src/keygen.ts +54 -0
- package/src/lifecycle/__tests__/bundle.test.ts +21 -3
- package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/manifest.yaml +1 -1
- package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/migrations/0001__init.sql +2 -2
- package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/schema.yaml +5 -2
- package/src/lifecycle/__tests__/fixtures/fixture-valid/manifest.yaml +1 -1
- package/src/lifecycle/__tests__/manifest-contract.test.ts +75 -3
- package/src/lifecycle/__tests__/smoke.test.ts +2 -2
- package/src/manifest-contract.ts +91 -8
- package/src/market/bootstrap.ts +26 -2
- package/src/market/command.ts +1 -0
- package/src/market/publish.ts +5 -1
- package/src/market/walkthrough.ts +24 -8
- package/src/shared-deps.ts +69 -19
- package/src/site-base.ts +4 -0
- package/src/site.ts +2 -0
- package/src/usage.ts +8 -4
- package/src/vendor.ts +5 -1
- package/templates/config.yaml +4 -2
- package/templates/init.include.yaml +8 -3
- package/templates/openapi.json +460 -1
- package/templates/src/app/api/_platform/_lifecycle/compensate.ts +136 -0
- package/templates/src/app/api/_platform/_lifecycle/installer.ts +71 -22
- package/templates/src/app/api/_platform/_lifecycle/lock.ts +23 -0
- package/templates/src/app/api/_platform/_lifecycle/steps/deploy-web.ts +28 -0
- package/templates/src/app/api/_platform/_lifecycle/steps/preflight.ts +46 -0
- package/templates/src/app/api/_platform/_lifecycle/steps/reload.ts +100 -36
- package/templates/src/app/api/_platform/_lifecycle/steps/smoke.ts +7 -15
- package/templates/src/app/api/_platform/_lifecycle/text-edit.ts +71 -19
- package/templates/src/app/api/_platform/_lifecycle/types.ts +26 -2
- package/templates/src/app/api/_platform/_lifecycle/uninstall.ts +93 -14
- package/templates/src/app/api/_platform/_shared/service/domain/settings.ts +8 -4
- package/templates/src/app/api/_platform/manifest.yaml +1 -0
- package/templates/src/app/api/_platform/market/api.ts +235 -10
- package/templates/src/app/api/_platform/migrations/0001__baseline.mysql.sql +178 -0
- package/templates/src/app/api/_platform/migrations/0001__baseline.pg.sql +173 -0
- package/templates/src/app/api/_platform/migrations/0001__baseline.sql +188 -0
- package/templates/src/app/api/_platform/migrations/0002__market_release.mysql.sql +17 -0
- package/templates/src/app/api/_platform/migrations/0002__market_release.pg.sql +16 -0
- package/templates/src/app/api/_platform/migrations/0002__market_release.sql +33 -0
- package/templates/src/app/api/_platform/schema.yaml +18 -0
- package/templates/src/app/api/_platform/seed.mysql.sql +10 -0
- package/templates/src/app/api/_platform/seed.pg.sql +10 -0
- package/templates/src/app/api/auth/_shared/wecom/client.ts +24 -4
- package/templates/src/app/api/home/migrations/0001__create_home_tables.mysql.sql +14 -0
- package/templates/src/app/api/home/migrations/0001__create_home_tables.pg.sql +14 -0
- package/templates/src/app/api/home/migrations/0002__tenant.pg.sql +4 -0
- package/templates/src/app/api/home/seed.mysql.sql +71 -0
- package/templates/src/app/api/home/seed.pg.sql +71 -0
- package/templates/src/app/api/notification/_shared/notify.ts +6 -6
- package/templates/src/app/api/notification/migrations/0001__create_notifications.mysql.sql +11 -0
- package/templates/src/app/api/notification/migrations/0001__create_notifications.pg.sql +11 -0
- package/templates/src/app/api/notification/migrations/0002__notifications_user.mysql.sql +8 -0
- package/templates/src/app/api/notification/migrations/0003__notifications_broadcast.mysql.sql +21 -0
- package/templates/src/app/api/notification/migrations/0003__notifications_broadcast.pg.sql +22 -0
- package/templates/src/app/api/notification/migrations/0004__notifications_broadcast_hidden.mysql.sql +10 -0
- package/templates/src/app/api/notification/migrations/0004__notifications_broadcast_hidden.pg.sql +11 -0
- package/templates/src/app/api/notification/migrations/0005__tenant.pg.sql +6 -0
- package/templates/src/app/api/notification/seed.mysql.sql +8 -0
- package/templates/src/app/api/notification/seed.pg.sql +11 -0
- package/templates/src/app/api/publish/_shared/identity.ts +69 -0
- package/templates/src/app/api/publish/api.ts +349 -0
- package/templates/src/app/api/publish/manifest.yaml +8 -0
- package/templates/src/app/api/publish/migrations/0001__publish_credential.mysql.sql +23 -0
- package/templates/src/app/api/publish/migrations/0001__publish_credential.pg.sql +23 -0
- package/templates/src/app/api/publish/migrations/0001__publish_credential.sql +39 -0
- package/templates/src/app/api/publish/schema.yaml +33 -0
- package/templates/src/app/api/system/login-methods/api.ts +2 -1
- package/templates/src/app/web/_platform/__client/api.schemas.ts +15 -0
- package/templates/src/app/web/_platform/__client/index.ts +25 -1
- package/templates/src/app/web/entry.ts +9 -5
- package/templates/src/app/web/home/pages/components/bar-chart.tsx +5 -3
- package/templates/src/app/web/home/pages/components/echarts-setup.ts +30 -0
- package/templates/src/app/web/home/pages/components/line-chart.tsx +5 -3
- package/templates/src/app/web/home/pages/components/pie-chart.tsx +6 -3
- package/templates/src/app/web/home/pages/index.tsx +3 -2
- package/templates/src/app/web/home/routes.ts +8 -2
- package/templates/src/app/web/login/locales/en-US.json +7 -1
- package/templates/src/app/web/login/locales/zh-CN.json +7 -1
- package/templates/src/app/web/login/pages/login.tsx +79 -86
- package/templates/src/app/web/login/routes.ts +54 -1
- package/templates/src/app/web/personal-center/locales/en-US.json +1 -0
- package/templates/src/app/web/personal-center/locales/zh-CN.json +1 -0
- package/templates/src/app/web/personal-center/pages/my-profile/index.tsx +5 -0
- package/templates/src/app/web/system/__client/api.schemas.ts +2 -1
- package/templates/src/app/web/system/locales/en-US.json +1 -8
- package/templates/src/app/web/system/locales/zh-CN.json +1 -8
- package/templates/src/app/web/system/pages/settings/index.tsx +17 -4
- package/templates/src/app/web/system/routes.ts +0 -6
- package/templates/src/oj-market/web/__tests__/CardGrid.test.tsx +33 -0
- package/templates/src/oj-market/web/__tests__/DetailDrawer.test.tsx +69 -0
- package/templates/src/oj-market/web/__tests__/marketClient.fake.ts +7 -1
- package/templates/src/oj-market/web/api/client.ts +3 -0
- package/templates/src/oj-market/web/api/defaultClient.ts +5 -0
- package/templates/src/oj-market/web/components/CardItem.tsx +44 -1
- package/templates/src/oj-market/web/components/DetailDrawer.tsx +17 -1
- package/templates/src/oj-market/web/entry.ts +31 -2
- package/templates/src/oj-market/web/locales/en-US.json +5 -1
- package/templates/src/oj-market/web/locales/zh-CN.json +5 -1
- package/templates/src/oj-market/web/publish/__client/api.schemas.ts +61 -0
- package/templates/src/oj-market/web/publish/__client/index.ts +164 -0
- package/templates/src/oj-market/web/publish/locales/en-US.json +80 -0
- package/templates/src/oj-market/web/publish/locales/zh-CN.json +80 -0
- package/templates/src/oj-market/web/publish/pages/history.tsx +112 -0
- package/templates/src/oj-market/web/publish/pages/index.tsx +196 -0
- package/templates/src/oj-market/web/publish/pages/model.ts +52 -0
- package/templates/src/oj-market/web/publish/pages/token-modal.tsx +82 -0
- package/templates/src/oj-market/web/publish/routes.ts +41 -0
- package/templates/src/oj-market/web/types.ts +30 -0
- package/templates/tests/lifecycle/compensate.spec.ts +207 -0
- package/templates/tests/lifecycle/deploy-smoke.spec.ts +38 -14
- package/templates/tests/lifecycle/installer.spec.ts +28 -9
- package/templates/tests/lifecycle/market-api.spec.ts +164 -12
- package/templates/tests/lifecycle/preflight.spec.ts +68 -0
- package/templates/tests/lifecycle/role-title-translate.spec.ts +2 -32
- package/templates/tests/lifecycle/text-edit.spec.ts +45 -27
- package/templates/tests/lifecycle/uninstall.spec.ts +36 -0
- package/templates/tests/publish-credential.spec.ts +354 -0
- package/templates/tests/publish-identity.spec.ts +44 -0
- package/templates/tests/wecom-client-timeout.spec.ts +33 -3
- package/templates/tests/wecom-landing.spec.ts +63 -1
- package/templates/tsconfig.paths.json +1 -0
- package/templates/web.config.ts +6 -4
- package/vendor/host-versions.json +3 -3
- package/shell-dist/assets/ahooks.js +0 -6909
- package/shell-dist/assets/antd-es-locale-zh_CN.js +0 -257
- package/shell-dist/assets/antd-img-crop.js +0 -1495
- package/shell-dist/assets/antd-locale-en_US.js +0 -374
- package/shell-dist/assets/antd-locale-zh_CN.js +0 -379
- package/shell-dist/assets/antd.js +0 -106874
- package/shell-dist/assets/clsx.js +0 -33
- package/shell-dist/assets/contract-errors.js +0 -30
- package/shell-dist/assets/contract.js +0 -18900
- package/shell-dist/assets/cssinjs.js +0 -2595
- package/shell-dist/assets/dayjs-plugin-advancedFormat.js +0 -98
- package/shell-dist/assets/dayjs-plugin-customParseFormat.js +0 -173
- package/shell-dist/assets/dayjs-plugin-isoWeek.js +0 -77
- package/shell-dist/assets/dayjs-plugin-localeData.js +0 -126
- package/shell-dist/assets/dayjs-plugin-quarterOfYear.js +0 -77
- package/shell-dist/assets/dayjs-plugin-relativeTime.js +0 -92
- package/shell-dist/assets/dayjs-plugin-weekOfYear.js +0 -73
- package/shell-dist/assets/dayjs-plugin-weekYear.js +0 -63
- package/shell-dist/assets/dayjs-plugin-weekday.js +0 -63
- package/shell-dist/assets/dayjs.js +0 -326
- package/shell-dist/assets/dnd-kit-core.js +0 -3601
- package/shell-dist/assets/dnd-kit-sortable.js +0 -717
- package/shell-dist/assets/dnd-kit-utilities.js +0 -340
- package/shell-dist/assets/i18next.js +0 -2321
- package/shell-dist/assets/icons-es-components-Context.js +0 -20
- package/shell-dist/assets/icons.js +0 -34961
- package/shell-dist/assets/jsx-dev-runtime.js +0 -75
- package/shell-dist/assets/jsx-runtime.js +0 -97
- package/shell-dist/assets/keepalive-for-react.js +0 -283
- package/shell-dist/assets/kit.js +0 -151
- package/shell-dist/assets/ky.js +0 -1427
- package/shell-dist/assets/motion-react.js +0 -13589
- package/shell-dist/assets/motion.js +0 -9959
- package/shell-dist/assets/nprogress.js +0 -314
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-BarsOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-CalendarOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretDownFilled.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretDownOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretUpOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-CheckCircleFilled.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-CheckOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-ClockCircleOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-CloseCircleFilled.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-CloseOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-CopyOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-DeleteOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-DoubleLeftOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-DoubleRightOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-DownOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-DownloadOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-EditOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-EllipsisOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-EnterOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-ExclamationCircleFilled.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-EyeInvisibleOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-EyeOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-FileOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-FileTextOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-FilterFilled.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-FolderOpenOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-FolderOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-HolderOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-InfoCircleFilled.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-LeftOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-LoadingOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-MinusOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-MinusSquareOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-PaperClipOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-PictureOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-PlusOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-PlusSquareOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-QuestionCircleOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-ReloadOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-RightOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-RotateLeftOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-RotateRightOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-SearchOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-StarFilled.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-SwapOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-SwapRightOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-UpOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-VerticalAlignTopOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-WarningFilled.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-ZoomInOutlined.js +0 -7
- package/shell-dist/assets/ojm--ant-design-icons-es-icons-ZoomOutOutlined.js +0 -7
- package/shell-dist/assets/ojm-dayjs-locale-zh-cn.js +0 -81
- package/shell-dist/assets/pinyin-pro.js +0 -25175
- package/shell-dist/assets/pro-components.js +0 -44627
- package/shell-dist/assets/react-countup.js +0 -525
- package/shell-dist/assets/react-dom-client.js +0 -12134
- package/shell-dist/assets/react-dom.js +0 -266
- package/shell-dist/assets/react-error-boundary.js +0 -141
- package/shell-dist/assets/react-i18next.js +0 -1773
- package/shell-dist/assets/react-jss.js +0 -4089
- package/shell-dist/assets/react-query.js +0 -3072
- package/shell-dist/assets/react-router-dom.js +0 -9927
- package/shell-dist/assets/react-router.js +0 -14584
- package/shell-dist/assets/react.js +0 -590
- package/shell-dist/assets/runtime.js +0 -28311
- package/shell-dist/assets/simplebar-react.js +0 -1119
- package/shell-dist/assets/spin-delay.js +0 -111
- package/shell-dist/assets/tailwind-merge.js +0 -3364
- package/shell-dist/assets/use-sync-external-store-shim.js +0 -130
- package/shell-dist/assets/zustand-middleware.js +0 -513
- package/shell-dist/assets/zustand-react-shallow.js +0 -27
- package/shell-dist/assets/zustand-react.js +0 -39
- package/shell-dist/assets/zustand-shallow.js +0 -22
- package/shell-dist/assets/zustand-vanilla-shallow.js +0 -71
- package/shell-dist/assets/zustand-vanilla.js +0 -40
- package/shell-dist/assets/zustand.js +0 -32
- package/templates/seed.mysql.sql +0 -6
- package/templates/seed.pg.sql +0 -6
- package/templates/src/app/api/_platform/migrations/0001__create_users.mysql.sql +0 -9
- package/templates/src/app/api/_platform/migrations/0001__create_users.pg.sql +0 -9
- package/templates/src/app/api/_platform/migrations/0001__create_users.sql +0 -7
- package/templates/src/app/api/_platform/migrations/0002__add_users_avatar_base64.mysql.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0002__add_users_avatar_base64.sql +0 -2
- package/templates/src/app/api/_platform/migrations/0003__users_source_display_name.mysql.sql +0 -5
- package/templates/src/app/api/_platform/migrations/0003__users_source_display_name.sql +0 -5
- package/templates/src/app/api/_platform/migrations/0004__sys_rbac.mysql.sql +0 -70
- package/templates/src/app/api/_platform/migrations/0004__sys_rbac.pg.sql +0 -67
- package/templates/src/app/api/_platform/migrations/0004__sys_rbac.sql +0 -67
- package/templates/src/app/api/_platform/migrations/0005__users_must_change.mysql.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0005__users_must_change.sql +0 -2
- package/templates/src/app/api/_platform/migrations/0006__users_is_forbidden.mysql.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0006__users_is_forbidden.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0007__users_email.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.mysql.sql +0 -10
- package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.pg.sql +0 -10
- package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.sql +0 -11
- package/templates/src/app/api/_platform/migrations/0009__sys_throttle.mysql.sql +0 -12
- package/templates/src/app/api/_platform/migrations/0009__sys_throttle.pg.sql +0 -12
- package/templates/src/app/api/_platform/migrations/0009__sys_throttle.sql +0 -13
- package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.mysql.sql +0 -17
- package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.pg.sql +0 -16
- package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.sql +0 -18
- package/templates/src/app/api/_platform/migrations/0011__sys_setting_group_label.mysql.sql +0 -2
- package/templates/src/app/api/_platform/migrations/0011__sys_setting_group_label.sql +0 -4
- package/templates/src/app/api/_platform/migrations/0012__market_history.mysql.sql +0 -13
- package/templates/src/app/api/_platform/migrations/0012__market_history.pg.sql +0 -12
- package/templates/src/app/api/_platform/migrations/0012__market_history.sql +0 -14
- package/templates/src/app/api/_platform/migrations/0013__users_profile.sql +0 -4
- package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.mysql.sql +0 -9
- package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.pg.sql +0 -7
- package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.sql +0 -8
- package/templates/src/app/api/_platform/migrations/0015__tenant.mysql.sql +0 -12
- package/templates/src/app/api/_platform/migrations/0015__tenant.pg.sql +0 -11
- package/templates/src/app/api/_platform/migrations/0015__tenant.sql +0 -44
- package/templates/src/app/api/_platform/migrations/0016__org.mysql.sql +0 -41
- package/templates/src/app/api/_platform/migrations/0016__org.pg.sql +0 -40
- package/templates/src/app/api/_platform/migrations/0016__org.sql +0 -62
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +0 -7
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +0 -4
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +0 -8
- package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.mysql.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.pg.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.sql +0 -8
- package/templates/src/app/web/system/pages/login-methods/index.tsx +0 -30
- /package/shell-dist/assets/{nprogress-css.js → nprogress-css.1cq2pzkre0.js} +0 -0
- /package/shell-dist/assets/{ojm-antd-es-button.js → ojm-antd-es-button.2uriphio50.js} +0 -0
- /package/shell-dist/assets/{ojm-antd-es-modal.js → ojm-antd-es-modal.bf3tgdh8v0.js} +0 -0
- /package/shell-dist/assets/{ojm-antd-es-slider.js → ojm-antd-es-slider.xyvptuhid0.js} +0 -0
- /package/shell-dist/assets/{ojm-antd-es-upload.js → ojm-antd-es-upload.13gx45e0g0.js} +0 -0
|
@@ -26,7 +26,7 @@ peerPlatform: '>=0.1.0' # C2-11(P2 起对宿主 API_VERSION 强制;本仓 AP
|
|
|
26
26
|
api:
|
|
27
27
|
module: demo-solution # C2-12(存在时必须 === name)
|
|
28
28
|
prefix: /api/demo-solution # C2-13(默认形态显式写出)
|
|
29
|
-
tables: [
|
|
29
|
+
tables: [demo_solution_note] # 与 schema.yaml 表集一致(oj 冗余校验同源);表名带 C2-12c 前缀
|
|
30
30
|
deps: # 键 = 模块名(C2-01 语法或 _platform);值 = 范围算子语法
|
|
31
31
|
_platform: '>=0.1.0'
|
|
32
32
|
anonymous: # C2-26:首段 === 模块段;无登录语义端点
|
|
@@ -14,7 +14,7 @@ import { parse } from "yaml";
|
|
|
14
14
|
// 跨端引用(双端一致钉):被测实现同仓分居两运行时(计划 §1「两运行时取舍」),
|
|
15
15
|
// 测试直接互调,比「两套件各读同一夹具」更强——漂移即本文件红。
|
|
16
16
|
import { preflight } from "../../../templates/src/app/api/_platform/_lifecycle/steps/preflight";
|
|
17
|
-
import { checkLocalesComplete, checkTablesAgainstSchema, parseManifest, RESERVED_NAMES, reservedNameSuggestions, validateContract } from "../../manifest-contract";
|
|
17
|
+
import { checkLocalesComplete, checkSchemaTablePrefix, checkTablesAgainstSchema, parseManifest, RESERVED_NAMES, reservedNameSuggestions, validateContract } from "../../manifest-contract";
|
|
18
18
|
|
|
19
19
|
const FIXTURES = join(dirname(fileURLToPath(import.meta.url)), "fixtures");
|
|
20
20
|
const readText = (p: string) => readFileSync(join(FIXTURES, p), "utf8");
|
|
@@ -214,19 +214,91 @@ describe("rEG-CS7 保留名扩项(红队 F5)", () => {
|
|
|
214
214
|
describe("checkTablesAgainstSchema(C2-12a:构建 ✅ 对照 schema.yaml)", () => {
|
|
215
215
|
const SCHEMA = readText("fixture-valid/api/src/demo-solution/schema.yaml");
|
|
216
216
|
|
|
217
|
-
it("fixture-valid tables
|
|
217
|
+
it("fixture-valid tables 与 schema.yaml 表集一致 → 零违规", () => {
|
|
218
218
|
expect(checkTablesAgainstSchema(structuredClone(VALID), SCHEMA)).toEqual([]);
|
|
219
219
|
});
|
|
220
220
|
|
|
221
221
|
it("tables 与 schema.yaml 表集不一致 → C2-12a 且指明差额", () => {
|
|
222
222
|
const m = structuredClone(VALID);
|
|
223
|
-
(m.api as any).tables = ["
|
|
223
|
+
(m.api as any).tables = ["demo_solution_note", "ghost"];
|
|
224
224
|
const v = checkTablesAgainstSchema(m, SCHEMA);
|
|
225
225
|
expect(v[0]).toContain("C2-12a");
|
|
226
226
|
expect(v[0]).toContain("ghost");
|
|
227
227
|
});
|
|
228
228
|
});
|
|
229
229
|
|
|
230
|
+
describe("checkSchemaTablePrefix(C2-12c:schema.yaml 表前缀)", () => {
|
|
231
|
+
const SCHEMA = readText("fixture-valid/api/src/demo-solution/schema.yaml");
|
|
232
|
+
|
|
233
|
+
it("fixture-valid 声明 prefix 且表名带前缀 → 零违规", () => {
|
|
234
|
+
expect(checkSchemaTablePrefix(SCHEMA)).toEqual([]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("允许自选短前缀(长模块名可用 crm_ 之类,不强制由模块名派生)", () => {
|
|
238
|
+
expect(checkSchemaTablePrefix("prefix: dm_\ntables:\n dm_note: {}\n")).toEqual([]);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("缺 prefix → C2-12c", () => {
|
|
242
|
+
const v = checkSchemaTablePrefix("tables:\n demo_solution_note: {}\n");
|
|
243
|
+
expect(v[0]).toContain("C2-12c");
|
|
244
|
+
expect(v[0]).toContain("prefix");
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("prefix 不以 _ 结尾 → C2-12c 语法违规", () => {
|
|
248
|
+
const v = checkSchemaTablePrefix("prefix: demo_solution\ntables:\n demo_solution_note: {}\n");
|
|
249
|
+
expect(v[0]).toContain("C2-12c");
|
|
250
|
+
expect(v[0]).toContain("_ 结尾");
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("prefix 超过 32 字符 → C2-12c(吃掉表名可用空间)", () => {
|
|
254
|
+
const long = `${"a".repeat(33)}_`;
|
|
255
|
+
const v = checkSchemaTablePrefix(`prefix: ${long}\ntables:\n ${long}note: {}\n`);
|
|
256
|
+
expect(v[0]).toContain("C2-12c");
|
|
257
|
+
expect(v[0]).toContain("超过上限 32");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("表名未带前缀 → C2-12c 并给出改名建议", () => {
|
|
261
|
+
const v = checkSchemaTablePrefix("prefix: demo_solution_\ntables:\n note: {}\n");
|
|
262
|
+
expect(v[0]).toContain("C2-12c");
|
|
263
|
+
expect(v[0]).toContain("demo_solution_note");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("表名总长边界:63 合法、64 拒绝(PostgreSQL 上限 63 字节)", () => {
|
|
267
|
+
const p = "ab_";
|
|
268
|
+
const name63 = `${p}${"c".repeat(60)}`; // 3 + 60 = 63
|
|
269
|
+
const name64 = `${p}${"c".repeat(61)}`; // 3 + 61 = 64
|
|
270
|
+
expect(name63.length).toBe(63);
|
|
271
|
+
expect(checkSchemaTablePrefix(`prefix: ${p}\ntables:\n ${name63}: {}\n`)).toEqual([]);
|
|
272
|
+
const v = checkSchemaTablePrefix(`prefix: ${p}\ntables:\n ${name64}: {}\n`);
|
|
273
|
+
expect(v[0]).toContain("C2-12c");
|
|
274
|
+
expect(v[0]).toContain("63 字符");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("引用式方案(api.tables 缺省)也做前缀检查", () => {
|
|
278
|
+
const m = structuredClone(VALID);
|
|
279
|
+
delete (m.api as any).tables;
|
|
280
|
+
const v = checkTablesAgainstSchema(m, "tables:\n note: {}\n");
|
|
281
|
+
expect(v[0]).toContain("C2-12c");
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("prefix 落在第一方保留段(oj_ / ojm_ 开头)→ C2-12c,整段禁用含派生形式", () => {
|
|
285
|
+
for (const p of ["oj_", "ojm_", "oj_crm_", "ojm_tools_"]) {
|
|
286
|
+
const v = checkSchemaTablePrefix(`prefix: ${p}\ntables:\n ${p}note: {}\n`);
|
|
287
|
+
expect(v.some(x => x.includes("C2-12c") && x.includes("保留段"))).toBe(true);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("自有前缀不落在保留段 → 零违规(保留段检查的对照)", () => {
|
|
292
|
+
expect(checkSchemaTablePrefix("prefix: crm_\ntables:\n crm_note: {}\n")).toEqual([]);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("_oj_ 因下划线开头只报语法违规,不重复报保留段", () => {
|
|
296
|
+
const v = checkSchemaTablePrefix("prefix: _oj_\ntables:\n _oj_note: {}\n");
|
|
297
|
+
expect(v[0]).toContain("必须匹配");
|
|
298
|
+
expect(v.every(x => !x.includes("保留段"))).toBe(true);
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
230
302
|
describe("checkLocalesComplete(C2-25:构建期缺 key fail-fast)", () => {
|
|
231
303
|
const ZH = readText("fixture-valid/web/src/demo-solution/locales/zh-CN.json");
|
|
232
304
|
const EN = readText("fixture-valid/web/src/demo-solution/locales/en-US.json");
|
|
@@ -37,11 +37,11 @@ describe.skipIf(!smokeReady)("真机冒烟(需 OJM_SMOKE_URL/OJM_SMOKE_USER/OJ
|
|
|
37
37
|
const apiDist = join(project, "dist/api/demo-solution-0.1.0");
|
|
38
38
|
const { mkdirSync, writeFileSync } = await import("node:fs");
|
|
39
39
|
mkdirSync(join(apiDist, "ping"), { recursive: true });
|
|
40
|
-
// 迁移 SQL 随夹具 src 真源(0001__init.sql 建
|
|
40
|
+
// 迁移 SQL 随夹具 src 真源(0001__init.sql 建 demo_solution_note 表);B-11 硬验收:appliedSeq > 0 可断言
|
|
41
41
|
cpSync(join(FIXTURE_VALID, "api/src/demo-solution/migrations"), join(apiDist, "migrations"), { recursive: true });
|
|
42
42
|
// schema.yaml 同随夹具真源(列集与迁移 SQL 一致):oj migrate 在 apply 后跑 schema
|
|
43
43
|
// reconcile(migrate_cmd.rs migrate_one),声明缺列而实库多列 = Err——合成空列集会炸
|
|
44
|
-
writeFileSync(join(apiDist, "manifest.yaml"), "name: demo-solution\ndesc: manifest.yaml 契约全字段合法夹具\nversion: 0.1.0\ntables:\n -
|
|
44
|
+
writeFileSync(join(apiDist, "manifest.yaml"), "name: demo-solution\ndesc: manifest.yaml 契约全字段合法夹具\nversion: 0.1.0\ntables:\n - demo_solution_note\n");
|
|
45
45
|
cpSync(join(FIXTURE_VALID, "api/src/demo-solution/schema.yaml"), join(apiDist, "schema.yaml"));
|
|
46
46
|
writeFileSync(join(apiDist, "routes.js"), "export {};\n");
|
|
47
47
|
writeFileSync(join(apiDist, "ping/api.js"), "export async function ping() {}\n");
|
package/src/manifest-contract.ts
CHANGED
|
@@ -297,8 +297,8 @@ export function validateContract(m: unknown): string[] {
|
|
|
297
297
|
}
|
|
298
298
|
if (api.tables !== undefined) {
|
|
299
299
|
const t = api.tables;
|
|
300
|
-
if (!Array.isArray(t) || !t.every(x => typeof x === "string" && /^[a-z][a-z0-9_]{0,
|
|
301
|
-
v0.push(v("C2-12a", "api.tables 每项必须匹配 ^[a-z][a-z0-9_]{0,
|
|
300
|
+
if (!Array.isArray(t) || !t.every(x => typeof x === "string" && /^[a-z][a-z0-9_]{0,62}$/.test(x))) {
|
|
301
|
+
v0.push(v("C2-12a", "api.tables 每项必须匹配 ^[a-z][a-z0-9_]{0,62}$ 的字符串(≤63 字符:PostgreSQL 标识符上限 63 字节、MySQL 64 字符,取最严值;与 api/src/<name>/schema.yaml 表集逐一相等另行对照)"));
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
304
|
if (api.deps !== undefined) {
|
|
@@ -682,30 +682,113 @@ export function checkLocalesComplete(m: unknown, zhText: string, enText: string)
|
|
|
682
682
|
return out;
|
|
683
683
|
}
|
|
684
684
|
|
|
685
|
+
/** C2-12c prefix 语法:小写字母开头,仅小写字母/数字/下划线,且以 `_` 结尾 */
|
|
686
|
+
const TABLE_PREFIX_RE = /^[a-z][a-z0-9_]*_$/;
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* 第一方保留的表前缀段:oj 引擎 / ojm CLI 的命名空间。
|
|
690
|
+
*
|
|
691
|
+
* 框架表(_platform / auth / home / notification 下的 users / sys_* / wecom_* 等)保持裸名,
|
|
692
|
+
* 是保留的框架命名空间;方案表名一律带作者自选前缀,与框架表名天然不重叠。把 `oj_` / `ojm_`
|
|
693
|
+
* 整段留给第一方,防止方案先占用、日后框架想用时反被占(S002 归属图以表名为全局主键)。
|
|
694
|
+
* 按「段」保留(startsWith),不只禁精确值——整个命名空间都归第一方。
|
|
695
|
+
* 注:下划线开头的 `_oj_`(oj 内建账本 `_oj_migrations`)本就不满足 TABLE_PREFIX_RE,无需列举。
|
|
696
|
+
*/
|
|
697
|
+
const RESERVED_TABLE_PREFIX_SEGMENTS = ["oj_", "ojm_"];
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* 跨库可移植的表名上限:PostgreSQL 标识符上限 63 **字节**(NAMEDATALEN-1),
|
|
701
|
+
* MySQL 是 64 字符、SQLite 无实际限制——取最严的 63。表名字符集限定 ASCII,
|
|
702
|
+
* 字符数 = 字节数,因此 63 字符在三种库上都安全。
|
|
703
|
+
*/
|
|
704
|
+
const MAX_TABLE_NAME_LEN = 63;
|
|
705
|
+
|
|
706
|
+
/** 前缀上限:给表名留 ≥31 字符(63 - 32),避免前缀把表名的可用空间吃光 */
|
|
707
|
+
const MAX_PREFIX_LEN = 32;
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* C2-12c `schema.yaml` 的 `prefix` 与逐表前缀校验。
|
|
711
|
+
*
|
|
712
|
+
* 表名在**全库共享**(oj 归属图以表名为主键),前缀把模块归属编码进名字,
|
|
713
|
+
* 让两个独立发布的方案不会因为都叫 `note` 而在安装期 S002 拒启。
|
|
714
|
+
* 前缀由作者选择(短、可读、尽量全局唯一),不强制由模块名派生——长模块名
|
|
715
|
+
* 可以用 `crm_` 之类短前缀,避免表名被模块名拖到数据库上限之外。
|
|
716
|
+
*
|
|
717
|
+
* 入参为 schema.yaml 文本(fs 在调用方)。YAML 解析失败返回 []——那是 C2-12a
|
|
718
|
+
* 的判罚面,同一事故不重复报两遍。
|
|
719
|
+
*/
|
|
720
|
+
export function checkSchemaTablePrefix(schemaYamlText: string): string[] {
|
|
721
|
+
let doc: unknown;
|
|
722
|
+
try {
|
|
723
|
+
doc = parse(schemaYamlText);
|
|
724
|
+
}
|
|
725
|
+
catch {
|
|
726
|
+
return [];
|
|
727
|
+
}
|
|
728
|
+
if (!isObj(doc) || !isObj(doc.tables))
|
|
729
|
+
return []; // 缺 tables / 非映射由 C2-12a 报
|
|
730
|
+
|
|
731
|
+
const keys = Object.keys(doc.tables);
|
|
732
|
+
const out: string[] = [];
|
|
733
|
+
const prefix = doc.prefix;
|
|
734
|
+
|
|
735
|
+
if (prefix === undefined) {
|
|
736
|
+
out.push(v("C2-12c", "schema.yaml 缺 prefix:必须声明表前缀(如 prefix: crm_;小写字母开头、以 _ 结尾、≤32 字符,建议与方案名相关且全局唯一);表名在全库共享,前缀把归属编码进名字,避免方案之间撞表"));
|
|
737
|
+
}
|
|
738
|
+
else if (typeof prefix !== "string" || !TABLE_PREFIX_RE.test(prefix)) {
|
|
739
|
+
out.push(v("C2-12c", `schema.yaml 的 prefix「${String(prefix)}」必须匹配 ^[a-z][a-z0-9_]*_$(小写字母开头、仅小写字母/数字/下划线、以 _ 结尾)`));
|
|
740
|
+
}
|
|
741
|
+
if (typeof prefix === "string" && prefix.length > MAX_PREFIX_LEN) {
|
|
742
|
+
out.push(v("C2-12c", `schema.yaml 的 prefix「${prefix}」长 ${prefix.length} 字符,超过上限 ${MAX_PREFIX_LEN}——表名总长须 ≤${MAX_TABLE_NAME_LEN},前缀过长会吃掉表名可用空间;请改用更短的方案前缀(如 crm_)`));
|
|
743
|
+
}
|
|
744
|
+
if (typeof prefix === "string" && TABLE_PREFIX_RE.test(prefix)) {
|
|
745
|
+
const seg = RESERVED_TABLE_PREFIX_SEGMENTS.find(s => prefix.startsWith(s));
|
|
746
|
+
if (seg !== undefined) {
|
|
747
|
+
out.push(v("C2-12c", `schema.yaml 的 prefix「${prefix}」落在第一方保留段「${seg}*」——oj 引擎 / ojm CLI 的表命名空间由框架独占,方案不得占用,请改用自有前缀(如 crm_、acme_)`));
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (typeof prefix === "string" && prefix !== "") {
|
|
752
|
+
const offenders = keys.filter(k => !k.startsWith(prefix));
|
|
753
|
+
if (offenders.length > 0) {
|
|
754
|
+
out.push(v("C2-12c", `表名必须以本方案前缀「${prefix}」开头:${offenders.map(k => `「${k}」→「${prefix}${k}」`).join("、")}`));
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
// 表名语法/总长(引用式方案没有 api.tables 语法列兜底,这里一并查)
|
|
758
|
+
const malformed = keys.filter(k => !/^[a-z][a-z0-9_]{0,62}$/.test(k));
|
|
759
|
+
if (malformed.length > 0) {
|
|
760
|
+
out.push(v("C2-12c", `schema.yaml 表名必须匹配 ^[a-z][a-z0-9_]{0,62}$(小写字母开头,仅小写字母/数字/下划线,≤${MAX_TABLE_NAME_LEN} 字符——PostgreSQL 上限 63 字节、MySQL 64 字符,取最严值):${malformed.join("、")}`));
|
|
761
|
+
}
|
|
762
|
+
return out;
|
|
763
|
+
}
|
|
764
|
+
|
|
685
765
|
/**
|
|
686
|
-
* C2-12a api.tables 与 `api/src/<name>/schema.yaml` 表集逐一相等(oj 冗余校验同源,不一致 fail-fast
|
|
766
|
+
* C2-12a api.tables 与 `api/src/<name>/schema.yaml` 表集逐一相等(oj 冗余校验同源,不一致 fail-fast);
|
|
767
|
+
* 同时执行 C2-12c 表前缀校验——引用式方案(api.tables 缺省)只要 schema.yaml 存在也必须带前缀。
|
|
687
768
|
* 入参为 schema.yaml 文本(fs 在调用方)。
|
|
688
769
|
*/
|
|
689
770
|
export function checkTablesAgainstSchema(m: unknown, schemaYamlText: string): string[] {
|
|
771
|
+
const prefixViolations = checkSchemaTablePrefix(schemaYamlText);
|
|
772
|
+
|
|
690
773
|
if (!isObj(m) || !isObj(m.api) || m.api.tables === undefined)
|
|
691
|
-
return
|
|
774
|
+
return prefixViolations;
|
|
692
775
|
const declared = m.api.tables;
|
|
693
776
|
if (!Array.isArray(declared))
|
|
694
|
-
return
|
|
777
|
+
return prefixViolations; // 语法违规归 validateContract(C2-12a 语法列),此处只对照表集
|
|
695
778
|
let schemaTables: unknown;
|
|
696
779
|
try {
|
|
697
780
|
const doc = parse(schemaYamlText);
|
|
698
781
|
schemaTables = isObj(doc) ? doc.tables : undefined;
|
|
699
782
|
}
|
|
700
783
|
catch {
|
|
701
|
-
return [v("C2-12a", "schema.yaml 不是合法 YAML,无法对照 api.tables 表集")];
|
|
784
|
+
return [...prefixViolations, v("C2-12a", "schema.yaml 不是合法 YAML,无法对照 api.tables 表集")];
|
|
702
785
|
}
|
|
703
786
|
if (!isObj(schemaTables)) {
|
|
704
|
-
return [v("C2-12a", "schema.yaml 缺 tables 映射,无法对照 api.tables 表集(oj 冗余校验同源)")];
|
|
787
|
+
return [...prefixViolations, v("C2-12a", "schema.yaml 缺 tables 映射,无法对照 api.tables 表集(oj 冗余校验同源)")];
|
|
705
788
|
}
|
|
706
789
|
const schemaKeys = new Set(Object.keys(schemaTables));
|
|
707
790
|
const declaredKeys = new Set(declared.filter(x => typeof x === "string"));
|
|
708
|
-
const out: string[] = [];
|
|
791
|
+
const out: string[] = [...prefixViolations];
|
|
709
792
|
const missingInSchema = [...declaredKeys].filter(k => !schemaKeys.has(k));
|
|
710
793
|
const undeclaredInManifest = [...schemaKeys].filter(k => !declaredKeys.has(k));
|
|
711
794
|
if (missingInSchema.length > 0) {
|
package/src/market/bootstrap.ts
CHANGED
|
@@ -69,6 +69,14 @@ export interface IsolatedSite {
|
|
|
69
69
|
cleanup: () => Promise<void>
|
|
70
70
|
/** 销毁清单(--keep 时给人看这里到底有什么) */
|
|
71
71
|
teardownReport: () => string[]
|
|
72
|
+
/**
|
|
73
|
+
* 重启隔离站点 oj serve(停当前进程,按同一派生 config 重新起站)。
|
|
74
|
+
* 用途:无守护形态安装会落 partial(vars/auth 等冻结段热改属 restart_pending,需进程
|
|
75
|
+
* 重启才激活)。walkthrough 在每次安装后置重启,模拟真实「部署 → 重启」时序,让模块真正
|
|
76
|
+
* 上线(路由/匿名端点注册),使后续深检/升级/重装断言落到活跃态而非空洞的 partial。
|
|
77
|
+
* token 仍有效(jwt_secret 落在 config 文件,重启不变);DB 在同根,会话延续。
|
|
78
|
+
*/
|
|
79
|
+
restart: () => Promise<void>
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
export interface BootstrapOptions {
|
|
@@ -178,8 +186,13 @@ export async function bootstrapIsolatedSite(opts: BootstrapOptions = {}): Promis
|
|
|
178
186
|
// 隔离站点直接预置被测方案的探活路径——与 scripts/verify-solution-smoke/bootstrap-host.mjs 同手法,
|
|
179
187
|
// 让 trial 验的是安装机制本身,而不是重启时序。
|
|
180
188
|
const healthPath = `/${parseFixtureSpec(opts.fixture).name}/ping`;
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
// 锚点行可能带行内注释与缩进(模板 config.yaml 的 /system/login-methods 即如此),
|
|
190
|
+
// 故用 lookahead 容忍尾注释;命中第一个 anonymous_paths 列表项(auth 守卫读取的那个,
|
|
191
|
+
// 与安装器 appendAnonymousPaths 同目标)即可——旧写法用 `$` 硬锚尾,注释使其失配导致预置
|
|
192
|
+
// 静默失效,安装器随后自己补匿名路径 → auth 进 restart_pending → 无守护形态判 partial。
|
|
193
|
+
const loginAnchor = /^(\s*)- \/system\/login-methods(?=\s*(?:#.*)?$)/m;
|
|
194
|
+
if (loginAnchor.test(cfg) && !cfg.includes(`- ${healthPath}`))
|
|
195
|
+
cfg = cfg.replace(loginAnchor, `$1- /system/login-methods\n$1- ${healthPath}`);
|
|
183
196
|
// fs 段:模板自 0.2.13 起已自带(安装器落盘面);旧模板没有时才补,避免重复键
|
|
184
197
|
if (!/^fs:/m.test(cfg))
|
|
185
198
|
cfg = `${cfg.trimEnd()}\nfs:\n root: .\n readonly: false\n`;
|
|
@@ -237,6 +250,16 @@ export async function bootstrapIsolatedSite(opts: BootstrapOptions = {}): Promis
|
|
|
237
250
|
process.on("SIGINT", onSignal);
|
|
238
251
|
process.on("SIGTERM", onSignal);
|
|
239
252
|
|
|
253
|
+
// 重启:停当前进程后按同一派生 config 重新起站(激活冻结段);cleanup 持有的是本作用域的
|
|
254
|
+
// `oj` 变量引用,故重启后 cleanup 停的是新进程(闭包捕获变量,非值)。
|
|
255
|
+
const restart = async () => {
|
|
256
|
+
log("[ojm] 隔离站点重启 oj serve(激活冻结段 auth/vars)…");
|
|
257
|
+
await oj.stop();
|
|
258
|
+
oj = starter(configPath, "/api", resolveLayout(root).apiDist);
|
|
259
|
+
await oj.ready;
|
|
260
|
+
log(`[ojm] 隔离站点重启就绪:http://127.0.0.1:${port}(根 ${root})`);
|
|
261
|
+
};
|
|
262
|
+
|
|
240
263
|
return {
|
|
241
264
|
root,
|
|
242
265
|
port,
|
|
@@ -249,6 +272,7 @@ export async function bootstrapIsolatedSite(opts: BootstrapOptions = {}): Promis
|
|
|
249
272
|
admin: { user: SEED_ADMIN_USER, pass: SEED_ADMIN_PASS },
|
|
250
273
|
cleanup,
|
|
251
274
|
teardownReport: () => teardownReport(root, port),
|
|
275
|
+
restart,
|
|
252
276
|
};
|
|
253
277
|
}
|
|
254
278
|
|
package/src/market/command.ts
CHANGED
|
@@ -109,6 +109,7 @@ export async function marketCommand(sub: string, argv: string[]): Promise<void>
|
|
|
109
109
|
key: flags.key,
|
|
110
110
|
check: flags.check,
|
|
111
111
|
registryId: flags.registryId,
|
|
112
|
+
rebuildIndex: flags.rebuildIndex === true,
|
|
112
113
|
});
|
|
113
114
|
if (flags.rebuildIndex === true)
|
|
114
115
|
await buildIndex({ id: flags.id, key: flags.key, check: flags.check });
|
package/src/market/publish.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface MarketPublishOptions {
|
|
|
38
38
|
check?: boolean
|
|
39
39
|
/** 签发方 registry 身份(REG-CS3 v2 防跨仓库重放;透传 signCommand --registry-id) */
|
|
40
40
|
registryId?: string
|
|
41
|
+
/** 命令层是否紧接着重建索引(仅用于措辞:true 时不打印「索引未重建」) */
|
|
42
|
+
rebuildIndex?: boolean
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export interface MarketPublishResult {
|
|
@@ -88,7 +90,9 @@ export async function publishPackage(opts: MarketPublishOptions = {}): Promise<M
|
|
|
88
90
|
const size = fs.statSync(tgzPath).size;
|
|
89
91
|
console.log(`[ojm] 包已落 CDN → ${path.relative(projectRoot, tgzPath)}(${size} 字节)`);
|
|
90
92
|
console.log(`[ojm] 发布方 ${signed.keyId};指纹 ${signed.fingerprint}`);
|
|
91
|
-
|
|
93
|
+
// 带了 --rebuild-index 时由命令层紧接着重建索引,这里不能再报「未重建」(否则前后两句自相矛盾)
|
|
94
|
+
if (opts.rebuildIndex !== true)
|
|
95
|
+
console.log("[ojm] 索引未重建:多人场景请由维护者跑 ojm market build-index");
|
|
92
96
|
|
|
93
97
|
return {
|
|
94
98
|
name,
|
|
@@ -70,6 +70,17 @@ export interface WalkthroughResult {
|
|
|
70
70
|
port: number
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* 安装成功终态:oj 0.1.122 起,无守护形态安装会落 partial(vars/auth 等冻结段热改属
|
|
75
|
+
* restart_pending,需进程重启才完全生效)——这是正确终态(P0 §3.6 / 验收 15),不是失败。
|
|
76
|
+
* 真正的失败 partial(锁后异常 + 补偿回滚)不会带 restartRequired,故以它区分二者。
|
|
77
|
+
* 本 walkthrough 在每次安装后置重启(IsolatedSite.restart),模拟真实「部署 → 重启」时序,
|
|
78
|
+
* 让模块真正上线。
|
|
79
|
+
*/
|
|
80
|
+
function installSucceeded(task: TaskRecord): boolean {
|
|
81
|
+
return task.status === "done" || (task.status === "partial" && task.restartRequired === true);
|
|
82
|
+
}
|
|
83
|
+
|
|
73
84
|
/**
|
|
74
85
|
* neg-* 专用版本:与主链路(0.1.0/0.2.0)完全隔离,收尾整包摘除。
|
|
75
86
|
* 必须低于主链路 latest(0.2.0),否则发布后它会成为 latest,摘除时 REG-CS2 单调性守卫
|
|
@@ -96,8 +107,8 @@ const OPS: Op[] = [
|
|
|
96
107
|
title: "安装(11 段)",
|
|
97
108
|
run: async (ctx) => {
|
|
98
109
|
const task = await install(ctx, ctx.version);
|
|
99
|
-
const
|
|
100
|
-
return { ok
|
|
110
|
+
const ok = installSucceeded(task);
|
|
111
|
+
return { ok, detail: ok ? `${task.stages.length} 段全 ok(终态 ${task.status}${task.restartRequired ? " + restartRequired" : ""},已重启激活)` : `status=${task.status} stage=${task.stage ?? "?"}:${task.message}` };
|
|
101
112
|
},
|
|
102
113
|
},
|
|
103
114
|
{
|
|
@@ -106,7 +117,7 @@ const OPS: Op[] = [
|
|
|
106
117
|
run: async (ctx) => {
|
|
107
118
|
await ensureInstalled(ctx);
|
|
108
119
|
const task = await install(ctx, ctx.version);
|
|
109
|
-
const ok = task
|
|
120
|
+
const ok = installSucceeded(task);
|
|
110
121
|
return { ok, detail: ok ? "重装成功(未报方案名占用)" : `status=${task.status}:${task.message}` };
|
|
111
122
|
},
|
|
112
123
|
},
|
|
@@ -159,7 +170,7 @@ const OPS: Op[] = [
|
|
|
159
170
|
await ensureInstalled(ctx); // 前置:0.1.0 在列(账本 head=1)
|
|
160
171
|
await publishVersion(ctx, "0.2.0");
|
|
161
172
|
const task = await install(ctx, "0.2.0");
|
|
162
|
-
if (task
|
|
173
|
+
if (!installSucceeded(task))
|
|
163
174
|
return { ok: false, detail: `升级安装未 done:status=${task.status} stage=${task.stage ?? "?"}:${task.message}` };
|
|
164
175
|
const card = await indexCard(ctx);
|
|
165
176
|
const distDir = resolveLayout(ctx.site.root).apiDist;
|
|
@@ -189,7 +200,7 @@ const OPS: Op[] = [
|
|
|
189
200
|
if (card?.installed !== "0.2.0") {
|
|
190
201
|
await publishVersion(ctx, "0.2.0");
|
|
191
202
|
const pre = await install(ctx, "0.2.0");
|
|
192
|
-
if (pre
|
|
203
|
+
if (!installSucceeded(pre))
|
|
193
204
|
return { ok: false, detail: `前置安装 0.2.0 未 done:status=${pre.status}:${pre.message}` };
|
|
194
205
|
card = await indexCard(ctx);
|
|
195
206
|
}
|
|
@@ -320,7 +331,7 @@ const OPS: Op[] = [
|
|
|
320
331
|
return { ok: false, detail: `未停 needs_trust:status=${first.status}:${first.message}` };
|
|
321
332
|
await callApi(fetch, ctx.base, ctx.token, "POST", "/api/_platform/market/trust", { task_id: first.id });
|
|
322
333
|
const second = await installOnce(ctx, NEG_VERSION, NEG_NAME);
|
|
323
|
-
if (second
|
|
334
|
+
if (!installSucceeded(second))
|
|
324
335
|
return { ok: false, detail: `确认后重装未 done:status=${second.status}:${second.message}` };
|
|
325
336
|
// 收尾(F3):显式 revoke 陌生 key,快照 diff 断言无残留——不靠 uninstall 兜底(它本就不摘信任)
|
|
326
337
|
const del = await callApi<{ revoked?: boolean }>(fetch, ctx.base, ctx.token, "DELETE", `/api/_platform/market/trust/key?key_id=${encodeURIComponent("stranger.publisher")}`);
|
|
@@ -417,13 +428,18 @@ export async function walkthrough(opts: WalkthroughOptions = {}): Promise<Walkth
|
|
|
417
428
|
}
|
|
418
429
|
}
|
|
419
430
|
|
|
420
|
-
/**
|
|
431
|
+
/**
|
|
432
|
+
* 安装 + 轮询;needs_trust 时自动确认(隔离站点,无外部信任可言)。
|
|
433
|
+
* 成功(done 或 restartRequired 的 partial)后置重启,激活冻结段,模拟真实部署时序。
|
|
434
|
+
*/
|
|
421
435
|
async function install(ctx: Ctx, version: string): Promise<TaskRecord> {
|
|
422
436
|
let task = await installOnce(ctx, version);
|
|
423
437
|
if (task.status === "needs_trust") {
|
|
424
438
|
await callApi(fetch, ctx.base, ctx.token, "POST", "/api/_platform/market/trust", { task_id: task.id });
|
|
425
439
|
task = await installOnce(ctx, version);
|
|
426
440
|
}
|
|
441
|
+
if (installSucceeded(task))
|
|
442
|
+
await ctx.site.restart();
|
|
427
443
|
return task;
|
|
428
444
|
}
|
|
429
445
|
|
|
@@ -462,7 +478,7 @@ async function ensureInstalled(ctx: Ctx): Promise<void> {
|
|
|
462
478
|
if (card?.installed === ctx.version)
|
|
463
479
|
return;
|
|
464
480
|
const task = await install(ctx, ctx.version);
|
|
465
|
-
if (task
|
|
481
|
+
if (!installSucceeded(task))
|
|
466
482
|
throw new Error(`前置安装失败:status=${task.status}:${task.message}`);
|
|
467
483
|
}
|
|
468
484
|
|
package/src/shared-deps.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* 共享依赖表 —— 单一常量源(设计文档 §4.3,解决 B11 / B12)。
|
|
3
5
|
*
|
|
@@ -21,15 +23,18 @@ export interface SharedDepEntry {
|
|
|
21
23
|
/** 硬共享:模块不得自带 */
|
|
22
24
|
hard: boolean
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
26
|
+
* 模块自打包:宿主 shell 不预构建该资产、importmap 不列该键,模块若 import 它
|
|
27
|
+
* 则自行打包进模块产物(设计文档 §4.3 的「软共享缺席→模块自带副本兜底」语义)。
|
|
28
|
+
* 用于**仅模块使用、宿主与共享 chunk 永不 import**的重型依赖(编辑器 / 图表 /
|
|
29
|
+
* Markdown),避免把它们 5~6MB 的体量无谓铺进 shell-dist 并要求公开链路多传
|
|
30
|
+
* 一堆文件。宿主侧有真实 import 的共享依赖(antd / pro-components / icons 内部
|
|
31
|
+
* 依赖的 motion / react-jss / dayjs / zustand / @dnd-kit 等)绝不能标 bundle ——
|
|
32
|
+
* 否则会被 antd 等共享 chunk 经 importmap 解析不到而 404。判定依据:宿主
|
|
33
|
+
* host.tsx + runtime.js 的传递 import 图,以及 antd/pro-components/icons 的
|
|
34
|
+
* 传递依赖图,都不含这些说明符。
|
|
35
|
+
*
|
|
36
|
+
* ⚠️ 本档位**不计入宿主版本一致性保障**:模块把副本打进产物,多副本不会报错,
|
|
37
|
+
* 只会重复体积。体积怎么控,见 docs/devkit/manual.md §2.6。
|
|
33
38
|
*/
|
|
34
39
|
bundle?: boolean
|
|
35
40
|
}
|
|
@@ -66,9 +71,10 @@ export const SHARED_DEPS: SharedDepEntry[] = [
|
|
|
66
71
|
* - 无单例语义。kit 必须唯一是因为多副本 = 两份 antd 上下文树;编辑器资产
|
|
67
72
|
* 把 @tiptap/*、@plane/ui 等**全部内联**在自身里,跨边界穿过的只有 React
|
|
68
73
|
* 家族(那是独立的硬共享条目),多一份编辑器副本不会拆散任何 context。
|
|
69
|
-
* -
|
|
70
|
-
*
|
|
71
|
-
*
|
|
74
|
+
* - 不是每个模块都用编辑器。软共享的语义是「允许缺席」——宿主**不预构建**
|
|
75
|
+
* 这份资产(bundle: true,不进 shell-dist、不进 importmap),模块 import 它
|
|
76
|
+
* 就自己打进产物。代价是每个用编辑器的模块各带一份约 5.4 MB,换来宿主
|
|
77
|
+
* 分发体积不被没人用的重型依赖撑大。
|
|
72
78
|
*/
|
|
73
79
|
{ specifier: "@plane/editor", asset: "editor", hard: false, bundle: true },
|
|
74
80
|
// 注:@rc-component/form(antd 6 Form 底层)曾尝试单例化以修 my-profile
|
|
@@ -120,6 +126,9 @@ export const SHARED_DEPS: SharedDepEntry[] = [
|
|
|
120
126
|
{ specifier: "dayjs/plugin/weekday", asset: "dayjs-plugin-weekday", hard: false },
|
|
121
127
|
{ specifier: "dayjs/plugin/weekOfYear", asset: "dayjs-plugin-weekOfYear", hard: false },
|
|
122
128
|
{ specifier: "dayjs/plugin/weekYear", asset: "dayjs-plugin-weekYear", hard: false },
|
|
129
|
+
// echarts 家族:模块自打包。整包 index(`import * as echarts from "echarts"`)会让
|
|
130
|
+
// 单个模块产物从 ~180KB 涨到 1.7MB(实测 echarts 918KB + zrender 221KB 占 87%),
|
|
131
|
+
// 故脚手架改用 `echarts/core` 按需注册;接入方写法见 docs/devkit/manual.md §2.6。
|
|
123
132
|
{ specifier: "echarts", asset: "echarts", hard: false, bundle: true },
|
|
124
133
|
{ specifier: "echarts/charts", asset: "echarts-charts", hard: false, bundle: true },
|
|
125
134
|
{ specifier: "echarts/features.js", asset: "echarts-features", hard: false, bundle: true },
|
|
@@ -147,18 +156,21 @@ export const SHARED_DEPS: SharedDepEntry[] = [
|
|
|
147
156
|
{ specifier: "tailwind-merge", asset: "tailwind-merge", hard: false },
|
|
148
157
|
/**
|
|
149
158
|
* Markdown 编辑器(Vditor v4,M5 回升自下游 oj-wiki):npm 包是 webpack **UMD**
|
|
150
|
-
* 单产物(main=dist/index.js,无 ESM 出口、仅 default
|
|
151
|
-
*
|
|
159
|
+
* 单产物(main=dist/index.js,无 ESM 出口、仅 default 导出)。bundle: true 表示
|
|
160
|
+
* 宿主不预构建 —— CJS/UMD interop 交给模块自己的 rolldown 打包链路处理。
|
|
152
161
|
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
162
|
+
* 为什么不预构建:体积大(gzip ≈700 KiB)且不是每个模块都用。宿主为它留一份
|
|
163
|
+
* /assets/vditor.js,等于给所有站点无条件加 700 KiB 分发体积,而只有编辑器页
|
|
164
|
+
* 才用得上。改由模块自带后,代价转给「真正 import 它的那个模块」。
|
|
165
|
+
*
|
|
166
|
+
* 副作用(写文档时容易说错):模块产物是单文件(codeSplitting: false),
|
|
167
|
+
* 所以 `import("vditor")` 会被内联回去,**动态导入并不能把它从首屏摘掉**;
|
|
168
|
+
* 要省这笔体积只能把编辑器页拆成独立模块/方案。
|
|
157
169
|
*
|
|
158
170
|
* 深路径只登记 CSS 主样式(nprogress 同款样式垫片)。vditor 其余 dist 深路径
|
|
159
171
|
* (lute / i18n / icons / 主题 CSS…)是**运行期经 cdn 选项按需拉取的资源**而非
|
|
160
172
|
* import 说明符,由模块侧自行处理(自注入或指向宿主可服务的路径);若模块代码
|
|
161
|
-
* 直接 import 它们,会命中前缀 external
|
|
173
|
+
* 直接 import 它们,会命中前缀 external 但无共享表键 → 构建期深路径报错,
|
|
162
174
|
* 属预期拦截(与 dayjs/plugin/* 同规则)。
|
|
163
175
|
*/
|
|
164
176
|
{ specifier: "vditor", asset: "vditor", hard: false, bundle: true },
|
|
@@ -182,6 +194,10 @@ export function generateShellEntries(): { name: string, pkg: string }[] {
|
|
|
182
194
|
/**
|
|
183
195
|
* 由共享表生成 importmap:说明符 → /assets/<asset>.js。
|
|
184
196
|
* baseUrl 供宿主部署在子路径时使用。
|
|
197
|
+
*
|
|
198
|
+
* 注意:这里给出的是**逻辑名**。shell 构建落盘时会把每个资产改名成
|
|
199
|
+
* `<asset>.<hash>.js`(见 `hashedAssetFileName`),importmap 由构建脚本同步改写,
|
|
200
|
+
* 运行时拿到的仍是 `/assets/<asset>.<hash>.js`。
|
|
185
201
|
*/
|
|
186
202
|
export function generateImportmap(baseUrl = ""): Record<string, string> {
|
|
187
203
|
const map: Record<string, string> = {};
|
|
@@ -196,3 +212,37 @@ export function generateImportmap(baseUrl = ""): Record<string, string> {
|
|
|
196
212
|
export function isPrebuiltDep(id: string): boolean {
|
|
197
213
|
return SHARED_DEPS.some(dep => !dep.bundle && (id === dep.specifier || id.startsWith(`${dep.specifier}/`)));
|
|
198
214
|
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 共享资产的内容哈希段:10 位 base36,且**保证**同时含数字与字母。
|
|
218
|
+
*
|
|
219
|
+
* 为什么必须保证:oj serve ≥ 0.1.116 的默认缓存策略按**文件名**判定
|
|
220
|
+
* (`serve/src/static_files.rs::default_cache_control`)——文件名 stem 按 `-` `_` `.`
|
|
221
|
+
* 切段,末段 ≥8 位且同时含数字与字母才发 `public, max-age=31536000, immutable`,
|
|
222
|
+
* 其余一律 `no-cache`。纯 base36 有十万分之一量级会全数字(vite 的 base64url 更糟:
|
|
223
|
+
* 8 位全字母约 19%),漏判就白丢一年缓存。这里按「缺哪类补哪类」定长补齐,
|
|
224
|
+
* 让每个资产 100% 命中长缓存,而不是赌概率。
|
|
225
|
+
*
|
|
226
|
+
* 9 位 base36 ≈ 46.5 bit,65 个资产量级碰撞概率可忽略。
|
|
227
|
+
*/
|
|
228
|
+
export function assetHash(content: string | Uint8Array): string {
|
|
229
|
+
const digest = createHash("sha256").update(content).digest();
|
|
230
|
+
const body = digest.readBigUInt64BE(0).toString(36).slice(0, 9).padStart(9, "0");
|
|
231
|
+
// 已有字母 → 补数字;全数字 → 补字母。两类必在其一,补齐另一类即可。
|
|
232
|
+
return /[a-z]/.test(body) ? `${body}0` : `${body}z`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** 内容寻址文件名:`<base>.<hash>.js`(js 与 map 同为 .js 后缀,map 保持原名不动) */
|
|
236
|
+
export function hashedAssetFileName(base: string, content: string | Uint8Array): string {
|
|
237
|
+
return `${base}.${assetHash(content)}.js`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* 与 oj serve 的 `default_cache_control` 同口径:末段 ≥8 位且数字字母都有 → 可发 immutable。
|
|
242
|
+
* 构建期用它断言产物名,避免哪天哈希算法换掉后长缓存静默失效。
|
|
243
|
+
*/
|
|
244
|
+
export function isImmutableAssetFileName(fileName: string): boolean {
|
|
245
|
+
const stem = fileName.replace(/\.[^.]*$/, "");
|
|
246
|
+
const seg = stem.split(/[-_.]/).pop() ?? "";
|
|
247
|
+
return seg.length >= 8 && /\d/.test(seg) && /[a-z]/i.test(seg);
|
|
248
|
+
}
|
package/src/site-base.ts
CHANGED
|
@@ -102,6 +102,10 @@ export function rewriteIndexHtml(html: string, siteBase: string | undefined, api
|
|
|
102
102
|
// 上一步已处理,二者不重叠。
|
|
103
103
|
out = out.replace(/(\b(?:href|src|data-ojm-entry)=")(\/[^/"][^"]*|\/")/g, (_m, attr: string, url: string) =>
|
|
104
104
|
`${attr}${withSiteBase(url, base)}`);
|
|
105
|
+
// data-ojm-preload 是**逗号分隔的 URL 列表**(构建期算好的首屏静态闭包),
|
|
106
|
+
// 上面那条单值正则匹配不到列表,漏改会让子路径部署下预取全 404(退化回瀑布,不白屏但慢)
|
|
107
|
+
out = out.replace(/(\bdata-ojm-preload=")([^"]*)"/g, (_m, attr: string, list: string) =>
|
|
108
|
+
`${attr}${list.split(",").map(url => url.startsWith("/") ? withSiteBase(url, base) : url).join(",")}"`);
|
|
105
109
|
globals.push(`window.__OJ_BASE__=${JSON.stringify(base)};`);
|
|
106
110
|
}
|
|
107
111
|
if (api) {
|
package/src/site.ts
CHANGED
|
@@ -43,6 +43,8 @@ export interface TaskRecord {
|
|
|
43
43
|
appliedSeq?: number
|
|
44
44
|
startedAt: number
|
|
45
45
|
finishedAt?: number
|
|
46
|
+
/** 无守护形态部分生效:冻结段(auth/vars)热改属 restart_pending,需宿主重启才完全生效 */
|
|
47
|
+
restartRequired?: boolean
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
/** 模板 serve 缺省端口(templates/config.yaml server.port: 9778;host 显式钉 127.0.0.1) */
|