@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,198 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { ContentService } from "@velora-cms/content-service";
|
|
5
|
+
// Session 149, Task 2 — D1: sample content is a PACKAGE DATA FILE
|
|
6
|
+
// (sample-content.json, next to manifest.json at the artifact root —
|
|
7
|
+
// packages/velora-plugin/src/copy-sample-content.ts puts it there at
|
|
8
|
+
// build time), read from the STORED package dir (plain readFileSync —
|
|
9
|
+
// the file is never tar-extracted separately) and seeded HOST-SIDE,
|
|
10
|
+
// AFTER PluginRegistryService.install() has already returned. The S88
|
|
11
|
+
// documentTypes install path (plugin-registry-service.ts) stays
|
|
12
|
+
// untouched by this module — it is invoked by the two install
|
|
13
|
+
// orchestrators (install-built-package.ts, install-from-marketplace.ts)
|
|
14
|
+
// instead, never from inside install() itself.
|
|
15
|
+
// Deliberately local to this module, not packages/api-schemas — this is
|
|
16
|
+
// PACKAGE DATA (an artifact-internal file format), not an API surface.
|
|
17
|
+
const SampleContentNodeSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
documentType: z.string().min(1),
|
|
20
|
+
slug: z.string().min(1),
|
|
21
|
+
parent: z.string().nullable(),
|
|
22
|
+
data: z.record(z.string(), z.unknown()),
|
|
23
|
+
})
|
|
24
|
+
.strict();
|
|
25
|
+
const SampleContentFileSchema = z
|
|
26
|
+
.object({
|
|
27
|
+
nodes: z.array(SampleContentNodeSchema),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
/**
|
|
31
|
+
* Seeds a template plugin's sample-content.json as real, published
|
|
32
|
+
* content — creation order in the file IS the seed order (parents
|
|
33
|
+
* precede children per D1's documented contract), and every node is
|
|
34
|
+
* created THEN published, so it lands as version 1, currently published,
|
|
35
|
+
* matching the immutable-versioning path exactly the way a human editor
|
|
36
|
+
* publishing through the admin UI would produce.
|
|
37
|
+
*
|
|
38
|
+
* D2 governs every early return here: idempotent-by-skip (no duplicates
|
|
39
|
+
* on a second install/second call) and non-fatal (a seeding problem never
|
|
40
|
+
* unwinds an otherwise-successful plugin install — the caller's install
|
|
41
|
+
* stands regardless of what this function returns).
|
|
42
|
+
*/
|
|
43
|
+
export async function seedTemplateSampleContent(deps, pluginId, storedPackageDir, createdBy) {
|
|
44
|
+
const filePath = join(storedPackageDir, "sample-content.json");
|
|
45
|
+
if (!existsSync(filePath)) {
|
|
46
|
+
return "none"; // Most plugins never ship this file — not worth a log line.
|
|
47
|
+
}
|
|
48
|
+
// Guard: only template-type manifests get seeded, even if a
|
|
49
|
+
// sample-content.json somehow sits next to a differently-typed
|
|
50
|
+
// package's manifest.json. Missing row is defensive only — the caller
|
|
51
|
+
// always invokes this after a successful install, so the row exists in
|
|
52
|
+
// practice.
|
|
53
|
+
const registryRow = await deps.prisma.pluginRegistry.findUnique({ where: { pluginId } });
|
|
54
|
+
if (!registryRow || registryRow.type !== "template") {
|
|
55
|
+
return "none";
|
|
56
|
+
}
|
|
57
|
+
let sampleContent;
|
|
58
|
+
try {
|
|
59
|
+
const raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
60
|
+
const result = SampleContentFileSchema.safeParse(raw);
|
|
61
|
+
if (!result.success) {
|
|
62
|
+
console.error(`[seed-template-content] "${pluginId}"'s sample-content.json failed validation: ${result.error.message}`);
|
|
63
|
+
return "invalid";
|
|
64
|
+
}
|
|
65
|
+
sampleContent = result.data;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error(`[seed-template-content] "${pluginId}"'s sample-content.json is not valid JSON:`, error);
|
|
69
|
+
return "invalid";
|
|
70
|
+
}
|
|
71
|
+
// Name -> minted id, via THIS plugin's own tracked document types —
|
|
72
|
+
// never a bare name lookup across every document type in the system,
|
|
73
|
+
// which could silently match an unrelated plugin's same-named type.
|
|
74
|
+
const tracked = await deps.prisma.pluginDocumentType.findMany({ where: { pluginId } });
|
|
75
|
+
const idByTypeName = new Map();
|
|
76
|
+
for (const row of tracked) {
|
|
77
|
+
const documentType = await deps.adapter.getDocumentType(row.documentTypeId);
|
|
78
|
+
if (documentType) {
|
|
79
|
+
idByTypeName.set(documentType.name, documentType.id);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const unknownTypeNames = [...new Set(sampleContent.nodes.map((node) => node.documentType))].filter((name) => !idByTypeName.has(name));
|
|
83
|
+
if (unknownTypeNames.length > 0) {
|
|
84
|
+
console.error(`[seed-template-content] "${pluginId}"'s sample-content.json references document type name(s) not declared ` +
|
|
85
|
+
`by this plugin: ${unknownTypeNames.join(", ")}`);
|
|
86
|
+
return "invalid";
|
|
87
|
+
}
|
|
88
|
+
// Structural validation, BEFORE any create: creation order in the file
|
|
89
|
+
// IS the seed order (D1), so a node's parent slug must already have been
|
|
90
|
+
// seen — i.e. declared strictly earlier in the array. Without this check,
|
|
91
|
+
// the create loop below resolves an unresolved parent slug via
|
|
92
|
+
// `nodeIdBySlug.get(parent) ?? null`, which silently roots the node
|
|
93
|
+
// instead of failing (catching an unknown parent only by luck, if that
|
|
94
|
+
// document type happens to reject root placement) — and a duplicate slug
|
|
95
|
+
// silently mis-parents later nodes, since every lookup map below is
|
|
96
|
+
// slug-keyed globally. Both are caught here, in one pass, before the
|
|
97
|
+
// loop that actually creates anything runs.
|
|
98
|
+
const seenSlugs = new Set();
|
|
99
|
+
for (const node of sampleContent.nodes) {
|
|
100
|
+
if (seenSlugs.has(node.slug)) {
|
|
101
|
+
console.error(`[seed-template-content] "${pluginId}"'s sample-content.json declares slug "${node.slug}" more than once`);
|
|
102
|
+
return "invalid";
|
|
103
|
+
}
|
|
104
|
+
if (node.parent !== null && !seenSlugs.has(node.parent)) {
|
|
105
|
+
console.error(`[seed-template-content] "${pluginId}"'s sample-content.json node "${node.slug}" declares parent "${node.parent}", ` +
|
|
106
|
+
`which does not name a slug declared earlier in the file`);
|
|
107
|
+
return "invalid";
|
|
108
|
+
}
|
|
109
|
+
seenSlugs.add(node.slug);
|
|
110
|
+
}
|
|
111
|
+
// Skip-if-exists: any ROOT node already at its declared path means a
|
|
112
|
+
// previous install/seed run already created this tree — re-seeding
|
|
113
|
+
// would create duplicates, so the whole step is skipped, not merged or
|
|
114
|
+
// partially retried.
|
|
115
|
+
for (const node of sampleContent.nodes) {
|
|
116
|
+
if (node.parent === null) {
|
|
117
|
+
const path = `/${node.slug}`;
|
|
118
|
+
if (await deps.adapter.getContentNodeByPath(path)) {
|
|
119
|
+
console.log(`[seed-template-content] "${pluginId}" sample content already exists at "${path}" — skipping`);
|
|
120
|
+
return "skipped";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// D2: the requesting admin's id where the caller has one; otherwise the
|
|
125
|
+
// first admin user (the seed-sandbox precedent — a REAL uuid; a
|
|
126
|
+
// placeholder string writes fine here but 500s every later GET of this
|
|
127
|
+
// content, per the S102-era gotcha). No admin at all -> skip, warn, and
|
|
128
|
+
// change nothing, exactly like the already-exists case above — there is
|
|
129
|
+
// no "system" user to attribute sample content to.
|
|
130
|
+
let resolvedCreatedBy = createdBy;
|
|
131
|
+
if (!resolvedCreatedBy) {
|
|
132
|
+
const firstAdmin = await deps.prisma.user.findFirst({
|
|
133
|
+
where: { role: "admin" },
|
|
134
|
+
orderBy: { createdAt: "asc" },
|
|
135
|
+
});
|
|
136
|
+
if (!firstAdmin) {
|
|
137
|
+
console.warn(`[seed-template-content] "${pluginId}" — no admin user exists to attribute sample content to; skipping seeding`);
|
|
138
|
+
return "skipped";
|
|
139
|
+
}
|
|
140
|
+
resolvedCreatedBy = firstAdmin.id;
|
|
141
|
+
}
|
|
142
|
+
// Deliberately un-wired: this ContentService is `new`-ed straight from
|
|
143
|
+
// the adapter, not read off app.ts's contentDb — so it never gets
|
|
144
|
+
// setOnContentEvent(createHookDispatcher(...)) called on it (S159). No
|
|
145
|
+
// content:afterPublish hook fires for the publishContent() calls below;
|
|
146
|
+
// template seeding is a one-shot data burst, not an editorial action a
|
|
147
|
+
// plugin should react to. Every path that reaches this function
|
|
148
|
+
// (install-from-marketplace.ts directly, install-built-package.ts —
|
|
149
|
+
// which the wizard's starter step also goes through) constructs its
|
|
150
|
+
// own instance the same un-wired way, so none of them fire hooks during
|
|
151
|
+
// seeding either. Contrast: a later EDITORIAL publish — a human
|
|
152
|
+
// republishing one of these seeded nodes through /api/content — goes
|
|
153
|
+
// through the app's own wired contentDb.contentService and DOES fire
|
|
154
|
+
// hooks.
|
|
155
|
+
const contentService = new ContentService(deps.adapter);
|
|
156
|
+
const nodeIdBySlug = new Map();
|
|
157
|
+
const pathBySlug = new Map();
|
|
158
|
+
const depthBySlug = new Map();
|
|
159
|
+
const nextSortOrderByParentSlug = new Map();
|
|
160
|
+
let created = 0;
|
|
161
|
+
try {
|
|
162
|
+
for (const node of sampleContent.nodes) {
|
|
163
|
+
const documentTypeId = idByTypeName.get(node.documentType);
|
|
164
|
+
const parentId = node.parent === null ? null : (nodeIdBySlug.get(node.parent) ?? null);
|
|
165
|
+
const parentPath = node.parent === null ? undefined : pathBySlug.get(node.parent);
|
|
166
|
+
const path = parentPath ? `${parentPath}/${node.slug}` : `/${node.slug}`;
|
|
167
|
+
const depth = node.parent === null ? 0 : (depthBySlug.get(node.parent) ?? 0) + 1;
|
|
168
|
+
const sortOrder = nextSortOrderByParentSlug.get(node.parent) ?? 0;
|
|
169
|
+
nextSortOrderByParentSlug.set(node.parent, sortOrder + 1);
|
|
170
|
+
const { node: createdNode } = await contentService.createContent({
|
|
171
|
+
documentTypeId,
|
|
172
|
+
parentId,
|
|
173
|
+
path,
|
|
174
|
+
depth,
|
|
175
|
+
sortOrder,
|
|
176
|
+
locale: "en",
|
|
177
|
+
isDefaultLocale: true,
|
|
178
|
+
data: node.data,
|
|
179
|
+
createdBy: resolvedCreatedBy,
|
|
180
|
+
});
|
|
181
|
+
await contentService.publishContent(createdNode.id);
|
|
182
|
+
nodeIdBySlug.set(node.slug, createdNode.id);
|
|
183
|
+
pathBySlug.set(node.slug, path);
|
|
184
|
+
depthBySlug.set(node.slug, depth);
|
|
185
|
+
created++;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
// D2: non-fatal — the install already stands by the time this runs,
|
|
190
|
+
// and the never-lose-data ethos cuts the other way here: don't tear
|
|
191
|
+
// down a healthy install over sample data. Whatever nodes were
|
|
192
|
+
// created before the failure are left in place (createContent +
|
|
193
|
+
// publishContent are each already immutable/atomic per node).
|
|
194
|
+
console.error(`[seed-template-content] seeding sample content for "${pluginId}" failed after creating ${created} node(s):`, error);
|
|
195
|
+
return "invalid";
|
|
196
|
+
}
|
|
197
|
+
return { created };
|
|
198
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// The server process's one datatype-serializer lookup (Session 111).
|
|
2
|
+
// Seeded with the built-ins from the React-free datatypes subpath; a
|
|
3
|
+
// remote/third-party plugin's serializer is unreachable server-side
|
|
4
|
+
// (Module Federation remotes only load in the browser — Session 69's
|
|
5
|
+
// scoping), so lookups for unknown editor ids return null and the
|
|
6
|
+
// pipeline passes the stored value through. registerServerSerializer
|
|
7
|
+
// exists for server-side registration (integration tests today; Session
|
|
8
|
+
// 112's serializer contributions are its intended real consumer).
|
|
9
|
+
import { BUILT_IN_SERIALIZERS } from "@velora-cms/plugin-sdk/datatypes";
|
|
10
|
+
const registry = new Map(Object.entries(BUILT_IN_SERIALIZERS));
|
|
11
|
+
export function getServerSerializer(editorId) {
|
|
12
|
+
return registry.get(editorId) ?? null;
|
|
13
|
+
}
|
|
14
|
+
export function registerServerSerializer(editorId, serializer) {
|
|
15
|
+
registry.set(editorId, serializer);
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from "@velora-cms/db-adapter";
|
|
2
|
+
import type { PluginRegistryService } from "./plugin-registry-service.js";
|
|
3
|
+
export declare class SimpleStorageAPI {
|
|
4
|
+
private readonly adapter;
|
|
5
|
+
private readonly registry;
|
|
6
|
+
constructor(adapter: DatabaseAdapter, registry: PluginRegistryService);
|
|
7
|
+
get(contentId: string, fieldId: string, pluginId: string): Promise<unknown>;
|
|
8
|
+
set(contentId: string, fieldId: string, pluginId: string, value: unknown): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// SimpleStorageAPI: the JSON key-value storage tier plugins default to
|
|
2
|
+
// (get/set by contentId+fieldId), as opposed to the opt-in, reviewed
|
|
3
|
+
// StructuredStorageAPI tier for relational needs.
|
|
4
|
+
// A thin pass-through: gate on the plugin being installed+active (the
|
|
5
|
+
// registry state a disabled plugin has no business touching storage
|
|
6
|
+
// through), then delegate to the adapter. All persistence logic lives in
|
|
7
|
+
// DatabaseAdapter.get/setPluginFieldValue — this class exists only to
|
|
8
|
+
// make the registry check impossible to forget at a call site, matching
|
|
9
|
+
// the "either tier goes through [this], never raw SQL" rule
|
|
10
|
+
// (plugin-sdk.md); business logic here never touches SQL directly.
|
|
11
|
+
export class SimpleStorageAPI {
|
|
12
|
+
adapter;
|
|
13
|
+
registry;
|
|
14
|
+
constructor(adapter, registry) {
|
|
15
|
+
this.adapter = adapter;
|
|
16
|
+
this.registry = registry;
|
|
17
|
+
}
|
|
18
|
+
async get(contentId, fieldId, pluginId) {
|
|
19
|
+
await this.registry.requireActivePlugin(pluginId);
|
|
20
|
+
return this.adapter.getPluginFieldValue(contentId, fieldId, pluginId);
|
|
21
|
+
}
|
|
22
|
+
async set(contentId, fieldId, pluginId, value) {
|
|
23
|
+
await this.registry.requireActivePlugin(pluginId);
|
|
24
|
+
await this.adapter.setPluginFieldValue(contentId, fieldId, pluginId, value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PrismaClient } from "@prisma/client";
|
|
2
|
+
import type { SnapshotGroup } from "@velora-cms/api-schemas";
|
|
3
|
+
export interface SnapshotRowFacts {
|
|
4
|
+
snapshotGroupId: string;
|
|
5
|
+
tableName: string;
|
|
6
|
+
trigger: string | null;
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
}
|
|
9
|
+
export declare function deriveSnapshotGroups(rows: SnapshotRowFacts[]): SnapshotGroup[];
|
|
10
|
+
export declare function listSnapshotGroups(prisma: PrismaClient, pluginId: string, page: {
|
|
11
|
+
cursor?: string;
|
|
12
|
+
limit: number;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
items: SnapshotGroup[];
|
|
15
|
+
nextCursor: string | null;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// S154 Task 4: snapshot GROUPS are derived, not tracked — there is no
|
|
2
|
+
// SnapshotGroup table. A group is every PluginStructuredSnapshot row
|
|
3
|
+
// sharing one snapshotGroupId (one snapshotPluginTables() call writes one
|
|
4
|
+
// row per tracked table, all under the same group id and trigger).
|
|
5
|
+
//
|
|
6
|
+
// The "complete vs partial" status is the whole point of Task 4: it
|
|
7
|
+
// compares a group's own tableNames against the UNION of distinct
|
|
8
|
+
// tableNames across ALL of the plugin's snapshot rows (every group, not
|
|
9
|
+
// just this one) — never against the plugin's CURRENT tracked-table count.
|
|
10
|
+
// PluginStructuredTable (the live tracking table) gets wiped by
|
|
11
|
+
// dropPluginTables() at uninstall, which is exactly when a group listing
|
|
12
|
+
// matters most, so it can't be the denominator. The union of everything
|
|
13
|
+
// this plugin ever snapshotted is the only count that survives that wipe
|
|
14
|
+
// and stays honest across a failed-then-retried uninstall (S87's wedge):
|
|
15
|
+
// an earlier attempt that captured all N tables reads "complete", a later
|
|
16
|
+
// retry that could only re-capture the tables still physically present
|
|
17
|
+
// (Task 3's tolerant skip-a-missing-table behavior) reads "partial" —
|
|
18
|
+
// never a false "complete" claim for a group that's missing rows another
|
|
19
|
+
// group in the same plugin's history actually has.
|
|
20
|
+
export function deriveSnapshotGroups(rows) {
|
|
21
|
+
const totalTableCount = new Set(rows.map((row) => row.tableName)).size;
|
|
22
|
+
const rowsByGroup = new Map();
|
|
23
|
+
for (const row of rows) {
|
|
24
|
+
const bucket = rowsByGroup.get(row.snapshotGroupId);
|
|
25
|
+
if (bucket) {
|
|
26
|
+
bucket.push(row);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
rowsByGroup.set(row.snapshotGroupId, [row]);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const groups = [];
|
|
33
|
+
for (const [snapshotGroupId, groupRows] of rowsByGroup) {
|
|
34
|
+
const tableNames = [...new Set(groupRows.map((row) => row.tableName))].sort();
|
|
35
|
+
const takenAtMs = Math.min(...groupRows.map((row) => row.createdAt.getTime()));
|
|
36
|
+
groups.push({
|
|
37
|
+
snapshotGroupId,
|
|
38
|
+
takenAt: new Date(takenAtMs).toISOString(),
|
|
39
|
+
// Every row written by one snapshotPluginTables() call carries the
|
|
40
|
+
// same trigger — reading it off the first row in the bucket is safe,
|
|
41
|
+
// not an arbitrary pick among differing values.
|
|
42
|
+
trigger: groupRows[0].trigger,
|
|
43
|
+
tableNames,
|
|
44
|
+
totalTableCount,
|
|
45
|
+
status: tableNames.length === totalTableCount ? "complete" : "partial",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// Newest first.
|
|
49
|
+
groups.sort((a, b) => new Date(b.takenAt).getTime() - new Date(a.takenAt).getTime());
|
|
50
|
+
return groups;
|
|
51
|
+
}
|
|
52
|
+
// The DB read + in-memory pagination over derived groups. Deliberately ONE
|
|
53
|
+
// indexed findMany (PluginStructuredSnapshot.pluginId is indexed) rather
|
|
54
|
+
// than a `GROUP BY snapshotGroupId` query plus a second query per page:
|
|
55
|
+
// the per-plugin group count is bounded by how many times an admin has
|
|
56
|
+
// attempted to uninstall that plugin, not by content or row volume, so
|
|
57
|
+
// pulling every row for one plugin and deriving+paginating groups in
|
|
58
|
+
// memory is honest — this can never become an unbounded scan the way
|
|
59
|
+
// loading a whole content subtree would be. The wire contract still
|
|
60
|
+
// exposes cursor+limit (not just "return everything") so the route stays
|
|
61
|
+
// pagination-shaped like every other list endpoint in this codebase, and
|
|
62
|
+
// so a future plugin with a genuinely large uninstall-retry history
|
|
63
|
+
// doesn't require a client-visible contract change to fix.
|
|
64
|
+
export async function listSnapshotGroups(prisma, pluginId, page) {
|
|
65
|
+
const rows = await prisma.pluginStructuredSnapshot.findMany({
|
|
66
|
+
where: { pluginId },
|
|
67
|
+
select: { snapshotGroupId: true, tableName: true, trigger: true, createdAt: true },
|
|
68
|
+
});
|
|
69
|
+
const allGroups = deriveSnapshotGroups(rows);
|
|
70
|
+
let startIndex = 0;
|
|
71
|
+
if (page.cursor) {
|
|
72
|
+
const cursorIndex = allGroups.findIndex((group) => group.snapshotGroupId === page.cursor);
|
|
73
|
+
// A cursor that doesn't match any derived group (stale link, or the
|
|
74
|
+
// underlying rows are gone) walks to the end rather than silently
|
|
75
|
+
// restarting the page from the top.
|
|
76
|
+
startIndex = cursorIndex === -1 ? allGroups.length : cursorIndex + 1;
|
|
77
|
+
}
|
|
78
|
+
const items = allGroups.slice(startIndex, startIndex + page.limit);
|
|
79
|
+
const nextCursor = startIndex + items.length < allGroups.length ? (items[items.length - 1]?.snapshotGroupId ?? null) : null;
|
|
80
|
+
return { items, nextCursor };
|
|
81
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { StructuredQueryOptions } from "@velora-cms/db-adapter";
|
|
2
|
+
export declare class InvalidStructuredQueryError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class StructuredQueryBuilder<T = Record<string, unknown>> implements PromiseLike<T[]> {
|
|
6
|
+
private readonly runQuery;
|
|
7
|
+
private whereConditions;
|
|
8
|
+
private orderBySpec?;
|
|
9
|
+
private limitValue?;
|
|
10
|
+
private paginateOptions?;
|
|
11
|
+
constructor(runQuery: (options: StructuredQueryOptions) => Promise<Record<string, unknown>[]>);
|
|
12
|
+
where(conditions: Record<string, unknown>): this;
|
|
13
|
+
orderBy(column: string, direction?: "asc" | "desc"): this;
|
|
14
|
+
limit(n: number): this;
|
|
15
|
+
paginate(options: {
|
|
16
|
+
cursor?: string;
|
|
17
|
+
limit: number;
|
|
18
|
+
}): this;
|
|
19
|
+
then<TResult1 = T[], TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
20
|
+
private exec;
|
|
21
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export class InvalidStructuredQueryError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "InvalidStructuredQueryError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
// Plugin-facing fluent query builder for StructuredStorageAPI.query().
|
|
8
|
+
// Deliberately dialect-unaware and DB-unaware — it just accumulates
|
|
9
|
+
// where/orderBy/limit/paginate state and, on await, hands a single
|
|
10
|
+
// StructuredQueryOptions object to whatever `runQuery` function its
|
|
11
|
+
// caller supplied. This is what lets the exact same builder be used
|
|
12
|
+
// both by the top-level StructuredStorageAPI (Task 8) and by
|
|
13
|
+
// StructuredStorageTransaction (Task 7) — only `runQuery`'s
|
|
14
|
+
// implementation differs between the two.
|
|
15
|
+
export class StructuredQueryBuilder {
|
|
16
|
+
runQuery;
|
|
17
|
+
whereConditions = {};
|
|
18
|
+
orderBySpec;
|
|
19
|
+
limitValue;
|
|
20
|
+
paginateOptions;
|
|
21
|
+
constructor(runQuery) {
|
|
22
|
+
this.runQuery = runQuery;
|
|
23
|
+
}
|
|
24
|
+
where(conditions) {
|
|
25
|
+
this.whereConditions = { ...this.whereConditions, ...conditions };
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
orderBy(column, direction = "asc") {
|
|
29
|
+
this.orderBySpec = { column, direction };
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
limit(n) {
|
|
33
|
+
this.limitValue = n;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
paginate(options) {
|
|
37
|
+
this.paginateOptions = options;
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
then(onfulfilled, onrejected) {
|
|
41
|
+
return this.exec().then(onfulfilled, onrejected);
|
|
42
|
+
}
|
|
43
|
+
async exec() {
|
|
44
|
+
if (this.orderBySpec && this.paginateOptions) {
|
|
45
|
+
throw new InvalidStructuredQueryError("orderBy() and paginate() cannot be used together");
|
|
46
|
+
}
|
|
47
|
+
if (this.limitValue === undefined && this.paginateOptions === undefined) {
|
|
48
|
+
throw new InvalidStructuredQueryError("call limit(n) or paginate({ cursor, limit }) before awaiting a query");
|
|
49
|
+
}
|
|
50
|
+
const options = this.paginateOptions
|
|
51
|
+
? {
|
|
52
|
+
where: this.whereConditions,
|
|
53
|
+
cursor: this.paginateOptions.cursor,
|
|
54
|
+
limit: this.paginateOptions.limit,
|
|
55
|
+
}
|
|
56
|
+
: { where: this.whereConditions, orderBy: this.orderBySpec, limit: this.limitValue };
|
|
57
|
+
const rows = await this.runQuery(options);
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PrismaClient } from "@prisma/client";
|
|
2
|
+
import type { DatabaseAdapter } from "@velora-cms/db-adapter";
|
|
3
|
+
import type { RestorePlanEntry } from "@velora-cms/api-schemas";
|
|
4
|
+
export declare class StructuredSnapshotService {
|
|
5
|
+
private readonly adapter;
|
|
6
|
+
private readonly prisma;
|
|
7
|
+
constructor(adapter: DatabaseAdapter, prisma: PrismaClient);
|
|
8
|
+
snapshotPluginTables(pluginId: string, trigger?: string): Promise<string>;
|
|
9
|
+
restoreSnapshot(snapshotGroupId: string): Promise<void>;
|
|
10
|
+
dropPluginTables(pluginId: string): Promise<void>;
|
|
11
|
+
planRestore(pluginId: string): Promise<RestorePlanEntry[]>;
|
|
12
|
+
restorePluginData(pluginId: string): Promise<{
|
|
13
|
+
preRestoreSnapshotGroupId: string | null;
|
|
14
|
+
restoredTables: string[];
|
|
15
|
+
}>;
|
|
16
|
+
}
|