@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,135 @@
1
+ import { ApiErrorSchema, MarketplaceInstallBodySchema, MarketplaceInstallResponseSchema, MarketplaceListQuerySchema, MarketplaceListResponseSchema, MarketplaceListingDetailSchema, MarketplacePluginIdParamsSchema, } from "@velora-cms/api-schemas";
2
+ import { requireAdmin } from "../auth/require-auth.js";
3
+ import { prisma } from "../db/prisma.js";
4
+ import { installFromMarketplace } from "../marketplace/install-from-marketplace.js";
5
+ import { MarketplaceNotFoundError, MarketplaceUnavailableError, fetchListing, fetchListings, } from "../marketplace/marketplace-client.js";
6
+ /** Compatibility is decided HERE, against this CMS's own dialect: the
7
+ * registry would refuse an incompatible install anyway (S64), so the
8
+ * browse screen says so up front rather than letting a user try. */
9
+ function annotate(listing, activeDialect) {
10
+ const dialects = listing.latestVersion?.supportedDialects ?? [];
11
+ return { ...listing, compatible: dialects.includes(activeDialect) };
12
+ }
13
+ export const marketplaceProxyRoutes = async (app, opts) => {
14
+ const { marketplaceBaseUrl, activeDialect, adapter } = opts;
15
+ app.get("/api/plugin-management/marketplace/listings", {
16
+ onRequest: requireAdmin,
17
+ schema: {
18
+ querystring: MarketplaceListQuerySchema,
19
+ response: { 200: MarketplaceListResponseSchema, 401: ApiErrorSchema, 403: ApiErrorSchema, 503: ApiErrorSchema },
20
+ },
21
+ }, async (request, reply) => {
22
+ try {
23
+ const body = (await fetchListings(marketplaceBaseUrl, request.query));
24
+ return {
25
+ items: body.items.map((item) => annotate(item, activeDialect)),
26
+ nextCursor: body.nextCursor ?? null,
27
+ activeDialect,
28
+ marketplaceBaseUrl,
29
+ };
30
+ }
31
+ catch (error) {
32
+ if (error instanceof MarketplaceUnavailableError) {
33
+ return reply.code(503).send({ message: error.message });
34
+ }
35
+ throw error;
36
+ }
37
+ });
38
+ app.get("/api/plugin-management/marketplace/listings/:pluginId", {
39
+ onRequest: requireAdmin,
40
+ schema: {
41
+ params: MarketplacePluginIdParamsSchema,
42
+ response: {
43
+ 200: MarketplaceListingDetailSchema,
44
+ 401: ApiErrorSchema,
45
+ 403: ApiErrorSchema,
46
+ 404: ApiErrorSchema,
47
+ 503: ApiErrorSchema,
48
+ },
49
+ },
50
+ }, async (request, reply) => {
51
+ try {
52
+ const listing = (await fetchListing(marketplaceBaseUrl, request.params.pluginId));
53
+ return {
54
+ ...annotate(listing, activeDialect),
55
+ versions: listing.versions ?? [],
56
+ activeDialect,
57
+ marketplaceBaseUrl,
58
+ };
59
+ }
60
+ catch (error) {
61
+ if (error instanceof MarketplaceNotFoundError) {
62
+ return reply.code(404).send({ message: error.message });
63
+ }
64
+ if (error instanceof MarketplaceUnavailableError) {
65
+ return reply.code(503).send({ message: error.message });
66
+ }
67
+ throw error;
68
+ }
69
+ });
70
+ // In-CMS marketplace install (Session 138) — the ordered
71
+ // already-installed -> listing/version -> (paid) license -> download ->
72
+ // verify -> register spine lives in installFromMarketplace so it stays
73
+ // reviewable in one place; this handler just maps its outcome to a
74
+ // status code.
75
+ app.post("/api/plugin-management/marketplace/install", {
76
+ onRequest: requireAdmin,
77
+ schema: {
78
+ body: MarketplaceInstallBodySchema,
79
+ response: {
80
+ 201: MarketplaceInstallResponseSchema,
81
+ 400: ApiErrorSchema,
82
+ 401: ApiErrorSchema,
83
+ 403: ApiErrorSchema,
84
+ 404: ApiErrorSchema,
85
+ 409: ApiErrorSchema,
86
+ 422: ApiErrorSchema,
87
+ 503: ApiErrorSchema,
88
+ },
89
+ },
90
+ }, async (request, reply) => {
91
+ try {
92
+ const outcome = await installFromMarketplace({ prisma, registry: app.pluginRegistry, marketplaceBaseUrl, adapter },
93
+ // installedBy is server-derived from the verified JWT (requireAdmin
94
+ // already ran via onRequest), never taken from the request body —
95
+ // D2's createdBy rule for any sample content this install seeds.
96
+ { ...request.body, installedBy: request.user.sub });
97
+ if (outcome.ok) {
98
+ return reply.code(201).send({
99
+ pluginId: outcome.pluginId,
100
+ version: outcome.version,
101
+ trustTier: outcome.trustTier,
102
+ licensed: outcome.licensed,
103
+ ...(outcome.members ? { members: outcome.members } : {}),
104
+ });
105
+ }
106
+ const status = {
107
+ "already-installed": 409,
108
+ "not-found": 404,
109
+ "license-required": 400,
110
+ "site-domain-missing": 409,
111
+ "license-rejected": 403,
112
+ "verification-failed": 422,
113
+ // M2 (S138 hardening review) — see InstallFailure's own comment in
114
+ // install-from-marketplace.ts for why 422 vs 409 was chosen for
115
+ // each of these three.
116
+ incompatible: 422,
117
+ "dependency-missing": 409,
118
+ "install-refused": 422,
119
+ // S152: a member of the requested bundle failed — the bundle
120
+ // package itself may be fine, but the SET it names can't be
121
+ // processed on this server right now. Same 422 family as
122
+ // verification-failed/install-refused; the message names the
123
+ // offending member and its underlying reason.
124
+ "bundle-member-failed": 422,
125
+ };
126
+ return reply.code(status[outcome.failure.code]).send({ message: outcome.failure.message });
127
+ }
128
+ catch (error) {
129
+ if (error instanceof MarketplaceUnavailableError) {
130
+ return reply.code(503).send({ message: error.message });
131
+ }
132
+ throw error;
133
+ }
134
+ });
135
+ };
@@ -0,0 +1,6 @@
1
+ import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
2
+ import type { MediaService } from "../media/media-service.js";
3
+ export interface MediaRoutesOptions {
4
+ mediaService: MediaService;
5
+ }
6
+ export declare const mediaRoutes: FastifyPluginAsyncZod<MediaRoutesOptions>;
@@ -0,0 +1,93 @@
1
+ import { z } from "zod";
2
+ import { ApiErrorSchema, MediaDeleteResponseSchema, MediaFileResponseSchema, MediaListQuerySchema, MediaListResponseSchema, } from "@velora-cms/api-schemas";
3
+ import { requireAuth } from "../auth/require-auth.js";
4
+ const MediaParamsSchema = z.object({ id: z.string().uuid() });
5
+ export const mediaRoutes = async (app, opts) => {
6
+ const { mediaService } = opts;
7
+ async function toResponse(media) {
8
+ return { ...media, downloadUrl: await mediaService.getDownloadUrl(media.id) };
9
+ }
10
+ app.post("/api/media", {
11
+ onRequest: requireAuth,
12
+ schema: {
13
+ // Multipart body — file part validated by hand below; Zod can't
14
+ // describe a multipart stream, so validation here is response-side
15
+ // plus the explicit 400s.
16
+ consumes: ["multipart/form-data"],
17
+ response: {
18
+ 201: MediaFileResponseSchema,
19
+ 400: ApiErrorSchema,
20
+ 401: ApiErrorSchema,
21
+ },
22
+ },
23
+ }, async (request, reply) => {
24
+ const file = await request.file();
25
+ if (!file || !file.filename) {
26
+ return reply.code(400).send({ message: "A multipart file field is required" });
27
+ }
28
+ const data = await file.toBuffer();
29
+ const media = await mediaService.upload({
30
+ filename: file.filename,
31
+ mimeType: file.mimetype,
32
+ data,
33
+ uploadedBy: request.user.sub,
34
+ });
35
+ return reply.code(201).send(await toResponse(media));
36
+ });
37
+ // Media library listing — admin-authed (SEC-6, S175 audit). This lists
38
+ // EVERY uploaded asset regardless of publish state, each with a working
39
+ // presigned downloadUrl, so it must not be anonymously enumerable;
40
+ // published/headless media has its own API-key-gated /api/v1/media
41
+ // surface. The admin UI reaches this through authFetch.
42
+ app.get("/api/media", {
43
+ onRequest: requireAuth,
44
+ schema: {
45
+ querystring: MediaListQuerySchema,
46
+ response: {
47
+ 200: MediaListResponseSchema,
48
+ 401: ApiErrorSchema,
49
+ },
50
+ },
51
+ }, async (request) => {
52
+ const { cursor, limit, search } = request.query;
53
+ const files = await mediaService.list({ cursor, limit, search });
54
+ const items = await Promise.all(files.map(toResponse));
55
+ const nextCursor = files.length === limit ? (files[files.length - 1]?.id ?? null) : null;
56
+ return { items, nextCursor };
57
+ });
58
+ app.get("/api/media/:id", {
59
+ onRequest: requireAuth,
60
+ schema: {
61
+ params: MediaParamsSchema,
62
+ response: {
63
+ 200: MediaFileResponseSchema,
64
+ 401: ApiErrorSchema,
65
+ 404: ApiErrorSchema,
66
+ },
67
+ },
68
+ }, async (request, reply) => {
69
+ const media = await mediaService.getById(request.params.id);
70
+ if (!media) {
71
+ return reply.code(404).send({ message: `No media file found for id ${request.params.id}` });
72
+ }
73
+ return toResponse(media);
74
+ });
75
+ app.delete("/api/media/:id", {
76
+ onRequest: requireAuth,
77
+ schema: {
78
+ params: MediaParamsSchema,
79
+ response: {
80
+ 200: MediaDeleteResponseSchema,
81
+ 401: ApiErrorSchema,
82
+ 404: ApiErrorSchema,
83
+ },
84
+ },
85
+ }, async (request, reply) => {
86
+ const media = await mediaService.getById(request.params.id);
87
+ if (!media) {
88
+ return reply.code(404).send({ message: `No media file found for id ${request.params.id}` });
89
+ }
90
+ await mediaService.delete(media.id);
91
+ return { success: true };
92
+ });
93
+ };
@@ -0,0 +1,12 @@
1
+ import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
2
+ import type { ContentService } from "@velora-cms/content-service";
3
+ import type { DatabaseAdapter } from "@velora-cms/db-adapter";
4
+ import type { PluginRegistryService } from "../plugins/plugin-registry-service.js";
5
+ import type { StructuredStorageAPI } from "../plugins/structured-storage-api.js";
6
+ export interface PluginApiRouteOptions {
7
+ registry: PluginRegistryService;
8
+ structuredStorageAPI: StructuredStorageAPI;
9
+ contentService: ContentService;
10
+ adapter: DatabaseAdapter;
11
+ }
12
+ export declare const pluginApiRoutes: FastifyPluginAsyncZod<PluginApiRouteOptions>;
@@ -0,0 +1,224 @@
1
+ import { ApiErrorSchema } from "@velora-cms/api-schemas";
2
+ import { PluginNotActiveError, PluginNotInstalledError } from "../plugins/plugin-registry-service.js";
3
+ import { buildContext } from "../plugins/plugin-context.js";
4
+ import { requireApiKey } from "../auth/api-keys.js";
5
+ import { rateLimitConfig } from "../auth/rate-limit.js";
6
+ import { requireAdmin } from "../auth/require-auth.js";
7
+ import { corsOptionsFromEnv } from "../config/cors.js";
8
+ function findDeclaredRoute(manifest, method, path) {
9
+ const declared = manifest.type === "section" ? (manifest.contributions.apiRoutes ?? []) : [];
10
+ return declared.find((route) => route.method === method && route.path === path);
11
+ }
12
+ // Real runtime mounting of the apiRoutes contribution (Month 8, Session
13
+ // 89) — previously manifest-validated only, never mounted anywhere.
14
+ // Fastify 5's router is sealed once the server starts listening, so a
15
+ // plugin's routes (only known once its manifest is installed/activated,
16
+ // possibly long after boot) can't be registered as literal Fastify
17
+ // routes. Instead, exactly 5 routes are registered here at boot — one
18
+ // per HTTP method contributions.apiRoutes supports — each a wildcard
19
+ // catch-all under /api/plugins/:pluginId/*, dispatching at request time
20
+ // to whichever route the target plugin's manifest actually declares.
21
+ // Mirrors plugin-storage.ts's exact same shape of problem (one fixed
22
+ // route, request-time dispatch via requireActivePlugin) generalized
23
+ // from a single fixed sub-path to an arbitrary plugin-declared set.
24
+ //
25
+ // Auth is enforced per-route as of Session 108: each declared route's
26
+ // `access` (Task 1's ApiRouteContribution.access) gates the dispatch,
27
+ // defaulting to "admin" when the manifest omits it — with "apiKey" as an
28
+ // explicit opt-in for headless consumers (e.g. Commerce's checkout).
29
+ // Session 160 adds a third tier, "public" — genuinely un-credentialed,
30
+ // for anonymous browser traffic like a contact-form submission, deferred
31
+ // back in Session 108 until a real consumer showed up. So a route CAN
32
+ // now be called with zero credentials, but only when its manifest opts
33
+ // in explicitly; every other value (including unrecognized ones) still
34
+ // falls back to "admin". Rate limiting for apiKey and public routes
35
+ // lands in this same session, as a separate, orthogonal concern (who may
36
+ // call vs. how often).
37
+ export const pluginApiRoutes = async (app, opts) => {
38
+ // Applies to every dispatch route registered in the prefixed child
39
+ // scope below — a parent context's hooks cascade to its children, so
40
+ // this doesn't need to move inside that scope to keep covering them.
41
+ await app.register(import("@fastify/rate-limit"), rateLimitConfig);
42
+ const { registry, structuredStorageAPI, contentService, adapter } = opts;
43
+ function dispatch(method) {
44
+ return async (request, reply) => {
45
+ const { pluginId } = request.params;
46
+ const wildcard = request.params["*"] ?? "";
47
+ const path = `/${wildcard}`;
48
+ try {
49
+ await registry.requireActivePlugin(pluginId);
50
+ }
51
+ catch (err) {
52
+ if (err instanceof PluginNotInstalledError) {
53
+ return reply.code(404).send({ message: err.message });
54
+ }
55
+ if (err instanceof PluginNotActiveError) {
56
+ return reply.code(403).send({ message: err.message });
57
+ }
58
+ throw err;
59
+ }
60
+ const attached = registry.getManifest(pluginId);
61
+ if (!attached) {
62
+ // Transient, not permanent: the plugin IS installed and active
63
+ // (requireActivePlugin above already confirmed the DB row) — its
64
+ // manifest just isn't attached in THIS process (no boot-time
65
+ // plugin loader re-attaches every active plugin's manifest on
66
+ // restart yet). A 503, not a 404, so a headless consumer knows
67
+ // this is retryable rather than "this route doesn't exist."
68
+ return reply.code(503).send({
69
+ message: `Plugin "${pluginId}" is active but its manifest is not loaded in this process`,
70
+ });
71
+ }
72
+ const route = findDeclaredRoute(attached, method, path);
73
+ if (!route) {
74
+ return reply.code(404).send({ message: `No route ${method} ${path} declared by plugin "${pluginId}"` });
75
+ }
76
+ // Enforce the route's declared access. Default (and any
77
+ // unrecognized value) is "admin" — secure by default, by
78
+ // construction. "public" is S160's genuinely un-credentialed tier
79
+ // for anonymous browser traffic (form submissions); such a route
80
+ // owns its own abuse controls, and the platform's contribution is
81
+ // the IP rate ceiling in rate-limit.ts plus the CORS allowlist.
82
+ // Runs here (not as a route-level onRequest hook) because the
83
+ // access level is only known once the declared route is resolved.
84
+ // requireApiKey/requireAdmin send their own 401/403 and we bail if
85
+ // they did.
86
+ const access = route.access ?? "admin";
87
+ if (access === "public") {
88
+ // No credential check by design.
89
+ }
90
+ else if (access === "apiKey") {
91
+ await requireApiKey(request, reply);
92
+ }
93
+ else {
94
+ await requireAdmin(request, reply);
95
+ }
96
+ if (reply.sent)
97
+ return reply;
98
+ // Re-bind to each schema's parsed output (not just validate-then-
99
+ // discard) — same behavior a normally-registered Fastify route gets
100
+ // from fastify-type-provider-zod, so a plugin's z.coerce/.default()/
101
+ // .transform() actually take effect for its handler, not just the
102
+ // raw wire value.
103
+ if (route.schema?.body) {
104
+ const result = route.schema.body.safeParse(request.body);
105
+ if (!result.success) {
106
+ return reply.code(400).send({ message: `Invalid request body: ${result.error.message}` });
107
+ }
108
+ request.body = result.data;
109
+ }
110
+ if (route.schema?.querystring) {
111
+ const result = route.schema.querystring.safeParse(request.query);
112
+ if (!result.success) {
113
+ return reply.code(400).send({ message: `Invalid query string: ${result.error.message}` });
114
+ }
115
+ request.query = result.data;
116
+ }
117
+ if (route.schema?.params) {
118
+ const result = route.schema.params.safeParse(request.params);
119
+ if (!result.success) {
120
+ return reply.code(400).send({ message: `Invalid params: ${result.error.message}` });
121
+ }
122
+ request.params = result.data;
123
+ }
124
+ try {
125
+ // route.handler's return is deliberately `unknown` at the SDK
126
+ // boundary (manifest.ts: "the handler's response is NOT
127
+ // schema-validated — trusted, same as SimpleStorageAPI's
128
+ // handlers today"). The response schema below only documents
129
+ // the error shapes (400/403/404/500), so fastify-type-provider-
130
+ // zod infers a strict `void | ApiError` return type for this
131
+ // handler — this cast is a compile-time-only acknowledgment of
132
+ // that intentionally-untyped success path, not a runtime lie:
133
+ // Fastify serializes whatever is returned regardless.
134
+ const context = buildContext(pluginId, structuredStorageAPI, contentService, adapter);
135
+ return (await route.handler(request, reply, context));
136
+ }
137
+ catch (cause) {
138
+ request.log.error({ err: cause, pluginId, path }, "plugin apiRoute handler threw");
139
+ return reply.code(500).send({ message: "Internal server error" });
140
+ }
141
+ };
142
+ }
143
+ const responseSchema = {
144
+ 400: ApiErrorSchema,
145
+ 401: ApiErrorSchema,
146
+ 403: ApiErrorSchema,
147
+ 404: ApiErrorSchema,
148
+ 500: ApiErrorSchema,
149
+ 503: ApiErrorSchema,
150
+ };
151
+ // CORS and the dispatch routes are registered inside a PREFIXED child
152
+ // scope, not directly on `app`, and that prefix is what actually
153
+ // confines this to plugin routes. @fastify/cors is fastify-plugin-
154
+ // wrapped, so its onRequest HOOK is properly encapsulated (same as
155
+ // rate-limit above) — but it also registers a real ROUTE
156
+ // (`fastify.options('*')`), and Fastify's route table is server-wide:
157
+ // encapsulation confines hooks and decorators, it does NOT confine
158
+ // where a route's path matches. Registered directly on `app` (whose
159
+ // own routes below already carried full literal paths, i.e. no
160
+ // prefix), that `*` would have matched EVERY path on the server, not
161
+ // just this plugin's dispatch routes — a browser on an allowlisted
162
+ // origin could preflight and then send a non-simple cross-origin
163
+ // request (JSON body, Authorization header) to any core endpoint, not
164
+ // just a plugin's. Reads stayed blocked (the response hook is scoped,
165
+ // so no Access-Control-Allow-Origin ever came back on those), but
166
+ // blind cross-origin writes to core endpoints were live. Wrapping both
167
+ // in a `{ prefix: "/api/plugins" }` child scope confines the ROUTE to
168
+ // `/api/plugins/*` — routes below are written relative to that prefix
169
+ // accordingly. The child scope still inherits the rate-limit hook
170
+ // registered on `app` above.
171
+ //
172
+ // That confines the route, but not the blast radius to just THIS
173
+ // scope's own dispatch routes: `OPTIONS /api/plugins/*` also matches
174
+ // GET /api/plugins (routes/plugins.ts), plugin-storage.ts's GET/PUT, and
175
+ // plugin-management.ts's routes — none of which are registered in this
176
+ // scope, so none of them get the CORS response hook, so none of them
177
+ // ever return Access-Control-Allow-Origin on the actual (non-preflight)
178
+ // request. That gap is NOT exploitable, and is not being restructured
179
+ // here — two things contain it: corsOptionsFromEnv's `methods` allowlist
180
+ // (GET/POST/OPTIONS only) means a preflight for plugin-management's
181
+ // DELETE or plugin-storage's PUT is refused before any cross-origin
182
+ // write can be attempted, and `credentials: false` plus app.ts
183
+ // registering fastifyJwt with no `cookie` option means jwtVerify() only
184
+ // ever reads the Authorization header — never a cookie a browser would
185
+ // attach ambiently — so there is no ambient-credential CSRF surface for
186
+ // the wildcard preflight to ride in on.
187
+ //
188
+ // One real, harmless behaviour change this DOES cause: with
189
+ // strictPreflight left at its default (true), `OPTIONS
190
+ // /api/plugins/anything` now 400s (missing Origin or
191
+ // Access-Control-Request-Method) instead of 404ing as it did before this
192
+ // scope existed — including when VELORA_CORS_ORIGINS is unset, since
193
+ // @fastify/cors is registered unconditionally and only the response
194
+ // headers it adds are gated on that var, not the OPTIONS route/hook
195
+ // themselves.
196
+ await app.register(async (scope) => {
197
+ // strictPreflight (the plugin's default: true) is left as-is: an
198
+ // OPTIONS request that's missing Origin or Access-Control-Request-
199
+ // Method now 400s only under this prefix, not server-wide, and
200
+ // nothing in the dispatch verbs below is OPTIONS — a bare OPTIONS
201
+ // hitting a plugin route is either a real preflight (always sends
202
+ // both headers) or a malformed one worth rejecting, not a generic
203
+ // uptime probe (those target /api/health, not a plugin path).
204
+ await scope.register(import("@fastify/cors"), corsOptionsFromEnv((msg) => scope.log.warn(msg)));
205
+ // bodyLimit (S160 final-fix, finding 3): app.ts sets no bodyLimit, so
206
+ // Fastify's 1 MB default would otherwise apply here too — including
207
+ // to the "public" (zero-credential) tier, where the body is parsed,
208
+ // and for forms's /submit route re-serialized and hashed, BEFORE any
209
+ // plugin-level size check (e.g. forms-api.ts's MAX_SUBMISSION_BYTES,
210
+ // 20 KB) or rate-bucket slot is ever spent. 64 KB is comfortably
211
+ // above every real admin payload these dispatch routes carry today
212
+ // (form/redirect-rule/webhook-endpoint definitions, a Commerce
213
+ // checkout body) while cutting the untrusted-parse ceiling by ~16x
214
+ // for the public route. Checked before verified above: reviewed
215
+ // every current plugins/* POST/PUT/PATCH route to confirm none sends
216
+ // a body anywhere near this size.
217
+ const bodyLimit = 65536;
218
+ scope.get("/:pluginId/*", { schema: { response: responseSchema } }, dispatch("GET"));
219
+ scope.post("/:pluginId/*", { bodyLimit, schema: { response: responseSchema } }, dispatch("POST"));
220
+ scope.put("/:pluginId/*", { bodyLimit, schema: { response: responseSchema } }, dispatch("PUT"));
221
+ scope.patch("/:pluginId/*", { bodyLimit, schema: { response: responseSchema } }, dispatch("PATCH"));
222
+ scope.delete("/:pluginId/*", { bodyLimit, schema: { response: responseSchema } }, dispatch("DELETE"));
223
+ }, { prefix: "/api/plugins" });
224
+ };
@@ -0,0 +1,8 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ export declare const PLUGIN_ASSETS_PREFIX = "/plugin-assets";
3
+ /** The server-relative remoteUrl a marketplace install stores (Task 2).
4
+ * Server-relative rather than absolute so a host/port/domain change never
5
+ * invalidates installed rows; the admin absolutizes against its own
6
+ * origin, which fronts this server for /api and /docs already. */
7
+ export declare function pluginAssetsRemoteUrl(pluginId: string, version: string): string;
8
+ export declare function pluginAssetsRoutes(app: FastifyInstance): Promise<void>;
@@ -0,0 +1,51 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import fastifyStatic from "@fastify/static";
3
+ import { resolvePluginsDir } from "../plugins/plugin-package-store.js";
4
+ // S143: serves the managed plugin package store (plugin-package-store.ts,
5
+ // .velora/plugins/<pluginId>/<version>/) over HTTP so the admin's Module
6
+ // Federation loader can fetch a marketplace-installed plugin's
7
+ // remoteEntry.js — closing the S142 documented gap that left
8
+ // marketplace-installed plugins silently skipped by boot-remote-plugins
9
+ // (empty remoteUrl). The URL path IS the store path: no indirection.
10
+ //
11
+ // Unauthenticated by design, the same posture as GET /api/plugins (this is
12
+ // the same package bytes anyone can download from the public marketplace).
13
+ // Rate limiting joins the Month 14 pre-deploy backlog with the rest of the
14
+ // public surface. Serving server/plugin.js alongside the client assets is
15
+ // accepted: identical bytes ship in the public package artifact.
16
+ // Stored VERBATIM in pluginRegistry.remoteUrl rows at install time —
17
+ // changing this prefix strands every already-installed row on the old
18
+ // literal path (plugins silently stop loading) until a backfill or
19
+ // reinstall. Treat it as frozen, not a config knob.
20
+ export const PLUGIN_ASSETS_PREFIX = "/plugin-assets";
21
+ /** The server-relative remoteUrl a marketplace install stores (Task 2).
22
+ * Server-relative rather than absolute so a host/port/domain change never
23
+ * invalidates installed rows; the admin absolutizes against its own
24
+ * origin, which fronts this server for /api and /docs already. */
25
+ export function pluginAssetsRemoteUrl(pluginId, version) {
26
+ return `${PLUGIN_ASSETS_PREFIX}/${pluginId}/${version}/remoteEntry.js`;
27
+ }
28
+ export async function pluginAssetsRoutes(app) {
29
+ const root = resolvePluginsDir();
30
+ // @fastify/static@10 throws "root must be a directory" at registration
31
+ // (the docs-static.ts lesson) — on a fresh instance nothing is installed
32
+ // yet, so create the store empty. It is server-owned, so this is safe.
33
+ mkdirSync(root, { recursive: true });
34
+ // Static infra, not Zod-validated JSON — hidden from Swagger up front
35
+ // (the docs-static.ts treatment) rather than leaking bare path entries.
36
+ app.addHook("onRoute", (routeOptions) => {
37
+ if (routeOptions.url.startsWith(PLUGIN_ASSETS_PREFIX)) {
38
+ routeOptions.schema = { ...routeOptions.schema, hide: true };
39
+ }
40
+ });
41
+ // Encapsulated child context with a trailing-slash prefix: confines
42
+ // setNotFoundHandler to this mount (docs-static.ts documents why — a
43
+ // bare prefix would hijack the app-wide 404, including /api/*).
44
+ await app.register(async (assetsApp) => {
45
+ await assetsApp.register(fastifyStatic, {
46
+ root,
47
+ decorateReply: false,
48
+ });
49
+ assetsApp.setNotFoundHandler((_req, reply) => reply.code(404).send({ message: "not found" }));
50
+ }, { prefix: `${PLUGIN_ASSETS_PREFIX}/` });
51
+ }
@@ -0,0 +1,7 @@
1
+ import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
2
+ import type { DatabaseAdapter } from "@velora-cms/db-adapter";
3
+ import type { StructuredSnapshotService } from "../plugins/structured-snapshot-service.js";
4
+ export declare const pluginManagementRoutes: FastifyPluginAsyncZod<{
5
+ adapter: DatabaseAdapter;
6
+ structuredSnapshotService: StructuredSnapshotService;
7
+ }>;