@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,581 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { extractArtifact } from "@velora-cms/plugin-package";
6
+ import { hasClientBundle } from "@velora-cms/plugin-sdk/manifest";
7
+ import { installPackageTarball } from "../plugins/install-package-tarball.js";
8
+ import { removePluginPackages } from "../plugins/plugin-package-store.js";
9
+ import { PluginDependencyError, PluginIncompatibleError, PluginInstallError, } from "../plugins/plugin-registry-service.js";
10
+ import { seedTemplateSampleContent } from "../plugins/seed-template-content.js";
11
+ import { checkPackageIdentity, resolveSidecarPath, trustTierFor, verifyPackage } from "../plugins/verify-package.js";
12
+ import { loadTrustedKeys } from "../plugins/trusted-keys.js";
13
+ import { getSiteDomain } from "../config/site.js";
14
+ import { pluginAssetsRemoteUrl } from "../routes/plugin-assets.js";
15
+ import { MarketplaceNotFoundError, downloadPackage, downloadSignature, fetchListing, validateLicenseUpstream } from "./marketplace-client.js";
16
+ import { installBundleMembers } from "./install-bundle-members.js";
17
+ /** Duck-typed on `.code`, not `instanceof Prisma.PrismaClientKnownRequestError`
18
+ * — same reasoning as routes/users.ts's own duplicate-email handling:
19
+ * `prisma` (db/prisma.ts) is loaded dynamically per VELORA_PRISMA_CLIENT_DIR
20
+ * (multi-dialect client generation), so the error thrown by that runtime's
21
+ * client is a *different* module instance than any statically imported
22
+ * `@prisma/client`, and `instanceof` across that boundary always returns
23
+ * false. `code` is a stable string on the wire regardless of which client
24
+ * instance threw it. */
25
+ function isUniqueConstraintViolation(error) {
26
+ return typeof error === "object" && error !== null && "code" in error && error.code === "P2002";
27
+ }
28
+ /** Reads manifest.json's declared id/version out of a downloaded tarball
29
+ * WITHOUT running any part of the real install — used only to bind what
30
+ * was actually downloaded to what the caller REQUESTED, before
31
+ * installPackageTarball (or even downloadSignature) ever runs. See the
32
+ * call site's comment for why this matters. Extracts into its own
33
+ * disposable subdirectory via the same, already-reviewed extractArtifact()
34
+ * the real install path uses internally — never a hand-rolled read of an
35
+ * untrusted archive. Throws on a genuinely malformed/corrupt archive or a
36
+ * missing/malformed manifest.json, exactly mirroring
37
+ * installPackageTarball's own extraction-failure behavior, so both land on
38
+ * the same catch-all + cleanup + rethrow path below. */
39
+ async function peekPackageIdentity(tarballPath, peekDir) {
40
+ const extracted = await extractArtifact(tarballPath, peekDir);
41
+ if (!extracted.ok) {
42
+ throw new Error(`Package could not be extracted: ${extracted.reason}`);
43
+ }
44
+ const manifestPath = join(peekDir, "manifest.json");
45
+ if (!existsSync(manifestPath)) {
46
+ throw new Error("No manifest.json found in the downloaded package — not a velora-plugin built package");
47
+ }
48
+ const summary = JSON.parse(readFileSync(manifestPath, "utf8"));
49
+ if (!summary.id || !summary.version) {
50
+ throw new Error("manifest.json in the downloaded package is missing id/version");
51
+ }
52
+ // S152: the member list rides the signed artifact's manifest.json
53
+ // (write-manifest-json.ts passes bare string entries through verbatim).
54
+ // Read here — after the caller's trust gate has verified the tarball —
55
+ // so "resolve members before installing anything" is possible at all.
56
+ const bundledPlugins = Array.isArray(summary.contributions?.bundledPlugins)
57
+ ? summary.contributions.bundledPlugins.filter((entry) => typeof entry === "string")
58
+ : undefined;
59
+ return { id: summary.id, version: summary.version, type: summary.type, bundledPlugins };
60
+ }
61
+ /** The same "(reason) — message — nothing was installed" wording for both
62
+ * verification-failure sites below (the pre-install gate and its
63
+ * post-install belt-and-braces twin) — factored out so the two can never
64
+ * drift into different phrasing for what is, by construction, the same
65
+ * verdict. */
66
+ function describeVerificationFailure(pluginId, version, verification) {
67
+ const reason = verification.status === "failed" ? verification.reason : verification.status;
68
+ const detail = verification.status === "failed" ? ` — ${verification.message}` : "";
69
+ return `Signature verification failed for "${pluginId}"@${version} (${reason})${detail} — nothing was installed`;
70
+ }
71
+ /** Best-effort teardown for a marketplace install that must not leave
72
+ * partial state behind. Shared by the post-install trust-tier
73
+ * belt-and-braces check and the catch-all below (an UNEXPECTED throw —
74
+ * e.g. a plugin's own onActivate() blowing up mid-install;
75
+ * PluginRegistryService's own activate() doc comment anticipates hooks
76
+ * throwing).
77
+ *
78
+ * `outcome` is installPackageTarball's own RegisterLocalPluginResult
79
+ * (undefined when it never returned at all — install() threw before
80
+ * reaching that point). This is BOTH the H2 fix and the M4 fix from the
81
+ * S138 hardening review, because they turn out to be the same missing
82
+ * piece of information:
83
+ *
84
+ * - H2: an "installed" outcome means THIS call's own
85
+ * PluginRegistryService.install() created the row — migrations applied,
86
+ * document types created, onInstall run, and (per registerLocalPlugin)
87
+ * activate() run too. The real inverse of all of that is
88
+ * PluginRegistryService.uninstall(), not a hand-rolled partial teardown
89
+ * — the old version of this function only ever deleted the registry
90
+ * row, leaving PluginMigrationRecord/PluginDocumentType rows (and the
91
+ * document types themselves) behind, which permanently 500'd every
92
+ * later reinstall attempt via a P2002 inside install()'s own migration
93
+ * loop. uninstall() rolls all of that back in the correct LIFO order.
94
+ * confirmStructuredDataCleanup: true because this is an orphan teardown
95
+ * of a moments-old failed install, not a user-facing uninstall with
96
+ * real accumulated content to protect behind a confirmation prompt.
97
+ * - M4: an "activated" or "already-active" outcome means a CONCURRENT
98
+ * request's install() created the row instead — registerLocalPlugin's
99
+ * own tolerant merge path, taken when this call's registration attempt
100
+ * observed an already-existing row for a pluginId step 1's own
101
+ * findUnique had, moments earlier, confirmed did not exist yet. None of
102
+ * the registry row, the in-process manifest attachment, or the
103
+ * package-store copy belongs to THIS request in that case — tearing any
104
+ * of it down would delete the winner's legitimate install, not undo
105
+ * this request's own (nonexistent) one.
106
+ * - undefined means install() threw before ever reaching its own
107
+ * create() call (PluginIncompatibleError, PluginDependencyError, a
108
+ * manifest-validation PluginInstallError, or install()'s own internal
109
+ * migration/onInstall rollback already ran) — no row exists for this
110
+ * pluginId under any of those, from this request or a concurrent one,
111
+ * so the manifest-detach + package-store cleanup below is always safe.
112
+ *
113
+ * Each step gets its OWN try/catch and only warns/logs on failure — a
114
+ * cleanup failure must never mask whatever real error is already
115
+ * propagating (or, on the trust-tier path, prevent the 422 response from
116
+ * being sent). */
117
+ async function cleanupOrphanedInstall(deps, pluginId, outcome) {
118
+ if (outcome === "activated" || outcome === "already-active") {
119
+ console.warn(`[install-from-marketplace] skipping cleanup for "${pluginId}" — a concurrent install already owns its registry row (outcome: "${outcome}"); nothing here belongs to this request`);
120
+ return;
121
+ }
122
+ if (outcome === "installed") {
123
+ try {
124
+ await deps.registry.uninstall(pluginId, { confirmStructuredDataCleanup: true });
125
+ }
126
+ catch (error) {
127
+ console.error(`[install-from-marketplace] failed to fully uninstall "${pluginId}" during cleanup — it may be left partially registered and need manual removal`, error);
128
+ }
129
+ }
130
+ else {
131
+ try {
132
+ deps.registry.detachManifest(pluginId);
133
+ }
134
+ catch (error) {
135
+ console.warn(`[install-from-marketplace] failed to detach the in-process manifest for "${pluginId}" during cleanup`, error);
136
+ }
137
+ try {
138
+ await deps.prisma.pluginRegistry.deleteMany({ where: { pluginId } });
139
+ }
140
+ catch (error) {
141
+ console.error(`[install-from-marketplace] failed to delete the registry row for "${pluginId}" during cleanup — it may be orphaned and need manual removal`, error);
142
+ }
143
+ }
144
+ // Windows file-lock reality (per this module's own test suite): a sync
145
+ // rmSync immediately after the just-imported server/plugin.js's dynamic
146
+ // import can race a held handle, so this always runs last and never
147
+ // blocks the DB-side cleanup above on its own success.
148
+ try {
149
+ removePluginPackages(pluginId);
150
+ }
151
+ catch (error) {
152
+ console.warn(`[install-from-marketplace] failed to remove the stored package for "${pluginId}" during cleanup`, error);
153
+ }
154
+ }
155
+ export async function installFromMarketplace(deps, input) {
156
+ const { prisma, registry, marketplaceBaseUrl, adapter } = deps;
157
+ const { pluginId, version, licenseKey, installedBy } = input;
158
+ // Step 1: already installed — a plain DB lookup, no network at all, so
159
+ // this is always the cheapest possible rejection. This check has an
160
+ // inherent TOCTOU gap against a second, concurrent install of the same
161
+ // pluginId (closed below, at the actual write, not here — see the
162
+ // isUniqueConstraintViolation branch).
163
+ const existingRow = await prisma.pluginRegistry.findUnique({ where: { pluginId } });
164
+ if (existingRow) {
165
+ return { ok: false, failure: { code: "already-installed", message: `Plugin "${pluginId}" is already installed` } };
166
+ }
167
+ // Step 2: the listing (and the requested version within it) must exist
168
+ // upstream before anything else is checked or downloaded.
169
+ let listing;
170
+ try {
171
+ listing = (await fetchListing(marketplaceBaseUrl, pluginId));
172
+ }
173
+ catch (error) {
174
+ if (error instanceof MarketplaceNotFoundError) {
175
+ return { ok: false, failure: { code: "not-found", message: error.message } };
176
+ }
177
+ throw error;
178
+ }
179
+ const matchedVersion = (listing.versions ?? []).some((v) => v.version === version);
180
+ if (!matchedVersion) {
181
+ return {
182
+ ok: false,
183
+ failure: { code: "not-found", message: `No published version "${version}" for plugin "${pluginId}"` },
184
+ };
185
+ }
186
+ // Step 3: paid plugins gate on a validated license BEFORE a single
187
+ // package byte is downloaded. siteDomain is hoisted to function scope
188
+ // so step 6 (the license row) reuses the exact value validated here
189
+ // rather than re-reading the env a second time.
190
+ let siteDomain;
191
+ if (listing.priceCents > 0) {
192
+ if (!licenseKey) {
193
+ return {
194
+ ok: false,
195
+ failure: { code: "license-required", message: `Plugin "${pluginId}" is a paid plugin — a license key is required` },
196
+ };
197
+ }
198
+ siteDomain = getSiteDomain();
199
+ if (!siteDomain) {
200
+ return {
201
+ ok: false,
202
+ failure: {
203
+ code: "site-domain-missing",
204
+ message: "VELORA_SITE_DOMAIN must be configured before installing a paid plugin — the license binds to this instance's declared domain on first validation, and installing without it would burn that one-shot binding on a meaningless value",
205
+ },
206
+ };
207
+ }
208
+ const verdict = await validateLicenseUpstream(marketplaceBaseUrl, { key: licenseKey, pluginId, domain: siteDomain });
209
+ if (verdict.status !== "valid") {
210
+ return {
211
+ ok: false,
212
+ failure: {
213
+ code: "license-rejected",
214
+ message: `License rejected for "${pluginId}" (${verdict.status}): ${verdict.reason ?? "no reason given"}`,
215
+ },
216
+ };
217
+ }
218
+ }
219
+ // Steps 4-5: download into a disposable temp directory and hand the
220
+ // tarball to the existing verify+install pipeline. Cleaned in the
221
+ // finally below regardless of outcome. Tracks which RegisterLocalPluginResult
222
+ // (if any) installPackageTarball reports, so cleanupOrphanedInstall knows
223
+ // whether THIS request created the row it might need to tear down (see
224
+ // that function's own doc comment — this is the H2/M4 fix).
225
+ const tempDir = await mkdtemp(join(tmpdir(), "velora-install-"));
226
+ let installOutcome;
227
+ try {
228
+ const tarballPath = join(tempDir, "package.tar.gz");
229
+ // M1: a MarketplaceNotFoundError here (the listing named a version
230
+ // whose artifact route is missing — a real upstream data-consistency
231
+ // failure, not a client bug) must map to the same 404 the listing
232
+ // fetch above already gets. Nothing has been extracted, registered, or
233
+ // stored yet at this point, so there is nothing for cleanupOrphanedInstall
234
+ // to undo — a plain early return is enough.
235
+ try {
236
+ await downloadPackage(marketplaceBaseUrl, pluginId, version, tarballPath);
237
+ }
238
+ catch (error) {
239
+ if (error instanceof MarketplaceNotFoundError) {
240
+ return { ok: false, failure: { code: "not-found", message: error.message } };
241
+ }
242
+ throw error;
243
+ }
244
+ // S152: declared here — in the OUTER try's body, above the inner try
245
+ // below — rather than inside that inner try, because the inner try's
246
+ // own catch (the P2002 / typed-refusal / rethrow handling further
247
+ // down) needs to read and call these too. A `let`/`const` declared
248
+ // inside a try block is not visible from that try's own catch block
249
+ // (they are sibling scopes, not parent/child) — only a declaration in
250
+ // an enclosing scope is visible from both.
251
+ let bundleMembers;
252
+ // S152: any failure AFTER the members are in must take them back out
253
+ // (the atomic-set invariant), including their ownership rows if the
254
+ // createMany below already ran.
255
+ const rollbackBundleMembers = async () => {
256
+ if (!bundleMembers || bundleMembers.installedIds.length === 0)
257
+ return;
258
+ for (const memberId of [...bundleMembers.installedIds].reverse()) {
259
+ await cleanupOrphanedInstall(deps, memberId, "installed");
260
+ }
261
+ try {
262
+ await prisma.pluginBundleMember.deleteMany({ where: { bundlePluginId: pluginId } });
263
+ }
264
+ catch (error) {
265
+ console.error(`[install-from-marketplace] failed to delete bundle ownership rows for "${pluginId}" during cleanup`, error);
266
+ }
267
+ };
268
+ try {
269
+ // Identity binding, before anything is registered. installPackageTarball's
270
+ // own checkPackageIdentity only cross-checks the SIGNATURE ENVELOPE
271
+ // against the tarball's OWN manifest.json — it has no idea what
272
+ // pluginId/version THIS request actually asked for. A marketplace
273
+ // whose /packages/:pluginId/:version route serves the wrong artifact
274
+ // (a bug, or a compromised/misconfigured upstream) could otherwise
275
+ // have its own, internally self-consistent signature verify
276
+ // "verified" while installing a completely different plugin than
277
+ // what was requested — and since the registry row lands under the
278
+ // MANIFEST's id, not the requested one, a cleanup keyed only on the
279
+ // requested id would miss it entirely. Peeking the manifest here,
280
+ // before installPackageTarball (or even downloadSignature) ever
281
+ // runs, means a mismatch is refused before any registration side
282
+ // effect exists at all — nothing to clean up under either id,
283
+ // because nothing was ever installed under either id.
284
+ const actualIdentity = await peekPackageIdentity(tarballPath, join(tempDir, "identity-peek"));
285
+ if (actualIdentity.id !== pluginId || actualIdentity.version !== version) {
286
+ return {
287
+ ok: false,
288
+ failure: {
289
+ code: "verification-failed",
290
+ message: `Requested "${pluginId}"@${version} but the downloaded package identifies itself as "${actualIdentity.id}"@${actualIdentity.version} — refusing to install a mismatched package`,
291
+ },
292
+ };
293
+ }
294
+ const signatureText = await downloadSignature(marketplaceBaseUrl, pluginId, version);
295
+ const sidecarPath = resolveSidecarPath(tarballPath);
296
+ if (signatureText !== null) {
297
+ // resolveSidecarPath's convention: `<tarballPath>.sig`, right next
298
+ // to the artifact. installPackageTarball's own default sidecar
299
+ // resolution (when no options.sidecarPath is passed below) looks
300
+ // for exactly this path via existsSync, so writing here is all
301
+ // that's needed to hand it the signature.
302
+ await writeFile(sidecarPath, signatureText);
303
+ }
304
+ // C1 (S138 hardening review): the trust gate, computed and enforced
305
+ // HERE — before installPackageTarball, and therefore before a single
306
+ // line of the package's code has run. Previously this same verdict
307
+ // was only checked AFTER installPackageTarball had already copied
308
+ // the package into the managed store, `import()`ed its
309
+ // server/plugin.js (executing its top-level code), and run its
310
+ // migrations/onInstall/onActivate — a tampered or unsigned package
311
+ // was refused only once it had already executed once. That inverted
312
+ // this module's own documented spine ("download -> identity check ->
313
+ // verify -> register") and the spec's Decision 2 ("a marketplace
314
+ // install BLOCKS on failed verification").
315
+ //
316
+ // This reuses verifyPackage + checkPackageIdentity — the exact same
317
+ // functions installPackageTarball calls internally — against the
318
+ // exact same inputs (tarballPath, sidecarPath, loadTrustedKeys()) it
319
+ // will use a few lines below, plus actualIdentity from the identity
320
+ // peek above (already extracted, so this costs one extra
321
+ // hashTarball and nothing else — no second extraction). Because the
322
+ // inputs are identical and verifyPackage is a pure function of them,
323
+ // installPackageTarball's own internal recomputation is guaranteed
324
+ // to reach the same verdict; it is kept anyway as a second,
325
+ // belt-and-braces gate (see the comment where it's checked below)
326
+ // rather than trusted blindly.
327
+ const preVerification = checkPackageIdentity(await verifyPackage(tarballPath, existsSync(sidecarPath) ? sidecarPath : null, loadTrustedKeys()), actualIdentity.id, actualIdentity.version);
328
+ if (trustTierFor(preVerification) !== "verified") {
329
+ // Nothing has been imported, registered, or copied into the
330
+ // managed store yet — installPackageTarball has not been called —
331
+ // so there is nothing for cleanupOrphanedInstall to undo. This IS
332
+ // "refuse before execution," not "execute then refuse."
333
+ return {
334
+ ok: false,
335
+ failure: { code: "verification-failed", message: describeVerificationFailure(pluginId, version, preVerification) },
336
+ };
337
+ }
338
+ // S152: bundle fan-out — members first, bundle last. Runs only after
339
+ // the bundle's own artifact is downloaded, identity-bound, and
340
+ // signature-verified (the member list is READ from that verified
341
+ // artifact), and before the bundle itself is registered, so a bundle
342
+ // registry row existing always means the full set is in place.
343
+ // installBundleMembers guarantees that on any non-ok return or throw
344
+ // it has already rolled back every member it installed.
345
+ if (actualIdentity.type === "bundle") {
346
+ if (input.refuseBundleType) {
347
+ return {
348
+ ok: false,
349
+ failure: {
350
+ code: "install-refused",
351
+ message: `"${pluginId}" is itself a bundle — nested bundles are not supported as bundle members`,
352
+ },
353
+ };
354
+ }
355
+ const result = await installBundleMembers({
356
+ fetchMemberListing: (memberId) => fetchListing(marketplaceBaseUrl, memberId),
357
+ installOne: (memberInput) => installFromMarketplace(deps, { ...memberInput, refuseBundleType: true }),
358
+ // A member's install ended fully registered AND active
359
+ // (registerLocalPlugin always activates), so its real inverse is
360
+ // the same "installed"-outcome cleanup the single-plugin path
361
+ // already uses: registry.uninstall with structured cleanup
362
+ // confirmed (moments-old data, nothing to protect) plus
363
+ // package-store removal.
364
+ rollbackOne: (memberId) => cleanupOrphanedInstall(deps, memberId, "installed"),
365
+ }, { bundleId: pluginId, memberIds: actualIdentity.bundledPlugins ?? [], installedBy });
366
+ if (!result.ok) {
367
+ return result;
368
+ }
369
+ bundleMembers = result;
370
+ }
371
+ const setRemoteUrl = async (id, url) => {
372
+ await prisma.pluginRegistry.update({ where: { pluginId: id }, data: { remoteUrl: url } });
373
+ };
374
+ const syncManifestFields = async (id, fields) => {
375
+ await prisma.pluginRegistry.update({ where: { pluginId: id }, data: fields });
376
+ };
377
+ const setPackagePath = async (id, packagePath) => {
378
+ await prisma.pluginRegistry.update({ where: { pluginId: id }, data: { packagePath } });
379
+ };
380
+ // remoteUrl: the S143 plugin-assets mount serves the stored copy of
381
+ // this exact package, so the row points there — server-relative (see
382
+ // pluginAssetsRemoteUrl's doc comment for why not absolute). Safe to
383
+ // compute from the REQUESTED id/version: the identity peek above
384
+ // already proved the package's manifest matches them, and
385
+ // installBuiltPackage stores under the manifest's own id/version.
386
+ // The RegisterLocalPluginResult half of the return ("installed" |
387
+ // "activated" | "already-active") IS captured here (into
388
+ // installOutcome, declared above the outer try) — cleanupOrphanedInstall
389
+ // needs it to tell "this request's own install()" apart from "a
390
+ // concurrent request's install() that won the race against step 1's
391
+ // own check," which registerLocalPlugin's tolerant `existing` merge
392
+ // path takes silently rather than throwing (see cleanupOrphanedInstall's
393
+ // doc comment for the full reasoning — this is the M4 fix).
394
+ // S144: only client-bearing types get a plugin-assets remoteUrl —
395
+ // a theme/template/bundle package has no remoteEntry.js, and a URL
396
+ // pointing at a 404 would make the admin loader log a load failure
397
+ // for every view-less plugin on every boot. Empty string = the
398
+ // loader's existing "nothing to load" contract. The peeked type is
399
+ // unvalidated here, which is fine: it only picks a URL; the full
400
+ // manifest validation still runs inside the install.
401
+ const remoteUrl = actualIdentity.type && !hasClientBundle(actualIdentity.type)
402
+ ? ""
403
+ : pluginAssetsRemoteUrl(pluginId, version);
404
+ const { outcome, verification } = await installPackageTarball(registry, tarballPath, remoteUrl, setRemoteUrl, syncManifestFields, setPackagePath);
405
+ installOutcome = outcome;
406
+ // Belt-and-braces twin of the pre-install gate above, not the primary
407
+ // gate — C1's fix moved the REAL trust decision ahead of
408
+ // installPackageTarball, using the identical tarballPath/sidecarPath/
409
+ // trustedKeys inputs, so this recomputation (done internally by
410
+ // installPackageTarball) is expected to always agree and this branch
411
+ // should be unreachable in practice. It stays rather than being
412
+ // deleted: if a future change ever lets the two diverge (a trusted-key
413
+ // reload mid-request, a code path inserted between the two that
414
+ // touches the sidecar), this is what still stops an unverified
415
+ // package from being left installed instead of silently trusting the
416
+ // earlier check forever. installOutcome (just captured above) tells
417
+ // cleanupOrphanedInstall whether this request actually owns what
418
+ // needs undoing.
419
+ if (trustTierFor(verification) !== "verified") {
420
+ await cleanupOrphanedInstall(deps, pluginId, installOutcome);
421
+ await rollbackBundleMembers();
422
+ return {
423
+ ok: false,
424
+ failure: { code: "verification-failed", message: describeVerificationFailure(pluginId, version, verification) },
425
+ };
426
+ }
427
+ // Step 6: paid plugins get their license persisted only once the
428
+ // install has actually succeeded and verified.
429
+ const licensed = listing.priceCents > 0;
430
+ if (licensed) {
431
+ await prisma.pluginLicense.upsert({
432
+ where: { pluginId },
433
+ create: { pluginId, key: licenseKey, boundDomain: siteDomain, status: "valid", lastValidatedAt: new Date() },
434
+ update: { key: licenseKey, boundDomain: siteDomain, status: "valid", lastReason: null, lastValidatedAt: new Date() },
435
+ });
436
+ }
437
+ // Step 7: attaching the manifest again here would be redundant, not
438
+ // missing. registerLocalPlugin's `!existing` branch (always taken
439
+ // here, since step 1 already proved no row existed) calls
440
+ // PluginRegistryService.install(), which itself does
441
+ // `this.manifests.set(plugin.id, plugin)` before returning — the
442
+ // manifest is already attached in THIS process by the time
443
+ // installPackageTarball resolves. app.pluginRegistry.getManifest(id)
444
+ // answers immediately, no restart and no extra call needed.
445
+ // Step 7.5 (Session 149, Task 2): seed a template plugin's sample
446
+ // content, but ONLY when this request's own call actually created
447
+ // the row (installOutcome === "installed") — "activated"/
448
+ // "already-active" mean a concurrent request owns the install, same
449
+ // reasoning cleanupOrphanedInstall's doc comment already spells out
450
+ // for those two outcomes. D1's "never from inside
451
+ // PluginRegistryService.install()" is honored: this runs entirely
452
+ // outside it, after installPackageTarball has already returned.
453
+ // packagePath was just persisted by setPackagePath above (installBuiltPackage
454
+ // always calls it before returning), so a fresh read gets the exact
455
+ // stored directory seedTemplateSampleContent needs. Wrapped in its
456
+ // own try/catch as a belt-and-braces guard on top of the seeder's
457
+ // own internal non-fatal handling — D2 requires this install to
458
+ // stand no matter what happens here.
459
+ if (installOutcome === "installed") {
460
+ try {
461
+ const row = await prisma.pluginRegistry.findUnique({ where: { pluginId } });
462
+ if (row?.packagePath) {
463
+ const seedResult = await seedTemplateSampleContent({ prisma, adapter }, pluginId, row.packagePath, installedBy);
464
+ if (typeof seedResult === "object") {
465
+ console.log(`[install-from-marketplace] seeded ${seedResult.created} sample content node(s) for "${pluginId}"`);
466
+ }
467
+ else if (seedResult !== "none") {
468
+ console.log(`[install-from-marketplace] sample content seeding for "${pluginId}": ${seedResult}`);
469
+ }
470
+ }
471
+ }
472
+ catch (error) {
473
+ console.error(`[install-from-marketplace] sample content seeding for "${pluginId}" failed — the install stands`, error);
474
+ }
475
+ }
476
+ // S152: ownership rows LAST — written only once the bundle's own row
477
+ // exists and everything else has succeeded, so a rolled-back install
478
+ // can never leave ownership rows behind. Only members THIS operation
479
+ // installed get a row (skip-don't-claim); position preserves install
480
+ // order for the uninstall cascade's reverse walk.
481
+ if (bundleMembers && bundleMembers.installedIds.length > 0) {
482
+ await prisma.pluginBundleMember.createMany({
483
+ data: bundleMembers.installedIds.map((memberPluginId, index) => ({
484
+ bundlePluginId: pluginId,
485
+ memberPluginId,
486
+ position: index,
487
+ })),
488
+ });
489
+ }
490
+ return {
491
+ ok: true,
492
+ pluginId,
493
+ version,
494
+ trustTier: "verified",
495
+ licensed,
496
+ ...(bundleMembers ? { members: bundleMembers.members } : {}),
497
+ };
498
+ }
499
+ catch (error) {
500
+ // A second, concurrent install of the SAME never-before-installed
501
+ // plugin can both pass step 1's findUnique (neither sees the
502
+ // other's row yet) and then race PluginRegistryService.install()'s
503
+ // own prisma.pluginRegistry.create() — the loser's create() throws a
504
+ // unique-constraint violation on pluginId. That is not a partial
505
+ // failure to unwind: the loser's own install() never reached
506
+ // `this.manifests.set(...)` (it only runs AFTER create() succeeds),
507
+ // so nothing of THIS call's making was ever attached, activated, or
508
+ // registered. storePackage() did already run (installBuiltPackage
509
+ // copies into the managed store before calling install()), but that
510
+ // copy is byte-identical to the winner's — both installs are
511
+ // extracting the SAME downloaded tarball — so there is nothing of
512
+ // ours to undo, only the winner's legitimate install to leave
513
+ // completely alone. Report it exactly like the ordinary
514
+ // already-installed case.
515
+ //
516
+ // S152: this branch deliberately does NOT roll bundle members back.
517
+ // On P2002 a CONCURRENT request owns the bundle row, and that
518
+ // winner's own fan-out saw our already-installed members as
519
+ // pre-existing skips (skip-don't-claim) — tearing ours down here
520
+ // would break the winner's set out from under it. The members stay
521
+ // installed but unowned, exactly as if they pre-existed: a benign,
522
+ // documented residual mirroring cleanupOrphanedInstall's own M4 skip.
523
+ //
524
+ // The `installOutcome !== "installed"` guard matters because P2002
525
+ // is not exclusively the create-race described above: once THIS
526
+ // request's own registerLocalPlugin has already returned "installed",
527
+ // two LATER statements inside this same try can also throw P2002 —
528
+ // the pluginLicense.upsert (a concurrent license write) and the
529
+ // S152 pluginBundleMember.createMany (unique on [bundlePluginId,
530
+ // memberPluginId], which a stale ownership row can violate if a
531
+ // prior cleanup's own deleteMany failed and was swallowed). Neither
532
+ // of those is the concurrent-install race: this request DID create
533
+ // the registry row, so returning "already-installed" here would
534
+ // leave a fully-installed, active plugin/bundle behind with no
535
+ // ownership rows and no rollback. Only fall onto the early return
536
+ // when this request never won its own create — i.e. installOutcome
537
+ // is not "installed" — otherwise fall through to the cleanup path
538
+ // below, exactly like any other unexpected failure.
539
+ if (isUniqueConstraintViolation(error) && installOutcome !== "installed") {
540
+ return { ok: false, failure: { code: "already-installed", message: `Plugin "${pluginId}" is already installed` } };
541
+ }
542
+ // Anything else — a plugin's onActivate() throwing, a corrupt
543
+ // archive the identity peek didn't already catch, a DB error on the
544
+ // license upsert, or any other genuinely unexpected failure — may
545
+ // have left the plugin partially registered/attached/stored. Restore
546
+ // state before letting the error propagate; the route's own handler
547
+ // still turns an unmapped throw into a 500, but only AFTER this
548
+ // completes. installOutcome is undefined here whenever install()
549
+ // itself threw one of the three typed refusals mapped just below (all
550
+ // three fire from inside install(), before installPackageTarball ever
551
+ // returns) — cleanupOrphanedInstall treats that the same as "nothing
552
+ // exists to protect," which is correct for all three (see its own doc
553
+ // comment).
554
+ await cleanupOrphanedInstall(deps, pluginId, installOutcome);
555
+ await rollbackBundleMembers();
556
+ // M2 (S138 hardening review): PluginRegistryService.install()'s own
557
+ // typed refusals, previously unmapped and left to fall through to the
558
+ // generic rethrow below — which the route's handler turns into a bare
559
+ // 500, even though each of these is a well-understood, well-messaged
560
+ // refusal rather than a server bug. See the InstallFailure union's own
561
+ // comment for why "incompatible"/"install-refused" get 422 and
562
+ // "dependency-missing" gets 409. Checked most-specific-subclass-first
563
+ // since PluginIncompatibleError and PluginDependencyError both extend
564
+ // the base PluginInstallError.
565
+ if (error instanceof PluginIncompatibleError) {
566
+ return { ok: false, failure: { code: "incompatible", message: error.message } };
567
+ }
568
+ if (error instanceof PluginDependencyError) {
569
+ return { ok: false, failure: { code: "dependency-missing", message: error.message } };
570
+ }
571
+ if (error instanceof PluginInstallError) {
572
+ return { ok: false, failure: { code: "install-refused", message: error.message } };
573
+ }
574
+ throw error;
575
+ }
576
+ }
577
+ finally {
578
+ // Step 8.
579
+ await rm(tempDir, { recursive: true, force: true });
580
+ }
581
+ }
@@ -0,0 +1,38 @@
1
+ import { type RevalidateDeps } from "./revalidate-license.js";
2
+ /** One sweep over every plugin that has a license row. A single plugin's
3
+ * failure is logged and never aborts the sweep (the Session 91 precedent:
4
+ * one plugin must never take the others down). */
5
+ export declare function runLicenseRevalidationPass(deps: RevalidateDeps): Promise<{
6
+ checked: number;
7
+ revoked: number;
8
+ failed: number;
9
+ }>;
10
+ /** Starts the first background timer in apps/server. Two things are
11
+ * non-negotiable: the handle is unref()'d so it can never hold the process
12
+ * open, and the caller MUST stop it on app close (app.ts registers an
13
+ * onClose hook) — otherwise every integration test that builds and closes
14
+ * an app leaves a live timer behind.
15
+ *
16
+ * Passes are intended never to overlap, but forward progress must never
17
+ * depend on a third-party hook actually resolving. What is ACTUALLY
18
+ * guaranteed (corrected from an earlier version of this comment, which
19
+ * claimed the wait was inherently bounded by the upstream 8s timeout
20
+ * alone — true of the license CHECK, but not of registry.deactivate(),
21
+ * which also awaits a plugin's onDeactivate hook with no timeout of its
22
+ * own):
23
+ * - stop() always resolves within ~passTimeoutMs of being called,
24
+ * regardless of whether the in-flight pass (or the hook it's stuck
25
+ * in) ever finishes. If it gives up, it logs clearly that it did.
26
+ * - A pass that's been "in flight" longer than passTimeoutMs is
27
+ * presumed wedged, and the overlap guard stops treating it as a
28
+ * reason to skip the next tick — so a hung onDeactivate hook can
29
+ * disable enforcement for at most one bad tick, not for the rest of
30
+ * the process's life.
31
+ * - Giving up on a wedged pass does not cancel it. It keeps running in
32
+ * the background and will still write whatever verdict it eventually
33
+ * reaches; it just can no longer block anyone else from making
34
+ * progress. */
35
+ export declare function startLicenseRevalidation(deps: RevalidateDeps, opts?: {
36
+ intervalMs?: number;
37
+ passTimeoutMs?: number;
38
+ }): () => Promise<void>;