@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,59 @@
|
|
|
1
|
+
export function createFieldCountRule(max, GraphQLErrorCtor) {
|
|
2
|
+
return function FieldCountRule(context) {
|
|
3
|
+
return {
|
|
4
|
+
Document: {
|
|
5
|
+
leave(document) {
|
|
6
|
+
const fragments = new Map();
|
|
7
|
+
for (const definition of document.definitions) {
|
|
8
|
+
if (definition.kind === "FragmentDefinition") {
|
|
9
|
+
fragments.set(definition.name.value, definition);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
let count = 0;
|
|
13
|
+
const activeSpreads = new Set();
|
|
14
|
+
function countSelectionSet(selectionSet) {
|
|
15
|
+
if (!selectionSet)
|
|
16
|
+
return;
|
|
17
|
+
for (const selection of selectionSet.selections) {
|
|
18
|
+
switch (selection.kind) {
|
|
19
|
+
case "Field": {
|
|
20
|
+
count++;
|
|
21
|
+
countSelectionSet(selection.selectionSet);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
case "InlineFragment": {
|
|
25
|
+
countSelectionSet(selection.selectionSet);
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
case "FragmentSpread": {
|
|
29
|
+
const name = selection.name.value;
|
|
30
|
+
// Guard (b): cycle — stop recursing here, don't loop
|
|
31
|
+
// forever. NoFragmentCyclesRule reports the cycle.
|
|
32
|
+
if (activeSpreads.has(name))
|
|
33
|
+
break;
|
|
34
|
+
const fragment = fragments.get(name);
|
|
35
|
+
// Guard (a): unknown fragment — count 0.
|
|
36
|
+
// KnownFragmentNamesRule reports it separately.
|
|
37
|
+
if (!fragment)
|
|
38
|
+
break;
|
|
39
|
+
activeSpreads.add(name);
|
|
40
|
+
countSelectionSet(fragment.selectionSet);
|
|
41
|
+
activeSpreads.delete(name);
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const definition of document.definitions) {
|
|
48
|
+
if (definition.kind === "OperationDefinition") {
|
|
49
|
+
countSelectionSet(definition.selectionSet);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (count > max) {
|
|
53
|
+
context.reportError(new GraphQLErrorCtor(`Query field count of ${count} exceeds the maximum allowed complexity of ${max}`));
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { GraphQLSchema } from "graphql";
|
|
2
|
+
import type { ContentService } from "@velora-cms/content-service";
|
|
3
|
+
import { type ContentNode } from "@velora-cms/api-schemas";
|
|
4
|
+
import type { MediaService } from "../media/media-service.js";
|
|
5
|
+
import { type PublishedItemDeps } from "../routes/published-item.js";
|
|
6
|
+
export interface GraphqlContext {
|
|
7
|
+
deps: PublishedItemDeps;
|
|
8
|
+
contentService: ContentService;
|
|
9
|
+
mediaService: MediaService;
|
|
10
|
+
getContentNode: (id: string) => Promise<ContentNode | null>;
|
|
11
|
+
}
|
|
12
|
+
export declare function buildGraphqlSchema(): GraphQLSchema;
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import SchemaBuilder from "@pothos/core";
|
|
2
|
+
import { JSONResolver } from "graphql-scalars";
|
|
3
|
+
import { PublicContentByPathQuerySchema, PublicContentItemQuerySchema, PublicContentListQuerySchema, PublicContentSearchQuerySchema, PublicDocumentTypeListQuerySchema, PublicMediaListQuerySchema, PublicTemplateListQuerySchema, } from "@velora-cms/api-schemas";
|
|
4
|
+
import { verifyPreviewToken } from "../auth/preview-tokens.js";
|
|
5
|
+
import { computeExtensions, preparePublishedItem, resolveItemNode, resolveSubtreeFilter, toPublicDocumentType, toPublicMediaFile, toSummaries, } from "../routes/published-item.js";
|
|
6
|
+
// Re-parses a GraphQL args object through the exact REST querystring Zod
|
|
7
|
+
// schema for the sibling endpoint. `undefined` fields fall through to the
|
|
8
|
+
// schema's own `.default()`s (same as an omitted REST querystring key);
|
|
9
|
+
// an out-of-bounds/malformed value fails validation exactly like REST's
|
|
10
|
+
// Fastify schema compiler does — the caller gets a GraphQL error, not a
|
|
11
|
+
// silent clamp.
|
|
12
|
+
function parseArgs(schema, value, label) {
|
|
13
|
+
const result = schema.safeParse(value);
|
|
14
|
+
if (!result.success) {
|
|
15
|
+
throw new Error(`Invalid ${label}: ${result.error?.message}`);
|
|
16
|
+
}
|
|
17
|
+
return result.data;
|
|
18
|
+
}
|
|
19
|
+
const builder = new SchemaBuilder({});
|
|
20
|
+
builder.addScalarType("JSON", JSONResolver, {});
|
|
21
|
+
const PublicContentItem = builder.objectRef("PublicContentItem").implement({
|
|
22
|
+
fields: (t) => ({
|
|
23
|
+
id: t.exposeID("id"),
|
|
24
|
+
documentTypeId: t.exposeID("documentTypeId"),
|
|
25
|
+
locale: t.exposeString("locale"),
|
|
26
|
+
publishedAt: t.exposeString("publishedAt", { nullable: true }),
|
|
27
|
+
version: t.exposeInt("version"),
|
|
28
|
+
data: t.expose("data", { type: "JSON" }),
|
|
29
|
+
extensions: t.expose("extensions", { type: "JSON", nullable: true }),
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
const PublicContentNodeSummaryRef = builder
|
|
33
|
+
.objectRef("PublicContentNodeSummary")
|
|
34
|
+
.implement({
|
|
35
|
+
fields: (t) => ({
|
|
36
|
+
id: t.exposeID("id"),
|
|
37
|
+
parentId: t.exposeID("parentId", { nullable: true }),
|
|
38
|
+
path: t.exposeString("path"),
|
|
39
|
+
depth: t.exposeInt("depth"),
|
|
40
|
+
sortOrder: t.exposeInt("sortOrder"),
|
|
41
|
+
documentTypeId: t.exposeID("documentTypeId"),
|
|
42
|
+
locale: t.exposeString("locale"),
|
|
43
|
+
publishedAt: t.exposeString("publishedAt"),
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
const PublicFieldValidationRef = builder
|
|
47
|
+
.objectRef("PublicFieldValidation")
|
|
48
|
+
.implement({
|
|
49
|
+
fields: (t) => ({
|
|
50
|
+
required: t.exposeBoolean("required", { nullable: true }),
|
|
51
|
+
minLength: t.exposeInt("minLength", { nullable: true }),
|
|
52
|
+
maxLength: t.exposeInt("maxLength", { nullable: true }),
|
|
53
|
+
min: t.exposeFloat("min", { nullable: true }),
|
|
54
|
+
max: t.exposeFloat("max", { nullable: true }),
|
|
55
|
+
pattern: t.exposeString("pattern", { nullable: true }),
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
const PublicFieldDefinitionRef = builder.objectRef("PublicFieldDefinition").implement({
|
|
59
|
+
fields: (t) => ({
|
|
60
|
+
id: t.exposeString("id"),
|
|
61
|
+
name: t.exposeString("name"),
|
|
62
|
+
datatypeId: t.exposeString("datatypeId"),
|
|
63
|
+
validation: t.expose("validation", { type: PublicFieldValidationRef, nullable: true }),
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
const PublicDocumentTypeRef = builder.objectRef("PublicDocumentType").implement({
|
|
67
|
+
fields: (t) => ({
|
|
68
|
+
id: t.exposeID("id"),
|
|
69
|
+
name: t.exposeString("name"),
|
|
70
|
+
icon: t.exposeString("icon"),
|
|
71
|
+
kind: t.exposeString("kind"),
|
|
72
|
+
fields: t.expose("fields", { type: [PublicFieldDefinitionRef] }),
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
const PublicTemplateRef = builder.objectRef("PublicTemplate").implement({
|
|
76
|
+
fields: (t) => ({
|
|
77
|
+
id: t.exposeID("id"),
|
|
78
|
+
name: t.exposeString("name"),
|
|
79
|
+
documentTypeId: t.exposeID("documentTypeId"),
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
const PublicMediaFileRef = builder.objectRef("PublicMediaFile").implement({
|
|
83
|
+
fields: (t) => ({
|
|
84
|
+
id: t.exposeID("id"),
|
|
85
|
+
filename: t.exposeString("filename"),
|
|
86
|
+
mimeType: t.exposeString("mimeType"),
|
|
87
|
+
size: t.exposeInt("size"),
|
|
88
|
+
downloadUrl: t.exposeString("downloadUrl"),
|
|
89
|
+
uploadedAt: t.exposeString("uploadedAt"),
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
const ContentNodeConnectionRef = builder
|
|
93
|
+
.objectRef("ContentNodeConnection")
|
|
94
|
+
.implement({
|
|
95
|
+
fields: (t) => ({
|
|
96
|
+
items: t.expose("items", { type: [PublicContentNodeSummaryRef] }),
|
|
97
|
+
nextCursor: t.exposeID("nextCursor", { nullable: true }),
|
|
98
|
+
}),
|
|
99
|
+
});
|
|
100
|
+
const DocumentTypeConnectionRef = builder
|
|
101
|
+
.objectRef("DocumentTypeConnection")
|
|
102
|
+
.implement({
|
|
103
|
+
fields: (t) => ({
|
|
104
|
+
items: t.expose("items", { type: [PublicDocumentTypeRef] }),
|
|
105
|
+
nextCursor: t.exposeID("nextCursor", { nullable: true }),
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
const TemplateConnectionRef = builder
|
|
109
|
+
.objectRef("TemplateConnection")
|
|
110
|
+
.implement({
|
|
111
|
+
fields: (t) => ({
|
|
112
|
+
items: t.expose("items", { type: [PublicTemplateRef] }),
|
|
113
|
+
nextCursor: t.exposeID("nextCursor", { nullable: true }),
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
const MediaConnectionRef = builder
|
|
117
|
+
.objectRef("MediaConnection")
|
|
118
|
+
.implement({
|
|
119
|
+
fields: (t) => ({
|
|
120
|
+
items: t.expose("items", { type: [PublicMediaFileRef] }),
|
|
121
|
+
nextCursor: t.exposeID("nextCursor", { nullable: true }),
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
builder.queryType({
|
|
125
|
+
fields: (t) => ({
|
|
126
|
+
// GET /api/v1/content/:id analog. Uniform null for unknown id, draft,
|
|
127
|
+
// and (once locale/fallback are set) an unresolvable/unpublished
|
|
128
|
+
// translation — same "no oracle" contract as REST's 404.
|
|
129
|
+
content: t.field({
|
|
130
|
+
type: PublicContentItem,
|
|
131
|
+
nullable: true,
|
|
132
|
+
args: {
|
|
133
|
+
id: t.arg.id({ required: true }),
|
|
134
|
+
locale: t.arg.string(),
|
|
135
|
+
fallback: t.arg.string(),
|
|
136
|
+
// Session 116: GraphQL analog of REST's ?previewToken= — same
|
|
137
|
+
// core (resolveItemNode + preparePublishedItem), verified once,
|
|
138
|
+
// matched against the FINAL resolved node. Deliberately not
|
|
139
|
+
// added to contentByPath or any list/connection query this
|
|
140
|
+
// session.
|
|
141
|
+
previewToken: t.arg.string(),
|
|
142
|
+
},
|
|
143
|
+
resolve: async (_root, args, ctx) => {
|
|
144
|
+
const query = parseArgs(PublicContentItemQuerySchema, {
|
|
145
|
+
locale: args.locale ?? undefined,
|
|
146
|
+
fallback: args.fallback ?? undefined,
|
|
147
|
+
previewToken: args.previewToken ?? undefined,
|
|
148
|
+
}, "content arguments");
|
|
149
|
+
const previewContentId = query.previewToken ? (verifyPreviewToken(query.previewToken)?.contentId ?? null) : null;
|
|
150
|
+
const anchor = await ctx.getContentNode(String(args.id));
|
|
151
|
+
const node = await resolveItemNode({ adapter: ctx.deps.adapter, contentService: ctx.contentService }, anchor, query, { previewContentId });
|
|
152
|
+
const prepared = await preparePublishedItem(ctx.deps, node, { previewContentId });
|
|
153
|
+
if (prepared.status === "notFound")
|
|
154
|
+
return null; // uniform: unknown AND draft
|
|
155
|
+
if (prepared.status === "brokenVersion")
|
|
156
|
+
throw new Error(prepared.message); // REST-500 analog
|
|
157
|
+
const extensions = await computeExtensions(prepared, ctx.deps.warn);
|
|
158
|
+
return { ...prepared.core, data: prepared.data, extensions: extensions ?? null };
|
|
159
|
+
},
|
|
160
|
+
}),
|
|
161
|
+
// GET /api/v1/content/by-path analog. Same uniform-null contract as
|
|
162
|
+
// `content`; the by-path anchor lookup is already publishedOnly.
|
|
163
|
+
contentByPath: t.field({
|
|
164
|
+
type: PublicContentItem,
|
|
165
|
+
nullable: true,
|
|
166
|
+
args: {
|
|
167
|
+
path: t.arg.string({ required: true }),
|
|
168
|
+
locale: t.arg.string(),
|
|
169
|
+
fallback: t.arg.string(),
|
|
170
|
+
},
|
|
171
|
+
resolve: async (_root, args, ctx) => {
|
|
172
|
+
const query = parseArgs(PublicContentByPathQuerySchema, { path: args.path, locale: args.locale ?? undefined, fallback: args.fallback ?? undefined }, "contentByPath arguments");
|
|
173
|
+
const anchor = await ctx.deps.adapter.getContentNodeByPath(query.path, { publishedOnly: true });
|
|
174
|
+
const node = await resolveItemNode({ adapter: ctx.deps.adapter, contentService: ctx.contentService }, anchor, query);
|
|
175
|
+
const prepared = await preparePublishedItem(ctx.deps, node);
|
|
176
|
+
if (prepared.status === "notFound")
|
|
177
|
+
return null;
|
|
178
|
+
if (prepared.status === "brokenVersion")
|
|
179
|
+
throw new Error(prepared.message);
|
|
180
|
+
const extensions = await computeExtensions(prepared, ctx.deps.warn);
|
|
181
|
+
return { ...prepared.core, data: prepared.data, extensions: extensions ?? null };
|
|
182
|
+
},
|
|
183
|
+
}),
|
|
184
|
+
// GET /api/v1/content/roots analog — REST never 404s this one, so the
|
|
185
|
+
// connection is always returned (never null).
|
|
186
|
+
contentRoots: t.field({
|
|
187
|
+
type: ContentNodeConnectionRef,
|
|
188
|
+
nullable: false,
|
|
189
|
+
args: { cursor: t.arg.id(), limit: t.arg.int() },
|
|
190
|
+
resolve: async (_root, args, ctx) => {
|
|
191
|
+
const { cursor, limit } = parseArgs(PublicContentListQuerySchema, { cursor: args.cursor ?? undefined, limit: args.limit ?? undefined }, "contentRoots arguments");
|
|
192
|
+
const items = await ctx.deps.adapter.getRootNodes({ cursor, limit, publishedOnly: true });
|
|
193
|
+
const nextCursor = items.length === limit ? (items[items.length - 1]?.id ?? null) : null;
|
|
194
|
+
return { items: toSummaries(items), nextCursor };
|
|
195
|
+
},
|
|
196
|
+
}),
|
|
197
|
+
// GET /api/v1/content/:id/children analog. Null connection (not an
|
|
198
|
+
// empty page) when the parent is unknown/draft, mirroring REST's 404.
|
|
199
|
+
contentChildren: t.field({
|
|
200
|
+
type: ContentNodeConnectionRef,
|
|
201
|
+
nullable: true,
|
|
202
|
+
args: { id: t.arg.id({ required: true }), cursor: t.arg.id(), limit: t.arg.int() },
|
|
203
|
+
resolve: async (_root, args, ctx) => {
|
|
204
|
+
const { cursor, limit } = parseArgs(PublicContentListQuerySchema, { cursor: args.cursor ?? undefined, limit: args.limit ?? undefined }, "contentChildren arguments");
|
|
205
|
+
const parent = await ctx.getContentNode(String(args.id));
|
|
206
|
+
if (!parent || parent.publishedAt === null)
|
|
207
|
+
return null;
|
|
208
|
+
const items = await ctx.deps.adapter.getChildren(parent.id, { cursor, limit, publishedOnly: true });
|
|
209
|
+
const nextCursor = items.length === limit ? (items[items.length - 1]?.id ?? null) : null;
|
|
210
|
+
return { items: toSummaries(items), nextCursor };
|
|
211
|
+
},
|
|
212
|
+
}),
|
|
213
|
+
// GET /api/v1/content analog (Session 115, Task 1) — combinable
|
|
214
|
+
// filters over the published surface: documentTypeId, a subtree
|
|
215
|
+
// anchor (under), and a case-insensitive substring (q), matching
|
|
216
|
+
// REST's PublicContentSearchQuerySchema-validated querystring
|
|
217
|
+
// exactly for documentTypeId/q/cursor/limit. `under` is deliberately
|
|
218
|
+
// NOT part of that parseArgs call — it's resolved via the same
|
|
219
|
+
// resolveSubtreeFilter helper REST's route now shares, unvalidated
|
|
220
|
+
// by the schema's `.uuid()` check, the same convention `content`/
|
|
221
|
+
// `documentType`/`mediaItem`/`contentChildren` already use for their
|
|
222
|
+
// own id args (a malformed value is a lookup miss, not a thrown
|
|
223
|
+
// validation error). Null (not an empty page) whenever `under`
|
|
224
|
+
// doesn't resolve to a published anchor — unknown, unpublished, or
|
|
225
|
+
// malformed — mirroring REST's 404 for the valid-format misses
|
|
226
|
+
// (unknown/unpublished); a malformed-format `under` never reaches
|
|
227
|
+
// REST's handler at all, since Fastify's querystring schema 400s it
|
|
228
|
+
// before the route runs.
|
|
229
|
+
contents: t.field({
|
|
230
|
+
type: ContentNodeConnectionRef,
|
|
231
|
+
nullable: true,
|
|
232
|
+
args: {
|
|
233
|
+
documentTypeId: t.arg.id(),
|
|
234
|
+
under: t.arg.id(),
|
|
235
|
+
q: t.arg.string(),
|
|
236
|
+
cursor: t.arg.id(),
|
|
237
|
+
limit: t.arg.int(),
|
|
238
|
+
},
|
|
239
|
+
resolve: async (_root, args, ctx) => {
|
|
240
|
+
const { documentTypeId, q, cursor, limit } = parseArgs(PublicContentSearchQuerySchema, {
|
|
241
|
+
documentTypeId: args.documentTypeId ?? undefined,
|
|
242
|
+
q: args.q ?? undefined,
|
|
243
|
+
cursor: args.cursor ?? undefined,
|
|
244
|
+
limit: args.limit ?? undefined,
|
|
245
|
+
}, "contents arguments");
|
|
246
|
+
let pathPrefix;
|
|
247
|
+
if (args.under) {
|
|
248
|
+
const resolved = await resolveSubtreeFilter(ctx.deps.adapter, args.under);
|
|
249
|
+
if (!resolved)
|
|
250
|
+
return null;
|
|
251
|
+
pathPrefix = resolved.pathPrefix;
|
|
252
|
+
}
|
|
253
|
+
const items = await ctx.deps.adapter.searchPublishedContent({
|
|
254
|
+
cursor,
|
|
255
|
+
limit,
|
|
256
|
+
documentTypeId,
|
|
257
|
+
pathPrefix,
|
|
258
|
+
text: q,
|
|
259
|
+
});
|
|
260
|
+
const nextCursor = items.length === limit ? (items[items.length - 1]?.id ?? null) : null;
|
|
261
|
+
return { items: toSummaries(items), nextCursor };
|
|
262
|
+
},
|
|
263
|
+
}),
|
|
264
|
+
// GET /api/v1/document-types/:id analog. Uniform null for unknown id.
|
|
265
|
+
documentType: t.field({
|
|
266
|
+
type: PublicDocumentTypeRef,
|
|
267
|
+
nullable: true,
|
|
268
|
+
args: { id: t.arg.id({ required: true }) },
|
|
269
|
+
resolve: async (_root, args, ctx) => {
|
|
270
|
+
const doc = await ctx.deps.adapter.getDocumentType(String(args.id));
|
|
271
|
+
return doc ? toPublicDocumentType(doc) : null;
|
|
272
|
+
},
|
|
273
|
+
}),
|
|
274
|
+
// GET /api/v1/document-types analog — never null, same as contentRoots.
|
|
275
|
+
documentTypes: t.field({
|
|
276
|
+
type: DocumentTypeConnectionRef,
|
|
277
|
+
nullable: false,
|
|
278
|
+
args: { cursor: t.arg.id(), limit: t.arg.int() },
|
|
279
|
+
resolve: async (_root, args, ctx) => {
|
|
280
|
+
const { cursor, limit } = parseArgs(PublicDocumentTypeListQuerySchema, { cursor: args.cursor ?? undefined, limit: args.limit ?? undefined }, "documentTypes arguments");
|
|
281
|
+
const items = await ctx.deps.adapter.listDocumentTypes({ cursor, limit });
|
|
282
|
+
const nextCursor = items.length === limit ? (items[items.length - 1]?.id ?? null) : null;
|
|
283
|
+
return { items: items.map(toPublicDocumentType), nextCursor };
|
|
284
|
+
},
|
|
285
|
+
}),
|
|
286
|
+
// GET /api/v1/templates analog — never null; no by-id REST route
|
|
287
|
+
// exists for templates, so there is no singular `template(id)` query.
|
|
288
|
+
templates: t.field({
|
|
289
|
+
type: TemplateConnectionRef,
|
|
290
|
+
nullable: false,
|
|
291
|
+
args: { cursor: t.arg.id(), limit: t.arg.int(), documentTypeId: t.arg.id() },
|
|
292
|
+
resolve: async (_root, args, ctx) => {
|
|
293
|
+
const { cursor, limit, documentTypeId } = parseArgs(PublicTemplateListQuerySchema, {
|
|
294
|
+
cursor: args.cursor ?? undefined,
|
|
295
|
+
limit: args.limit ?? undefined,
|
|
296
|
+
documentTypeId: args.documentTypeId ?? undefined,
|
|
297
|
+
}, "templates arguments");
|
|
298
|
+
const items = await ctx.deps.adapter.listTemplates({ cursor, limit, documentTypeId });
|
|
299
|
+
const nextCursor = items.length === limit ? (items[items.length - 1]?.id ?? null) : null;
|
|
300
|
+
return { items, nextCursor };
|
|
301
|
+
},
|
|
302
|
+
}),
|
|
303
|
+
// GET /api/v1/media/:id analog. Uniform null for unknown id.
|
|
304
|
+
mediaItem: t.field({
|
|
305
|
+
type: PublicMediaFileRef,
|
|
306
|
+
nullable: true,
|
|
307
|
+
args: { id: t.arg.id({ required: true }) },
|
|
308
|
+
resolve: async (_root, args, ctx) => {
|
|
309
|
+
const media = await ctx.mediaService.getById(String(args.id));
|
|
310
|
+
return media ? toPublicMediaFile(ctx.mediaService, media) : null;
|
|
311
|
+
},
|
|
312
|
+
}),
|
|
313
|
+
// GET /api/v1/media analog — never null, same as contentRoots.
|
|
314
|
+
media: t.field({
|
|
315
|
+
type: MediaConnectionRef,
|
|
316
|
+
nullable: false,
|
|
317
|
+
args: { cursor: t.arg.id(), limit: t.arg.int(), search: t.arg.string() },
|
|
318
|
+
resolve: async (_root, args, ctx) => {
|
|
319
|
+
const { cursor, limit, search } = parseArgs(PublicMediaListQuerySchema, { cursor: args.cursor ?? undefined, limit: args.limit ?? undefined, search: args.search ?? undefined }, "media arguments");
|
|
320
|
+
const files = await ctx.mediaService.list({ cursor, limit, search });
|
|
321
|
+
const items = await Promise.all(files.map((file) => toPublicMediaFile(ctx.mediaService, file)));
|
|
322
|
+
const nextCursor = files.length === limit ? (files[files.length - 1]?.id ?? null) : null;
|
|
323
|
+
return { items, nextCursor };
|
|
324
|
+
},
|
|
325
|
+
}),
|
|
326
|
+
}),
|
|
327
|
+
});
|
|
328
|
+
export function buildGraphqlSchema() {
|
|
329
|
+
return builder.toSchema();
|
|
330
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { loadEnvFile } from "./config/load-env.js";
|
|
2
|
+
import { normalizeDatabaseUrl } from "./setup/normalize-database-url.js";
|
|
3
|
+
import { countAdminUsers, resolveBootMode } from "./boot/resolve-boot-mode.js";
|
|
4
|
+
import { judgeCoreMigrationFailure } from "./boot/core-migration-gate.js";
|
|
5
|
+
// FIRST, before anything reads process.env. app.js's import graph reaches
|
|
6
|
+
// db/prisma.ts, whose module body is `export const prisma = await
|
|
7
|
+
// loadPrismaClient(resolveClientDir())` — a top-level await that runs as a
|
|
8
|
+
// side effect of the import. If that module were imported before the env
|
|
9
|
+
// file is merged, it would connect with whatever DATABASE_URL happened to
|
|
10
|
+
// already be set. Hence a static import of the loader here and DYNAMIC
|
|
11
|
+
// imports of the apps below, deferred until after the boot mode is known.
|
|
12
|
+
loadEnvFile();
|
|
13
|
+
// An externally injected DATABASE_URL gets the same treatment the wizard's
|
|
14
|
+
// own input gets — a relative sqlite path becomes absolute exactly once,
|
|
15
|
+
// here, before anything downstream reads it. Doing it in place means the
|
|
16
|
+
// Prisma CLI (setup/provision-database.ts), the content-db adapter and
|
|
17
|
+
// every other reader see one identical absolute string.
|
|
18
|
+
if (process.env.DATABASE_URL) {
|
|
19
|
+
process.env.DATABASE_URL = normalizeDatabaseUrl(process.env.DATABASE_URL);
|
|
20
|
+
}
|
|
21
|
+
// S185: self-heal the Prisma client and bring the Prisma-owned schema's
|
|
22
|
+
// migration ledger up to date BEFORE resolveBootMode below — its
|
|
23
|
+
// countAdminUsers builds a Prisma client of its own, which needs both the
|
|
24
|
+
// generated client and the migrated schema to exist. Still dynamic imports,
|
|
25
|
+
// for the same reason as the app-selection block further down: nothing
|
|
26
|
+
// Prisma-shaped may load before loadEnvFile()/normalizeDatabaseUrl() have
|
|
27
|
+
// settled process.env.DATABASE_URL. An unset or dialect-less DATABASE_URL
|
|
28
|
+
// falls through untouched — the setup app path (no DATABASE_URL yet) is
|
|
29
|
+
// unchanged. An injected DATABASE_URL may point at a completely empty
|
|
30
|
+
// database the wizard hasn't provisioned yet; applyCoreMigrations(mode:
|
|
31
|
+
// "boot") handles that itself (outcome "skipped-empty"), so nothing here
|
|
32
|
+
// pre-checks for it.
|
|
33
|
+
//
|
|
34
|
+
// Fix wave (review): migrations are attempted FIRST, before resolveBootMode
|
|
35
|
+
// runs, so a "full" (configured) boot never proceeds against an
|
|
36
|
+
// un-migrated schema. But refusal on failure is deferred until AFTER mode
|
|
37
|
+
// is known — resolveBootMode is the actual decision-maker here and is
|
|
38
|
+
// fail-closed by design (see its own header): a DATABASE_URL pointing at a
|
|
39
|
+
// database that doesn't exist as a catalog entry at all (P1003) is a
|
|
40
|
+
// deliberately supported shape, not a crash — it reads as zero admins and
|
|
41
|
+
// resolves to a setup-* mode so the wizard can create the database. Any
|
|
42
|
+
// error here is therefore captured and remembered rather than thrown, and
|
|
43
|
+
// only judged once the mode is known: it refuses "full" (a configured
|
|
44
|
+
// install must never run the app against a database whose core schema
|
|
45
|
+
// didn't or couldn't migrate) but is expected, and logged quietly, for the
|
|
46
|
+
// setup-* modes, where an empty or missing database is exactly why the
|
|
47
|
+
// wizard exists.
|
|
48
|
+
let coreMigrationError;
|
|
49
|
+
let coreMigrationPhase;
|
|
50
|
+
if (process.env.DATABASE_URL) {
|
|
51
|
+
const { dialectFromUrlScheme } = await import("./db/content-db.js");
|
|
52
|
+
const dialect = dialectFromUrlScheme(process.env.DATABASE_URL);
|
|
53
|
+
if (dialect) {
|
|
54
|
+
// Which of the two awaits below was in flight when/if the catch fires —
|
|
55
|
+
// set right before each one starts, so a throw from either leaves the
|
|
56
|
+
// right phase behind for judgeCoreMigrationFailure to word correctly.
|
|
57
|
+
let phase = "client-generation";
|
|
58
|
+
try {
|
|
59
|
+
const { ensurePrismaClientGenerated } = await import("./setup/ensure-prisma-client.js");
|
|
60
|
+
const { applyCoreMigrations } = await import("./db/core-migrations/index.js");
|
|
61
|
+
await ensurePrismaClientGenerated(dialect);
|
|
62
|
+
phase = "core-migrations";
|
|
63
|
+
await applyCoreMigrations({ dialect, databaseUrl: process.env.DATABASE_URL, mode: "boot" });
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
coreMigrationError = err instanceof Error ? err : new Error(String(err));
|
|
67
|
+
coreMigrationPhase = phase;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
let mode;
|
|
72
|
+
try {
|
|
73
|
+
mode = await resolveBootMode(process.env, countAdminUsers);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
// A plain process.stderr.write (what console.error calls internally) can
|
|
77
|
+
// be buffered (notably on Windows, and on any piped stderr) — an
|
|
78
|
+
// immediately following process.exit(1) can cut the write off before it
|
|
79
|
+
// flushes, and this message is the ONLY explanation the operator gets for
|
|
80
|
+
// a refused boot. Wait for the write's own callback so it has actually
|
|
81
|
+
// left the process before exiting.
|
|
82
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
83
|
+
// If a core-migration/client-generation error was ALSO captured above,
|
|
84
|
+
// don't let resolveBootMode's own failure bury it: that earlier error is
|
|
85
|
+
// frequently the actual root cause (e.g. an un-migrated schema is exactly
|
|
86
|
+
// the kind of thing that can make the admin-count probe below it fail
|
|
87
|
+
// too), and without this line it would be silently dropped — the operator
|
|
88
|
+
// would see only the downstream symptom.
|
|
89
|
+
const lines = [message];
|
|
90
|
+
if (coreMigrationError && coreMigrationPhase) {
|
|
91
|
+
lines.push(`Earlier failure while preparing the database (${coreMigrationPhase}): ${coreMigrationError.message}`);
|
|
92
|
+
}
|
|
93
|
+
await new Promise((resolve) => {
|
|
94
|
+
process.stderr.write(`${lines.join("\n")}\n`, () => resolve());
|
|
95
|
+
});
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
// A captured core-migration/client-generation error only matters once mode
|
|
99
|
+
// is known — see the comment above the capture. judgeCoreMigrationFailure
|
|
100
|
+
// is the pure decision (unit-tested in boot/core-migration-gate.test.ts,
|
|
101
|
+
// including the never-boot-un-migrated invariant for mode "full"); this is
|
|
102
|
+
// just the process.exit/console plumbing around its answer, using the same
|
|
103
|
+
// flushed-stderr + exit shape the resolveBootMode catch above uses so the
|
|
104
|
+
// operator always gets the named cause.
|
|
105
|
+
if (coreMigrationError && coreMigrationPhase) {
|
|
106
|
+
const gate = judgeCoreMigrationFailure(coreMigrationError, mode, coreMigrationPhase);
|
|
107
|
+
if (gate.action === "refuse") {
|
|
108
|
+
await new Promise((resolve) => {
|
|
109
|
+
process.stderr.write(`${gate.message}\n`, () => resolve());
|
|
110
|
+
});
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
else if (gate.action === "log") {
|
|
114
|
+
console.log(gate.message);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Deferred until the mode is known, and still dynamic, for two reasons:
|
|
118
|
+
// (1) db/prisma.ts's top-level-await `prisma` singleton (see above) must
|
|
119
|
+
// not be constructed before loadEnvFile()/normalizeDatabaseUrl() have
|
|
120
|
+
// settled process.env.DATABASE_URL, and (2) resolve-boot-mode.ts's
|
|
121
|
+
// countAdminUsers deliberately builds its own short-lived client instead of
|
|
122
|
+
// importing that singleton, specifically so the mode decision can be made
|
|
123
|
+
// before committing to a connection — importing app.js (which statically
|
|
124
|
+
// imports db/prisma.ts) unconditionally, before that decision ran, would
|
|
125
|
+
// construct the singleton anyway and defeat the point. This also means
|
|
126
|
+
// setup-full/setup-admin-only no longer load the entire buildApp() import
|
|
127
|
+
// graph at all.
|
|
128
|
+
let app;
|
|
129
|
+
if (mode === "full") {
|
|
130
|
+
const { buildApp } = await import("./app.js");
|
|
131
|
+
app = await buildApp();
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const { buildSetupOnlyApp } = await import("./setup-app.js");
|
|
135
|
+
app = await buildSetupOnlyApp({ databaseConfigured: mode === "setup-admin-only" });
|
|
136
|
+
}
|
|
137
|
+
const port = Number(process.env.PORT ?? 3000);
|
|
138
|
+
const host = process.env.HOST ?? "127.0.0.1";
|
|
139
|
+
try {
|
|
140
|
+
await app.listen({ port, host });
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
app.log.error(err);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
// Without this, SIGTERM/SIGINT (Docker stop, systemd, a Windows service, or a
|
|
147
|
+
// plain Ctrl-C) hit Node's default signal action — immediate termination —
|
|
148
|
+
// and app.close() never runs. That means every onClose hook (content DB
|
|
149
|
+
// disconnect, the license-revalidation job's own bounded drain) is dead code
|
|
150
|
+
// in production, only ever exercised by tests that call app.close() directly.
|
|
151
|
+
// Verified live against a running server, Session 139.
|
|
152
|
+
//
|
|
153
|
+
// SHUTDOWN_TIMEOUT_MS sits above the license job's own 15s pass-timeout bound
|
|
154
|
+
// (license-revalidation-job.ts) so that job's own drain has room to give up
|
|
155
|
+
// cleanly and log first; this is the outer safety net for the rest of
|
|
156
|
+
// app.close() (e.g. the content DB disconnect) hanging too.
|
|
157
|
+
const SHUTDOWN_TIMEOUT_MS = 20_000;
|
|
158
|
+
let shuttingDown = false;
|
|
159
|
+
function shutdown(signal) {
|
|
160
|
+
// A second signal while already closing must not start a second close.
|
|
161
|
+
if (shuttingDown)
|
|
162
|
+
return;
|
|
163
|
+
shuttingDown = true;
|
|
164
|
+
app.log.info(`[shutdown] received ${signal}, closing…`);
|
|
165
|
+
const forceExit = setTimeout(() => {
|
|
166
|
+
app.log.error(`[shutdown] app.close() did not resolve within ${SHUTDOWN_TIMEOUT_MS}ms — forcing exit`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}, SHUTDOWN_TIMEOUT_MS);
|
|
169
|
+
app.close().then(() => {
|
|
170
|
+
app.log.info("[shutdown] closed cleanly");
|
|
171
|
+
clearTimeout(forceExit);
|
|
172
|
+
process.exit(0);
|
|
173
|
+
}, (closeErr) => {
|
|
174
|
+
app.log.error(closeErr, "[shutdown] error while closing");
|
|
175
|
+
clearTimeout(forceExit);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
180
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
package/dist/lib.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare function createApp(): Promise<import("fastify").FastifyInstance<import("fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>>;
|
|
2
|
+
export declare const buildApp: typeof createApp;
|
package/dist/lib.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// The embed surface (S185). createApp() mirrors the preparation the
|
|
2
|
+
// `velora-server` bin (index.ts) performs before building the app — env
|
|
3
|
+
// file load, DATABASE_URL normalization, Prisma-client self-heal, core
|
|
4
|
+
// migrations — then returns the built (not-listening) Fastify instance for
|
|
5
|
+
// the caller to mount or listen on themselves.
|
|
6
|
+
//
|
|
7
|
+
// Everything below is imported DYNAMICALLY and only inside the call:
|
|
8
|
+
// db/prisma.ts holds a top-level-await Prisma client that must not be
|
|
9
|
+
// constructed at import time (see index.ts's own comments on the same
|
|
10
|
+
// point), and a consumer must be able to set process.env AFTER importing
|
|
11
|
+
// this module and BEFORE calling createApp().
|
|
12
|
+
//
|
|
13
|
+
// Unlike index.ts, this does NOT reproduce the bin's boot-mode gate
|
|
14
|
+
// (resolveBootMode / judgeCoreMigrationFailure) or its process.exit
|
|
15
|
+
// refusal behavior — those belong to the standalone server process, not to
|
|
16
|
+
// a library call. Here, any failure (env parsing, client generation,
|
|
17
|
+
// migrations, or buildApp itself) simply propagates as a rejected promise
|
|
18
|
+
// for the caller to catch. That IS the right embed behavior: a library
|
|
19
|
+
// function must never call process.exit or decide a host process's fate.
|
|
20
|
+
export async function createApp() {
|
|
21
|
+
const { loadEnvFile } = await import("./config/load-env.js");
|
|
22
|
+
const { normalizeDatabaseUrl } = await import("./setup/normalize-database-url.js");
|
|
23
|
+
loadEnvFile();
|
|
24
|
+
if (process.env.DATABASE_URL) {
|
|
25
|
+
process.env.DATABASE_URL = normalizeDatabaseUrl(process.env.DATABASE_URL);
|
|
26
|
+
const { dialectFromUrlScheme } = await import("./db/content-db.js");
|
|
27
|
+
const dialect = dialectFromUrlScheme(process.env.DATABASE_URL);
|
|
28
|
+
if (dialect) {
|
|
29
|
+
const { ensurePrismaClientGenerated } = await import("./setup/ensure-prisma-client.js");
|
|
30
|
+
const { applyCoreMigrations } = await import("./db/core-migrations/index.js");
|
|
31
|
+
await ensurePrismaClientGenerated(dialect);
|
|
32
|
+
await applyCoreMigrations({ dialect, databaseUrl: process.env.DATABASE_URL, mode: "boot" });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const { buildApp } = await import("./app.js");
|
|
36
|
+
return buildApp();
|
|
37
|
+
}
|
|
38
|
+
// Alias kept for symmetry with app.ts's internal name.
|
|
39
|
+
export const buildApp = createApp;
|