@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,286 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { deriveSnapshotGroups } from "./snapshot-groups.js";
|
|
3
|
+
const SNAPSHOT_PAGE_SIZE = 500;
|
|
4
|
+
// True only for a genuine "table does not exist" error from the active
|
|
5
|
+
// dialect's driver — checked unconditionally across all three shapes rather
|
|
6
|
+
// than switching on adapter.dialect, since the codes never collide:
|
|
7
|
+
// Postgres (node-postgres) sets SQLSTATE 42P01 (undefined_table) on `.code`;
|
|
8
|
+
// MySQL (mysql2) sets `.errno` 1146 / `.code` "ER_NO_SUCH_TABLE"; SQLite
|
|
9
|
+
// (better-sqlite3) has no error code for this and only a message matching
|
|
10
|
+
// /no such table/i. Anything else (a dropped connection, a real bug, a
|
|
11
|
+
// transient network blip) must NOT match here — snapshotPluginTables
|
|
12
|
+
// rethrows those, which aborts the uninstall before its cleanup step runs,
|
|
13
|
+
// so a live table's rows are never dropped without ever having been
|
|
14
|
+
// captured.
|
|
15
|
+
function isTableMissingError(error) {
|
|
16
|
+
if (!(error instanceof Error))
|
|
17
|
+
return false;
|
|
18
|
+
const err = error;
|
|
19
|
+
if (err.code === "42P01")
|
|
20
|
+
return true; // Postgres: undefined_table
|
|
21
|
+
if (err.errno === 1146 || err.code === "ER_NO_SUCH_TABLE")
|
|
22
|
+
return true; // MySQL
|
|
23
|
+
if (/no such table/i.test(err.message))
|
|
24
|
+
return true; // SQLite
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
// Not a reuse of packages/content-service's SnapshotService — that one is
|
|
28
|
+
// a version POINTER (content is immutable, nothing is ever really at
|
|
29
|
+
// risk). Structured-storage rows are genuinely mutable, so this has to be
|
|
30
|
+
// a real schema+data dump. Talks to DatabaseAdapter directly, never
|
|
31
|
+
// through the registry-gated StructuredStorageAPI — by the time any of
|
|
32
|
+
// this runs the plugin is either mid-uninstall or already uninstalled,
|
|
33
|
+
// so the registry gate would reject it anyway.
|
|
34
|
+
export class StructuredSnapshotService {
|
|
35
|
+
adapter;
|
|
36
|
+
prisma;
|
|
37
|
+
constructor(adapter, prisma) {
|
|
38
|
+
this.adapter = adapter;
|
|
39
|
+
this.prisma = prisma;
|
|
40
|
+
}
|
|
41
|
+
async snapshotPluginTables(pluginId, trigger) {
|
|
42
|
+
const tables = await this.prisma.pluginStructuredTable.findMany({ where: { pluginId } });
|
|
43
|
+
const snapshotGroupId = randomUUID();
|
|
44
|
+
// No transaction wraps this loop: if a later table rethrows (genuine
|
|
45
|
+
// failure, not a missing table), any PluginStructuredSnapshot rows
|
|
46
|
+
// already written for earlier tables under this snapshotGroupId are
|
|
47
|
+
// NOT rolled back. That's fine — the caller never sees a returned
|
|
48
|
+
// groupId for an aborted call (the throw propagates out before the
|
|
49
|
+
// `return` below), so nothing ever restores or drops against this
|
|
50
|
+
// groupId. The leftover partial rows are inert orphans, not a
|
|
51
|
+
// correctness or data-loss risk.
|
|
52
|
+
for (const table of tables) {
|
|
53
|
+
const schema = table.schema;
|
|
54
|
+
const pkColumn = schema.columns.find((c) => c.primaryKey).name;
|
|
55
|
+
const rows = [];
|
|
56
|
+
let cursor;
|
|
57
|
+
try {
|
|
58
|
+
// Cursor-walks to completion rather than one unbounded query — same
|
|
59
|
+
// pattern the Session 86 conformance tests already establish.
|
|
60
|
+
for (;;) {
|
|
61
|
+
const page = await this.adapter.queryRows(table.physicalTableName, schema, {
|
|
62
|
+
cursor,
|
|
63
|
+
limit: SNAPSHOT_PAGE_SIZE,
|
|
64
|
+
});
|
|
65
|
+
rows.push(...page);
|
|
66
|
+
if (page.length < SNAPSHOT_PAGE_SIZE)
|
|
67
|
+
break;
|
|
68
|
+
cursor = String(page[page.length - 1][pkColumn]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (!isTableMissingError(error)) {
|
|
73
|
+
// Anything other than a genuine "table does not exist" error —
|
|
74
|
+
// e.g. a transient connection failure on a table that's still
|
|
75
|
+
// very much live — must propagate. Swallowing it here would mean
|
|
76
|
+
// a table with rows never gets a snapshot entry, and the caller
|
|
77
|
+
// (uninstall) would then drop it via dropPluginTables believing
|
|
78
|
+
// it was safely captured: permanent data loss. Rethrowing aborts
|
|
79
|
+
// snapshotPluginTables (and therefore uninstall, before its
|
|
80
|
+
// cleanup step ever runs) instead — the safe outcome.
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
// A tracked table whose physical table is genuinely already gone
|
|
84
|
+
// means an earlier snapshot attempt (before a mid-loop drop
|
|
85
|
+
// failure) already captured its rows — see
|
|
86
|
+
// dropPluginTables/restoreSnapshot. Skip it rather than throw so an
|
|
87
|
+
// uninstall retry, which re-snapshots every tracked table, isn't
|
|
88
|
+
// wedged by a table a prior attempt already finished dropping.
|
|
89
|
+
// Scoped to this table's iteration only, so a missing table never
|
|
90
|
+
// skips the next one.
|
|
91
|
+
console.warn(`[structured-snapshot] table ${table.physicalTableName} for ${pluginId} no longer exists — skipped (rows preserved in the earlier snapshot that captured them): ${String(error instanceof Error ? error.message : error)}`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
await this.prisma.pluginStructuredSnapshot.create({
|
|
95
|
+
data: {
|
|
96
|
+
snapshotGroupId,
|
|
97
|
+
pluginId,
|
|
98
|
+
tableName: table.tableName,
|
|
99
|
+
physicalTableName: table.physicalTableName,
|
|
100
|
+
schema: table.schema,
|
|
101
|
+
rows: rows,
|
|
102
|
+
...(trigger ? { trigger } : {}),
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return snapshotGroupId;
|
|
107
|
+
}
|
|
108
|
+
async restoreSnapshot(snapshotGroupId) {
|
|
109
|
+
const snapshots = await this.prisma.pluginStructuredSnapshot.findMany({
|
|
110
|
+
where: { snapshotGroupId },
|
|
111
|
+
});
|
|
112
|
+
for (const snap of snapshots) {
|
|
113
|
+
const schema = snap.schema;
|
|
114
|
+
const rows = snap.rows;
|
|
115
|
+
await this.adapter.createTable({ ...schema, tableName: snap.physicalTableName });
|
|
116
|
+
for (const row of rows) {
|
|
117
|
+
await this.adapter.insertRow(snap.physicalTableName, schema, row);
|
|
118
|
+
}
|
|
119
|
+
await this.prisma.pluginStructuredTable.create({
|
|
120
|
+
data: {
|
|
121
|
+
pluginId: snap.pluginId,
|
|
122
|
+
tableName: snap.tableName,
|
|
123
|
+
physicalTableName: snap.physicalTableName,
|
|
124
|
+
schema: snap.schema,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async dropPluginTables(pluginId) {
|
|
130
|
+
const tables = await this.prisma.pluginStructuredTable.findMany({ where: { pluginId } });
|
|
131
|
+
for (const table of tables) {
|
|
132
|
+
await this.adapter.dropTable(table.physicalTableName);
|
|
133
|
+
}
|
|
134
|
+
await this.prisma.pluginStructuredTable.deleteMany({ where: { pluginId } });
|
|
135
|
+
}
|
|
136
|
+
// S154 Task 5: the UNION restore plan (mockup semantics) — the newest
|
|
137
|
+
// available copy of each DISTINCT table this plugin has ever
|
|
138
|
+
// snapshotted, picked independently per table across ALL of the
|
|
139
|
+
// plugin's snapshot groups. Reuses Task 4's deriveSnapshotGroups (newest
|
|
140
|
+
// groups first) rather than re-deriving "newest per table" from scratch:
|
|
141
|
+
// walking the already-sorted groups and taking the first (freshest)
|
|
142
|
+
// group that contains each table name is exactly the union-pick, and
|
|
143
|
+
// keeps this selection logic defined in exactly one place.
|
|
144
|
+
async planRestore(pluginId) {
|
|
145
|
+
const rows = await this.prisma.pluginStructuredSnapshot.findMany({
|
|
146
|
+
where: { pluginId },
|
|
147
|
+
select: { snapshotGroupId: true, tableName: true, trigger: true, createdAt: true },
|
|
148
|
+
});
|
|
149
|
+
if (rows.length === 0)
|
|
150
|
+
return [];
|
|
151
|
+
const groups = deriveSnapshotGroups(rows); // newest first
|
|
152
|
+
const pickedByTable = new Map();
|
|
153
|
+
for (const group of groups) {
|
|
154
|
+
// A "restore"-trigger group is the PRE-RESTORE SAFETY SNAPSHOT that
|
|
155
|
+
// restorePluginData takes of itself, not a genuine prior backup — it
|
|
156
|
+
// exists only to protect whatever was live the instant some earlier
|
|
157
|
+
// restore ran, which makes it (by construction) the newest group for
|
|
158
|
+
// every table it holds. It still lists normally on the groups
|
|
159
|
+
// surface (deriveSnapshotGroups/listSnapshotGroups are untouched)
|
|
160
|
+
// and still counts toward totalTableCount there — only the
|
|
161
|
+
// SELECTION below skips it. Leaving it eligible would make restore
|
|
162
|
+
// selection unstable: an immediate second confirmed restore would
|
|
163
|
+
// re-select it and "restore" the just-protected pre-restore state
|
|
164
|
+
// right back, silently REVERTING the first restore (a double-
|
|
165
|
+
// submit / stale-screen hazard), and a crash between the safety
|
|
166
|
+
// snapshot and the replace loop would make a crash-retry restore the
|
|
167
|
+
// half-replaced pre-restore state instead of completing the
|
|
168
|
+
// originally intended restore. Excluding restore-trigger groups here
|
|
169
|
+
// is what makes a double-confirmed restore idempotent (both calls
|
|
170
|
+
// re-select the same underlying source groups) and a crash-retry
|
|
171
|
+
// resume the original intent instead of undoing it.
|
|
172
|
+
if (group.trigger === "restore")
|
|
173
|
+
continue;
|
|
174
|
+
for (const tableName of group.tableNames) {
|
|
175
|
+
if (!pickedByTable.has(tableName)) {
|
|
176
|
+
pickedByTable.set(tableName, {
|
|
177
|
+
tableName,
|
|
178
|
+
snapshotGroupId: group.snapshotGroupId,
|
|
179
|
+
groupTakenAt: group.takenAt,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Deterministic order (by tableName) rather than Map insertion order,
|
|
185
|
+
// which would depend on which group happened to be walked first.
|
|
186
|
+
return [...pickedByTable.values()].sort((a, b) => a.tableName.localeCompare(b.tableName));
|
|
187
|
+
}
|
|
188
|
+
// S154 Task 5: replace-not-merge restore from the union plan above.
|
|
189
|
+
//
|
|
190
|
+
// The plan is computed FIRST, before the pre-restore safety snapshot.
|
|
191
|
+
// planRestore()'s own exclusion of trigger === "restore" groups (see its
|
|
192
|
+
// comment) is what actually makes a just-taken safety snapshot
|
|
193
|
+
// ineligible as a restore source — but computing the plan here before
|
|
194
|
+
// that snapshot is taken is a cheap second line of defense against the
|
|
195
|
+
// same failure mode, so a bug in that exclusion alone couldn't make this
|
|
196
|
+
// call's own safety snapshot self-select.
|
|
197
|
+
//
|
|
198
|
+
// The safety snapshot itself still runs before any destructive step
|
|
199
|
+
// (database.md's hard rule + the mockup's promised safety line): a
|
|
200
|
+
// crash between the snapshot and the per-table drop/create loop below
|
|
201
|
+
// leaves that pre-restore group fully intact — but the actual recovery
|
|
202
|
+
// is retrying THIS call (restorePluginData(pluginId)) again, not
|
|
203
|
+
// restoreSnapshot(groupId) on the safety group. restoreSnapshot() does a
|
|
204
|
+
// bare createTable for every snapshotted table with no drop first, so
|
|
205
|
+
// calling it here would fail outright: the plugin's live tables are
|
|
206
|
+
// still standing (the crash was before the drop/create loop ran), and
|
|
207
|
+
// createTable against an already-existing table errors. Retrying
|
|
208
|
+
// restorePluginData is safe specifically because of the eligibility fix
|
|
209
|
+
// above — planRestore's exclusion of trigger === "restore" groups means
|
|
210
|
+
// the retry recomputes the same original plan instead of self-selecting
|
|
211
|
+
// the safety snapshot it just took, so it resumes the intended restore
|
|
212
|
+
// rather than restoring the pre-restore state back onto itself.
|
|
213
|
+
async restorePluginData(pluginId) {
|
|
214
|
+
const plan = await this.planRestore(pluginId);
|
|
215
|
+
const trackedTables = await this.prisma.pluginStructuredTable.findMany({ where: { pluginId } });
|
|
216
|
+
let preRestoreSnapshotGroupId = null;
|
|
217
|
+
if (trackedTables.length > 0) {
|
|
218
|
+
const safetyGroupId = await this.snapshotPluginTables(pluginId, "restore");
|
|
219
|
+
// snapshotPluginTables ALWAYS returns a groupId, even when every one
|
|
220
|
+
// of the plugin's tracked tables turned out to have its physical
|
|
221
|
+
// table already gone (the isTableMissingError skip path in
|
|
222
|
+
// snapshotPluginTables) and zero PluginStructuredSnapshot rows
|
|
223
|
+
// actually got written under it. A groupId with nothing behind it
|
|
224
|
+
// protects nothing, and — being backed by zero rows — never appears
|
|
225
|
+
// on the groups listing either; reporting it to the caller as "your
|
|
226
|
+
// data was protected" would be a lie, so it collapses back to null
|
|
227
|
+
// here exactly like the "nothing was tracked at all" case above.
|
|
228
|
+
const capturedAnything = await this.prisma.pluginStructuredSnapshot.findFirst({
|
|
229
|
+
where: { snapshotGroupId: safetyGroupId },
|
|
230
|
+
select: { id: true },
|
|
231
|
+
});
|
|
232
|
+
preRestoreSnapshotGroupId = capturedAnything ? safetyGroupId : null;
|
|
233
|
+
}
|
|
234
|
+
const restoredTables = [];
|
|
235
|
+
for (const entry of plan) {
|
|
236
|
+
const snap = await this.prisma.pluginStructuredSnapshot.findFirst({
|
|
237
|
+
where: { snapshotGroupId: entry.snapshotGroupId, tableName: entry.tableName },
|
|
238
|
+
});
|
|
239
|
+
// Defensive only: the plan was derived from these exact rows a
|
|
240
|
+
// moment ago and nothing in between can delete a snapshot row, so
|
|
241
|
+
// this should never actually miss.
|
|
242
|
+
if (!snap)
|
|
243
|
+
continue;
|
|
244
|
+
const schema = snap.schema;
|
|
245
|
+
const rows = snap.rows;
|
|
246
|
+
// Idempotent drop THEN delete-the-tracking-row THEN recreate is
|
|
247
|
+
// what makes restoring onto a REINSTALLED plugin (live table +
|
|
248
|
+
// live tracking row already present, the deferred S87 collision)
|
|
249
|
+
// and restoring onto empty (neither present) the same safe path:
|
|
250
|
+
// a bare createTable would fail "table already exists" in the
|
|
251
|
+
// collision case, and the tracking-row create would hit a P2002
|
|
252
|
+
// unique violation on (pluginId, tableName) — dropping and
|
|
253
|
+
// deleting first means createTable and the tracking-row create
|
|
254
|
+
// always run against a clean slate either way.
|
|
255
|
+
await this.adapter.dropTable(snap.physicalTableName);
|
|
256
|
+
await this.prisma.pluginStructuredTable.deleteMany({
|
|
257
|
+
where: { pluginId, tableName: entry.tableName },
|
|
258
|
+
});
|
|
259
|
+
await this.adapter.createTable({ ...schema, tableName: snap.physicalTableName });
|
|
260
|
+
// The tracking row is created immediately after the table — BEFORE
|
|
261
|
+
// the insertRow loop below, not after. If a row partway through that
|
|
262
|
+
// loop fails (a bad value, a transient DB error), creating the
|
|
263
|
+
// tracking row first still leaves this table VISIBLE, as a
|
|
264
|
+
// (partially-filled) tracked table, to dropPluginTables /
|
|
265
|
+
// snapshotPluginTables / StructuredStorageAPI — a retried restore's
|
|
266
|
+
// replace path (drop -> delete-tracking -> recreate, same as this
|
|
267
|
+
// one) can then clean it up and finish. Creating the tracking row
|
|
268
|
+
// last would instead leave a live physical table with rows in it
|
|
269
|
+
// that no tracking row points to: invisible to every one of those
|
|
270
|
+
// callers, a silent leak no retry could ever find.
|
|
271
|
+
await this.prisma.pluginStructuredTable.create({
|
|
272
|
+
data: {
|
|
273
|
+
pluginId,
|
|
274
|
+
tableName: entry.tableName,
|
|
275
|
+
physicalTableName: snap.physicalTableName,
|
|
276
|
+
schema: snap.schema,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
for (const row of rows) {
|
|
280
|
+
await this.adapter.insertRow(snap.physicalTableName, schema, row);
|
|
281
|
+
}
|
|
282
|
+
restoredTables.push(entry.tableName);
|
|
283
|
+
}
|
|
284
|
+
return { preRestoreSnapshotGroupId, restoredTables };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PrismaClient } from "@prisma/client";
|
|
2
|
+
import { type DatabaseAdapter, type PluginTableSchema } from "@velora-cms/db-adapter";
|
|
3
|
+
import type { PluginRegistryService } from "./plugin-registry-service.js";
|
|
4
|
+
import { StructuredQueryBuilder } from "./structured-query-builder.js";
|
|
5
|
+
import { PluginTableNotDefinedError, StructuredStorageTransaction } from "./structured-storage-transaction.js";
|
|
6
|
+
export declare class PluginTableAlreadyDefinedError extends Error {
|
|
7
|
+
constructor(pluginId: string, tableName: string);
|
|
8
|
+
}
|
|
9
|
+
export { PluginTableNotDefinedError };
|
|
10
|
+
export declare class StructuredStorageAPI {
|
|
11
|
+
private readonly adapter;
|
|
12
|
+
private readonly registry;
|
|
13
|
+
private readonly prisma;
|
|
14
|
+
constructor(adapter: DatabaseAdapter, registry: PluginRegistryService, prisma: PrismaClient);
|
|
15
|
+
defineTable(pluginId: string, schema: PluginTableSchema): Promise<void>;
|
|
16
|
+
private resolveTable;
|
|
17
|
+
insert(pluginId: string, tableName: string, data: Record<string, unknown>): Promise<void>;
|
|
18
|
+
update(pluginId: string, tableName: string, where: Record<string, unknown>, data: Record<string, unknown>): Promise<number>;
|
|
19
|
+
delete(pluginId: string, tableName: string, where: Record<string, unknown>): Promise<number>;
|
|
20
|
+
query<T = Record<string, unknown>>(pluginId: string, tableName: string): StructuredQueryBuilder<T>;
|
|
21
|
+
transaction(pluginId: string, fn: (tx: StructuredStorageTransaction) => Promise<void>): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { validatePluginTableSchema } from "@velora-cms/db-adapter";
|
|
2
|
+
import { StructuredQueryBuilder } from "./structured-query-builder.js";
|
|
3
|
+
import { PluginTableNotDefinedError, resolvePluginTable, StructuredStorageTransaction, } from "./structured-storage-transaction.js";
|
|
4
|
+
import { physicalTableName } from "./structured-table-naming.js";
|
|
5
|
+
export class PluginTableAlreadyDefinedError extends Error {
|
|
6
|
+
constructor(pluginId, tableName) {
|
|
7
|
+
super(`Plugin "${pluginId}" has already defined a structured table named "${tableName}"`);
|
|
8
|
+
this.name = "PluginTableAlreadyDefinedError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export { PluginTableNotDefinedError };
|
|
12
|
+
// StructuredStorageAPI (Month 8). Mirrors SimpleStorageAPI's shape:
|
|
13
|
+
// every method opens with registry.requireActivePlugin(pluginId), all
|
|
14
|
+
// real persistence delegates to DatabaseAdapter, zero SQL of its own.
|
|
15
|
+
export class StructuredStorageAPI {
|
|
16
|
+
adapter;
|
|
17
|
+
registry;
|
|
18
|
+
prisma;
|
|
19
|
+
constructor(adapter, registry, prisma) {
|
|
20
|
+
this.adapter = adapter;
|
|
21
|
+
this.registry = registry;
|
|
22
|
+
this.prisma = prisma;
|
|
23
|
+
}
|
|
24
|
+
async defineTable(pluginId, schema) {
|
|
25
|
+
await this.registry.requireActivePlugin(pluginId);
|
|
26
|
+
validatePluginTableSchema(schema);
|
|
27
|
+
const existing = await this.prisma.pluginStructuredTable.findUnique({
|
|
28
|
+
where: { pluginId_tableName: { pluginId, tableName: schema.tableName } },
|
|
29
|
+
});
|
|
30
|
+
if (existing) {
|
|
31
|
+
throw new PluginTableAlreadyDefinedError(pluginId, schema.tableName);
|
|
32
|
+
}
|
|
33
|
+
const physical = physicalTableName(pluginId, schema.tableName);
|
|
34
|
+
await this.adapter.createTable({ ...schema, tableName: physical });
|
|
35
|
+
try {
|
|
36
|
+
await this.prisma.pluginStructuredTable.create({
|
|
37
|
+
data: {
|
|
38
|
+
pluginId,
|
|
39
|
+
tableName: schema.tableName,
|
|
40
|
+
physicalTableName: physical,
|
|
41
|
+
// PluginTableSchema's optional fields (primaryKey?, notNull?,
|
|
42
|
+
// etc.) type as `T | undefined`, which Prisma's Json input
|
|
43
|
+
// type structurally rejects (only `T | null` is JSON-safe) —
|
|
44
|
+
// the value itself is genuinely JSON-serializable, so this is
|
|
45
|
+
// a type-system escape hatch, not a runtime concern.
|
|
46
|
+
schema: schema,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (cause) {
|
|
51
|
+
// Never leave an untracked physical table behind — Session 87's
|
|
52
|
+
// snapshot-before-uninstall can only see what's in
|
|
53
|
+
// PluginStructuredTable, so an orphaned table here would become a
|
|
54
|
+
// silent data-loss blind spot later.
|
|
55
|
+
await this.adapter.dropTable(physical).catch(() => { });
|
|
56
|
+
throw cause;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async resolveTable(pluginId, tableName) {
|
|
60
|
+
return resolvePluginTable(this.prisma, pluginId, tableName);
|
|
61
|
+
}
|
|
62
|
+
async insert(pluginId, tableName, data) {
|
|
63
|
+
await this.registry.requireActivePlugin(pluginId);
|
|
64
|
+
const { physicalTableName, schema } = await this.resolveTable(pluginId, tableName);
|
|
65
|
+
await this.adapter.insertRow(physicalTableName, schema, data);
|
|
66
|
+
}
|
|
67
|
+
async update(pluginId, tableName, where, data) {
|
|
68
|
+
await this.registry.requireActivePlugin(pluginId);
|
|
69
|
+
const { physicalTableName, schema } = await this.resolveTable(pluginId, tableName);
|
|
70
|
+
return this.adapter.updateRows(physicalTableName, schema, where, data);
|
|
71
|
+
}
|
|
72
|
+
async delete(pluginId, tableName, where) {
|
|
73
|
+
await this.registry.requireActivePlugin(pluginId);
|
|
74
|
+
const { physicalTableName, schema } = await this.resolveTable(pluginId, tableName);
|
|
75
|
+
return this.adapter.deleteRows(physicalTableName, schema, where);
|
|
76
|
+
}
|
|
77
|
+
query(pluginId, tableName) {
|
|
78
|
+
return new StructuredQueryBuilder(async (options) => {
|
|
79
|
+
await this.registry.requireActivePlugin(pluginId);
|
|
80
|
+
const { physicalTableName, schema } = await this.resolveTable(pluginId, tableName);
|
|
81
|
+
return this.adapter.queryRows(physicalTableName, schema, options);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async transaction(pluginId, fn) {
|
|
85
|
+
await this.registry.requireActivePlugin(pluginId);
|
|
86
|
+
await this.adapter.transaction(async (tx) => {
|
|
87
|
+
const handle = new StructuredStorageTransaction(tx, this.adapter.dialect, this.prisma, pluginId);
|
|
88
|
+
await fn(handle);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PrismaClient } from "@prisma/client";
|
|
2
|
+
import { type DatabaseDialect, type PluginTableSchema, type TransactionContext } from "@velora-cms/db-adapter";
|
|
3
|
+
import { StructuredQueryBuilder } from "./structured-query-builder.js";
|
|
4
|
+
export declare class PluginTableNotDefinedError extends Error {
|
|
5
|
+
constructor(pluginId: string, tableName: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function resolvePluginTable(prisma: PrismaClient, pluginId: string, tableName: string): Promise<{
|
|
8
|
+
physicalTableName: string;
|
|
9
|
+
schema: PluginTableSchema;
|
|
10
|
+
}>;
|
|
11
|
+
export declare class StructuredStorageTransaction {
|
|
12
|
+
private readonly tx;
|
|
13
|
+
private readonly dialect;
|
|
14
|
+
private readonly prisma;
|
|
15
|
+
private readonly pluginId;
|
|
16
|
+
constructor(tx: TransactionContext, dialect: DatabaseDialect, prisma: PrismaClient, pluginId: string);
|
|
17
|
+
private resolveTable;
|
|
18
|
+
insert(tableName: string, data: Record<string, unknown>): Promise<void>;
|
|
19
|
+
update(tableName: string, where: Record<string, unknown>, data: Record<string, unknown>): Promise<number>;
|
|
20
|
+
delete(tableName: string, where: Record<string, unknown>): Promise<number>;
|
|
21
|
+
query<T = Record<string, unknown>>(tableName: string): StructuredQueryBuilder<T>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { buildDeleteStatement, buildInsertStatement, buildSelectStatement, buildUpdateStatement, deserializeRow, } from "@velora-cms/db-adapter";
|
|
2
|
+
import { StructuredQueryBuilder } from "./structured-query-builder.js";
|
|
3
|
+
export class PluginTableNotDefinedError extends Error {
|
|
4
|
+
constructor(pluginId, tableName) {
|
|
5
|
+
super(`Plugin "${pluginId}" has not defined a structured table named "${tableName}"`);
|
|
6
|
+
this.name = "PluginTableNotDefinedError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
// Shared by StructuredStorageAPI and StructuredStorageTransaction — both
|
|
10
|
+
// need to resolve a plugin's declared table name to its physical table
|
|
11
|
+
// name and recorded schema before touching the adapter. Kept here (rather
|
|
12
|
+
// than duplicated in both files) so the two paths can't drift.
|
|
13
|
+
export async function resolvePluginTable(prisma, pluginId, tableName) {
|
|
14
|
+
const row = await prisma.pluginStructuredTable.findUnique({
|
|
15
|
+
where: { pluginId_tableName: { pluginId, tableName } },
|
|
16
|
+
});
|
|
17
|
+
if (!row) {
|
|
18
|
+
throw new PluginTableNotDefinedError(pluginId, tableName);
|
|
19
|
+
}
|
|
20
|
+
return { physicalTableName: row.physicalTableName, schema: row.schema };
|
|
21
|
+
}
|
|
22
|
+
// Plugin-safe transaction handle passed into StructuredStorageAPI's
|
|
23
|
+
// transaction(pluginId, fn) callback. Deliberately does NOT expose the
|
|
24
|
+
// raw TransactionContext (no plugin ever writes SQL) —
|
|
25
|
+
// only insert/update/query, scoped to the plugin fixed for this
|
|
26
|
+
// transaction's lifetime, reusing the exact same statement builders the
|
|
27
|
+
// top-level (non-transactional) StructuredStorageAPI path uses.
|
|
28
|
+
export class StructuredStorageTransaction {
|
|
29
|
+
tx;
|
|
30
|
+
dialect;
|
|
31
|
+
prisma;
|
|
32
|
+
pluginId;
|
|
33
|
+
constructor(tx, dialect, prisma, pluginId) {
|
|
34
|
+
this.tx = tx;
|
|
35
|
+
this.dialect = dialect;
|
|
36
|
+
this.prisma = prisma;
|
|
37
|
+
this.pluginId = pluginId;
|
|
38
|
+
}
|
|
39
|
+
async resolveTable(tableName) {
|
|
40
|
+
return resolvePluginTable(this.prisma, this.pluginId, tableName);
|
|
41
|
+
}
|
|
42
|
+
async insert(tableName, data) {
|
|
43
|
+
const { physicalTableName, schema } = await this.resolveTable(tableName);
|
|
44
|
+
await this.tx.execute(buildInsertStatement(this.dialect, physicalTableName, schema, data));
|
|
45
|
+
}
|
|
46
|
+
async update(tableName, where, data) {
|
|
47
|
+
const { physicalTableName, schema } = await this.resolveTable(tableName);
|
|
48
|
+
const result = await this.tx.execute(buildUpdateStatement(this.dialect, physicalTableName, schema, where, data));
|
|
49
|
+
return result.rowCount;
|
|
50
|
+
}
|
|
51
|
+
async delete(tableName, where) {
|
|
52
|
+
const { physicalTableName, schema } = await this.resolveTable(tableName);
|
|
53
|
+
const result = await this.tx.execute(buildDeleteStatement(this.dialect, physicalTableName, schema, where));
|
|
54
|
+
return result.rowCount;
|
|
55
|
+
}
|
|
56
|
+
query(tableName) {
|
|
57
|
+
return new StructuredQueryBuilder(async (options) => {
|
|
58
|
+
const { physicalTableName, schema } = await this.resolveTable(tableName);
|
|
59
|
+
const rows = await this.tx.query(buildSelectStatement(this.dialect, physicalTableName, schema, options));
|
|
60
|
+
return rows.map((row) => deserializeRow(schema, row));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function physicalTableName(pluginId: string, tableName: string, maxLength?: number): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
// Plugin ids are reverse-domain strings (e.g. "com.velora.commerce") — not
|
|
3
|
+
// safe as bare SQL identifiers — and unlike SimpleStorageAPI's shared
|
|
4
|
+
// content_field_values table, structured storage has no shared table with
|
|
5
|
+
// a plugin_id column to filter by: each plugin gets its own physical
|
|
6
|
+
// table, so isolation has to happen at the naming level instead.
|
|
7
|
+
//
|
|
8
|
+
// Single-path, deterministic scheme (a hash prefix always present, never
|
|
9
|
+
// a "short name passthrough vs. long name hash fallback" branch) — one
|
|
10
|
+
// invariant to prove, not two, since Session 87's snapshot logic depends
|
|
11
|
+
// on this staying stable forever.
|
|
12
|
+
export function physicalTableName(pluginId, tableName, maxLength = 63) {
|
|
13
|
+
const sanitize = (value) => value.replace(/[^a-z0-9]+/gi, "_").toLowerCase();
|
|
14
|
+
const digest = createHash("sha256").update(`${pluginId}:${tableName}`).digest("hex").slice(0, 10);
|
|
15
|
+
const fixed = `p_${digest}_`;
|
|
16
|
+
const human = `${sanitize(pluginId)}_${sanitize(tableName)}`.slice(0, maxLength - fixed.length);
|
|
17
|
+
return `${fixed}${human}`;
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TrustedKey } from "@velora-cms/plugin-package";
|
|
2
|
+
/**
|
|
3
|
+
* The trusted key set for this process: what shipped with the CMS, plus
|
|
4
|
+
* anything VELORA_TRUSTED_SIGNING_KEYS supplies (later entries win on a
|
|
5
|
+
* keyId collision, so an operator can override a shipped key).
|
|
6
|
+
*
|
|
7
|
+
* An empty result is a legitimate, non-error state meaning "trust nobody".
|
|
8
|
+
* A malformed override warns and is ignored — a config typo must never
|
|
9
|
+
* stop the server booting.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadTrustedKeys(): TrustedKey[];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { deriveKeyId } from "@velora-cms/plugin-package";
|
|
2
|
+
import { SHIPPED_TRUSTED_KEYS } from "./trusted-signing-keys.js";
|
|
3
|
+
/**
|
|
4
|
+
* keyId is otherwise an unauthenticated label: nothing stops a {keyId,
|
|
5
|
+
* publicKey} pair whose keyId does not actually belong to that publicKey
|
|
6
|
+
* from entering the trusted set, at which point describeVerifyResult could
|
|
7
|
+
* log "verified against trusted key <keyId>" for a signature made by a
|
|
8
|
+
* completely different key. Re-deriving and comparing here is what makes
|
|
9
|
+
* D2's "cannot drift" claim true rather than aspirational — applied to
|
|
10
|
+
* BOTH the shipped set and any override, since a future entry in either
|
|
11
|
+
* place could get this wrong. deriveKeyId THROWS on a wrong-length key
|
|
12
|
+
* (envelope.ts's rawPublicKey), so it must be guarded, not assumed safe.
|
|
13
|
+
*/
|
|
14
|
+
function keyIdMatchesPublicKey(entry, source) {
|
|
15
|
+
let derived;
|
|
16
|
+
try {
|
|
17
|
+
derived = deriveKeyId(entry.publicKey);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.warn(`[plugin-verify] ${source} entry with keyId "${entry.keyId}" has a malformed publicKey — ignoring it: ${error instanceof Error ? error.message : String(error)}`);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (derived !== entry.keyId) {
|
|
24
|
+
console.warn(`[plugin-verify] ${source} entry declares keyId "${entry.keyId}" but its publicKey derives to keyId "${derived}" — ignoring it`);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
function parseOverride(raw) {
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
console.warn("[plugin-verify] VELORA_TRUSTED_SIGNING_KEYS is not valid JSON — ignoring it");
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
const keys = parsed?.keys;
|
|
39
|
+
if (!Array.isArray(keys)) {
|
|
40
|
+
console.warn('[plugin-verify] VELORA_TRUSTED_SIGNING_KEYS must be {"keys":[…]} — ignoring it');
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
return keys
|
|
44
|
+
.filter((entry) => {
|
|
45
|
+
const candidate = entry;
|
|
46
|
+
return typeof candidate?.keyId === "string" && typeof candidate?.publicKey === "string";
|
|
47
|
+
})
|
|
48
|
+
.filter((entry) => keyIdMatchesPublicKey(entry, "VELORA_TRUSTED_SIGNING_KEYS"));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The trusted key set for this process: what shipped with the CMS, plus
|
|
52
|
+
* anything VELORA_TRUSTED_SIGNING_KEYS supplies (later entries win on a
|
|
53
|
+
* keyId collision, so an operator can override a shipped key).
|
|
54
|
+
*
|
|
55
|
+
* An empty result is a legitimate, non-error state meaning "trust nobody".
|
|
56
|
+
* A malformed override warns and is ignored — a config typo must never
|
|
57
|
+
* stop the server booting.
|
|
58
|
+
*/
|
|
59
|
+
export function loadTrustedKeys() {
|
|
60
|
+
const raw = process.env.VELORA_TRUSTED_SIGNING_KEYS;
|
|
61
|
+
const shipped = SHIPPED_TRUSTED_KEYS.filter((entry) => keyIdMatchesPublicKey(entry, "SHIPPED_TRUSTED_KEYS"));
|
|
62
|
+
const merged = new Map();
|
|
63
|
+
for (const key of [...shipped, ...(raw ? parseOverride(raw) : [])]) {
|
|
64
|
+
merged.set(key.keyId, key);
|
|
65
|
+
}
|
|
66
|
+
return [...merged.values()];
|
|
67
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TrustedKey } from "@velora-cms/plugin-package";
|
|
2
|
+
/**
|
|
3
|
+
* The signing keys this CMS build trusts, shipped with the CMS.
|
|
4
|
+
*
|
|
5
|
+
* SHIPPED AS OF SESSION 165: the production marketplace signing key,
|
|
6
|
+
* ROTATED in Session 180 — the active key's private half is held solely
|
|
7
|
+
* by the operator outside this repository; the production marketplace's
|
|
8
|
+
* environment receives it. It is never written to this repo, the dev
|
|
9
|
+
* `.env`, or any database — the marketplace's signing_keys table stores
|
|
10
|
+
* only public halves. The pre-rotation key was retired in Session 182 —
|
|
11
|
+
* see the active entry's own comment below.
|
|
12
|
+
*
|
|
13
|
+
* Consequence, stated plainly: a stock install now verifies any plugin
|
|
14
|
+
* signed by the real marketplace with zero configuration — the
|
|
15
|
+
* VELORA_TRUSTED_SIGNING_KEYS era of hand-fed keys is over for the
|
|
16
|
+
* default case. An operator can still add or override keys with that
|
|
17
|
+
* env var (e.g. a private/self-hosted marketplace signing under its own
|
|
18
|
+
* key).
|
|
19
|
+
*
|
|
20
|
+
* Rotation is add-new + re-sign current artifacts + retire old — never
|
|
21
|
+
* delete a key that signed an artifact any CMS may still need to verify
|
|
22
|
+
* (the one-version-one-artifact rule, Session 127).
|
|
23
|
+
*
|
|
24
|
+
* A SET rather than a single key from day one: the Trust model leaves the
|
|
25
|
+
* door open for v1.x enterprise org-signing.
|
|
26
|
+
*/
|
|
27
|
+
export declare const SHIPPED_TRUSTED_KEYS: TrustedKey[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The signing keys this CMS build trusts, shipped with the CMS.
|
|
3
|
+
*
|
|
4
|
+
* SHIPPED AS OF SESSION 165: the production marketplace signing key,
|
|
5
|
+
* ROTATED in Session 180 — the active key's private half is held solely
|
|
6
|
+
* by the operator outside this repository; the production marketplace's
|
|
7
|
+
* environment receives it. It is never written to this repo, the dev
|
|
8
|
+
* `.env`, or any database — the marketplace's signing_keys table stores
|
|
9
|
+
* only public halves. The pre-rotation key was retired in Session 182 —
|
|
10
|
+
* see the active entry's own comment below.
|
|
11
|
+
*
|
|
12
|
+
* Consequence, stated plainly: a stock install now verifies any plugin
|
|
13
|
+
* signed by the real marketplace with zero configuration — the
|
|
14
|
+
* VELORA_TRUSTED_SIGNING_KEYS era of hand-fed keys is over for the
|
|
15
|
+
* default case. An operator can still add or override keys with that
|
|
16
|
+
* env var (e.g. a private/self-hosted marketplace signing under its own
|
|
17
|
+
* key).
|
|
18
|
+
*
|
|
19
|
+
* Rotation is add-new + re-sign current artifacts + retire old — never
|
|
20
|
+
* delete a key that signed an artifact any CMS may still need to verify
|
|
21
|
+
* (the one-version-one-artifact rule, Session 127).
|
|
22
|
+
*
|
|
23
|
+
* A SET rather than a single key from day one: the Trust model leaves the
|
|
24
|
+
* door open for v1.x enterprise org-signing.
|
|
25
|
+
*/
|
|
26
|
+
export const SHIPPED_TRUSTED_KEYS = [
|
|
27
|
+
{
|
|
28
|
+
// Active production marketplace signing key, since the Session 180
|
|
29
|
+
// rotation (2026-08-30). Its private half is held solely by the
|
|
30
|
+
// operator outside this repository; the production marketplace's
|
|
31
|
+
// environment receives it. Rotation is add-new + re-sign current
|
|
32
|
+
// artifacts + retire old — never delete a key that signed an artifact
|
|
33
|
+
// any CMS may still need to verify (the one-version-one-artifact
|
|
34
|
+
// rule, Session 127).
|
|
35
|
+
// Pre-rotation key 91d5529a9f997602 removed in S182 — every listed
|
|
36
|
+
// marketplace version was re-signed with this key in S180.
|
|
37
|
+
keyId: "73e409f633dbbea3",
|
|
38
|
+
publicKey: "MCowBQYDK2VwAyEAcTQOhyCUsvGCWooiC4cp1idRWzXE3W8acCU/uKzBOro=",
|
|
39
|
+
},
|
|
40
|
+
];
|