@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,29 @@
1
+ import type { PrismaClient } from "@prisma/client";
2
+ import type { DatabaseAdapter } from "@velora-cms/db-adapter";
3
+ import { type PluginRegistryService } from "./plugin-registry-service.js";
4
+ export declare class BundleMemberBlockedError extends Error {
5
+ constructor(memberPluginId: string, dependentPluginId: string);
6
+ }
7
+ export declare class BundleUninstallRequiresConfirmationError extends Error {
8
+ readonly memberSnapshots: Array<{
9
+ pluginId: string;
10
+ snapshotGroupId: string;
11
+ }>;
12
+ constructor(bundlePluginId: string, memberSnapshots: Array<{
13
+ pluginId: string;
14
+ snapshotGroupId: string;
15
+ }>);
16
+ }
17
+ export interface UninstallBundleDeps {
18
+ prisma: PrismaClient;
19
+ registry: PluginRegistryService;
20
+ adapter: DatabaseAdapter;
21
+ }
22
+ export declare function uninstallBundle(deps: UninstallBundleDeps, bundlePluginId: string, options: {
23
+ confirmStructuredDataCleanup: boolean;
24
+ }): Promise<{
25
+ memberSnapshots: Array<{
26
+ pluginId: string;
27
+ snapshotGroupId: string;
28
+ }>;
29
+ }>;
@@ -0,0 +1,150 @@
1
+ import { DocumentTypeService } from "@velora-cms/content-service";
2
+ import { removePluginPackages } from "./plugin-package-store.js";
3
+ import { StructuredDataCleanupRequiresConfirmationError, } from "./plugin-registry-service.js";
4
+ // S152's bundle-uninstall cascade. Lives ABOVE PluginRegistryService —
5
+ // uninstall() stays single-plugin — at the same layer the DELETE route
6
+ // already orchestrates its per-plugin cross-cutting cleanup (package
7
+ // store, manifest detach, license rows), because the cascade is exactly
8
+ // that orchestration repeated per owned member.
9
+ //
10
+ // Order (the S88 check-first discipline, cross-plugin):
11
+ // 1. preflight EVERYTHING (doc-type deletability, outside dependents)
12
+ // 2. aggregate the structured-data confirmation gate (snapshots taken,
13
+ // nothing destroyed — the S87 safe-retry property, kept)
14
+ // 3. only then cascade: owned members in reverse install order, bundle
15
+ // row last.
16
+ export class BundleMemberBlockedError extends Error {
17
+ constructor(memberPluginId, dependentPluginId) {
18
+ super(`Cannot uninstall this bundle: its member "${memberPluginId}" is a dependency of "${dependentPluginId}", which is not part of the bundle — uninstall "${dependentPluginId}" first`);
19
+ this.name = "BundleMemberBlockedError";
20
+ }
21
+ }
22
+ export class BundleUninstallRequiresConfirmationError extends Error {
23
+ memberSnapshots;
24
+ constructor(bundlePluginId, memberSnapshots) {
25
+ super(`Uninstalling bundle "${bundlePluginId}" will drop structured data for: ${memberSnapshots
26
+ .map((s) => `"${s.pluginId}"`)
27
+ .join(", ")}. Snapshots have been taken; retry with confirmStructuredDataCleanup: true to proceed`);
28
+ this.memberSnapshots = memberSnapshots;
29
+ this.name = "BundleUninstallRequiresConfirmationError";
30
+ }
31
+ }
32
+ export async function uninstallBundle(deps, bundlePluginId, options) {
33
+ const { prisma, registry, adapter } = deps;
34
+ // Owned members, in install order. Rows whose member registry row is
35
+ // gone (individually uninstalled since the bundle install — the
36
+ // recorded tolerated case) contribute no work; their stale ownership
37
+ // rows are swept at the end.
38
+ const ownershipRows = await prisma.pluginBundleMember.findMany({
39
+ where: { bundlePluginId },
40
+ orderBy: { position: "asc" },
41
+ });
42
+ const ownedIds = ownershipRows.map((row) => row.memberPluginId);
43
+ const memberRows = await prisma.pluginRegistry.findMany({ where: { pluginId: { in: ownedIds } } });
44
+ const existingMemberIds = new Set(memberRows.map((row) => row.pluginId));
45
+ const removalIds = new Set([bundlePluginId, ...existingMemberIds]);
46
+ // Preflight 1: every tracked document type of every plugin in the
47
+ // removal set must be deletable BEFORE anything is removed — the same
48
+ // guard uninstall() runs per plugin, hoisted so a blocked member is
49
+ // found while the whole set is still intact. Throws
50
+ // DocumentTypeInUseError (the route already maps it to 409).
51
+ const documentTypeService = new DocumentTypeService(adapter);
52
+ const trackedDocTypes = await prisma.pluginDocumentType.findMany({
53
+ where: { pluginId: { in: [...removalIds] } },
54
+ });
55
+ for (const tracked of trackedDocTypes) {
56
+ await documentTypeService.assertDocumentTypeDeletable(tracked.documentTypeId);
57
+ }
58
+ // Preflight 2: no plugin OUTSIDE the removal set may depend on an owned
59
+ // member (uninstall() itself has no dependents guard — without this, a
60
+ // cascade would silently break a dependent that had every right to rely
61
+ // on the "installed AND enabled" check that admitted it). Attached
62
+ // manifests are the source (complete on a booted server since S122); a
63
+ // row with no attached manifest cannot be checked and is skipped —
64
+ // consistent with uninstall()'s own missing-manifest tolerance.
65
+ const allRows = await prisma.pluginRegistry.findMany();
66
+ for (const row of allRows) {
67
+ if (removalIds.has(row.pluginId))
68
+ continue;
69
+ const manifest = registry.getManifest(row.pluginId);
70
+ const dependencies = manifest?.pluginDependencies ?? [];
71
+ const blocked = dependencies.find((dependency) => existingMemberIds.has(dependency));
72
+ if (blocked) {
73
+ throw new BundleMemberBlockedError(blocked, row.pluginId);
74
+ }
75
+ }
76
+ // Preflight 3: the aggregated structured-data gate. Calling uninstall()
77
+ // UNCONFIRMED on a structured plugin snapshots it and then throws
78
+ // having changed nothing else (S87's ordering) — so harvesting each
79
+ // structured plugin's snapshotGroupId this way is non-destructive by
80
+ // construction. Simple-storage plugins are never dry-run (an
81
+ // unconfirmed uninstall() would really uninstall them).
82
+ if (!options.confirmStructuredDataCleanup) {
83
+ const structuredIds = [
84
+ ...memberRows.filter((row) => row.storageType === "structured").map((row) => row.pluginId),
85
+ ];
86
+ const bundleRow = await prisma.pluginRegistry.findUnique({ where: { pluginId: bundlePluginId } });
87
+ if (bundleRow?.storageType === "structured") {
88
+ structuredIds.push(bundlePluginId);
89
+ }
90
+ if (structuredIds.length > 0) {
91
+ const memberSnapshots = [];
92
+ for (const structuredId of structuredIds) {
93
+ try {
94
+ await registry.uninstall(structuredId, { confirmStructuredDataCleanup: false });
95
+ throw new Error(`uninstall("${structuredId}") completed without the structured-data confirmation gate firing — refusing to continue a bundle cascade on inconsistent state`);
96
+ }
97
+ catch (error) {
98
+ if (error instanceof StructuredDataCleanupRequiresConfirmationError) {
99
+ memberSnapshots.push({ pluginId: structuredId, snapshotGroupId: error.snapshotGroupId });
100
+ }
101
+ else {
102
+ throw error;
103
+ }
104
+ }
105
+ }
106
+ throw new BundleUninstallRequiresConfirmationError(bundlePluginId, memberSnapshots);
107
+ }
108
+ }
109
+ // Cascade: owned members in REVERSE install order, each with the same
110
+ // route-level cleanup a single-plugin uninstall gets, deleting its
111
+ // ownership row as it completes so a mid-cascade failure leaves an
112
+ // honest picture for a retry. Bundle row last.
113
+ //
114
+ // Task 6: each confirmed uninstall() call now hands back the fresh
115
+ // snapshotGroupId it minted (null for a simple-storage member) — collect
116
+ // only the non-null ones here, same shape as the aggregated 409 above,
117
+ // so a confirmed bundle uninstall can report exactly which structured
118
+ // members got a fresh snapshot.
119
+ const memberSnapshots = [];
120
+ const orderedForRemoval = [...ownershipRows].reverse();
121
+ for (const ownership of orderedForRemoval) {
122
+ if (existingMemberIds.has(ownership.memberPluginId)) {
123
+ const { snapshotGroupId } = await registry.uninstall(ownership.memberPluginId, {
124
+ confirmStructuredDataCleanup: options.confirmStructuredDataCleanup,
125
+ });
126
+ if (snapshotGroupId) {
127
+ memberSnapshots.push({ pluginId: ownership.memberPluginId, snapshotGroupId });
128
+ }
129
+ removePluginPackages(ownership.memberPluginId);
130
+ registry.detachManifest(ownership.memberPluginId);
131
+ await prisma.pluginLicense.deleteMany({ where: { pluginId: ownership.memberPluginId } });
132
+ }
133
+ await prisma.pluginBundleMember.deleteMany({
134
+ where: { bundlePluginId, memberPluginId: ownership.memberPluginId },
135
+ });
136
+ }
137
+ const { snapshotGroupId: bundleSnapshotGroupId } = await registry.uninstall(bundlePluginId, {
138
+ confirmStructuredDataCleanup: options.confirmStructuredDataCleanup,
139
+ });
140
+ if (bundleSnapshotGroupId) {
141
+ memberSnapshots.push({ pluginId: bundlePluginId, snapshotGroupId: bundleSnapshotGroupId });
142
+ }
143
+ removePluginPackages(bundlePluginId);
144
+ registry.detachManifest(bundlePluginId);
145
+ await prisma.pluginLicense.deleteMany({ where: { pluginId: bundlePluginId } });
146
+ // Belt-and-braces: no rows should remain, but a row for a member that
147
+ // vanished mid-flight must not survive the bundle.
148
+ await prisma.pluginBundleMember.deleteMany({ where: { bundlePluginId } });
149
+ return { memberSnapshots };
150
+ }
@@ -0,0 +1,36 @@
1
+ import type { TrustedKey, VerifyResult } from "@velora-cms/plugin-package";
2
+ /** The detached sidecar convention: <artifact>.tar.gz.sig beside the
3
+ * artifact. Detached rather than embedded because the signature is over the
4
+ * tarball's own hash and therefore cannot live inside it. */
5
+ export declare function resolveSidecarPath(tarballPath: string): string;
6
+ export declare function verifyPackage(tarballPath: string, sidecarPath: string | null, trustedKeys: TrustedKey[]): Promise<VerifyResult>;
7
+ /**
8
+ * The post-extraction half of verification. The envelope's pluginId and
9
+ * version can only be compared against the package's own manifest.json
10
+ * AFTER extraction, so this runs separately from verifyPackage and can
11
+ * DOWNGRADE a verified result.
12
+ *
13
+ * The envelope comes off the `verified` result itself (the one
14
+ * `verifyEnvelope` already authenticated), never from a second,
15
+ * unauthenticated read of the sidecar file — that would let a mismatched
16
+ * re-read silently replace the identity that was actually verified. If a
17
+ * `verified` result somehow carries no envelope, this DOWNGRADES rather
18
+ * than passing it through: a trust decision must never fail open by
19
+ * skipping the one check it exists to make.
20
+ *
21
+ * A cryptographically valid signature over the wrong package's identity
22
+ * must never yield "verified" — that is precisely the mistake a half-built
23
+ * approval flow could introduce.
24
+ */
25
+ export declare function checkPackageIdentity(result: VerifyResult, pluginId: string, version: string): VerifyResult;
26
+ /** One place that decides how each outcome is surfaced. An unsigned package
27
+ * is silent — velora-dev and private agency plugins are unsigned by design
28
+ * and must not nag. A package that CLAIMS a signature and fails is loud and
29
+ * names the reason. */
30
+ export declare function describeVerifyResult(result: VerifyResult, tarballPath: string): void;
31
+ /** The two trust tiers a registry row can carry. Narrower than `string` so
32
+ * `PluginRegistryService.install`, `registerLocalPlugin`, and
33
+ * `installBuiltPackage` can't silently accept an arbitrary tier value. */
34
+ export type TrustTier = "verified" | "unverified";
35
+ /** The tier a verdict maps to. Only a clean "verified" earns the tier. */
36
+ export declare function trustTierFor(result: VerifyResult): TrustTier;
@@ -0,0 +1,121 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { hashTarball, verifyEnvelope } from "@velora-cms/plugin-package";
3
+ // Install-time signature verification (Session 127).
4
+ //
5
+ // NEVER throws, and NEVER blocks an install. Per the Trust model,
6
+ // sideloading is a requirement rather than a loophole: an unverifiable
7
+ // package still installs, as "unverified", with a loud warning. Only
8
+ // extraction can fail an install, because an archive that violates a
9
+ // hardening rule is not a package at all.
10
+ const LOG_PREFIX = "[plugin-verify]";
11
+ /** A generous ceiling for an envelope plus a base64 Ed25519 signature — a
12
+ * few KB at most. The tarball itself is streamed rather than buffered (see
13
+ * hashTarball), with its own size bound arriving later from the extractor's
14
+ * MAX_TOTAL_BYTES; the sidecar, by contrast, is read fully into memory
15
+ * below, so an attacker-supplied sidecar needs its own ceiling — one that
16
+ * had none before this, so an oversized file is rejected before it is ever
17
+ * read into memory. */
18
+ const MAX_SIDECAR_BYTES = 8192;
19
+ /** The detached sidecar convention: <artifact>.tar.gz.sig beside the
20
+ * artifact. Detached rather than embedded because the signature is over the
21
+ * tarball's own hash and therefore cannot live inside it. */
22
+ export function resolveSidecarPath(tarballPath) {
23
+ return `${tarballPath}.sig`;
24
+ }
25
+ export async function verifyPackage(tarballPath, sidecarPath, trustedKeys) {
26
+ if (!sidecarPath || !existsSync(sidecarPath)) {
27
+ return { status: "unsigned" };
28
+ }
29
+ let sidecar;
30
+ try {
31
+ // statSync lives inside this try, not before it, even though it only
32
+ // "checks a size": this module never throws, and a file that passed
33
+ // existsSync a moment ago can still fail to stat — deleted or replaced
34
+ // in the gap, or (on Windows) held open exclusively by whatever wrote
35
+ // it (EBUSY/EPERM). Hoisting it back above this try would let that race
36
+ // escape verifyPackage and abort an install that should instead proceed
37
+ // as "unverified".
38
+ if (statSync(sidecarPath).size > MAX_SIDECAR_BYTES) {
39
+ return {
40
+ status: "failed",
41
+ reason: "malformed-sidecar",
42
+ message: `${sidecarPath} exceeds the ${MAX_SIDECAR_BYTES}-byte sidecar size ceiling`,
43
+ };
44
+ }
45
+ sidecar = JSON.parse(readFileSync(sidecarPath, "utf8"));
46
+ }
47
+ catch (error) {
48
+ return {
49
+ status: "failed",
50
+ reason: "malformed-sidecar",
51
+ message: `${sidecarPath} could not be read as a signature sidecar: ${error instanceof Error ? error.message : String(error)}`,
52
+ };
53
+ }
54
+ let sha256;
55
+ try {
56
+ sha256 = await hashTarball(tarballPath);
57
+ }
58
+ catch (error) {
59
+ return {
60
+ status: "failed",
61
+ reason: "artifact-unreadable",
62
+ message: `${tarballPath} could not be read: ${error instanceof Error ? error.message : String(error)}`,
63
+ };
64
+ }
65
+ return verifyEnvelope(sidecar, sha256, trustedKeys);
66
+ }
67
+ /**
68
+ * The post-extraction half of verification. The envelope's pluginId and
69
+ * version can only be compared against the package's own manifest.json
70
+ * AFTER extraction, so this runs separately from verifyPackage and can
71
+ * DOWNGRADE a verified result.
72
+ *
73
+ * The envelope comes off the `verified` result itself (the one
74
+ * `verifyEnvelope` already authenticated), never from a second,
75
+ * unauthenticated read of the sidecar file — that would let a mismatched
76
+ * re-read silently replace the identity that was actually verified. If a
77
+ * `verified` result somehow carries no envelope, this DOWNGRADES rather
78
+ * than passing it through: a trust decision must never fail open by
79
+ * skipping the one check it exists to make.
80
+ *
81
+ * A cryptographically valid signature over the wrong package's identity
82
+ * must never yield "verified" — that is precisely the mistake a half-built
83
+ * approval flow could introduce.
84
+ */
85
+ export function checkPackageIdentity(result, pluginId, version) {
86
+ if (result.status !== "verified")
87
+ return result;
88
+ const { envelope } = result;
89
+ if (!envelope) {
90
+ return {
91
+ status: "failed",
92
+ reason: "identity-mismatch",
93
+ message: "verified result carried no envelope to check identity against — refusing to trust it",
94
+ };
95
+ }
96
+ if (envelope.pluginId === pluginId && envelope.version === version)
97
+ return result;
98
+ return {
99
+ status: "failed",
100
+ reason: "identity-mismatch",
101
+ message: `signature covers ${envelope.pluginId}@${envelope.version} but the package contains ${pluginId}@${version}`,
102
+ };
103
+ }
104
+ /** One place that decides how each outcome is surfaced. An unsigned package
105
+ * is silent — velora-dev and private agency plugins are unsigned by design
106
+ * and must not nag. A package that CLAIMS a signature and fails is loud and
107
+ * names the reason. */
108
+ export function describeVerifyResult(result, tarballPath) {
109
+ if (result.status === "verified") {
110
+ console.log(`${LOG_PREFIX} verified ${tarballPath} against trusted key ${result.keyId}`);
111
+ return;
112
+ }
113
+ if (result.status === "failed") {
114
+ console.warn(`${LOG_PREFIX} SIGNATURE VERIFICATION FAILED for ${tarballPath} (${result.reason}): ${result.message}`);
115
+ console.warn(`${LOG_PREFIX} installing as "unverified" — the package is NOT blocked, but it is NOT trusted`);
116
+ }
117
+ }
118
+ /** The tier a verdict maps to. Only a clean "verified" earns the tier. */
119
+ export function trustTierFor(result) {
120
+ return result.status === "verified" ? "verified" : "unverified";
121
+ }
@@ -0,0 +1,8 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ interface AdminDirResolution {
3
+ dir: string | null;
4
+ invalidOverride: string | undefined;
5
+ }
6
+ export declare function resolveAdminDir(): AdminDirResolution;
7
+ export declare function adminStaticRoutes(app: FastifyInstance): Promise<void>;
8
+ export {};
@@ -0,0 +1,196 @@
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 { ADMIN_RATE_LIMIT, ipOnlyRateLimitConfig } from "../auth/route-rate-limits.js";
6
+ import { isAdminUiEnabled } from "../config/admin-ui.js";
7
+ // A usable admin directory: it must actually be a directory (not a file —
8
+ // @fastify/static@10.1.3 throws "root must be a directory" at registration
9
+ // otherwise, crashing buildApp()) and it must contain the SPA's index.html
10
+ // — an empty directory would otherwise resolve successfully and then serve
11
+ // bare per-route 404s instead of the clear 404-with-cause. index.html is
12
+ // also what the fallback route below reads on every deep link, so its
13
+ // presence is a hard precondition for this module, not a heuristic.
14
+ function isUsableAdminDir(candidate) {
15
+ try {
16
+ return statSync(candidate).isDirectory() && existsSync(join(candidate, "index.html"));
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ export function resolveAdminDir() {
23
+ const override = process.env.VELORA_ADMIN_DIR;
24
+ if (override) {
25
+ // path.resolve() before anything else: @fastify/static@10.1.3 also
26
+ // throws ("root must be an absolute path") at registration when root
27
+ // is relative. Resolving against cwd up front means a relative
28
+ // override behaves the same as an absolute one instead of crashing
29
+ // boot.
30
+ const resolved = resolve(override);
31
+ if (isUsableAdminDir(resolved))
32
+ return { dir: resolved, invalidOverride: undefined };
33
+ return { dir: null, invalidOverride: override };
34
+ }
35
+ const here = dirname(fileURLToPath(import.meta.url)); // dist/routes or src/routes
36
+ const candidates = [
37
+ join(here, "..", "admin-bundle"),
38
+ join(here, "..", "..", "..", "..", "apps", "admin-ui", "dist-bundle"),
39
+ ];
40
+ const dir = candidates.find((c) => isUsableAdminDir(c)) ?? null;
41
+ return { dir, invalidOverride: undefined };
42
+ }
43
+ // Local copies of docs-static.ts's private helpers rather than a shared
44
+ // export: that module deliberately keeps them private, and duplicating
45
+ // three tiny functions is cheaper than inventing a shared "degraded static
46
+ // response" abstraction for two callers.
47
+ function escapeHtml(text) {
48
+ return text
49
+ .replace(/&/g, "&amp;")
50
+ .replace(/</g, "&lt;")
51
+ .replace(/>/g, "&gt;")
52
+ .replace(/"/g, "&quot;");
53
+ }
54
+ // /velora is opened directly in a browser tab (it IS the browser surface),
55
+ // so a degraded response there should be readable HTML, not raw JSON.
56
+ // Anything else (curl, tests, uptime probes) keeps the JSON shape.
57
+ function wantsHtml(req) {
58
+ return (req.headers.accept ?? "").includes("text/html");
59
+ }
60
+ function sendDegraded(req, reply, message) {
61
+ if (wantsHtml(req)) {
62
+ return reply
63
+ .code(404)
64
+ .type("text/html")
65
+ .send(`<!doctype html><html><head><meta charset="utf-8"><title>Admin unavailable</title></head>` +
66
+ `<body><h1>Admin unavailable</h1><p>${escapeHtml(message)}</p></body></html>`);
67
+ }
68
+ return reply.code(404).send({ message });
69
+ }
70
+ export async function adminStaticRoutes(app) {
71
+ // S162 precedent: static-ish reads, but not unbounded. Registered
72
+ // per-scope, matching auth.ts/docs-static.ts/plugin-api-routes.ts/
73
+ // public-api.ts — @fastify/rate-limit decorates the instance, so it is
74
+ // registered in child scopes OR at the root, never both.
75
+ //
76
+ // ipOnlyRateLimitConfig(), NOT the shared three-lane rateLimitConfig
77
+ // docs-static.ts uses. That is the one place this module cannot copy
78
+ // docs-static, and it is the S162-I6 constraint verbatim: auth/
79
+ // rate-limit.ts imports auth/api-keys.ts, which imports db/prisma.ts —
80
+ // a module with a top-level `await loadPrismaClient(...)`. This module is
81
+ // registered in buildSetupOnlyApp() too, the boot path documented as
82
+ // never touching Prisma and which runs precisely when nothing is
83
+ // configured yet, so importing the three-lane config here would drag
84
+ // Prisma into it and could fail that boot outright. setup.ts made the
85
+ // same swap for the same reason.
86
+ //
87
+ // The scope's ceiling is the ADMIN lane rather than the generic IP one,
88
+ // and this is where ADMIN_RATE_LIMIT actually earns its keep: with
89
+ // wildcard:false below, @fastify/static declares ~100 hashed asset
90
+ // routes in this scope and gives this plugin no way to attach per-route
91
+ // config to them (its internal routeOpts carries no `config` key), so
92
+ // the plugin-level default is the ONLY lever that reaches them. Leaving
93
+ // it at the generic 60/IP/minute would mean a cold load of the SPA plus
94
+ // one hard refresh could 429 the login screen.
95
+ await app.register(import("@fastify/rate-limit"), {
96
+ ...ipOnlyRateLimitConfig(),
97
+ max: ADMIN_RATE_LIMIT.max(),
98
+ timeWindow: ADMIN_RATE_LIMIT.timeWindow(),
99
+ });
100
+ // This module's routes serve static HTML/asset infra, not Zod-validated
101
+ // JSON — api-design.md's "every route needs a request/response schema"
102
+ // rule governs the Zod→OpenAPI chain, which these deliberately sit
103
+ // outside of. Hidden from Swagger up front, same treatment docs-static.ts
104
+ // gives /docs. (@fastify/static hides its own generated routes already;
105
+ // this covers the ones declared below.) The hook is registered before any
106
+ // route in this scope so it sees all of them, and it is scoped to this
107
+ // encapsulated child, so `url === "/"` can only ever match the root
108
+ // redirect declared here.
109
+ app.addHook("onRoute", (routeOptions) => {
110
+ if (routeOptions.url.startsWith("/velora") || routeOptions.url === "/") {
111
+ routeOptions.schema = { ...routeOptions.schema, hide: true };
112
+ }
113
+ });
114
+ // Every route below already sits on the ADMIN lane by way of the
115
+ // plugin-level default registered above, so no per-route `config` is
116
+ // attached: repeating the same numbers on each route would suggest the
117
+ // asset routes (which cannot carry it) are on a different lane than they
118
+ // are.
119
+ // VELORA_ADMIN=off (the hosted demo's setting): the SPA is not served,
120
+ // but /velora/* still answers a 404 that NAMES the flag rather than the
121
+ // app-wide "route not found" — an operator who set the flag on one host
122
+ // and forgot it on another gets told why, which is the same
123
+ // 404-with-cause contract the missing-bundle branch honours. `/` is
124
+ // deliberately NOT registered in this branch, so the root keeps today's
125
+ // headless-bridge behaviour exactly.
126
+ if (!isAdminUiEnabled()) {
127
+ const message = "The admin UI is disabled on this instance (VELORA_ADMIN=off). The API is unaffected; " +
128
+ "unset VELORA_ADMIN (or set it to anything other than `off`) to serve the admin at /velora/.";
129
+ app.get("/velora", async (req, reply) => sendDegraded(req, reply, message));
130
+ app.get("/velora/", async (req, reply) => sendDegraded(req, reply, message));
131
+ app.get("/velora/*", async (req, reply) => sendDegraded(req, reply, message));
132
+ app.log.info("[admin-static] VELORA_ADMIN=off — the admin SPA is not served");
133
+ return;
134
+ }
135
+ const { dir: adminDir, invalidOverride } = resolveAdminDir();
136
+ // Exact-path redirects. Fastify 5's Reply#redirect signature is
137
+ // (url, code), not (code, url) — verified against this repo's installed
138
+ // fastify@5.8.5 source (lib/reply.js), which differs from older majors.
139
+ // Both are registered before the missing-bundle branch returns: a
140
+ // degraded instance should still send you to the URL that explains
141
+ // itself, rather than silently keeping the pre-S184 root behaviour.
142
+ app.get("/velora", async (_req, reply) => reply.redirect("/velora/", 302));
143
+ app.get("/", async (_req, reply) => reply.redirect("/velora/", 302));
144
+ if (!adminDir) {
145
+ const message = invalidOverride
146
+ ? `VELORA_ADMIN_DIR is set to "${invalidOverride}" but it is not usable (it must resolve to a ` +
147
+ "directory containing index.html). Fix the override or unset VELORA_ADMIN_DIR to fall back " +
148
+ "to the built-in bundle location."
149
+ : "The admin bundle is not installed. Expected it inside the server build " +
150
+ "(dist/admin-bundle) or at VELORA_ADMIN_DIR. Build with `turbo run build` to produce it.";
151
+ app.get("/velora/", async (req, reply) => sendDegraded(req, reply, message));
152
+ app.get("/velora/*", async (req, reply) => sendDegraded(req, reply, message));
153
+ app.log.warn("[admin-static] no admin bundle found — /velora/* will answer 404-with-cause");
154
+ return;
155
+ }
156
+ // Registered directly on this scope (not in a further encapsulated
157
+ // child): unlike docs-static.ts, this module needs no setNotFoundHandler
158
+ // at all — every path under /velora/ is owned by a real route, so nothing
159
+ // here can leak a 404 shape onto /api/*.
160
+ //
161
+ // wildcard: false makes @fastify/static enumerate the bundle at
162
+ // registration and declare one route per file (plus HEAD), leaving deep
163
+ // links unmatched so the SPA fallback below owns them. The bundle is
164
+ // immutable and hashed, so a boot-time enumeration is exactly right.
165
+ //
166
+ // index: false is load-bearing, not cosmetic. With the default
167
+ // (index.html), the wildcard:false branch ALSO declares a route for each
168
+ // index directory — here "/velora/" — which collides head-on with the
169
+ // explicit "/velora/" route below (FST_ERR_DUPLICATED_ROUTE at boot).
170
+ // Turning it off keeps this module's own routes as the single owner of
171
+ // the SPA entry point, so the index is served through one code path
172
+ // whether it was requested at /velora/ or reached by fallback.
173
+ //
174
+ // decorateReply: false, as in docs-static.ts — nothing here needs
175
+ // reply.sendFile, and not decorating keeps this registration from ever
176
+ // colliding with another @fastify/static mount on the same instance.
177
+ await app.register(fastifyStatic, {
178
+ root: adminDir,
179
+ prefix: "/velora/",
180
+ wildcard: false,
181
+ index: false,
182
+ decorateReply: false,
183
+ });
184
+ // The SPA entry point, and the fallback that makes client-side routing
185
+ // survive a refresh or a pasted deep link. decorateReply: false means
186
+ // reply.sendFile isn't available here (docs-static.ts reads its 404.html
187
+ // the same way) — read index.html and send it per request (a single
188
+ // small file behind a rate limit). NOTE: unlike the docs bundle, a
189
+ // bundle swapped underneath a RUNNING server needs a restart — with
190
+ // wildcard: false the asset routes are enumerated once at registration,
191
+ // so a new build's hashed assets would have no routes.
192
+ const indexHtml = join(adminDir, "index.html");
193
+ const sendIndex = (reply) => reply.type("text/html").send(readFileSync(indexHtml));
194
+ app.get("/velora/", async (_req, reply) => sendIndex(reply));
195
+ app.get("/velora/*", async (_req, reply) => sendIndex(reply));
196
+ }
@@ -0,0 +1,2 @@
1
+ import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
2
+ export declare const apiKeyRoutes: FastifyPluginAsyncZod;
@@ -0,0 +1,95 @@
1
+ import { z } from "zod";
2
+ import { ApiErrorSchema, ApiKeyListQuerySchema, ApiKeyListResponseSchema, CreateApiKeyRequestSchema, CreateApiKeyResponseSchema, RevokeApiKeyResponseSchema, } from "@velora-cms/api-schemas";
3
+ import { prisma } from "../db/prisma.js";
4
+ import { generateApiKey, hashApiKey } from "../auth/api-keys.js";
5
+ import { requireAdmin } from "../auth/require-auth.js";
6
+ const ApiKeyParamsSchema = z.object({ id: z.string().uuid() });
7
+ // Deliberately reconstructed field by field — keyHash must never ride
8
+ // along by spread (users.ts's toSummary precedent).
9
+ function toSummary(key) {
10
+ return {
11
+ id: key.id,
12
+ name: key.name,
13
+ scope: key.scope,
14
+ createdAt: key.createdAt.toISOString(),
15
+ lastUsedAt: key.lastUsedAt?.toISOString() ?? null,
16
+ revokedAt: key.revokedAt?.toISOString() ?? null,
17
+ };
18
+ }
19
+ // Every route here is admin-only, like /api/users — key management is
20
+ // backoffice surface, JWT-gated; the public API these keys unlock is the
21
+ // separate /api/v1 namespace, and a key can never manage keys.
22
+ export const apiKeyRoutes = async (app) => {
23
+ app.get("/api/api-keys", {
24
+ onRequest: requireAdmin,
25
+ schema: {
26
+ querystring: ApiKeyListQuerySchema,
27
+ response: {
28
+ 200: ApiKeyListResponseSchema,
29
+ 401: ApiErrorSchema,
30
+ 403: ApiErrorSchema,
31
+ },
32
+ },
33
+ }, async (request) => {
34
+ const { cursor, limit } = request.query;
35
+ const rows = await prisma.apiKey.findMany({
36
+ orderBy: [{ createdAt: "asc" }, { id: "asc" }],
37
+ take: limit,
38
+ ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}),
39
+ });
40
+ const nextCursor = rows.length === limit ? (rows[rows.length - 1]?.id ?? null) : null;
41
+ return { items: rows.map(toSummary), nextCursor };
42
+ });
43
+ app.post("/api/api-keys", {
44
+ onRequest: requireAdmin,
45
+ schema: {
46
+ body: CreateApiKeyRequestSchema,
47
+ response: {
48
+ 201: CreateApiKeyResponseSchema,
49
+ 401: ApiErrorSchema,
50
+ 403: ApiErrorSchema,
51
+ },
52
+ },
53
+ }, async (request, reply) => {
54
+ // The plaintext exists only in this scope and this response — only
55
+ // its hash is persisted, so it can never be shown again.
56
+ const secret = generateApiKey();
57
+ const row = await prisma.apiKey.create({
58
+ data: {
59
+ name: request.body.name,
60
+ // Zod defaults an omitted scope to "read" — a key can never
61
+ // become a write key by accident.
62
+ scope: request.body.scope,
63
+ keyHash: hashApiKey(secret),
64
+ },
65
+ });
66
+ return reply.code(201).send({ apiKey: toSummary(row), secret });
67
+ });
68
+ app.post("/api/api-keys/:id/revoke", {
69
+ onRequest: requireAdmin,
70
+ schema: {
71
+ params: ApiKeyParamsSchema,
72
+ response: {
73
+ 200: RevokeApiKeyResponseSchema,
74
+ 401: ApiErrorSchema,
75
+ 403: ApiErrorSchema,
76
+ 404: ApiErrorSchema,
77
+ },
78
+ },
79
+ }, async (request, reply) => {
80
+ const existing = await prisma.apiKey.findUnique({ where: { id: request.params.id } });
81
+ if (!existing) {
82
+ return reply.code(404).send({ message: `No API key found for id ${request.params.id}` });
83
+ }
84
+ // Idempotent: revoking an already-revoked key returns it unchanged —
85
+ // the caller's goal (key unusable) is already true, and keeping the
86
+ // original revokedAt preserves the audit trail.
87
+ const row = existing.revokedAt
88
+ ? existing
89
+ : await prisma.apiKey.update({
90
+ where: { id: existing.id },
91
+ data: { revokedAt: new Date() },
92
+ });
93
+ return { apiKey: toSummary(row) };
94
+ });
95
+ };
@@ -0,0 +1,2 @@
1
+ import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
2
+ export declare const apiPlaygroundRoutes: FastifyPluginAsyncZod;