@velora-cms/server 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +65 -0
- package/dist/admin-bundle/assets/__virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react__loadShare__.js_commonjs-proxy-Ng0fOpdU.js +1 -0
- package/dist/admin-bundle/assets/__virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_2_dom__loadShare__.js_commonjs-proxy-oc1i_iBU.js +9 -0
- package/dist/admin-bundle/assets/_virtual_mf-localSharedImportMap___mfe_internal__velora_admin__mf_owner__1-DOKdWYyj.js +1 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare___mf_0_velora_mf_2_cms_mf_1_plugin_mf_2_sdk__loadShare__.js-C1-ZAxwO.js +2 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react__loadShare__.js-BuUBskgj.js +9 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-DMaku-HC.js +9 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_2_dom__loadShare__.js-DYfqn12F.js +1 -0
- package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-BPoj3DC9.js +25 -0
- package/dist/admin-bundle/assets/content-trash.schema-DfXHkC-w.js +72 -0
- package/dist/admin-bundle/assets/hostInit--C4xK6nj.js +1 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-400-normal-CyU-ddYS.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-400-normal-EA1o-u_Q.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-500-normal-C4MQITzh.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-500-normal-DPXcppbm.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-600-normal-0pRdybE_.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-600-normal-MJZ0liF5.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-400-normal-Bo5KPYvw.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-400-normal-CFZh1unN.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-500-normal-BletK1Hp.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-500-normal-Cd6jVIg7.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-600-normal-Dk5eISD_.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-600-normal-KrqB56Mw.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-400-normal-BeVLg08x.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-400-normal-BpLfq-63.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-500-normal-Ck_W4G78.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-500-normal-eJxPnL1P.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-600-normal-DT29Q8HW.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-600-normal-DjDAeNQD.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
- package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
- package/dist/admin-bundle/assets/index-BIAF0ShP.js +542 -0
- package/dist/admin-bundle/assets/index-DhMY-0sC.js +148 -0
- package/dist/admin-bundle/assets/index-DnZV_gsg.css +1 -0
- package/dist/admin-bundle/assets/index-Do71v6NZ.js +7 -0
- package/dist/admin-bundle/assets/literata-cyrillic-ext-opsz-italic-5yUt4G_9.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-cyrillic-ext-opsz-normal-C5tfwALZ.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-cyrillic-opsz-italic-Bj0ndbyp.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-cyrillic-opsz-normal-DgmypKXX.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-greek-ext-opsz-italic-DFpO5j-Z.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-greek-ext-opsz-normal-DGm64vGR.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-greek-opsz-italic-CHERNRFq.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-greek-opsz-normal-DFglUmAd.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-latin-ext-opsz-italic-4y8iAqqs.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-latin-ext-opsz-normal-STiTPs0F.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-latin-opsz-italic-CJ93D38p.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-latin-opsz-normal-BHZsWIlc.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-vietnamese-opsz-italic-BdcrZMcO.woff2 +0 -0
- package/dist/admin-bundle/assets/literata-vietnamese-opsz-normal-Gqw5JQlq.woff2 +0 -0
- package/dist/admin-bundle/assets/mf-entry-bootstrap-0-aef015cd.js +41 -0
- package/dist/admin-bundle/assets/remoteEntry-CNyN68bc.js +14 -0
- package/dist/admin-bundle/assets/virtualExposes-BDCSo5OS.js +1 -0
- package/dist/admin-bundle/assets/vite-preload-helper-Cgledu0r.js +1 -0
- package/dist/admin-bundle/index.html +16 -0
- package/dist/app.d.ts +8 -0
- package/dist/app.js +252 -0
- package/dist/auth/api-content-author.d.ts +2 -0
- package/dist/auth/api-content-author.js +27 -0
- package/dist/auth/api-keys.d.ts +7 -0
- package/dist/auth/api-keys.js +85 -0
- package/dist/auth/jwt-payload.d.ts +16 -0
- package/dist/auth/jwt-payload.js +1 -0
- package/dist/auth/preview-tokens.d.ts +7 -0
- package/dist/auth/preview-tokens.js +76 -0
- package/dist/auth/rate-limit.d.ts +16 -0
- package/dist/auth/rate-limit.js +283 -0
- package/dist/auth/require-auth.d.ts +4 -0
- package/dist/auth/require-auth.js +28 -0
- package/dist/auth/route-rate-limits.d.ts +31 -0
- package/dist/auth/route-rate-limits.js +155 -0
- package/dist/auth/tokens.d.ts +15 -0
- package/dist/auth/tokens.js +38 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +6 -0
- package/dist/boot/core-migration-gate.d.ts +12 -0
- package/dist/boot/core-migration-gate.js +52 -0
- package/dist/boot/resolve-boot-mode.d.ts +6 -0
- package/dist/boot/resolve-boot-mode.js +206 -0
- package/dist/bundled-templates/com.velora.blog/manifest.json +39 -0
- package/dist/bundled-templates/com.velora.blog/package.tar.gz +0 -0
- package/dist/bundled-templates/com.velora.blog/sample-content.json +14 -0
- package/dist/config/admin-ui.d.ts +1 -0
- package/dist/config/admin-ui.js +10 -0
- package/dist/config/cors.d.ts +7 -0
- package/dist/config/cors.js +53 -0
- package/dist/config/env-file-path.d.ts +2 -0
- package/dist/config/env-file-path.js +22 -0
- package/dist/config/graphql-limits.d.ts +2 -0
- package/dist/config/graphql-limits.js +26 -0
- package/dist/config/headless.d.ts +1 -0
- package/dist/config/headless.js +8 -0
- package/dist/config/license-revalidation.d.ts +2 -0
- package/dist/config/license-revalidation.js +16 -0
- package/dist/config/load-env.d.ts +1 -0
- package/dist/config/load-env.js +53 -0
- package/dist/config/marketplace.d.ts +11 -0
- package/dist/config/marketplace.js +19 -0
- package/dist/config/preview.d.ts +3 -0
- package/dist/config/preview.js +39 -0
- package/dist/config/public-docs.d.ts +1 -0
- package/dist/config/public-docs.js +9 -0
- package/dist/config/site.d.ts +7 -0
- package/dist/config/site.js +12 -0
- package/dist/config/trust-proxy.d.ts +2 -0
- package/dist/config/trust-proxy.js +42 -0
- package/dist/data-types/validate-data-type-settings.d.ts +1 -0
- package/dist/data-types/validate-data-type-settings.js +33 -0
- package/dist/db/content-db.d.ts +16 -0
- package/dist/db/content-db.js +109 -0
- package/dist/db/core-migrations/index.d.ts +14 -0
- package/dist/db/core-migrations/index.js +184 -0
- package/dist/db/core-migrations/steps.d.ts +4 -0
- package/dist/db/core-migrations/steps.js +66 -0
- package/dist/db/load-prisma-client.d.ts +2 -0
- package/dist/db/load-prisma-client.js +44 -0
- package/dist/db/prisma.d.ts +3 -0
- package/dist/db/prisma.js +67 -0
- package/dist/db/resolve-client-dir.d.ts +2 -0
- package/dist/db/resolve-client-dir.js +83 -0
- package/dist/docs/filter-public-api-spec.d.ts +2 -0
- package/dist/docs/filter-public-api-spec.js +52 -0
- package/dist/docs-bundle/404.html +41 -0
- package/dist/docs-bundle/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js +1 -0
- package/dist/docs-bundle/_astro/Search.astro_astro_type_script_index_0_lang.CavldbAs.js +1 -0
- package/dist/docs-bundle/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js +1 -0
- package/dist/docs-bundle/_astro/common.DTXA7sXM.css +3 -0
- package/dist/docs-bundle/_astro/ec.0vx5m.js +3 -0
- package/dist/docs-bundle/_astro/ec.w36nc.css +1 -0
- package/dist/docs-bundle/_astro/page.Dwipeu-R.js +1 -0
- package/dist/docs-bundle/_astro/print.ehPL0gv-.css +1 -0
- package/dist/docs-bundle/_astro/starlight-toc.CTCQXEge.js +1 -0
- package/dist/docs-bundle/_astro/ui-core.D-7Scnhs.js +2 -0
- package/dist/docs-bundle/favicon.svg +1 -0
- package/dist/docs-bundle/index.html +1 -0
- package/dist/docs-bundle/pagefind/fragment/en_18a36d7.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_1d9eb24.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_1ef320c.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_3099406.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_4a5b6fe.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_9aa652e.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_baccdf6.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_d81f3fe.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_d855550.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_e6d88d8.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/fragment/en_e9d3189.pf_fragment +0 -0
- package/dist/docs-bundle/pagefind/index/en_5feeed7.pf_index +0 -0
- package/dist/docs-bundle/pagefind/index/en_c6981f9.pf_index +0 -0
- package/dist/docs-bundle/pagefind/index/en_ce4f6bf.pf_index +0 -0
- package/dist/docs-bundle/pagefind/pagefind-component-ui.css +1509 -0
- package/dist/docs-bundle/pagefind/pagefind-component-ui.js +56 -0
- package/dist/docs-bundle/pagefind/pagefind-entry.json +1 -0
- package/dist/docs-bundle/pagefind/pagefind-highlight.js +1070 -0
- package/dist/docs-bundle/pagefind/pagefind-modular-ui.css +214 -0
- package/dist/docs-bundle/pagefind/pagefind-modular-ui.js +8 -0
- package/dist/docs-bundle/pagefind/pagefind-ui.css +1 -0
- package/dist/docs-bundle/pagefind/pagefind-ui.js +2 -0
- package/dist/docs-bundle/pagefind/pagefind-worker.js +6 -0
- package/dist/docs-bundle/pagefind/pagefind.en_1e94abd852.pf_meta +0 -0
- package/dist/docs-bundle/pagefind/pagefind.js +6 -0
- package/dist/docs-bundle/pagefind/wasm.en.pagefind +0 -0
- package/dist/docs-bundle/pagefind/wasm.unknown.pagefind +0 -0
- package/dist/docs-bundle/v1/index.html +86 -0
- package/dist/docs-bundle/v1/using-velora/build-a-headless-frontend/index.html +413 -0
- package/dist/docs-bundle/v1/using-velora/content-model/index.html +858 -0
- package/dist/docs-bundle/v1/using-velora/editing/index.html +748 -0
- package/dist/docs-bundle/v1/using-velora/getting-started/index.html +588 -0
- package/dist/docs-bundle/v1/using-velora/headless-api/index.html +1620 -0
- package/dist/docs-bundle/v1/using-velora/index.html +215 -0
- package/dist/docs-bundle/v1/using-velora/installation/index.html +886 -0
- package/dist/docs-bundle/v1/using-velora/installing-plugins/index.html +836 -0
- package/dist/docs-bundle/v1/using-velora/media/index.html +468 -0
- package/dist/docs-bundle/v1/using-velora/users-and-roles/index.html +561 -0
- package/dist/graphql/field-count-rule.d.ts +5 -0
- package/dist/graphql/field-count-rule.js +59 -0
- package/dist/graphql/schema.d.ts +12 -0
- package/dist/graphql/schema.js +330 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +180 -0
- package/dist/lib.d.ts +2 -0
- package/dist/lib.js +39 -0
- package/dist/marketplace/install-bundle-members.d.ts +33 -0
- package/dist/marketplace/install-bundle-members.js +137 -0
- package/dist/marketplace/install-from-marketplace.d.ts +60 -0
- package/dist/marketplace/install-from-marketplace.js +581 -0
- package/dist/marketplace/license-revalidation-job.d.ts +38 -0
- package/dist/marketplace/license-revalidation-job.js +139 -0
- package/dist/marketplace/marketplace-client.d.ts +43 -0
- package/dist/marketplace/marketplace-client.js +228 -0
- package/dist/marketplace/revalidate-license.d.ts +39 -0
- package/dist/marketplace/revalidate-license.js +103 -0
- package/dist/media/media-service.d.ts +36 -0
- package/dist/media/media-service.js +162 -0
- package/dist/plugins/boot-plugin-loader.d.ts +32 -0
- package/dist/plugins/boot-plugin-loader.js +57 -0
- package/dist/plugins/hook-dispatcher.d.ts +13 -0
- package/dist/plugins/hook-dispatcher.js +68 -0
- package/dist/plugins/install-built-package.d.ts +36 -0
- package/dist/plugins/install-built-package.js +199 -0
- package/dist/plugins/install-package-tarball.d.ts +25 -0
- package/dist/plugins/install-package-tarball.js +73 -0
- package/dist/plugins/plugin-context.d.ts +5 -0
- package/dist/plugins/plugin-context.js +26 -0
- package/dist/plugins/plugin-package-store.d.ts +31 -0
- package/dist/plugins/plugin-package-store.js +64 -0
- package/dist/plugins/plugin-registry-service.d.ts +227 -0
- package/dist/plugins/plugin-registry-service.js +636 -0
- package/dist/plugins/seed-template-content.d.ts +23 -0
- package/dist/plugins/seed-template-content.js +198 -0
- package/dist/plugins/server-serializers.d.ts +3 -0
- package/dist/plugins/server-serializers.js +16 -0
- package/dist/plugins/simple-storage-api.d.ts +9 -0
- package/dist/plugins/simple-storage-api.js +26 -0
- package/dist/plugins/snapshot-groups.d.ts +16 -0
- package/dist/plugins/snapshot-groups.js +81 -0
- package/dist/plugins/structured-query-builder.d.ts +21 -0
- package/dist/plugins/structured-query-builder.js +60 -0
- package/dist/plugins/structured-snapshot-service.d.ts +16 -0
- package/dist/plugins/structured-snapshot-service.js +286 -0
- package/dist/plugins/structured-storage-api.d.ts +22 -0
- package/dist/plugins/structured-storage-api.js +91 -0
- package/dist/plugins/structured-storage-transaction.d.ts +22 -0
- package/dist/plugins/structured-storage-transaction.js +63 -0
- package/dist/plugins/structured-table-naming.d.ts +1 -0
- package/dist/plugins/structured-table-naming.js +18 -0
- package/dist/plugins/trusted-keys.d.ts +11 -0
- package/dist/plugins/trusted-keys.js +67 -0
- package/dist/plugins/trusted-signing-keys.d.ts +27 -0
- package/dist/plugins/trusted-signing-keys.js +40 -0
- package/dist/plugins/uninstall-bundle.d.ts +29 -0
- package/dist/plugins/uninstall-bundle.js +150 -0
- package/dist/plugins/verify-package.d.ts +36 -0
- package/dist/plugins/verify-package.js +121 -0
- package/dist/routes/admin-static.d.ts +8 -0
- package/dist/routes/admin-static.js +196 -0
- package/dist/routes/api-keys.d.ts +2 -0
- package/dist/routes/api-keys.js +95 -0
- package/dist/routes/api-playground.d.ts +2 -0
- package/dist/routes/api-playground.js +21 -0
- package/dist/routes/auth.d.ts +2 -0
- package/dist/routes/auth.js +102 -0
- package/dist/routes/content.d.ts +3 -0
- package/dist/routes/content.js +471 -0
- package/dist/routes/data-types.d.ts +3 -0
- package/dist/routes/data-types.js +100 -0
- package/dist/routes/docs-static.d.ts +8 -0
- package/dist/routes/docs-static.js +137 -0
- package/dist/routes/document-types.d.ts +3 -0
- package/dist/routes/document-types.js +149 -0
- package/dist/routes/headless-bridge.d.ts +10 -0
- package/dist/routes/headless-bridge.js +207 -0
- package/dist/routes/health.d.ts +2 -0
- package/dist/routes/health.js +15 -0
- package/dist/routes/marketplace-proxy.d.ts +8 -0
- package/dist/routes/marketplace-proxy.js +135 -0
- package/dist/routes/media.d.ts +6 -0
- package/dist/routes/media.js +93 -0
- package/dist/routes/plugin-api-routes.d.ts +12 -0
- package/dist/routes/plugin-api-routes.js +224 -0
- package/dist/routes/plugin-assets.d.ts +8 -0
- package/dist/routes/plugin-assets.js +51 -0
- package/dist/routes/plugin-management.d.ts +7 -0
- package/dist/routes/plugin-management.js +525 -0
- package/dist/routes/plugin-storage.d.ts +8 -0
- package/dist/routes/plugin-storage.js +104 -0
- package/dist/routes/plugins.d.ts +2 -0
- package/dist/routes/plugins.js +36 -0
- package/dist/routes/public-api.d.ts +9 -0
- package/dist/routes/public-api.js +545 -0
- package/dist/routes/published-item.d.ts +57 -0
- package/dist/routes/published-item.js +236 -0
- package/dist/routes/setup.d.ts +9 -0
- package/dist/routes/setup.js +477 -0
- package/dist/routes/snapshots.d.ts +3 -0
- package/dist/routes/snapshots.js +65 -0
- package/dist/routes/templates.d.ts +3 -0
- package/dist/routes/templates.js +37 -0
- package/dist/routes/themes.d.ts +2 -0
- package/dist/routes/themes.js +30 -0
- package/dist/routes/users.d.ts +2 -0
- package/dist/routes/users.js +113 -0
- package/dist/sandbox/register-local-plugin.d.ts +36 -0
- package/dist/sandbox/register-local-plugin.js +108 -0
- package/dist/sandbox/seed-sandbox.d.ts +20 -0
- package/dist/sandbox/seed-sandbox.js +122 -0
- package/dist/scripts/copy-admin-bundle.d.ts +1 -0
- package/dist/scripts/copy-admin-bundle.js +24 -0
- package/dist/scripts/copy-docs-bundle.d.ts +1 -0
- package/dist/scripts/copy-docs-bundle.js +22 -0
- package/dist/scripts/create-api-key.d.ts +5 -0
- package/dist/scripts/create-api-key.js +56 -0
- package/dist/scripts/generate-prisma-clients.d.ts +6 -0
- package/dist/scripts/generate-prisma-clients.js +125 -0
- package/dist/scripts/pack-bundled-templates.d.ts +1 -0
- package/dist/scripts/pack-bundled-templates.js +87 -0
- package/dist/setup/build-connection-string.d.ts +19 -0
- package/dist/setup/build-connection-string.js +14 -0
- package/dist/setup/bundled-templates.d.ts +19 -0
- package/dist/setup/bundled-templates.js +121 -0
- package/dist/setup/ensure-prisma-client.d.ts +3 -0
- package/dist/setup/ensure-prisma-client.js +23 -0
- package/dist/setup/install-starter.d.ts +14 -0
- package/dist/setup/install-starter.js +76 -0
- package/dist/setup/normalize-database-url.d.ts +2 -0
- package/dist/setup/normalize-database-url.js +35 -0
- package/dist/setup/persist-env-config.d.ts +2 -0
- package/dist/setup/persist-env-config.js +63 -0
- package/dist/setup/provision-database.d.ts +2 -0
- package/dist/setup/provision-database.js +89 -0
- package/dist/setup-app.d.ts +4 -0
- package/dist/setup-app.js +43 -0
- package/package.json +96 -0
- package/prisma/schema.prisma +303 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
import { validatePluginManifest, } from "@velora-cms/plugin-sdk/manifest";
|
|
2
|
+
import { DocumentTypeInUseError, DocumentTypeService } from "@velora-cms/content-service";
|
|
3
|
+
/** Manifest failed Session 60's validation — carries the readable
|
|
4
|
+
* `path: message` errors for the future install route to surface. */
|
|
5
|
+
export class PluginInstallError extends Error {
|
|
6
|
+
errors;
|
|
7
|
+
constructor(message, errors = []) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.errors = errors;
|
|
10
|
+
this.name = "PluginInstallError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** The manifest is valid but declares it doesn't support the site's
|
|
14
|
+
* active database — blocked with the declared reason, never a silent
|
|
15
|
+
* failure. Subclass of PluginInstallError so general install handling
|
|
16
|
+
* keeps working while the marketplace/UI can distinguish. */
|
|
17
|
+
export class PluginIncompatibleError extends PluginInstallError {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "PluginIncompatibleError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class PluginNotInstalledError extends Error {
|
|
24
|
+
constructor(pluginId) {
|
|
25
|
+
super(`No installed plugin with id "${pluginId}"`);
|
|
26
|
+
this.name = "PluginNotInstalledError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** The manifest declares pluginDependencies that aren't all installed AND
|
|
30
|
+
* active. Rejected with the full list, not just the first miss, so the
|
|
31
|
+
* installer can resolve everything in one pass rather than discovering
|
|
32
|
+
* missing dependencies one at a time. Subclass of PluginInstallError so
|
|
33
|
+
* existing catch sites keep working. */
|
|
34
|
+
export class PluginDependencyError extends PluginInstallError {
|
|
35
|
+
missingDependencies;
|
|
36
|
+
constructor(pluginId, missingDependencies) {
|
|
37
|
+
super(`Plugin "${pluginId}" requires the following plugin(s), which must be installed and active first: ${missingDependencies.join(", ")}`);
|
|
38
|
+
this.missingDependencies = missingDependencies;
|
|
39
|
+
this.name = "PluginDependencyError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** A declared migration's up()/down() threw during install. Carries the
|
|
43
|
+
* plugin id, the specific migration id that failed, and the original
|
|
44
|
+
* cause — install() has already rolled back every migration that
|
|
45
|
+
* succeeded earlier in this same batch by the time this is thrown, so
|
|
46
|
+
* the plugin is left with no partial migration state and no registry
|
|
47
|
+
* row. Subclass of PluginInstallError so existing catch sites keep
|
|
48
|
+
* working. */
|
|
49
|
+
export class PluginMigrationError extends PluginInstallError {
|
|
50
|
+
migrationId;
|
|
51
|
+
cause;
|
|
52
|
+
constructor(pluginId, migrationId, cause) {
|
|
53
|
+
super(`Plugin "${pluginId}"'s migration "${migrationId}" failed: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
54
|
+
this.migrationId = migrationId;
|
|
55
|
+
this.cause = cause;
|
|
56
|
+
this.name = "PluginMigrationError";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Installed but disabled — the gate SimpleStorageAPI needs before an
|
|
60
|
+
* HTTP request touches a plugin's stored values: a disabled plugin's
|
|
61
|
+
* views aren't mounted anywhere, so its storage shouldn't be live either. */
|
|
62
|
+
export class PluginNotActiveError extends Error {
|
|
63
|
+
constructor(pluginId) {
|
|
64
|
+
super(`Plugin "${pluginId}" is installed but not active — enable it before using its storage`);
|
|
65
|
+
this.name = "PluginNotActiveError";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** The plugin is installed but its manifest (the CODE — hooks are
|
|
69
|
+
* functions) isn't attached in this process, so it can't be activated:
|
|
70
|
+
* enabled:true for something that isn't running would be a lying state.
|
|
71
|
+
* Whatever loads plugin code at boot re-attaches via attachManifest. */
|
|
72
|
+
export class PluginNotLoadedError extends Error {
|
|
73
|
+
constructor(pluginId) {
|
|
74
|
+
super(`Plugin "${pluginId}" is installed but its manifest is not loaded in this process — ` +
|
|
75
|
+
`attach it with attachManifest() before activating`);
|
|
76
|
+
this.name = "PluginNotLoadedError";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Thrown when uninstall() is called on a structured-storage plugin
|
|
80
|
+
* without confirmStructuredDataCleanup: true. The snapshot has ALREADY
|
|
81
|
+
* been taken by this point — nothing else has happened (no onUninstall,
|
|
82
|
+
* no migration rollback, no row deletion) — so retrying with
|
|
83
|
+
* confirmStructuredDataCleanup: true is always safe. */
|
|
84
|
+
export class StructuredDataCleanupRequiresConfirmationError extends Error {
|
|
85
|
+
snapshotGroupId;
|
|
86
|
+
constructor(pluginId, snapshotGroupId) {
|
|
87
|
+
super(`Uninstalling structured-storage plugin "${pluginId}" requires confirmation to proceed — ` +
|
|
88
|
+
`its data was snapshotted as "${snapshotGroupId}". Call uninstall() again with ` +
|
|
89
|
+
`{ confirmStructuredDataCleanup: true } to actually delete its tables.`);
|
|
90
|
+
this.snapshotGroupId = snapshotGroupId;
|
|
91
|
+
this.name = "StructuredDataCleanupRequiresConfirmationError";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** F1 QA fix wave: thrown by uninstall()'s document-type guard when one or
|
|
95
|
+
* MORE tracked document types are still in use — aggregated across EVERY
|
|
96
|
+
* tracked document type, not just the first to fail, so the operator sees
|
|
97
|
+
* the whole blocking set in one 409 instead of discovering it one retry at
|
|
98
|
+
* a time. Distinct from content-service's own DocumentTypeInUseError
|
|
99
|
+
* (which only ever describes a single document type, by id) — that class
|
|
100
|
+
* is still what assertDocumentTypeDeletable() throws per-call; this one is
|
|
101
|
+
* what uninstall() throws once it's walked every tracked document type and
|
|
102
|
+
* collected every failure. */
|
|
103
|
+
export class PluginDocumentTypesInUseError extends Error {
|
|
104
|
+
blockers;
|
|
105
|
+
constructor(pluginId, blockers) {
|
|
106
|
+
super(`Cannot uninstall plugin "${pluginId}": the following document type(s) are still in use: ` +
|
|
107
|
+
blockers.map((blocker) => blocker.documentType).join(", "));
|
|
108
|
+
this.blockers = blockers;
|
|
109
|
+
this.name = "PluginDocumentTypesInUseError";
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// install() runs a plugin's declared migrations and onInstall hook
|
|
113
|
+
// before persisting the registry row (Session 72) — a failure at either
|
|
114
|
+
// step leaves no partial state, matching activate()'s "hook before
|
|
115
|
+
// state" philosophy. uninstall() checks every tracked document type is
|
|
116
|
+
// safe to delete FIRST (before anything else, including the
|
|
117
|
+
// structured-data snapshot), then runs onUninstall and rolls migrations
|
|
118
|
+
// back before deleting the row, tolerating a missing manifest the same
|
|
119
|
+
// way deactivate() does (destructive operations must never be blocked by
|
|
120
|
+
// a missing manifest) — see both methods' own doc comments for the full
|
|
121
|
+
// ordering rationale.
|
|
122
|
+
export class PluginRegistryService {
|
|
123
|
+
prisma;
|
|
124
|
+
adapter;
|
|
125
|
+
// Attached manifests — the runtime half the DB row can't hold (hooks
|
|
126
|
+
// are functions). install() attaches automatically; after a server
|
|
127
|
+
// restart the loader (Session 66's federation loader, Month 6's local
|
|
128
|
+
// plugin loader) re-attaches via attachManifest().
|
|
129
|
+
manifests = new Map();
|
|
130
|
+
// In-memory mirror of the DB `enabled` flag (Session 112) — the public
|
|
131
|
+
// read path's per-request serializer lookup needs a sync, in-process
|
|
132
|
+
// answer and must not hit the DB on every content read. Kept in lockstep
|
|
133
|
+
// with every write to the flag below (install/activate/deactivate/
|
|
134
|
+
// uninstall/attachManifest); never read independently as a source of
|
|
135
|
+
// truth — requireActivePlugin() etc. still go through the DB row.
|
|
136
|
+
enabledIds = new Set();
|
|
137
|
+
activeDialect;
|
|
138
|
+
structuredDataLifecycle;
|
|
139
|
+
documentTypeService;
|
|
140
|
+
constructor(prisma, adapter, options) {
|
|
141
|
+
this.prisma = prisma;
|
|
142
|
+
this.adapter = adapter;
|
|
143
|
+
this.activeDialect = options.activeDialect;
|
|
144
|
+
this.structuredDataLifecycle = options.structuredDataLifecycle ?? {
|
|
145
|
+
snapshot: async () => "",
|
|
146
|
+
cleanup: async () => { },
|
|
147
|
+
};
|
|
148
|
+
this.documentTypeService = new DocumentTypeService(adapter);
|
|
149
|
+
}
|
|
150
|
+
/** Non-throwing lookup — the read every other method here already does
|
|
151
|
+
* privately, exposed for callers (SimpleStorageAPI) that need to check
|
|
152
|
+
* installation state without triggering a lifecycle transition. */
|
|
153
|
+
async getInstalledPlugin(pluginId) {
|
|
154
|
+
return this.prisma.pluginRegistry.findUnique({ where: { pluginId } });
|
|
155
|
+
}
|
|
156
|
+
/** Installed AND enabled, or throw — the exact gate SimpleStorageAPI
|
|
157
|
+
* needs on every get()/set(). */
|
|
158
|
+
async requireActivePlugin(pluginId) {
|
|
159
|
+
const row = await this.getInstalledPlugin(pluginId);
|
|
160
|
+
if (!row) {
|
|
161
|
+
throw new PluginNotInstalledError(pluginId);
|
|
162
|
+
}
|
|
163
|
+
if (!row.enabled) {
|
|
164
|
+
throw new PluginNotActiveError(pluginId);
|
|
165
|
+
}
|
|
166
|
+
return row;
|
|
167
|
+
}
|
|
168
|
+
/** Read-only exposure of the attached manifest for a plugin id — used
|
|
169
|
+
* by request-time dispatchers (plugin-api-routes.ts) that need a
|
|
170
|
+
* plugin's declared contributions without going through install()/
|
|
171
|
+
* activate() again. Returns undefined if the manifest isn't attached
|
|
172
|
+
* in this process (never installed, or a restart happened and
|
|
173
|
+
* attachManifest() hasn't re-attached it yet). */
|
|
174
|
+
getManifest(pluginId) {
|
|
175
|
+
return this.manifests.get(pluginId);
|
|
176
|
+
}
|
|
177
|
+
/** Re-attach a loaded plugin's manifest to an existing installation
|
|
178
|
+
* (post-restart bridge for the boot-time plugin loader). */
|
|
179
|
+
async attachManifest(manifest) {
|
|
180
|
+
const row = await this.prisma.pluginRegistry.findUnique({
|
|
181
|
+
where: { pluginId: manifest.id },
|
|
182
|
+
});
|
|
183
|
+
if (!row) {
|
|
184
|
+
throw new PluginNotInstalledError(manifest.id);
|
|
185
|
+
}
|
|
186
|
+
this.manifests.set(manifest.id, manifest);
|
|
187
|
+
// Post-restart bridge: the enabled mirror is process-local and empty
|
|
188
|
+
// on a fresh process, so re-attaching an already-active plugin must
|
|
189
|
+
// seed it from the row the lookup above already fetched.
|
|
190
|
+
if (row.enabled) {
|
|
191
|
+
this.enabledIds.add(manifest.id);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
this.enabledIds.delete(manifest.id);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** The inverse of attachManifest: forget the in-process manifest so
|
|
198
|
+
* request-time consumers (the S89 apiRoutes dispatcher's
|
|
199
|
+
* requireActivePlugin/route lookup) stop answering for this plugin
|
|
200
|
+
* immediately — the uninstall route calls this so no restart is
|
|
201
|
+
* needed. A manifest that was never attached is a no-op. */
|
|
202
|
+
detachManifest(pluginId) {
|
|
203
|
+
this.manifests.delete(pluginId);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Validate a manifest (Session 60's validator — the contribution
|
|
207
|
+
* matrix included) and persist it. Session 127: the trust tier is now
|
|
208
|
+
* an optional caller-supplied value (`opts.trustTier`), defaulting to
|
|
209
|
+
* 'unverified' so every pre-S127 caller (which never supplied one)
|
|
210
|
+
* behaves identically. Local/dev plugins remain unsigned by
|
|
211
|
+
* definition and so always take the default.
|
|
212
|
+
* Installed ≠ active — rows start disabled; activate() is Session 63.
|
|
213
|
+
*/
|
|
214
|
+
async install(manifest, opts) {
|
|
215
|
+
const result = validatePluginManifest(manifest);
|
|
216
|
+
if (!result.ok) {
|
|
217
|
+
throw new PluginInstallError(`Plugin manifest failed validation:\n${result.errors.map((e) => ` - ${e}`).join("\n")}`, result.errors);
|
|
218
|
+
}
|
|
219
|
+
const { manifest: plugin } = result;
|
|
220
|
+
// Database compatibility gate (Session 64): blocked with the
|
|
221
|
+
// declared reason, never a silent failure — the marketplace
|
|
222
|
+
// pre-filters by dialect, but sideloads land here too.
|
|
223
|
+
const { supported, unsupportedReason } = plugin.databaseCompatibility;
|
|
224
|
+
if (!supported.includes(this.activeDialect)) {
|
|
225
|
+
const reason = unsupportedReason?.[this.activeDialect];
|
|
226
|
+
throw new PluginIncompatibleError(`Plugin "${plugin.id}" does not support the active database (${this.activeDialect}). ` +
|
|
227
|
+
`Supported: ${supported.join(", ")}.${reason ? ` Reason: ${reason}` : ""}`);
|
|
228
|
+
}
|
|
229
|
+
const existing = await this.prisma.pluginRegistry.findUnique({
|
|
230
|
+
where: { pluginId: plugin.id },
|
|
231
|
+
});
|
|
232
|
+
if (existing) {
|
|
233
|
+
throw new PluginInstallError(`Plugin "${plugin.id}" is already installed (version ${existing.version})`);
|
|
234
|
+
}
|
|
235
|
+
// Dependency resolution (Session 71): every declared dependency must
|
|
236
|
+
// already be installed AND active — a merely-installed-but-disabled
|
|
237
|
+
// dependency's views/routes aren't actually running, so this plugin
|
|
238
|
+
// couldn't rely on it either. Collect every miss in one pass rather
|
|
239
|
+
// than failing on the first, so the installer sees the whole picture.
|
|
240
|
+
if (plugin.pluginDependencies && plugin.pluginDependencies.length > 0) {
|
|
241
|
+
const missing = [];
|
|
242
|
+
for (const dependencyId of plugin.pluginDependencies) {
|
|
243
|
+
const dependency = await this.prisma.pluginRegistry.findUnique({
|
|
244
|
+
where: { pluginId: dependencyId },
|
|
245
|
+
});
|
|
246
|
+
if (!dependency || !dependency.enabled) {
|
|
247
|
+
missing.push(dependencyId);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (missing.length > 0) {
|
|
251
|
+
throw new PluginDependencyError(plugin.id, missing);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Migrations + onInstall (Session 72): the last gate before the
|
|
255
|
+
// registry row exists, so a failure here leaves no partial state —
|
|
256
|
+
// same "hook before state" philosophy as activate(). ctx is
|
|
257
|
+
// deliberately {} today (PluginMigrationContext, manifest.ts) — this
|
|
258
|
+
// is the only sense in which migrations run "against whichever Tier
|
|
259
|
+
// 1 database is active": the activeDialect check above, not a live
|
|
260
|
+
// adapter connection plugins could use to write raw SQL.
|
|
261
|
+
const applied = [];
|
|
262
|
+
if (plugin.migrations && plugin.migrations.length > 0) {
|
|
263
|
+
for (const migration of plugin.migrations) {
|
|
264
|
+
try {
|
|
265
|
+
await migration.up({});
|
|
266
|
+
await this.prisma.pluginMigrationRecord.create({
|
|
267
|
+
data: { pluginId: plugin.id, migrationId: migration.id },
|
|
268
|
+
});
|
|
269
|
+
applied.push(migration);
|
|
270
|
+
}
|
|
271
|
+
catch (cause) {
|
|
272
|
+
await this.rollbackMigrations(plugin.id, applied);
|
|
273
|
+
throw new PluginMigrationError(plugin.id, migration.id, cause);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Document types the manifest provisions at install (Month 8, Session
|
|
278
|
+
// 88) — only section/template plugins carry this contribution. Runs
|
|
279
|
+
// AFTER migrations (mirrors why onInstall already runs after
|
|
280
|
+
// migrations) and BEFORE onInstall, so a plugin's own install hook can
|
|
281
|
+
// rely on its document types already existing.
|
|
282
|
+
//
|
|
283
|
+
// Two-phase (Session 149, Task 2 amendment): a manifest declares
|
|
284
|
+
// treeRules' allowedParentTypes/allowedChildTypes by document type
|
|
285
|
+
// NAME (there's no id to reference at manifest-authoring time — the
|
|
286
|
+
// server mints one per entry, right here), but
|
|
287
|
+
// ContentService.assertValidPlacement compares those arrays against
|
|
288
|
+
// the real minted ids. Before this fix the name strings rode straight
|
|
289
|
+
// through to storage and could never match, silently breaking any
|
|
290
|
+
// cross-referencing treeRules (plugins/commerce never hit this — both
|
|
291
|
+
// its document types declare empty treeRules arrays).
|
|
292
|
+
//
|
|
293
|
+
// Phase 1 creates every declared type with its name-bearing arrays
|
|
294
|
+
// stripped (allowedAtRoot needs no resolution and is preserved),
|
|
295
|
+
// recording declaredName -> mintedId as each row is created. Phase 2
|
|
296
|
+
// walks the ORIGINAL declared requests, resolves every name against
|
|
297
|
+
// that map, and applies the resolved treeRules via the same update
|
|
298
|
+
// surface the admin Document Type editor's PUT /api/document-types/:id
|
|
299
|
+
// route uses (apps/server/src/routes/document-types.ts) —
|
|
300
|
+
// adapter.updateDocumentType, a full replace of the mutable
|
|
301
|
+
// definition (kind/defaultTemplateId untouched). There's no
|
|
302
|
+
// DocumentTypeService wrapper for update (the route calls the adapter
|
|
303
|
+
// directly too), so this mirrors that rather than inventing one. A
|
|
304
|
+
// name that isn't one of THIS plugin's own declared types is an
|
|
305
|
+
// install failure — the single try/catch below rolls back every
|
|
306
|
+
// document type phase 1 created (plus migrations) exactly like a
|
|
307
|
+
// phase-1 failure already did.
|
|
308
|
+
const createdDocumentTypes = [];
|
|
309
|
+
if (plugin.type === "section" || plugin.type === "template") {
|
|
310
|
+
const declared = plugin.contributions.documentTypes ?? [];
|
|
311
|
+
try {
|
|
312
|
+
const mintedIdByName = new Map();
|
|
313
|
+
for (const request of declared) {
|
|
314
|
+
// Fail before the side effect: a manifest declaring the same
|
|
315
|
+
// document type name twice would otherwise let
|
|
316
|
+
// mintedIdByName.set() below silently overwrite the first
|
|
317
|
+
// minted id with the second (last-wins) — every name resolution
|
|
318
|
+
// in phase 2 is load-bearing for tree-rule enforcement, so a
|
|
319
|
+
// silently ambiguous name must never reach it.
|
|
320
|
+
if (mintedIdByName.has(request.name)) {
|
|
321
|
+
throw new PluginInstallError(`Plugin "${plugin.id}" declares document type "${request.name}" more than once — document type names must be unique within a single plugin`);
|
|
322
|
+
}
|
|
323
|
+
const phase1Request = request.kind === "page"
|
|
324
|
+
? {
|
|
325
|
+
...request,
|
|
326
|
+
treeRules: {
|
|
327
|
+
allowedAtRoot: request.treeRules.allowedAtRoot,
|
|
328
|
+
allowedParentTypes: [],
|
|
329
|
+
allowedChildTypes: [],
|
|
330
|
+
},
|
|
331
|
+
}
|
|
332
|
+
: request;
|
|
333
|
+
const created = await this.documentTypeService.createDocumentType(phase1Request);
|
|
334
|
+
await this.prisma.pluginDocumentType.create({
|
|
335
|
+
data: { pluginId: plugin.id, documentTypeId: created.id },
|
|
336
|
+
});
|
|
337
|
+
createdDocumentTypes.push(created);
|
|
338
|
+
mintedIdByName.set(request.name, created.id);
|
|
339
|
+
}
|
|
340
|
+
for (const [index, request] of declared.entries()) {
|
|
341
|
+
if (request.kind !== "page")
|
|
342
|
+
continue;
|
|
343
|
+
const { allowedParentTypes, allowedChildTypes } = request.treeRules;
|
|
344
|
+
if (allowedParentTypes.length === 0 && allowedChildTypes.length === 0)
|
|
345
|
+
continue;
|
|
346
|
+
const resolveNames = (names) => names.map((name) => {
|
|
347
|
+
const id = mintedIdByName.get(name);
|
|
348
|
+
if (!id) {
|
|
349
|
+
throw new PluginInstallError(`Plugin "${plugin.id}" document type "${request.name}" declares a treeRules reference to "${name}", ` +
|
|
350
|
+
`which is not one of this plugin's own declared document types (${declared.map((d) => d.name).join(", ")})`);
|
|
351
|
+
}
|
|
352
|
+
return id;
|
|
353
|
+
});
|
|
354
|
+
const createdType = createdDocumentTypes[index];
|
|
355
|
+
await this.adapter.updateDocumentType(createdType.id, {
|
|
356
|
+
name: createdType.name,
|
|
357
|
+
icon: createdType.icon,
|
|
358
|
+
fields: createdType.fields,
|
|
359
|
+
tabs: createdType.tabs,
|
|
360
|
+
treeRules: {
|
|
361
|
+
allowedAtRoot: request.treeRules.allowedAtRoot,
|
|
362
|
+
allowedParentTypes: resolveNames(allowedParentTypes),
|
|
363
|
+
allowedChildTypes: resolveNames(allowedChildTypes),
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
catch (cause) {
|
|
369
|
+
await this.rollbackDocumentTypes(plugin.id, createdDocumentTypes);
|
|
370
|
+
await this.rollbackMigrations(plugin.id, applied);
|
|
371
|
+
throw cause;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
try {
|
|
375
|
+
await plugin.onInstall?.();
|
|
376
|
+
}
|
|
377
|
+
catch (cause) {
|
|
378
|
+
await this.rollbackDocumentTypes(plugin.id, createdDocumentTypes);
|
|
379
|
+
await this.rollbackMigrations(plugin.id, applied);
|
|
380
|
+
throw cause;
|
|
381
|
+
}
|
|
382
|
+
const row = await this.prisma.pluginRegistry.create({
|
|
383
|
+
data: {
|
|
384
|
+
pluginId: plugin.id,
|
|
385
|
+
name: plugin.name,
|
|
386
|
+
type: plugin.type,
|
|
387
|
+
version: plugin.version,
|
|
388
|
+
// Session 127: the tier a verified install earned, defaulting to
|
|
389
|
+
// the pre-S127 literal so every existing caller is unchanged.
|
|
390
|
+
trustTier: opts?.trustTier ?? "unverified",
|
|
391
|
+
storageType: plugin.databaseCompatibility.storageType,
|
|
392
|
+
enabled: false,
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
this.manifests.set(plugin.id, plugin);
|
|
396
|
+
// Mirror the row just persisted — install() always writes enabled:
|
|
397
|
+
// false today, so this is a no-op in practice, but it's written
|
|
398
|
+
// against row.enabled (not a hardcoded false) so a future
|
|
399
|
+
// auto-activate-on-install path can't silently desync the mirror.
|
|
400
|
+
if (row.enabled) {
|
|
401
|
+
this.enabledIds.add(plugin.id);
|
|
402
|
+
}
|
|
403
|
+
return row;
|
|
404
|
+
}
|
|
405
|
+
/** Roll back a batch of already-applied migrations in reverse order,
|
|
406
|
+
* removing each tracking row as it rolls back. Shared by install()'s
|
|
407
|
+
* two failure paths (a later migration failing, or onInstall failing
|
|
408
|
+
* after all migrations succeeded). */
|
|
409
|
+
async rollbackMigrations(pluginId, migrations) {
|
|
410
|
+
for (const migration of [...migrations].reverse()) {
|
|
411
|
+
await migration.down({});
|
|
412
|
+
await this.prisma.pluginMigrationRecord.deleteMany({
|
|
413
|
+
where: { pluginId, migrationId: migration.id },
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/** Delete every document type created so far in THIS install() call, in
|
|
418
|
+
* reverse order, plus its PluginDocumentType tracking row — always safe
|
|
419
|
+
* to hard-delete without assertDocumentTypeDeletable first, since these
|
|
420
|
+
* were just created inside this same call and cannot yet have any
|
|
421
|
+
* content or template referencing them. Shared by install()'s two
|
|
422
|
+
* documentTypes-era failure paths (a later entry failing, or onInstall
|
|
423
|
+
* failing after all entries succeeded) — same shape as
|
|
424
|
+
* rollbackMigrations. */
|
|
425
|
+
async rollbackDocumentTypes(pluginId, documentTypes) {
|
|
426
|
+
for (const documentType of [...documentTypes].reverse()) {
|
|
427
|
+
await this.documentTypeService.deleteDocumentType(documentType.id);
|
|
428
|
+
await this.prisma.pluginDocumentType.deleteMany({
|
|
429
|
+
where: { pluginId, documentTypeId: documentType.id },
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Enable an installed plugin. Hooks fire on TRANSITIONS only —
|
|
435
|
+
* activating an already-active plugin is a no-op. onActivate runs
|
|
436
|
+
* BEFORE enabled:true is persisted: a plugin that fails to start
|
|
437
|
+
* stays disabled. Requires the manifest to be attached (installed
|
|
438
|
+
* but unloaded code can't be "active" — that would be a lying state).
|
|
439
|
+
*/
|
|
440
|
+
async activate(pluginId) {
|
|
441
|
+
const row = await this.prisma.pluginRegistry.findUnique({ where: { pluginId } });
|
|
442
|
+
if (!row) {
|
|
443
|
+
throw new PluginNotInstalledError(pluginId);
|
|
444
|
+
}
|
|
445
|
+
if (row.enabled) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const manifest = this.manifests.get(pluginId);
|
|
449
|
+
if (!manifest) {
|
|
450
|
+
throw new PluginNotLoadedError(pluginId);
|
|
451
|
+
}
|
|
452
|
+
await manifest.onActivate?.();
|
|
453
|
+
await this.prisma.pluginRegistry.update({
|
|
454
|
+
where: { pluginId },
|
|
455
|
+
data: { enabled: true },
|
|
456
|
+
});
|
|
457
|
+
this.enabledIds.add(pluginId);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Disable an installed plugin without losing its configuration or
|
|
461
|
+
* data. Deliberately asymmetric with activate(): enabled:false is
|
|
462
|
+
* persisted FIRST and onDeactivate runs after (a hook error
|
|
463
|
+
* propagates, but the plugin is already off — you deactivate things
|
|
464
|
+
* BECAUSE they're broken, so disabling must never be blocked), and a
|
|
465
|
+
* missing manifest is fine (post-restart you can still switch a
|
|
466
|
+
* plugin off; the hook is skipped).
|
|
467
|
+
*/
|
|
468
|
+
async deactivate(pluginId) {
|
|
469
|
+
const row = await this.prisma.pluginRegistry.findUnique({ where: { pluginId } });
|
|
470
|
+
if (!row) {
|
|
471
|
+
throw new PluginNotInstalledError(pluginId);
|
|
472
|
+
}
|
|
473
|
+
if (!row.enabled) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
await this.prisma.pluginRegistry.update({
|
|
477
|
+
where: { pluginId },
|
|
478
|
+
data: { enabled: false },
|
|
479
|
+
});
|
|
480
|
+
this.enabledIds.delete(pluginId);
|
|
481
|
+
await this.manifests.get(pluginId)?.onDeactivate?.();
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Remove an installed plugin. For structured-storage plugins, the
|
|
485
|
+
* confirmation gate is checked FIRST — before onUninstall, before
|
|
486
|
+
* migration rollback, before anything else destructive (Month 8,
|
|
487
|
+
* Session 87): a snapshot is always taken, and if the caller hasn't
|
|
488
|
+
* passed confirmStructuredDataCleanup: true, uninstall() throws
|
|
489
|
+
* StructuredDataCleanupRequiresConfirmationError right there, having
|
|
490
|
+
* changed nothing else — the row, migrations, and manifest are all
|
|
491
|
+
* untouched, so retrying with confirmation is always safe. Only once
|
|
492
|
+
* past that gate does the rest of teardown proceed: fallible cleanup
|
|
493
|
+
* (onUninstall, migration rollback) runs BEFORE the irreversible row
|
|
494
|
+
* delete (Session 72) — a failure there leaves the plugin
|
|
495
|
+
* still-installed and retryable rather than orphaned. Order is
|
|
496
|
+
* stack-shaped, inverse of install(): onUninstall (the last thing
|
|
497
|
+
* installed) runs first, migrations (the first thing installed) roll
|
|
498
|
+
* back last, in LIFO order — reversing manifest.migrations' own
|
|
499
|
+
* declared array, the same source of truth install() walked forward
|
|
500
|
+
* through, rather than sorting the tracked rows by appliedAt. Two
|
|
501
|
+
* migrations applied back-to-back inside the same install() call can
|
|
502
|
+
* land the exact same DateTime (Postgres' now() has finite, and on
|
|
503
|
+
* some platforms coarse, resolution — reproduced on real CI: two
|
|
504
|
+
* inserts a few microseconds apart still tied, and ORDER BY gives no
|
|
505
|
+
* guarantee which tied row sorts first), which silently rolled
|
|
506
|
+
* migrations back out of order. The manifest array has no such
|
|
507
|
+
* ambiguity: array position is exact. If the manifest isn't attached
|
|
508
|
+
* (no server restart re-attached it), both are silently skipped —
|
|
509
|
+
* can't call functions that don't exist in this process — matching
|
|
510
|
+
* deactivate()'s "destructive operations must never be blocked by a
|
|
511
|
+
* missing manifest," applied more strongly here since uninstall is far
|
|
512
|
+
* less reversible than deactivate. The actual structured-data cleanup
|
|
513
|
+
* (dropping the plugin's tables) runs only after confirmation, right
|
|
514
|
+
* before the registry row is deleted.
|
|
515
|
+
*/
|
|
516
|
+
async uninstall(pluginId, options) {
|
|
517
|
+
const row = await this.prisma.pluginRegistry.findUnique({ where: { pluginId } });
|
|
518
|
+
if (!row) {
|
|
519
|
+
throw new PluginNotInstalledError(pluginId);
|
|
520
|
+
}
|
|
521
|
+
// Document-type deletion guard, checked FIRST — cheaper and
|
|
522
|
+
// unconditional (never confirmable, unlike structured-data cleanup
|
|
523
|
+
// below), so it fails fast before any other side effect. A blocked
|
|
524
|
+
// document type is not a "confirm to proceed" situation: the caller
|
|
525
|
+
// must resolve the underlying content/template references themselves.
|
|
526
|
+
// F1 QA fix wave: walks every tracked document type and aggregates
|
|
527
|
+
// ALL failures (not just the first) into one PluginDocumentTypesInUseError
|
|
528
|
+
// — an admin with three content-bearing document types used to learn
|
|
529
|
+
// about them one at a time, each retry surfacing the next. The name
|
|
530
|
+
// comes from adapter.getDocumentType() (an existing, already-
|
|
531
|
+
// implemented-for-all-three-dialects read — not a new adapter method)
|
|
532
|
+
// since the tracking row itself only has the id.
|
|
533
|
+
const trackedDocumentTypes = await this.prisma.pluginDocumentType.findMany({ where: { pluginId } });
|
|
534
|
+
const blockers = [];
|
|
535
|
+
for (const tracked of trackedDocumentTypes) {
|
|
536
|
+
try {
|
|
537
|
+
await this.documentTypeService.assertDocumentTypeDeletable(tracked.documentTypeId);
|
|
538
|
+
}
|
|
539
|
+
catch (error) {
|
|
540
|
+
if (!(error instanceof DocumentTypeInUseError))
|
|
541
|
+
throw error;
|
|
542
|
+
const documentType = await this.adapter.getDocumentType(tracked.documentTypeId);
|
|
543
|
+
blockers.push({
|
|
544
|
+
documentTypeId: tracked.documentTypeId,
|
|
545
|
+
documentType: documentType?.name ?? tracked.documentTypeId,
|
|
546
|
+
reason: error.reason,
|
|
547
|
+
contentCount: error.contentCount,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (blockers.length > 0) {
|
|
552
|
+
throw new PluginDocumentTypesInUseError(pluginId, blockers);
|
|
553
|
+
}
|
|
554
|
+
// Checked next, before onUninstall/migration rollback/anything else
|
|
555
|
+
// — a rejected call must be safe to retry, which means nothing else
|
|
556
|
+
// can have happened yet when this throws. simple-storage plugins never
|
|
557
|
+
// take this branch, so snapshotGroupId stays null for them (Task 6:
|
|
558
|
+
// the CONFIRMED pass's minted id is threaded through to the return
|
|
559
|
+
// below — the unconfirmed pass still throws with its id on the error,
|
|
560
|
+
// as before).
|
|
561
|
+
let snapshotGroupId = null;
|
|
562
|
+
if (row.storageType === "structured") {
|
|
563
|
+
snapshotGroupId = await this.structuredDataLifecycle.snapshot(pluginId);
|
|
564
|
+
if (!options?.confirmStructuredDataCleanup) {
|
|
565
|
+
throw new StructuredDataCleanupRequiresConfirmationError(pluginId, snapshotGroupId);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
const manifest = this.manifests.get(pluginId);
|
|
569
|
+
if (manifest) {
|
|
570
|
+
await manifest.onUninstall?.();
|
|
571
|
+
const records = await this.prisma.pluginMigrationRecord.findMany({ where: { pluginId } });
|
|
572
|
+
const trackedByMigrationId = new Map(records.map((record) => [record.migrationId, record]));
|
|
573
|
+
// Reversing manifest.migrations for rollback order (rather than
|
|
574
|
+
// trusting the tracked rows' appliedAt) assumes that array is
|
|
575
|
+
// APPEND-ONLY for an already-installed plugin: a plugin update may
|
|
576
|
+
// only add new entries after the existing ones, never reorder or
|
|
577
|
+
// remove entries an earlier install already applied. Violate that
|
|
578
|
+
// and this reversal silently rolls back in the wrong order --
|
|
579
|
+
// there is no other signal here to detect or correct for it.
|
|
580
|
+
for (const migration of [...(manifest.migrations ?? [])].reverse()) {
|
|
581
|
+
const record = trackedByMigrationId.get(migration.id);
|
|
582
|
+
if (!record)
|
|
583
|
+
continue;
|
|
584
|
+
await migration.down({});
|
|
585
|
+
await this.prisma.pluginMigrationRecord.delete({ where: { id: record.id } });
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (row.storageType === "structured") {
|
|
589
|
+
await this.structuredDataLifecycle.cleanup(pluginId);
|
|
590
|
+
}
|
|
591
|
+
for (const tracked of trackedDocumentTypes) {
|
|
592
|
+
await this.documentTypeService.deleteDocumentType(tracked.documentTypeId);
|
|
593
|
+
}
|
|
594
|
+
await this.prisma.pluginDocumentType.deleteMany({ where: { pluginId } });
|
|
595
|
+
await this.prisma.pluginRegistry.delete({ where: { pluginId } });
|
|
596
|
+
// Sweeps any tracking rows the unattached-manifest path above
|
|
597
|
+
// couldn't roll back individually (down() needs the manifest's
|
|
598
|
+
// function reference, which doesn't exist without it).
|
|
599
|
+
await this.prisma.pluginMigrationRecord.deleteMany({ where: { pluginId } });
|
|
600
|
+
this.manifests.delete(pluginId);
|
|
601
|
+
this.enabledIds.delete(pluginId);
|
|
602
|
+
return { snapshotGroupId };
|
|
603
|
+
}
|
|
604
|
+
/** Active plugins with an attached manifest declaring serializers —
|
|
605
|
+
* the public read path's per-request lookup (Session 112). Sync and
|
|
606
|
+
* in-memory: an active-but-unattached plugin (the standing S89
|
|
607
|
+
* no-boot-loader gap) is skipped exactly like its apiRoutes are. */
|
|
608
|
+
listActiveSerializerContributions() {
|
|
609
|
+
const out = [];
|
|
610
|
+
for (const [pluginId, manifest] of this.manifests) {
|
|
611
|
+
if (!this.enabledIds.has(pluginId))
|
|
612
|
+
continue;
|
|
613
|
+
const serializers = manifest.contributions.serializers;
|
|
614
|
+
if (serializers)
|
|
615
|
+
out.push({ pluginId, serializers });
|
|
616
|
+
}
|
|
617
|
+
return out;
|
|
618
|
+
}
|
|
619
|
+
/** Active plugins with an attached manifest declaring a handler for
|
|
620
|
+
* this specific event — the hook dispatcher's per-event lookup (S159).
|
|
621
|
+
* Sync and in-memory, same shape as listActiveSerializerContributions:
|
|
622
|
+
* an active-but-unattached plugin is skipped exactly like its apiRoutes
|
|
623
|
+
* and serializers are. */
|
|
624
|
+
listActiveHookContributions(event) {
|
|
625
|
+
const out = [];
|
|
626
|
+
for (const [pluginId, manifest] of this.manifests) {
|
|
627
|
+
if (!this.enabledIds.has(pluginId))
|
|
628
|
+
continue;
|
|
629
|
+
const hooks = manifest.contributions.hooks;
|
|
630
|
+
const handler = hooks?.[event];
|
|
631
|
+
if (handler)
|
|
632
|
+
out.push({ pluginId, handler });
|
|
633
|
+
}
|
|
634
|
+
return out;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PrismaClient } from "@prisma/client";
|
|
2
|
+
import type { DatabaseAdapter } from "@velora-cms/db-adapter";
|
|
3
|
+
export interface SeedTemplateContentDeps {
|
|
4
|
+
prisma: PrismaClient;
|
|
5
|
+
adapter: DatabaseAdapter;
|
|
6
|
+
}
|
|
7
|
+
export type SeedTemplateContentResult = "none" | "invalid" | "skipped" | {
|
|
8
|
+
created: number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Seeds a template plugin's sample-content.json as real, published
|
|
12
|
+
* content — creation order in the file IS the seed order (parents
|
|
13
|
+
* precede children per D1's documented contract), and every node is
|
|
14
|
+
* created THEN published, so it lands as version 1, currently published,
|
|
15
|
+
* matching the immutable-versioning path exactly the way a human editor
|
|
16
|
+
* publishing through the admin UI would produce.
|
|
17
|
+
*
|
|
18
|
+
* D2 governs every early return here: idempotent-by-skip (no duplicates
|
|
19
|
+
* on a second install/second call) and non-fatal (a seeding problem never
|
|
20
|
+
* unwinds an otherwise-successful plugin install — the caller's install
|
|
21
|
+
* stands regardless of what this function returns).
|
|
22
|
+
*/
|
|
23
|
+
export declare function seedTemplateSampleContent(deps: SeedTemplateContentDeps, pluginId: string, storedPackageDir: string, createdBy?: string): Promise<SeedTemplateContentResult>;
|