@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.
Files changed (367) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +65 -0
  3. package/dist/admin-bundle/assets/__virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react__loadShare__.js_commonjs-proxy-Ng0fOpdU.js +1 -0
  4. 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
  5. package/dist/admin-bundle/assets/_virtual_mf-localSharedImportMap___mfe_internal__velora_admin__mf_owner__1-DOKdWYyj.js +1 -0
  6. 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
  7. package/dist/admin-bundle/assets/_virtual_mf___mfe_internal__velora_admin__mf_owner__1__loadShare__react__loadShare__.js-BuUBskgj.js +9 -0
  8. 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
  9. 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
  10. 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
  11. package/dist/admin-bundle/assets/content-trash.schema-DfXHkC-w.js +72 -0
  12. package/dist/admin-bundle/assets/hostInit--C4xK6nj.js +1 -0
  13. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  14. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  15. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  16. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  17. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  18. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  19. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  20. package/dist/admin-bundle/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  21. package/dist/admin-bundle/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  22. package/dist/admin-bundle/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  23. package/dist/admin-bundle/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  24. package/dist/admin-bundle/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  25. package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  26. package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  27. package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  28. package/dist/admin-bundle/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  29. package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  30. package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  31. package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  32. package/dist/admin-bundle/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  33. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-400-normal-CyU-ddYS.woff2 +0 -0
  34. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-400-normal-EA1o-u_Q.woff +0 -0
  35. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-500-normal-C4MQITzh.woff2 +0 -0
  36. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-500-normal-DPXcppbm.woff +0 -0
  37. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-600-normal-0pRdybE_.woff2 +0 -0
  38. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-arabic-600-normal-MJZ0liF5.woff +0 -0
  39. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-400-normal-Bo5KPYvw.woff2 +0 -0
  40. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-400-normal-CFZh1unN.woff +0 -0
  41. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-500-normal-BletK1Hp.woff +0 -0
  42. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-500-normal-Cd6jVIg7.woff2 +0 -0
  43. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-600-normal-Dk5eISD_.woff +0 -0
  44. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-600-normal-KrqB56Mw.woff2 +0 -0
  45. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-400-normal-BeVLg08x.woff +0 -0
  46. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-400-normal-BpLfq-63.woff2 +0 -0
  47. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-500-normal-Ck_W4G78.woff2 +0 -0
  48. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-500-normal-eJxPnL1P.woff +0 -0
  49. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-600-normal-DT29Q8HW.woff +0 -0
  50. package/dist/admin-bundle/assets/ibm-plex-sans-arabic-latin-ext-600-normal-DjDAeNQD.woff2 +0 -0
  51. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-400-normal-BTotfTJu.woff +0 -0
  52. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-400-normal-DZqxrq2p.woff2 +0 -0
  53. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-500-normal-ByOcLdNv.woff +0 -0
  54. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-500-normal-CocWQlwt.woff2 +0 -0
  55. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-600-normal-71GNu3SW.woff2 +0 -0
  56. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-600-normal-BGq0mW3O.woff +0 -0
  57. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-400-normal-Dsrv2Tcn.woff +0 -0
  58. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-400-normal-g30qAdWV.woff2 +0 -0
  59. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-500-normal-Cs5J6C77.woff2 +0 -0
  60. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-500-normal-DB5PtV2g.woff +0 -0
  61. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-600-normal-Bz0x94Yp.woff +0 -0
  62. package/dist/admin-bundle/assets/ibm-plex-sans-cyrillic-ext-600-normal-DUMzJB7m.woff2 +0 -0
  63. package/dist/admin-bundle/assets/ibm-plex-sans-greek-400-normal-D9ESIMu3.woff +0 -0
  64. package/dist/admin-bundle/assets/ibm-plex-sans-greek-400-normal-_efipK4i.woff2 +0 -0
  65. package/dist/admin-bundle/assets/ibm-plex-sans-greek-500-normal-CuWXN6rf.woff +0 -0
  66. package/dist/admin-bundle/assets/ibm-plex-sans-greek-500-normal-JMMifIXV.woff2 +0 -0
  67. package/dist/admin-bundle/assets/ibm-plex-sans-greek-600-normal-D-CqTdkO.woff +0 -0
  68. package/dist/admin-bundle/assets/ibm-plex-sans-greek-600-normal-DzTrcv_p.woff2 +0 -0
  69. package/dist/admin-bundle/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  70. package/dist/admin-bundle/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  71. package/dist/admin-bundle/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  72. package/dist/admin-bundle/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  73. package/dist/admin-bundle/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  74. package/dist/admin-bundle/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  75. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-400-normal-C5H60-Va.woff2 +0 -0
  76. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-400-normal-RBey6euL.woff +0 -0
  77. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-500-normal-D0aIdm-b.woff +0 -0
  78. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-500-normal-DakdToA3.woff2 +0 -0
  79. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-600-normal-DIrixKbi.woff +0 -0
  80. package/dist/admin-bundle/assets/ibm-plex-sans-latin-ext-600-normal-DOrvGEcy.woff2 +0 -0
  81. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-400-normal-DG4YqDda.woff2 +0 -0
  82. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-400-normal-fK1oJ5dG.woff +0 -0
  83. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-500-normal-BEb3_waV.woff +0 -0
  84. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-500-normal-e4dixQRQ.woff2 +0 -0
  85. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-600-normal-DgdngZtN.woff +0 -0
  86. package/dist/admin-bundle/assets/ibm-plex-sans-vietnamese-600-normal-DpPYBSTl.woff2 +0 -0
  87. package/dist/admin-bundle/assets/index-BIAF0ShP.js +542 -0
  88. package/dist/admin-bundle/assets/index-DhMY-0sC.js +148 -0
  89. package/dist/admin-bundle/assets/index-DnZV_gsg.css +1 -0
  90. package/dist/admin-bundle/assets/index-Do71v6NZ.js +7 -0
  91. package/dist/admin-bundle/assets/literata-cyrillic-ext-opsz-italic-5yUt4G_9.woff2 +0 -0
  92. package/dist/admin-bundle/assets/literata-cyrillic-ext-opsz-normal-C5tfwALZ.woff2 +0 -0
  93. package/dist/admin-bundle/assets/literata-cyrillic-opsz-italic-Bj0ndbyp.woff2 +0 -0
  94. package/dist/admin-bundle/assets/literata-cyrillic-opsz-normal-DgmypKXX.woff2 +0 -0
  95. package/dist/admin-bundle/assets/literata-greek-ext-opsz-italic-DFpO5j-Z.woff2 +0 -0
  96. package/dist/admin-bundle/assets/literata-greek-ext-opsz-normal-DGm64vGR.woff2 +0 -0
  97. package/dist/admin-bundle/assets/literata-greek-opsz-italic-CHERNRFq.woff2 +0 -0
  98. package/dist/admin-bundle/assets/literata-greek-opsz-normal-DFglUmAd.woff2 +0 -0
  99. package/dist/admin-bundle/assets/literata-latin-ext-opsz-italic-4y8iAqqs.woff2 +0 -0
  100. package/dist/admin-bundle/assets/literata-latin-ext-opsz-normal-STiTPs0F.woff2 +0 -0
  101. package/dist/admin-bundle/assets/literata-latin-opsz-italic-CJ93D38p.woff2 +0 -0
  102. package/dist/admin-bundle/assets/literata-latin-opsz-normal-BHZsWIlc.woff2 +0 -0
  103. package/dist/admin-bundle/assets/literata-vietnamese-opsz-italic-BdcrZMcO.woff2 +0 -0
  104. package/dist/admin-bundle/assets/literata-vietnamese-opsz-normal-Gqw5JQlq.woff2 +0 -0
  105. package/dist/admin-bundle/assets/mf-entry-bootstrap-0-aef015cd.js +41 -0
  106. package/dist/admin-bundle/assets/remoteEntry-CNyN68bc.js +14 -0
  107. package/dist/admin-bundle/assets/virtualExposes-BDCSo5OS.js +1 -0
  108. package/dist/admin-bundle/assets/vite-preload-helper-Cgledu0r.js +1 -0
  109. package/dist/admin-bundle/index.html +16 -0
  110. package/dist/app.d.ts +8 -0
  111. package/dist/app.js +252 -0
  112. package/dist/auth/api-content-author.d.ts +2 -0
  113. package/dist/auth/api-content-author.js +27 -0
  114. package/dist/auth/api-keys.d.ts +7 -0
  115. package/dist/auth/api-keys.js +85 -0
  116. package/dist/auth/jwt-payload.d.ts +16 -0
  117. package/dist/auth/jwt-payload.js +1 -0
  118. package/dist/auth/preview-tokens.d.ts +7 -0
  119. package/dist/auth/preview-tokens.js +76 -0
  120. package/dist/auth/rate-limit.d.ts +16 -0
  121. package/dist/auth/rate-limit.js +283 -0
  122. package/dist/auth/require-auth.d.ts +4 -0
  123. package/dist/auth/require-auth.js +28 -0
  124. package/dist/auth/route-rate-limits.d.ts +31 -0
  125. package/dist/auth/route-rate-limits.js +155 -0
  126. package/dist/auth/tokens.d.ts +15 -0
  127. package/dist/auth/tokens.js +38 -0
  128. package/dist/bin.d.ts +2 -0
  129. package/dist/bin.js +6 -0
  130. package/dist/boot/core-migration-gate.d.ts +12 -0
  131. package/dist/boot/core-migration-gate.js +52 -0
  132. package/dist/boot/resolve-boot-mode.d.ts +6 -0
  133. package/dist/boot/resolve-boot-mode.js +206 -0
  134. package/dist/bundled-templates/com.velora.blog/manifest.json +39 -0
  135. package/dist/bundled-templates/com.velora.blog/package.tar.gz +0 -0
  136. package/dist/bundled-templates/com.velora.blog/sample-content.json +14 -0
  137. package/dist/config/admin-ui.d.ts +1 -0
  138. package/dist/config/admin-ui.js +10 -0
  139. package/dist/config/cors.d.ts +7 -0
  140. package/dist/config/cors.js +53 -0
  141. package/dist/config/env-file-path.d.ts +2 -0
  142. package/dist/config/env-file-path.js +22 -0
  143. package/dist/config/graphql-limits.d.ts +2 -0
  144. package/dist/config/graphql-limits.js +26 -0
  145. package/dist/config/headless.d.ts +1 -0
  146. package/dist/config/headless.js +8 -0
  147. package/dist/config/license-revalidation.d.ts +2 -0
  148. package/dist/config/license-revalidation.js +16 -0
  149. package/dist/config/load-env.d.ts +1 -0
  150. package/dist/config/load-env.js +53 -0
  151. package/dist/config/marketplace.d.ts +11 -0
  152. package/dist/config/marketplace.js +19 -0
  153. package/dist/config/preview.d.ts +3 -0
  154. package/dist/config/preview.js +39 -0
  155. package/dist/config/public-docs.d.ts +1 -0
  156. package/dist/config/public-docs.js +9 -0
  157. package/dist/config/site.d.ts +7 -0
  158. package/dist/config/site.js +12 -0
  159. package/dist/config/trust-proxy.d.ts +2 -0
  160. package/dist/config/trust-proxy.js +42 -0
  161. package/dist/data-types/validate-data-type-settings.d.ts +1 -0
  162. package/dist/data-types/validate-data-type-settings.js +33 -0
  163. package/dist/db/content-db.d.ts +16 -0
  164. package/dist/db/content-db.js +109 -0
  165. package/dist/db/core-migrations/index.d.ts +14 -0
  166. package/dist/db/core-migrations/index.js +184 -0
  167. package/dist/db/core-migrations/steps.d.ts +4 -0
  168. package/dist/db/core-migrations/steps.js +66 -0
  169. package/dist/db/load-prisma-client.d.ts +2 -0
  170. package/dist/db/load-prisma-client.js +44 -0
  171. package/dist/db/prisma.d.ts +3 -0
  172. package/dist/db/prisma.js +67 -0
  173. package/dist/db/resolve-client-dir.d.ts +2 -0
  174. package/dist/db/resolve-client-dir.js +83 -0
  175. package/dist/docs/filter-public-api-spec.d.ts +2 -0
  176. package/dist/docs/filter-public-api-spec.js +52 -0
  177. package/dist/docs-bundle/404.html +41 -0
  178. package/dist/docs-bundle/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.BcSo_yiZ.js +1 -0
  179. package/dist/docs-bundle/_astro/Search.astro_astro_type_script_index_0_lang.CavldbAs.js +1 -0
  180. package/dist/docs-bundle/_astro/TableOfContents.astro_astro_type_script_index_0_lang.Csloo1VZ.js +1 -0
  181. package/dist/docs-bundle/_astro/common.DTXA7sXM.css +3 -0
  182. package/dist/docs-bundle/_astro/ec.0vx5m.js +3 -0
  183. package/dist/docs-bundle/_astro/ec.w36nc.css +1 -0
  184. package/dist/docs-bundle/_astro/page.Dwipeu-R.js +1 -0
  185. package/dist/docs-bundle/_astro/print.ehPL0gv-.css +1 -0
  186. package/dist/docs-bundle/_astro/starlight-toc.CTCQXEge.js +1 -0
  187. package/dist/docs-bundle/_astro/ui-core.D-7Scnhs.js +2 -0
  188. package/dist/docs-bundle/favicon.svg +1 -0
  189. package/dist/docs-bundle/index.html +1 -0
  190. package/dist/docs-bundle/pagefind/fragment/en_18a36d7.pf_fragment +0 -0
  191. package/dist/docs-bundle/pagefind/fragment/en_1d9eb24.pf_fragment +0 -0
  192. package/dist/docs-bundle/pagefind/fragment/en_1ef320c.pf_fragment +0 -0
  193. package/dist/docs-bundle/pagefind/fragment/en_3099406.pf_fragment +0 -0
  194. package/dist/docs-bundle/pagefind/fragment/en_4a5b6fe.pf_fragment +0 -0
  195. package/dist/docs-bundle/pagefind/fragment/en_9aa652e.pf_fragment +0 -0
  196. package/dist/docs-bundle/pagefind/fragment/en_baccdf6.pf_fragment +0 -0
  197. package/dist/docs-bundle/pagefind/fragment/en_d81f3fe.pf_fragment +0 -0
  198. package/dist/docs-bundle/pagefind/fragment/en_d855550.pf_fragment +0 -0
  199. package/dist/docs-bundle/pagefind/fragment/en_e6d88d8.pf_fragment +0 -0
  200. package/dist/docs-bundle/pagefind/fragment/en_e9d3189.pf_fragment +0 -0
  201. package/dist/docs-bundle/pagefind/index/en_5feeed7.pf_index +0 -0
  202. package/dist/docs-bundle/pagefind/index/en_c6981f9.pf_index +0 -0
  203. package/dist/docs-bundle/pagefind/index/en_ce4f6bf.pf_index +0 -0
  204. package/dist/docs-bundle/pagefind/pagefind-component-ui.css +1509 -0
  205. package/dist/docs-bundle/pagefind/pagefind-component-ui.js +56 -0
  206. package/dist/docs-bundle/pagefind/pagefind-entry.json +1 -0
  207. package/dist/docs-bundle/pagefind/pagefind-highlight.js +1070 -0
  208. package/dist/docs-bundle/pagefind/pagefind-modular-ui.css +214 -0
  209. package/dist/docs-bundle/pagefind/pagefind-modular-ui.js +8 -0
  210. package/dist/docs-bundle/pagefind/pagefind-ui.css +1 -0
  211. package/dist/docs-bundle/pagefind/pagefind-ui.js +2 -0
  212. package/dist/docs-bundle/pagefind/pagefind-worker.js +6 -0
  213. package/dist/docs-bundle/pagefind/pagefind.en_1e94abd852.pf_meta +0 -0
  214. package/dist/docs-bundle/pagefind/pagefind.js +6 -0
  215. package/dist/docs-bundle/pagefind/wasm.en.pagefind +0 -0
  216. package/dist/docs-bundle/pagefind/wasm.unknown.pagefind +0 -0
  217. package/dist/docs-bundle/v1/index.html +86 -0
  218. package/dist/docs-bundle/v1/using-velora/build-a-headless-frontend/index.html +413 -0
  219. package/dist/docs-bundle/v1/using-velora/content-model/index.html +858 -0
  220. package/dist/docs-bundle/v1/using-velora/editing/index.html +748 -0
  221. package/dist/docs-bundle/v1/using-velora/getting-started/index.html +588 -0
  222. package/dist/docs-bundle/v1/using-velora/headless-api/index.html +1620 -0
  223. package/dist/docs-bundle/v1/using-velora/index.html +215 -0
  224. package/dist/docs-bundle/v1/using-velora/installation/index.html +886 -0
  225. package/dist/docs-bundle/v1/using-velora/installing-plugins/index.html +836 -0
  226. package/dist/docs-bundle/v1/using-velora/media/index.html +468 -0
  227. package/dist/docs-bundle/v1/using-velora/users-and-roles/index.html +561 -0
  228. package/dist/graphql/field-count-rule.d.ts +5 -0
  229. package/dist/graphql/field-count-rule.js +59 -0
  230. package/dist/graphql/schema.d.ts +12 -0
  231. package/dist/graphql/schema.js +330 -0
  232. package/dist/index.d.ts +1 -0
  233. package/dist/index.js +180 -0
  234. package/dist/lib.d.ts +2 -0
  235. package/dist/lib.js +39 -0
  236. package/dist/marketplace/install-bundle-members.d.ts +33 -0
  237. package/dist/marketplace/install-bundle-members.js +137 -0
  238. package/dist/marketplace/install-from-marketplace.d.ts +60 -0
  239. package/dist/marketplace/install-from-marketplace.js +581 -0
  240. package/dist/marketplace/license-revalidation-job.d.ts +38 -0
  241. package/dist/marketplace/license-revalidation-job.js +139 -0
  242. package/dist/marketplace/marketplace-client.d.ts +43 -0
  243. package/dist/marketplace/marketplace-client.js +228 -0
  244. package/dist/marketplace/revalidate-license.d.ts +39 -0
  245. package/dist/marketplace/revalidate-license.js +103 -0
  246. package/dist/media/media-service.d.ts +36 -0
  247. package/dist/media/media-service.js +162 -0
  248. package/dist/plugins/boot-plugin-loader.d.ts +32 -0
  249. package/dist/plugins/boot-plugin-loader.js +57 -0
  250. package/dist/plugins/hook-dispatcher.d.ts +13 -0
  251. package/dist/plugins/hook-dispatcher.js +68 -0
  252. package/dist/plugins/install-built-package.d.ts +36 -0
  253. package/dist/plugins/install-built-package.js +199 -0
  254. package/dist/plugins/install-package-tarball.d.ts +25 -0
  255. package/dist/plugins/install-package-tarball.js +73 -0
  256. package/dist/plugins/plugin-context.d.ts +5 -0
  257. package/dist/plugins/plugin-context.js +26 -0
  258. package/dist/plugins/plugin-package-store.d.ts +31 -0
  259. package/dist/plugins/plugin-package-store.js +64 -0
  260. package/dist/plugins/plugin-registry-service.d.ts +227 -0
  261. package/dist/plugins/plugin-registry-service.js +636 -0
  262. package/dist/plugins/seed-template-content.d.ts +23 -0
  263. package/dist/plugins/seed-template-content.js +198 -0
  264. package/dist/plugins/server-serializers.d.ts +3 -0
  265. package/dist/plugins/server-serializers.js +16 -0
  266. package/dist/plugins/simple-storage-api.d.ts +9 -0
  267. package/dist/plugins/simple-storage-api.js +26 -0
  268. package/dist/plugins/snapshot-groups.d.ts +16 -0
  269. package/dist/plugins/snapshot-groups.js +81 -0
  270. package/dist/plugins/structured-query-builder.d.ts +21 -0
  271. package/dist/plugins/structured-query-builder.js +60 -0
  272. package/dist/plugins/structured-snapshot-service.d.ts +16 -0
  273. package/dist/plugins/structured-snapshot-service.js +286 -0
  274. package/dist/plugins/structured-storage-api.d.ts +22 -0
  275. package/dist/plugins/structured-storage-api.js +91 -0
  276. package/dist/plugins/structured-storage-transaction.d.ts +22 -0
  277. package/dist/plugins/structured-storage-transaction.js +63 -0
  278. package/dist/plugins/structured-table-naming.d.ts +1 -0
  279. package/dist/plugins/structured-table-naming.js +18 -0
  280. package/dist/plugins/trusted-keys.d.ts +11 -0
  281. package/dist/plugins/trusted-keys.js +67 -0
  282. package/dist/plugins/trusted-signing-keys.d.ts +27 -0
  283. package/dist/plugins/trusted-signing-keys.js +40 -0
  284. package/dist/plugins/uninstall-bundle.d.ts +29 -0
  285. package/dist/plugins/uninstall-bundle.js +150 -0
  286. package/dist/plugins/verify-package.d.ts +36 -0
  287. package/dist/plugins/verify-package.js +121 -0
  288. package/dist/routes/admin-static.d.ts +8 -0
  289. package/dist/routes/admin-static.js +196 -0
  290. package/dist/routes/api-keys.d.ts +2 -0
  291. package/dist/routes/api-keys.js +95 -0
  292. package/dist/routes/api-playground.d.ts +2 -0
  293. package/dist/routes/api-playground.js +21 -0
  294. package/dist/routes/auth.d.ts +2 -0
  295. package/dist/routes/auth.js +102 -0
  296. package/dist/routes/content.d.ts +3 -0
  297. package/dist/routes/content.js +471 -0
  298. package/dist/routes/data-types.d.ts +3 -0
  299. package/dist/routes/data-types.js +100 -0
  300. package/dist/routes/docs-static.d.ts +8 -0
  301. package/dist/routes/docs-static.js +137 -0
  302. package/dist/routes/document-types.d.ts +3 -0
  303. package/dist/routes/document-types.js +149 -0
  304. package/dist/routes/headless-bridge.d.ts +10 -0
  305. package/dist/routes/headless-bridge.js +207 -0
  306. package/dist/routes/health.d.ts +2 -0
  307. package/dist/routes/health.js +15 -0
  308. package/dist/routes/marketplace-proxy.d.ts +8 -0
  309. package/dist/routes/marketplace-proxy.js +135 -0
  310. package/dist/routes/media.d.ts +6 -0
  311. package/dist/routes/media.js +93 -0
  312. package/dist/routes/plugin-api-routes.d.ts +12 -0
  313. package/dist/routes/plugin-api-routes.js +224 -0
  314. package/dist/routes/plugin-assets.d.ts +8 -0
  315. package/dist/routes/plugin-assets.js +51 -0
  316. package/dist/routes/plugin-management.d.ts +7 -0
  317. package/dist/routes/plugin-management.js +525 -0
  318. package/dist/routes/plugin-storage.d.ts +8 -0
  319. package/dist/routes/plugin-storage.js +104 -0
  320. package/dist/routes/plugins.d.ts +2 -0
  321. package/dist/routes/plugins.js +36 -0
  322. package/dist/routes/public-api.d.ts +9 -0
  323. package/dist/routes/public-api.js +545 -0
  324. package/dist/routes/published-item.d.ts +57 -0
  325. package/dist/routes/published-item.js +236 -0
  326. package/dist/routes/setup.d.ts +9 -0
  327. package/dist/routes/setup.js +477 -0
  328. package/dist/routes/snapshots.d.ts +3 -0
  329. package/dist/routes/snapshots.js +65 -0
  330. package/dist/routes/templates.d.ts +3 -0
  331. package/dist/routes/templates.js +37 -0
  332. package/dist/routes/themes.d.ts +2 -0
  333. package/dist/routes/themes.js +30 -0
  334. package/dist/routes/users.d.ts +2 -0
  335. package/dist/routes/users.js +113 -0
  336. package/dist/sandbox/register-local-plugin.d.ts +36 -0
  337. package/dist/sandbox/register-local-plugin.js +108 -0
  338. package/dist/sandbox/seed-sandbox.d.ts +20 -0
  339. package/dist/sandbox/seed-sandbox.js +122 -0
  340. package/dist/scripts/copy-admin-bundle.d.ts +1 -0
  341. package/dist/scripts/copy-admin-bundle.js +24 -0
  342. package/dist/scripts/copy-docs-bundle.d.ts +1 -0
  343. package/dist/scripts/copy-docs-bundle.js +22 -0
  344. package/dist/scripts/create-api-key.d.ts +5 -0
  345. package/dist/scripts/create-api-key.js +56 -0
  346. package/dist/scripts/generate-prisma-clients.d.ts +6 -0
  347. package/dist/scripts/generate-prisma-clients.js +125 -0
  348. package/dist/scripts/pack-bundled-templates.d.ts +1 -0
  349. package/dist/scripts/pack-bundled-templates.js +87 -0
  350. package/dist/setup/build-connection-string.d.ts +19 -0
  351. package/dist/setup/build-connection-string.js +14 -0
  352. package/dist/setup/bundled-templates.d.ts +19 -0
  353. package/dist/setup/bundled-templates.js +121 -0
  354. package/dist/setup/ensure-prisma-client.d.ts +3 -0
  355. package/dist/setup/ensure-prisma-client.js +23 -0
  356. package/dist/setup/install-starter.d.ts +14 -0
  357. package/dist/setup/install-starter.js +76 -0
  358. package/dist/setup/normalize-database-url.d.ts +2 -0
  359. package/dist/setup/normalize-database-url.js +35 -0
  360. package/dist/setup/persist-env-config.d.ts +2 -0
  361. package/dist/setup/persist-env-config.js +63 -0
  362. package/dist/setup/provision-database.d.ts +2 -0
  363. package/dist/setup/provision-database.js +89 -0
  364. package/dist/setup-app.d.ts +4 -0
  365. package/dist/setup-app.js +43 -0
  366. package/package.json +96 -0
  367. package/prisma/schema.prisma +303 -0
@@ -0,0 +1,100 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { z } from "zod";
3
+ import { ApiErrorSchema, CreateDataTypeRequestSchema, DataTypeListQuerySchema, DataTypeListResponseSchema, DataTypeResponseSchema, UpdateDataTypeRequestSchema, } from "@velora-cms/api-schemas";
4
+ import { requireAuth } from "../auth/require-auth.js";
5
+ import { validateDataTypeSettings } from "../data-types/validate-data-type-settings.js";
6
+ const DataTypeIdParamsSchema = z.object({ id: z.string().uuid() });
7
+ // Data Types: named, admin-created configurations of registry editors.
8
+ // Auth tiers mirror document-types exactly (mutations need a JWT, reads
9
+ // are public) — they're the same kind of schema object.
10
+ export const dataTypeRoutes = async (app, opts) => {
11
+ const { adapter } = opts;
12
+ app.post("/api/data-types", {
13
+ onRequest: requireAuth,
14
+ schema: {
15
+ body: CreateDataTypeRequestSchema,
16
+ response: {
17
+ 201: DataTypeResponseSchema,
18
+ 400: ApiErrorSchema,
19
+ 401: ApiErrorSchema,
20
+ },
21
+ },
22
+ }, async (request, reply) => {
23
+ const dataType = { id: randomUUID(), ...request.body };
24
+ const settingsErrors = validateDataTypeSettings(dataType.editorId, dataType.settings);
25
+ if (settingsErrors.length > 0) {
26
+ return reply.code(400).send({
27
+ message: `Invalid settings for editor "${dataType.editorId}": ${settingsErrors.join("; ")}`,
28
+ });
29
+ }
30
+ await adapter.createDataTypeConfig(dataType);
31
+ return reply.code(201).send({ dataType });
32
+ });
33
+ app.get("/api/data-types", {
34
+ schema: {
35
+ querystring: DataTypeListQuerySchema,
36
+ response: {
37
+ 200: DataTypeListResponseSchema,
38
+ },
39
+ },
40
+ }, async (request) => {
41
+ const { cursor, limit } = request.query;
42
+ const items = await adapter.listDataTypeConfigs({ cursor, limit });
43
+ const nextCursor = items.length === limit ? (items[items.length - 1]?.id ?? null) : null;
44
+ return { items, nextCursor };
45
+ });
46
+ app.get("/api/data-types/:id", {
47
+ schema: {
48
+ params: DataTypeIdParamsSchema,
49
+ response: {
50
+ 200: DataTypeResponseSchema,
51
+ 404: ApiErrorSchema,
52
+ },
53
+ },
54
+ }, async (request, reply) => {
55
+ const dataType = await adapter.getDataTypeConfig(request.params.id);
56
+ if (!dataType) {
57
+ return reply.code(404).send({ message: `No data type found with id ${request.params.id}` });
58
+ }
59
+ return { dataType };
60
+ });
61
+ // Partial update of the two mutable columns; editorId is immutable
62
+ // (absent from the request schema on purpose — see
63
+ // data-type-api.schema.ts).
64
+ app.patch("/api/data-types/:id", {
65
+ onRequest: requireAuth,
66
+ schema: {
67
+ params: DataTypeIdParamsSchema,
68
+ body: UpdateDataTypeRequestSchema,
69
+ response: {
70
+ 200: DataTypeResponseSchema,
71
+ 400: ApiErrorSchema,
72
+ 401: ApiErrorSchema,
73
+ 404: ApiErrorSchema,
74
+ },
75
+ },
76
+ }, async (request, reply) => {
77
+ const existing = await adapter.getDataTypeConfig(request.params.id);
78
+ if (!existing) {
79
+ return reply.code(404).send({ message: `No data type found with id ${request.params.id}` });
80
+ }
81
+ // Validate on every update, even name-only ones — a self-healing
82
+ // invariant once this ships (existing.settings is always valid
83
+ // going forward). If a built-in's settingsSchema ever tightens
84
+ // later, an old config with now-invalid stored settings would
85
+ // block until fixed.
86
+ const settings = request.body.settings ?? existing.settings;
87
+ const settingsErrors = validateDataTypeSettings(existing.editorId, settings);
88
+ if (settingsErrors.length > 0) {
89
+ return reply.code(400).send({
90
+ message: `Invalid settings for editor "${existing.editorId}": ${settingsErrors.join("; ")}`,
91
+ });
92
+ }
93
+ await adapter.updateDataTypeConfig(existing.id, {
94
+ name: request.body.name ?? existing.name,
95
+ settings,
96
+ });
97
+ const dataType = await adapter.getDataTypeConfig(existing.id);
98
+ return { dataType: dataType };
99
+ });
100
+ };
@@ -0,0 +1,8 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ interface DocsDirResolution {
3
+ dir: string | null;
4
+ invalidOverride: string | undefined;
5
+ }
6
+ export declare function resolveDocsDir(): DocsDirResolution;
7
+ export declare function docsStaticRoutes(app: FastifyInstance): Promise<void>;
8
+ export {};
@@ -0,0 +1,137 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import fastifyStatic from "@fastify/static";
5
+ import { rateLimitConfig } from "../auth/rate-limit.js";
6
+ import { DOCS_RATE_LIMIT } from "../auth/route-rate-limits.js";
7
+ // A usable docs directory: it must actually be a directory (not a file —
8
+ // @fastify/static@10.1.2 throws "root must be a directory" at registration
9
+ // otherwise, crashing buildApp()) and it must contain a "v1" edition — an
10
+ // empty directory would otherwise resolve successfully and then serve bare
11
+ // per-route 404s instead of the clear 404-with-cause.
12
+ function isUsableDocsDir(candidate) {
13
+ try {
14
+ return statSync(candidate).isDirectory() && existsSync(join(candidate, "v1"));
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ export function resolveDocsDir() {
21
+ const override = process.env.VELORA_DOCS_DIR;
22
+ if (override) {
23
+ // path.resolve() before anything else: @fastify/static@10.1.2 also
24
+ // throws ("root must be an absolute path") at registration when root
25
+ // is relative. Resolving against cwd up front means a relative
26
+ // override behaves the same as an absolute one instead of crashing
27
+ // boot.
28
+ const resolved = resolve(override);
29
+ if (isUsableDocsDir(resolved))
30
+ return { dir: resolved, invalidOverride: undefined };
31
+ return { dir: null, invalidOverride: override };
32
+ }
33
+ const here = dirname(fileURLToPath(import.meta.url)); // dist/routes or src/routes
34
+ const candidates = [
35
+ join(here, "..", "docs-bundle"),
36
+ join(here, "..", "..", "..", "..", "apps", "docs", "dist-bundle"),
37
+ ];
38
+ const dir = candidates.find((c) => isUsableDocsDir(c)) ?? null;
39
+ return { dir, invalidOverride: undefined };
40
+ }
41
+ function escapeHtml(text) {
42
+ return text
43
+ .replace(/&/g, "&amp;")
44
+ .replace(/</g, "&lt;")
45
+ .replace(/>/g, "&gt;")
46
+ .replace(/"/g, "&quot;");
47
+ }
48
+ // The admin's always-visible Docs tab opens /docs/* directly in a browser
49
+ // tab, so a degraded response there should be readable HTML, not raw JSON.
50
+ // Anything else (headless API consumers, tests) keeps the existing JSON
51
+ // shape.
52
+ function wantsHtml(req) {
53
+ return (req.headers.accept ?? "").includes("text/html");
54
+ }
55
+ function sendDegraded(req, reply, message) {
56
+ if (wantsHtml(req)) {
57
+ return reply
58
+ .code(404)
59
+ .type("text/html")
60
+ .send(`<!doctype html><html><head><meta charset="utf-8"><title>Documentation unavailable</title></head>` +
61
+ `<body><h1>Documentation unavailable</h1><p>${escapeHtml(message)}</p></body></html>`);
62
+ }
63
+ return reply.code(404).send({ message });
64
+ }
65
+ export async function docsStaticRoutes(app) {
66
+ const { dir: docsDir, invalidOverride } = resolveDocsDir();
67
+ // S162: static-ish reads, but not unbounded — this family previously had
68
+ // no ceiling at all. Registered per-scope, matching the precedent in
69
+ // auth.ts/plugin-api-routes.ts/public-api.ts: @fastify/rate-limit
70
+ // decorates the instance, so it is registered in child scopes OR at the
71
+ // root, never both.
72
+ await app.register(import("@fastify/rate-limit"), rateLimitConfig);
73
+ // This module's routes serve static HTML/asset infra, not Zod-validated
74
+ // JSON — api-design.md's "every route needs a request/response schema"
75
+ // rule governs the Zod→OpenAPI chain, which these deliberately sit
76
+ // outside of. Hidden from Swagger up front (the same treatment
77
+ // public-api.ts gives mercurius's schema-less GraphiQL routes) rather
78
+ // than left to leak into the spec as bare, undocumented path entries.
79
+ app.addHook("onRoute", (routeOptions) => {
80
+ if (routeOptions.url.startsWith("/docs")) {
81
+ routeOptions.schema = { ...routeOptions.schema, hide: true };
82
+ }
83
+ });
84
+ // Exact-path redirects win over the wildcard the static handler adds.
85
+ // Fastify 5's Reply#redirect signature is (url, code), not (code, url) —
86
+ // verified against this repo's installed fastify@5.8.5 source
87
+ // (lib/reply.js), which differs from older Fastify majors.
88
+ const redirect = (reply) => reply.redirect("/docs/v1/", 302);
89
+ const docsRateLimit = { rateLimit: { max: DOCS_RATE_LIMIT.max(), timeWindow: DOCS_RATE_LIMIT.timeWindow() } };
90
+ app.get("/docs", { config: docsRateLimit }, async (_req, reply) => redirect(reply));
91
+ if (!docsDir) {
92
+ const message = invalidOverride
93
+ ? `VELORA_DOCS_DIR is set to "${invalidOverride}" but it is not usable (it must resolve to a ` +
94
+ 'directory containing a "v1" edition). Fix the override or unset VELORA_DOCS_DIR to fall back ' +
95
+ "to the built-in bundle location."
96
+ : "The documentation bundle is not installed. Expected it inside the server build " +
97
+ "(dist/docs-bundle) or at VELORA_DOCS_DIR. Build with `turbo run build` to produce it.";
98
+ app.get("/docs/", { config: docsRateLimit }, async (req, reply) => sendDegraded(req, reply, message));
99
+ app.get("/docs/*", { config: docsRateLimit }, async (req, reply) => sendDegraded(req, reply, message));
100
+ app.log.warn("[docs-static] no docs bundle found — /docs/* will answer 404-with-cause");
101
+ return;
102
+ }
103
+ // Registering the static mount and its not-found handler inside their own
104
+ // encapsulated child context (rather than directly on `app`) confines
105
+ // setNotFoundHandler to routes under this context's prefix. Fastify scopes
106
+ // setNotFoundHandler by the context's prefix — calling it directly on
107
+ // `app` (prefix "") would replace the app-wide 404 (and its JSON shape)
108
+ // for every unmatched route, including /api/*. The prefix here is
109
+ // "/docs/" (trailing slash, not "/docs") deliberately: Fastify collapses
110
+ // a route path of "/" registered inside a prefixed context down to the
111
+ // bare prefix string itself, so a "/docs" prefix would make the child's
112
+ // `get("/", ...)` land on "/docs" — colliding with the exact `app.get
113
+ // ("/docs", ...)` redirect above. Using "/docs/" as the prefix makes that
114
+ // same collapse land exactly on "/docs/", which is what we want.
115
+ // Isolation is covered by docs-static.integration.test.ts's "does not
116
+ // leak the docs-scoped 404 handler onto unrelated routes" test (an
117
+ // unknown /api/... path keeps Fastify's default 404 shape) and its
118
+ // /api/health check in the no-bundle-installed case.
119
+ await app.register(async (docsApp) => {
120
+ docsApp.get("/", { config: docsRateLimit }, async (_req, reply) => redirect(reply));
121
+ await docsApp.register(fastifyStatic, {
122
+ root: docsDir,
123
+ decorateReply: false,
124
+ redirect: true, // /docs/v1 -> /docs/v1/ so index resolution works
125
+ });
126
+ // Unknown paths under /docs/ get the bundle's own 404 page when present.
127
+ docsApp.setNotFoundHandler((_req, reply) => {
128
+ const notFoundPage = join(docsDir, "404.html");
129
+ if (existsSync(notFoundPage)) {
130
+ // decorateReply: false means reply.sendFile isn't available here —
131
+ // read the bundle's 404 page directly and send it.
132
+ return reply.code(404).type("text/html").send(readFileSync(notFoundPage));
133
+ }
134
+ return reply.code(404).send({ message: "not found" });
135
+ });
136
+ }, { prefix: "/docs/" });
137
+ }
@@ -0,0 +1,3 @@
1
+ import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
2
+ import type { ContentDb } from "../db/content-db.js";
3
+ export declare const documentTypeRoutes: FastifyPluginAsyncZod<ContentDb>;
@@ -0,0 +1,149 @@
1
+ import { z } from "zod";
2
+ import { ApiErrorSchema, CreateDocumentTypeRequestSchema, CreateDocumentTypeResponseSchema, DeleteDocumentTypeResponseSchema, DocumentTypeListQuerySchema, DocumentTypeListResponseSchema, SetDefaultTemplateRequestSchema, UpdateDocumentTypeRequestSchema, } from "@velora-cms/api-schemas";
3
+ import { DocumentTypeInUseError, DocumentTypeService, DocumentTypeUnknownDataTypesError } from "@velora-cms/content-service";
4
+ import { requireAuth } from "../auth/require-auth.js";
5
+ const DocumentTypeIdParamsSchema = z.object({ id: z.string().uuid() });
6
+ export const documentTypeRoutes = async (app, opts) => {
7
+ const { adapter } = opts;
8
+ const documentTypeService = new DocumentTypeService(adapter);
9
+ app.post("/api/document-types", {
10
+ onRequest: requireAuth,
11
+ schema: {
12
+ body: CreateDocumentTypeRequestSchema,
13
+ response: {
14
+ 201: CreateDocumentTypeResponseSchema,
15
+ 400: ApiErrorSchema,
16
+ 401: ApiErrorSchema,
17
+ },
18
+ },
19
+ }, async (request, reply) => {
20
+ try {
21
+ const documentType = await documentTypeService.createDocumentType(request.body);
22
+ return reply.code(201).send({ documentType });
23
+ }
24
+ catch (error) {
25
+ if (error instanceof DocumentTypeUnknownDataTypesError) {
26
+ return reply.code(400).send({ message: error.message });
27
+ }
28
+ throw error;
29
+ }
30
+ });
31
+ // Full edit of the mutable definition (Session 51). kind can never
32
+ // change (the strict schema has no kind key) and the default-template
33
+ // pointer is untouched — that stays with its dedicated PATCH below.
34
+ app.put("/api/document-types/:id", {
35
+ onRequest: requireAuth,
36
+ schema: {
37
+ params: DocumentTypeIdParamsSchema,
38
+ body: UpdateDocumentTypeRequestSchema,
39
+ response: {
40
+ 200: CreateDocumentTypeResponseSchema,
41
+ 400: ApiErrorSchema,
42
+ 401: ApiErrorSchema,
43
+ 404: ApiErrorSchema,
44
+ },
45
+ },
46
+ }, async (request, reply) => {
47
+ const existing = await adapter.getDocumentType(request.params.id);
48
+ if (!existing) {
49
+ return reply.code(404).send({ message: `No document type found with id ${request.params.id}` });
50
+ }
51
+ // treeRules semantics follow the STORED kind — the update schema
52
+ // can't know it.
53
+ if (existing.kind === "page" && !request.body.treeRules) {
54
+ return reply.code(400).send({ message: "treeRules is required when updating a page document type" });
55
+ }
56
+ if (existing.kind === "element" && request.body.treeRules) {
57
+ return reply.code(400).send({ message: "treeRules is not allowed on an element document type" });
58
+ }
59
+ const unknown = await documentTypeService.findUnknownDataTypeIds(request.body.fields);
60
+ if (unknown.length > 0) {
61
+ return reply.code(400).send({
62
+ message: `Unknown data type id(s): ${unknown.join(", ")}`,
63
+ });
64
+ }
65
+ await adapter.updateDocumentType(existing.id, {
66
+ name: request.body.name,
67
+ icon: request.body.icon,
68
+ fields: request.body.fields,
69
+ tabs: request.body.tabs,
70
+ treeRules: request.body.treeRules,
71
+ });
72
+ const documentType = await adapter.getDocumentType(existing.id);
73
+ return { documentType: documentType };
74
+ });
75
+ // No cascade: content and templates both block the delete rather than
76
+ // being silently swept away (the project's no-silent-data-loss rule).
77
+ app.delete("/api/document-types/:id", {
78
+ onRequest: requireAuth,
79
+ schema: {
80
+ params: DocumentTypeIdParamsSchema,
81
+ response: {
82
+ 200: DeleteDocumentTypeResponseSchema,
83
+ 401: ApiErrorSchema,
84
+ 404: ApiErrorSchema,
85
+ 409: ApiErrorSchema,
86
+ },
87
+ },
88
+ }, async (request, reply) => {
89
+ const existing = await adapter.getDocumentType(request.params.id);
90
+ if (!existing) {
91
+ return reply.code(404).send({ message: `No document type found with id ${request.params.id}` });
92
+ }
93
+ try {
94
+ await documentTypeService.assertDocumentTypeDeletable(existing.id);
95
+ }
96
+ catch (error) {
97
+ if (error instanceof DocumentTypeInUseError) {
98
+ const message = error.reason === "has-content"
99
+ ? `${error.contentCount} content item(s) use this document type — delete or move them first`
100
+ : "This document type still has templates — remove them first";
101
+ return reply.code(409).send({ message });
102
+ }
103
+ throw error;
104
+ }
105
+ // S154: deleteDocumentType now purges any trashed content of this
106
+ // type first (the one real hard delete in the trash lifecycle — see
107
+ // DocumentTypeService.deleteDocumentType) before removing the type
108
+ // row itself. The guard above already passed once that content was
109
+ // trashed, since countContentByDocumentType excludes trashed rows.
110
+ await documentTypeService.deleteDocumentType(existing.id);
111
+ return { success: true };
112
+ });
113
+ app.get("/api/document-types", {
114
+ schema: {
115
+ querystring: DocumentTypeListQuerySchema,
116
+ response: {
117
+ 200: DocumentTypeListResponseSchema,
118
+ },
119
+ },
120
+ }, async (request) => {
121
+ const { cursor, limit } = request.query;
122
+ const items = await adapter.listDocumentTypes({ cursor, limit });
123
+ const nextCursor = items.length === limit ? (items[items.length - 1]?.id ?? null) : null;
124
+ return { items, nextCursor };
125
+ });
126
+ // Narrow, single-purpose pointer-update endpoint — a template can't
127
+ // exist before its document type does, so defaultTemplateId is never set
128
+ // at create time. Deliberately not a general document-type update route.
129
+ app.patch("/api/document-types/:id/default-template", {
130
+ onRequest: requireAuth,
131
+ schema: {
132
+ params: DocumentTypeIdParamsSchema,
133
+ body: SetDefaultTemplateRequestSchema,
134
+ response: {
135
+ 200: CreateDocumentTypeResponseSchema,
136
+ 401: ApiErrorSchema,
137
+ 404: ApiErrorSchema,
138
+ },
139
+ },
140
+ }, async (request, reply) => {
141
+ const existing = await adapter.getDocumentType(request.params.id);
142
+ if (!existing) {
143
+ return reply.code(404).send({ message: `No document type found with id ${request.params.id}` });
144
+ }
145
+ await adapter.setDefaultTemplate(request.params.id, request.body.templateId);
146
+ const documentType = await adapter.getDocumentType(request.params.id);
147
+ return { documentType: documentType };
148
+ });
149
+ };
@@ -0,0 +1,10 @@
1
+ import type { DatabaseAdapter } from "@velora-cms/db-adapter";
2
+ import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
3
+ export declare function toContentPath(rawUrl: string): string | null;
4
+ export declare function createFrameworkErrorHandler(): (error: Error & {
5
+ code?: string;
6
+ statusCode?: number;
7
+ }, request: FastifyRequest, reply: FastifyReply) => void;
8
+ export declare function registerHeadlessBridge(app: FastifyInstance, deps: {
9
+ adapter: DatabaseAdapter;
10
+ }): void;
@@ -0,0 +1,207 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { STATUS_CODES } from "node:http";
3
+ import { createRequire } from "node:module";
4
+ import { isHeadlessEnabled } from "../config/headless.js";
5
+ // S156: the honest-headless bridge. Velora is headless-first (the template
6
+ // rendering engine is the v1.x epic) — until it lands, a content node's
7
+ // URL on this host renders an explainer instead of a bare 404, and every
8
+ // non-content path gets one neutral page that never leaks whether an
9
+ // unpublished or trashed node exists there. Only GET + Accept: text/html
10
+ // sees any of this; API consumers keep Fastify's stock JSON 404
11
+ // byte-identical. Deliberately claims the app-wide not-found handler —
12
+ // the docs-static.ts warning about doing so is exactly the intent here
13
+ // (that handler is scoped to its own "/docs/" prefixed child context, so
14
+ // it never collides with this app-wide one). registerHeadlessBridge below
15
+ // covers ordinary unmatched routes; createFrameworkErrorHandler (wired into
16
+ // Fastify's constructor-time `frameworkErrors` option in app.ts, not into
17
+ // this function) covers the separate low-level "URL couldn't even be
18
+ // routed" case find-my-way raises for hostile encodings — see its own doc
19
+ // comment for why that needs a different hook entirely.
20
+ // Rate limiting deferred to S162 (tracked for a future session).
21
+ // tokens.css is generated at build time (not committed) — resolved through
22
+ // the package's exported "./tokens.css" subpath (NOT dist/tokens.css, which
23
+ // is not an exported subpath — recorded gotcha). A missing build must not
24
+ // fail boot: the page degrades to its fallback values.
25
+ function loadTokensCss() {
26
+ try {
27
+ const require = createRequire(import.meta.url);
28
+ return readFileSync(require.resolve("@velora-cms/design-tokens/tokens.css"), "utf8");
29
+ }
30
+ catch {
31
+ return "";
32
+ }
33
+ }
34
+ function escapeHtml(text) {
35
+ return text
36
+ .replace(/&/g, "&amp;")
37
+ .replace(/</g, "&lt;")
38
+ .replace(/>/g, "&gt;")
39
+ .replace(/"/g, "&quot;");
40
+ }
41
+ function wantsHtml(req) {
42
+ return (req.headers.accept ?? "").includes("text/html");
43
+ }
44
+ // True when `text` contains a C0 control character (code points 0-31,
45
+ // i.e. NUL through unit separator). Written as a charCode scan rather
46
+ // than a regex literal so no raw control-byte source text has to survive
47
+ // tool round-tripping intact.
48
+ function hasControlChars(text) {
49
+ for (let i = 0; i < text.length; i++) {
50
+ if (text.charCodeAt(i) <= 31)
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ // Normalizes the request URL into a candidate content path, or null when
56
+ // it can't be one: undecodable escapes and control characters render the
57
+ // neutral page rather than a 500.
58
+ export function toContentPath(rawUrl) {
59
+ let pathname;
60
+ try {
61
+ pathname = decodeURIComponent(new URL(rawUrl, "http://placeholder").pathname);
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ if (hasControlChars(pathname))
67
+ return null;
68
+ if (pathname.length > 1 && pathname.endsWith("/"))
69
+ pathname = pathname.slice(0, -1);
70
+ return pathname;
71
+ }
72
+ // Real token names (packages/design-tokens/dist/tokens.css, generated by
73
+ // `pnpm --filter @velora-cms/design-tokens build`): there is no --color-background
74
+ // / --color-foreground / --color-muted / --color-primary pair — those were
75
+ // placeholder guesses. The actual scale is --color-background-base,
76
+ // --color-text-primary, --color-background-hover (used here as the
77
+ // <code> block's fill), and --color-primary-600 (the 600 step reads as
78
+ // "link" contrast against the light background token). Fallback values
79
+ // are kept so a missing build still renders something reasonable.
80
+ function pageHtml(tokensCss, content) {
81
+ return (`<!doctype html><html dir="auto"><head><meta charset="utf-8">` +
82
+ `<meta name="viewport" content="width=device-width, initial-scale=1">` +
83
+ `<meta name="robots" content="noindex">` +
84
+ `<title>${escapeHtml(content.title)} — Velora</title>` +
85
+ `<style>${tokensCss}` +
86
+ `body{margin:0;font-family:system-ui,sans-serif;background:var(--color-background-base,#f8fafc);` +
87
+ `color:var(--color-text-primary,#0f172a);display:grid;min-block-size:100vh;place-items:center}` +
88
+ `main{max-inline-size:40rem;padding-block:2rem;padding-inline:1.5rem}` +
89
+ `h1{font-size:1.5rem;margin-block-end:.5rem}` +
90
+ `p{line-height:1.6;margin-block:.5rem}` +
91
+ `code{display:block;overflow-x:auto;padding-block:.75rem;padding-inline:1rem;` +
92
+ `border-radius:.5rem;background:var(--color-background-hover,#e2e8f0);font-size:.875rem}` +
93
+ `a{color:var(--color-primary-600,#2563eb)}` +
94
+ `.type{font-size:.875rem;opacity:.7}` +
95
+ `</style></head><body><main>${content.bodyHtml}</main></body></html>`);
96
+ }
97
+ function apiSectionHtml(origin, path) {
98
+ if (!isHeadlessEnabled()) {
99
+ return (`<p>The public content API is not enabled on this instance ` +
100
+ `(<code style="display:inline;padding:0">VELORA_HEADLESS</code> is off), so there is no ` +
101
+ `public URL for this content. An administrator can enable it at deploy time.</p>`);
102
+ }
103
+ const byPathUrl = `${origin}/api/v1/content/by-path?path=${encodeURIComponent(path)}`;
104
+ return (`<p>Fetch it from the headless API (requests need an API key):</p>` +
105
+ `<code>${escapeHtml(byPathUrl)}</code>` +
106
+ `<p>GraphQL is available at <code style="display:inline;padding:0">/api/v1/graphql</code>.</p>`);
107
+ }
108
+ // The one neutral page shared by missing, unpublished, and trashed paths —
109
+ // existence is never leaked. The copy doubles as the instance's front door
110
+ // (this is also what "/" renders).
111
+ function neutralPageContent() {
112
+ const bodyHtml = `<h1>This is a headless Velora CMS</h1>` +
113
+ `<p>Nothing is published at this URL. Content on this instance is not rendered ` +
114
+ `into pages here — it is served through the API to a frontend of your choice ` +
115
+ `(see the starter frontends in the documentation).</p>` +
116
+ `<p><a href="/docs/v1/">Documentation</a></p>`;
117
+ return { title: "Headless Velora", bodyHtml };
118
+ }
119
+ // True for requests this bridge answers at all: a browser-tab GET outside
120
+ // /api (which keeps its own JSON 404s — /docs and /plugin-assets have
121
+ // their own scoped handlers and never reach either handler below).
122
+ function isBridgedRequest(request) {
123
+ return request.method === "GET" && wantsHtml(request) && !(request.raw.url ?? "").startsWith("/api/");
124
+ }
125
+ // find-my-way rejects some malformed URLs (a lone "%ff" with no valid UTF-8
126
+ // decoding, "%00", etc.) at the routing layer itself — before Fastify ever
127
+ // builds a matched or not-found route. Verified against the installed
128
+ // fastify@5.8.5 source (fastify.js's onBadUrl): with no `frameworkErrors`
129
+ // option, that path answers via a raw `res.writeHead(400, ...); res.end(...)`
130
+ // call that bypasses setNotFoundHandler, setErrorHandler, hooks — everything
131
+ // — so setNotFoundHandler alone (the brief's original sketch) can never see
132
+ // or reshape this case. `frameworkErrors`, a Fastify constructor-time-only
133
+ // option, is the one hook the framework offers into that path; it also
134
+ // covers FST_ERR_ASYNC_CONSTRAINT (unreachable here — this app registers no
135
+ // route constraints), so anything other than a bridged bad-url request
136
+ // reproduces Fastify's own no-frameworkErrors JSON body/status exactly,
137
+ // changing nothing this bridge doesn't own.
138
+ export function createFrameworkErrorHandler() {
139
+ const tokensCss = loadTokensCss();
140
+ return (error, request, reply) => {
141
+ if (error.code === "FST_ERR_BAD_URL" && isBridgedRequest(request)) {
142
+ reply.header("X-Robots-Tag", "noindex");
143
+ reply.header("Cache-Control", "no-store");
144
+ reply.code(404).type("text/html").send(pageHtml(tokensCss, neutralPageContent()));
145
+ return;
146
+ }
147
+ const statusCode = error.statusCode ?? 500;
148
+ reply.code(statusCode).send({
149
+ error: STATUS_CODES[statusCode] ?? "Internal Server Error",
150
+ code: error.code,
151
+ message: error.message,
152
+ statusCode,
153
+ });
154
+ };
155
+ }
156
+ export function registerHeadlessBridge(app, deps) {
157
+ const tokensCss = loadTokensCss();
158
+ if (!tokensCss) {
159
+ app.log.warn("[headless-bridge] tokens.css not found — explainer pages use fallback styling");
160
+ }
161
+ app.setNotFoundHandler(async (request, reply) => {
162
+ if (!isBridgedRequest(request)) {
163
+ const message = `Route ${request.method}:${request.url} not found`;
164
+ // Fastify's default 404 handler logs this line; keep parity so
165
+ // operators grepping for it don't lose unmatched-route visibility.
166
+ request.log.info(message);
167
+ return reply.code(404).send({
168
+ message,
169
+ error: "Not Found",
170
+ statusCode: 404,
171
+ });
172
+ }
173
+ reply.header("X-Robots-Tag", "noindex");
174
+ reply.header("Cache-Control", "no-store");
175
+ const path = toContentPath(request.url);
176
+ try {
177
+ const node = path
178
+ ? await deps.adapter.getContentNodeByPath(path, { publishedOnly: true })
179
+ : null;
180
+ if (!node || !path) {
181
+ return reply.code(404).type("text/html").send(pageHtml(tokensCss, neutralPageContent()));
182
+ }
183
+ const label = path.split("/").filter(Boolean).pop() ?? path;
184
+ const documentType = await deps.adapter.getDocumentType(node.documentTypeId);
185
+ const origin = `${request.protocol}://${request.headers.host ?? "localhost"}`;
186
+ const bodyHtml = `<h1>${escapeHtml(label)}</h1>` +
187
+ (documentType ? `<p class="type">${escapeHtml(documentType.name)}</p>` : "") +
188
+ `<p>This content exists — but this Velora instance is headless, so content URLs ` +
189
+ `don't render pages on this host.</p>` +
190
+ apiSectionHtml(origin, path) +
191
+ `<p><a href="/docs/v1/">Documentation</a></p>`;
192
+ return reply.code(200).type("text/html").send(pageHtml(tokensCss, { title: label, bodyHtml }));
193
+ }
194
+ catch (error) {
195
+ // A rejected adapter call (e.g. a dropped DB connection) must never
196
+ // reach Fastify's default error handler here: this is the public
197
+ // origin, and that handler serializes err.message — a raw adapter/DB
198
+ // error string — into a 500 body for an anonymous visitor. The
199
+ // neutral 404 is the correct degraded answer: it's already what
200
+ // "genuinely nothing here" and "can't tell" both render as, so a
201
+ // lookup failure just joins that same bucket rather than leaking
202
+ // internals or the fact that a lookup was attempted at all.
203
+ app.log.error({ err: error }, "[headless-bridge] adapter lookup failed; degrading to the neutral 404");
204
+ return reply.code(404).type("text/html").send(pageHtml(tokensCss, neutralPageContent()));
205
+ }
206
+ });
207
+ }
@@ -0,0 +1,2 @@
1
+ import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
2
+ export declare const healthRoutes: FastifyPluginAsyncZod;
@@ -0,0 +1,15 @@
1
+ import { HealthCheckSchema } from "@velora-cms/api-schemas";
2
+ export const healthRoutes = async (app) => {
3
+ app.get("/api/health", {
4
+ schema: {
5
+ response: {
6
+ 200: HealthCheckSchema,
7
+ },
8
+ },
9
+ }, async () => {
10
+ return {
11
+ status: "ok",
12
+ timestamp: new Date().toISOString(),
13
+ };
14
+ });
15
+ };
@@ -0,0 +1,8 @@
1
+ import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
2
+ import type { DatabaseAdapter } from "@velora-cms/db-adapter";
3
+ export interface MarketplaceProxyOptions {
4
+ marketplaceBaseUrl: string;
5
+ activeDialect: string;
6
+ adapter: DatabaseAdapter;
7
+ }
8
+ export declare const marketplaceProxyRoutes: FastifyPluginAsyncZod<MarketplaceProxyOptions>;