@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,286 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { deriveSnapshotGroups } from "./snapshot-groups.js";
3
+ const SNAPSHOT_PAGE_SIZE = 500;
4
+ // True only for a genuine "table does not exist" error from the active
5
+ // dialect's driver — checked unconditionally across all three shapes rather
6
+ // than switching on adapter.dialect, since the codes never collide:
7
+ // Postgres (node-postgres) sets SQLSTATE 42P01 (undefined_table) on `.code`;
8
+ // MySQL (mysql2) sets `.errno` 1146 / `.code` "ER_NO_SUCH_TABLE"; SQLite
9
+ // (better-sqlite3) has no error code for this and only a message matching
10
+ // /no such table/i. Anything else (a dropped connection, a real bug, a
11
+ // transient network blip) must NOT match here — snapshotPluginTables
12
+ // rethrows those, which aborts the uninstall before its cleanup step runs,
13
+ // so a live table's rows are never dropped without ever having been
14
+ // captured.
15
+ function isTableMissingError(error) {
16
+ if (!(error instanceof Error))
17
+ return false;
18
+ const err = error;
19
+ if (err.code === "42P01")
20
+ return true; // Postgres: undefined_table
21
+ if (err.errno === 1146 || err.code === "ER_NO_SUCH_TABLE")
22
+ return true; // MySQL
23
+ if (/no such table/i.test(err.message))
24
+ return true; // SQLite
25
+ return false;
26
+ }
27
+ // Not a reuse of packages/content-service's SnapshotService — that one is
28
+ // a version POINTER (content is immutable, nothing is ever really at
29
+ // risk). Structured-storage rows are genuinely mutable, so this has to be
30
+ // a real schema+data dump. Talks to DatabaseAdapter directly, never
31
+ // through the registry-gated StructuredStorageAPI — by the time any of
32
+ // this runs the plugin is either mid-uninstall or already uninstalled,
33
+ // so the registry gate would reject it anyway.
34
+ export class StructuredSnapshotService {
35
+ adapter;
36
+ prisma;
37
+ constructor(adapter, prisma) {
38
+ this.adapter = adapter;
39
+ this.prisma = prisma;
40
+ }
41
+ async snapshotPluginTables(pluginId, trigger) {
42
+ const tables = await this.prisma.pluginStructuredTable.findMany({ where: { pluginId } });
43
+ const snapshotGroupId = randomUUID();
44
+ // No transaction wraps this loop: if a later table rethrows (genuine
45
+ // failure, not a missing table), any PluginStructuredSnapshot rows
46
+ // already written for earlier tables under this snapshotGroupId are
47
+ // NOT rolled back. That's fine — the caller never sees a returned
48
+ // groupId for an aborted call (the throw propagates out before the
49
+ // `return` below), so nothing ever restores or drops against this
50
+ // groupId. The leftover partial rows are inert orphans, not a
51
+ // correctness or data-loss risk.
52
+ for (const table of tables) {
53
+ const schema = table.schema;
54
+ const pkColumn = schema.columns.find((c) => c.primaryKey).name;
55
+ const rows = [];
56
+ let cursor;
57
+ try {
58
+ // Cursor-walks to completion rather than one unbounded query — same
59
+ // pattern the Session 86 conformance tests already establish.
60
+ for (;;) {
61
+ const page = await this.adapter.queryRows(table.physicalTableName, schema, {
62
+ cursor,
63
+ limit: SNAPSHOT_PAGE_SIZE,
64
+ });
65
+ rows.push(...page);
66
+ if (page.length < SNAPSHOT_PAGE_SIZE)
67
+ break;
68
+ cursor = String(page[page.length - 1][pkColumn]);
69
+ }
70
+ }
71
+ catch (error) {
72
+ if (!isTableMissingError(error)) {
73
+ // Anything other than a genuine "table does not exist" error —
74
+ // e.g. a transient connection failure on a table that's still
75
+ // very much live — must propagate. Swallowing it here would mean
76
+ // a table with rows never gets a snapshot entry, and the caller
77
+ // (uninstall) would then drop it via dropPluginTables believing
78
+ // it was safely captured: permanent data loss. Rethrowing aborts
79
+ // snapshotPluginTables (and therefore uninstall, before its
80
+ // cleanup step ever runs) instead — the safe outcome.
81
+ throw error;
82
+ }
83
+ // A tracked table whose physical table is genuinely already gone
84
+ // means an earlier snapshot attempt (before a mid-loop drop
85
+ // failure) already captured its rows — see
86
+ // dropPluginTables/restoreSnapshot. Skip it rather than throw so an
87
+ // uninstall retry, which re-snapshots every tracked table, isn't
88
+ // wedged by a table a prior attempt already finished dropping.
89
+ // Scoped to this table's iteration only, so a missing table never
90
+ // skips the next one.
91
+ console.warn(`[structured-snapshot] table ${table.physicalTableName} for ${pluginId} no longer exists — skipped (rows preserved in the earlier snapshot that captured them): ${String(error instanceof Error ? error.message : error)}`);
92
+ continue;
93
+ }
94
+ await this.prisma.pluginStructuredSnapshot.create({
95
+ data: {
96
+ snapshotGroupId,
97
+ pluginId,
98
+ tableName: table.tableName,
99
+ physicalTableName: table.physicalTableName,
100
+ schema: table.schema,
101
+ rows: rows,
102
+ ...(trigger ? { trigger } : {}),
103
+ },
104
+ });
105
+ }
106
+ return snapshotGroupId;
107
+ }
108
+ async restoreSnapshot(snapshotGroupId) {
109
+ const snapshots = await this.prisma.pluginStructuredSnapshot.findMany({
110
+ where: { snapshotGroupId },
111
+ });
112
+ for (const snap of snapshots) {
113
+ const schema = snap.schema;
114
+ const rows = snap.rows;
115
+ await this.adapter.createTable({ ...schema, tableName: snap.physicalTableName });
116
+ for (const row of rows) {
117
+ await this.adapter.insertRow(snap.physicalTableName, schema, row);
118
+ }
119
+ await this.prisma.pluginStructuredTable.create({
120
+ data: {
121
+ pluginId: snap.pluginId,
122
+ tableName: snap.tableName,
123
+ physicalTableName: snap.physicalTableName,
124
+ schema: snap.schema,
125
+ },
126
+ });
127
+ }
128
+ }
129
+ async dropPluginTables(pluginId) {
130
+ const tables = await this.prisma.pluginStructuredTable.findMany({ where: { pluginId } });
131
+ for (const table of tables) {
132
+ await this.adapter.dropTable(table.physicalTableName);
133
+ }
134
+ await this.prisma.pluginStructuredTable.deleteMany({ where: { pluginId } });
135
+ }
136
+ // S154 Task 5: the UNION restore plan (mockup semantics) — the newest
137
+ // available copy of each DISTINCT table this plugin has ever
138
+ // snapshotted, picked independently per table across ALL of the
139
+ // plugin's snapshot groups. Reuses Task 4's deriveSnapshotGroups (newest
140
+ // groups first) rather than re-deriving "newest per table" from scratch:
141
+ // walking the already-sorted groups and taking the first (freshest)
142
+ // group that contains each table name is exactly the union-pick, and
143
+ // keeps this selection logic defined in exactly one place.
144
+ async planRestore(pluginId) {
145
+ const rows = await this.prisma.pluginStructuredSnapshot.findMany({
146
+ where: { pluginId },
147
+ select: { snapshotGroupId: true, tableName: true, trigger: true, createdAt: true },
148
+ });
149
+ if (rows.length === 0)
150
+ return [];
151
+ const groups = deriveSnapshotGroups(rows); // newest first
152
+ const pickedByTable = new Map();
153
+ for (const group of groups) {
154
+ // A "restore"-trigger group is the PRE-RESTORE SAFETY SNAPSHOT that
155
+ // restorePluginData takes of itself, not a genuine prior backup — it
156
+ // exists only to protect whatever was live the instant some earlier
157
+ // restore ran, which makes it (by construction) the newest group for
158
+ // every table it holds. It still lists normally on the groups
159
+ // surface (deriveSnapshotGroups/listSnapshotGroups are untouched)
160
+ // and still counts toward totalTableCount there — only the
161
+ // SELECTION below skips it. Leaving it eligible would make restore
162
+ // selection unstable: an immediate second confirmed restore would
163
+ // re-select it and "restore" the just-protected pre-restore state
164
+ // right back, silently REVERTING the first restore (a double-
165
+ // submit / stale-screen hazard), and a crash between the safety
166
+ // snapshot and the replace loop would make a crash-retry restore the
167
+ // half-replaced pre-restore state instead of completing the
168
+ // originally intended restore. Excluding restore-trigger groups here
169
+ // is what makes a double-confirmed restore idempotent (both calls
170
+ // re-select the same underlying source groups) and a crash-retry
171
+ // resume the original intent instead of undoing it.
172
+ if (group.trigger === "restore")
173
+ continue;
174
+ for (const tableName of group.tableNames) {
175
+ if (!pickedByTable.has(tableName)) {
176
+ pickedByTable.set(tableName, {
177
+ tableName,
178
+ snapshotGroupId: group.snapshotGroupId,
179
+ groupTakenAt: group.takenAt,
180
+ });
181
+ }
182
+ }
183
+ }
184
+ // Deterministic order (by tableName) rather than Map insertion order,
185
+ // which would depend on which group happened to be walked first.
186
+ return [...pickedByTable.values()].sort((a, b) => a.tableName.localeCompare(b.tableName));
187
+ }
188
+ // S154 Task 5: replace-not-merge restore from the union plan above.
189
+ //
190
+ // The plan is computed FIRST, before the pre-restore safety snapshot.
191
+ // planRestore()'s own exclusion of trigger === "restore" groups (see its
192
+ // comment) is what actually makes a just-taken safety snapshot
193
+ // ineligible as a restore source — but computing the plan here before
194
+ // that snapshot is taken is a cheap second line of defense against the
195
+ // same failure mode, so a bug in that exclusion alone couldn't make this
196
+ // call's own safety snapshot self-select.
197
+ //
198
+ // The safety snapshot itself still runs before any destructive step
199
+ // (database.md's hard rule + the mockup's promised safety line): a
200
+ // crash between the snapshot and the per-table drop/create loop below
201
+ // leaves that pre-restore group fully intact — but the actual recovery
202
+ // is retrying THIS call (restorePluginData(pluginId)) again, not
203
+ // restoreSnapshot(groupId) on the safety group. restoreSnapshot() does a
204
+ // bare createTable for every snapshotted table with no drop first, so
205
+ // calling it here would fail outright: the plugin's live tables are
206
+ // still standing (the crash was before the drop/create loop ran), and
207
+ // createTable against an already-existing table errors. Retrying
208
+ // restorePluginData is safe specifically because of the eligibility fix
209
+ // above — planRestore's exclusion of trigger === "restore" groups means
210
+ // the retry recomputes the same original plan instead of self-selecting
211
+ // the safety snapshot it just took, so it resumes the intended restore
212
+ // rather than restoring the pre-restore state back onto itself.
213
+ async restorePluginData(pluginId) {
214
+ const plan = await this.planRestore(pluginId);
215
+ const trackedTables = await this.prisma.pluginStructuredTable.findMany({ where: { pluginId } });
216
+ let preRestoreSnapshotGroupId = null;
217
+ if (trackedTables.length > 0) {
218
+ const safetyGroupId = await this.snapshotPluginTables(pluginId, "restore");
219
+ // snapshotPluginTables ALWAYS returns a groupId, even when every one
220
+ // of the plugin's tracked tables turned out to have its physical
221
+ // table already gone (the isTableMissingError skip path in
222
+ // snapshotPluginTables) and zero PluginStructuredSnapshot rows
223
+ // actually got written under it. A groupId with nothing behind it
224
+ // protects nothing, and — being backed by zero rows — never appears
225
+ // on the groups listing either; reporting it to the caller as "your
226
+ // data was protected" would be a lie, so it collapses back to null
227
+ // here exactly like the "nothing was tracked at all" case above.
228
+ const capturedAnything = await this.prisma.pluginStructuredSnapshot.findFirst({
229
+ where: { snapshotGroupId: safetyGroupId },
230
+ select: { id: true },
231
+ });
232
+ preRestoreSnapshotGroupId = capturedAnything ? safetyGroupId : null;
233
+ }
234
+ const restoredTables = [];
235
+ for (const entry of plan) {
236
+ const snap = await this.prisma.pluginStructuredSnapshot.findFirst({
237
+ where: { snapshotGroupId: entry.snapshotGroupId, tableName: entry.tableName },
238
+ });
239
+ // Defensive only: the plan was derived from these exact rows a
240
+ // moment ago and nothing in between can delete a snapshot row, so
241
+ // this should never actually miss.
242
+ if (!snap)
243
+ continue;
244
+ const schema = snap.schema;
245
+ const rows = snap.rows;
246
+ // Idempotent drop THEN delete-the-tracking-row THEN recreate is
247
+ // what makes restoring onto a REINSTALLED plugin (live table +
248
+ // live tracking row already present, the deferred S87 collision)
249
+ // and restoring onto empty (neither present) the same safe path:
250
+ // a bare createTable would fail "table already exists" in the
251
+ // collision case, and the tracking-row create would hit a P2002
252
+ // unique violation on (pluginId, tableName) — dropping and
253
+ // deleting first means createTable and the tracking-row create
254
+ // always run against a clean slate either way.
255
+ await this.adapter.dropTable(snap.physicalTableName);
256
+ await this.prisma.pluginStructuredTable.deleteMany({
257
+ where: { pluginId, tableName: entry.tableName },
258
+ });
259
+ await this.adapter.createTable({ ...schema, tableName: snap.physicalTableName });
260
+ // The tracking row is created immediately after the table — BEFORE
261
+ // the insertRow loop below, not after. If a row partway through that
262
+ // loop fails (a bad value, a transient DB error), creating the
263
+ // tracking row first still leaves this table VISIBLE, as a
264
+ // (partially-filled) tracked table, to dropPluginTables /
265
+ // snapshotPluginTables / StructuredStorageAPI — a retried restore's
266
+ // replace path (drop -> delete-tracking -> recreate, same as this
267
+ // one) can then clean it up and finish. Creating the tracking row
268
+ // last would instead leave a live physical table with rows in it
269
+ // that no tracking row points to: invisible to every one of those
270
+ // callers, a silent leak no retry could ever find.
271
+ await this.prisma.pluginStructuredTable.create({
272
+ data: {
273
+ pluginId,
274
+ tableName: entry.tableName,
275
+ physicalTableName: snap.physicalTableName,
276
+ schema: snap.schema,
277
+ },
278
+ });
279
+ for (const row of rows) {
280
+ await this.adapter.insertRow(snap.physicalTableName, schema, row);
281
+ }
282
+ restoredTables.push(entry.tableName);
283
+ }
284
+ return { preRestoreSnapshotGroupId, restoredTables };
285
+ }
286
+ }
@@ -0,0 +1,22 @@
1
+ import type { PrismaClient } from "@prisma/client";
2
+ import { type DatabaseAdapter, type PluginTableSchema } from "@velora-cms/db-adapter";
3
+ import type { PluginRegistryService } from "./plugin-registry-service.js";
4
+ import { StructuredQueryBuilder } from "./structured-query-builder.js";
5
+ import { PluginTableNotDefinedError, StructuredStorageTransaction } from "./structured-storage-transaction.js";
6
+ export declare class PluginTableAlreadyDefinedError extends Error {
7
+ constructor(pluginId: string, tableName: string);
8
+ }
9
+ export { PluginTableNotDefinedError };
10
+ export declare class StructuredStorageAPI {
11
+ private readonly adapter;
12
+ private readonly registry;
13
+ private readonly prisma;
14
+ constructor(adapter: DatabaseAdapter, registry: PluginRegistryService, prisma: PrismaClient);
15
+ defineTable(pluginId: string, schema: PluginTableSchema): Promise<void>;
16
+ private resolveTable;
17
+ insert(pluginId: string, tableName: string, data: Record<string, unknown>): Promise<void>;
18
+ update(pluginId: string, tableName: string, where: Record<string, unknown>, data: Record<string, unknown>): Promise<number>;
19
+ delete(pluginId: string, tableName: string, where: Record<string, unknown>): Promise<number>;
20
+ query<T = Record<string, unknown>>(pluginId: string, tableName: string): StructuredQueryBuilder<T>;
21
+ transaction(pluginId: string, fn: (tx: StructuredStorageTransaction) => Promise<void>): Promise<void>;
22
+ }
@@ -0,0 +1,91 @@
1
+ import { validatePluginTableSchema } from "@velora-cms/db-adapter";
2
+ import { StructuredQueryBuilder } from "./structured-query-builder.js";
3
+ import { PluginTableNotDefinedError, resolvePluginTable, StructuredStorageTransaction, } from "./structured-storage-transaction.js";
4
+ import { physicalTableName } from "./structured-table-naming.js";
5
+ export class PluginTableAlreadyDefinedError extends Error {
6
+ constructor(pluginId, tableName) {
7
+ super(`Plugin "${pluginId}" has already defined a structured table named "${tableName}"`);
8
+ this.name = "PluginTableAlreadyDefinedError";
9
+ }
10
+ }
11
+ export { PluginTableNotDefinedError };
12
+ // StructuredStorageAPI (Month 8). Mirrors SimpleStorageAPI's shape:
13
+ // every method opens with registry.requireActivePlugin(pluginId), all
14
+ // real persistence delegates to DatabaseAdapter, zero SQL of its own.
15
+ export class StructuredStorageAPI {
16
+ adapter;
17
+ registry;
18
+ prisma;
19
+ constructor(adapter, registry, prisma) {
20
+ this.adapter = adapter;
21
+ this.registry = registry;
22
+ this.prisma = prisma;
23
+ }
24
+ async defineTable(pluginId, schema) {
25
+ await this.registry.requireActivePlugin(pluginId);
26
+ validatePluginTableSchema(schema);
27
+ const existing = await this.prisma.pluginStructuredTable.findUnique({
28
+ where: { pluginId_tableName: { pluginId, tableName: schema.tableName } },
29
+ });
30
+ if (existing) {
31
+ throw new PluginTableAlreadyDefinedError(pluginId, schema.tableName);
32
+ }
33
+ const physical = physicalTableName(pluginId, schema.tableName);
34
+ await this.adapter.createTable({ ...schema, tableName: physical });
35
+ try {
36
+ await this.prisma.pluginStructuredTable.create({
37
+ data: {
38
+ pluginId,
39
+ tableName: schema.tableName,
40
+ physicalTableName: physical,
41
+ // PluginTableSchema's optional fields (primaryKey?, notNull?,
42
+ // etc.) type as `T | undefined`, which Prisma's Json input
43
+ // type structurally rejects (only `T | null` is JSON-safe) —
44
+ // the value itself is genuinely JSON-serializable, so this is
45
+ // a type-system escape hatch, not a runtime concern.
46
+ schema: schema,
47
+ },
48
+ });
49
+ }
50
+ catch (cause) {
51
+ // Never leave an untracked physical table behind — Session 87's
52
+ // snapshot-before-uninstall can only see what's in
53
+ // PluginStructuredTable, so an orphaned table here would become a
54
+ // silent data-loss blind spot later.
55
+ await this.adapter.dropTable(physical).catch(() => { });
56
+ throw cause;
57
+ }
58
+ }
59
+ async resolveTable(pluginId, tableName) {
60
+ return resolvePluginTable(this.prisma, pluginId, tableName);
61
+ }
62
+ async insert(pluginId, tableName, data) {
63
+ await this.registry.requireActivePlugin(pluginId);
64
+ const { physicalTableName, schema } = await this.resolveTable(pluginId, tableName);
65
+ await this.adapter.insertRow(physicalTableName, schema, data);
66
+ }
67
+ async update(pluginId, tableName, where, data) {
68
+ await this.registry.requireActivePlugin(pluginId);
69
+ const { physicalTableName, schema } = await this.resolveTable(pluginId, tableName);
70
+ return this.adapter.updateRows(physicalTableName, schema, where, data);
71
+ }
72
+ async delete(pluginId, tableName, where) {
73
+ await this.registry.requireActivePlugin(pluginId);
74
+ const { physicalTableName, schema } = await this.resolveTable(pluginId, tableName);
75
+ return this.adapter.deleteRows(physicalTableName, schema, where);
76
+ }
77
+ query(pluginId, tableName) {
78
+ return new StructuredQueryBuilder(async (options) => {
79
+ await this.registry.requireActivePlugin(pluginId);
80
+ const { physicalTableName, schema } = await this.resolveTable(pluginId, tableName);
81
+ return this.adapter.queryRows(physicalTableName, schema, options);
82
+ });
83
+ }
84
+ async transaction(pluginId, fn) {
85
+ await this.registry.requireActivePlugin(pluginId);
86
+ await this.adapter.transaction(async (tx) => {
87
+ const handle = new StructuredStorageTransaction(tx, this.adapter.dialect, this.prisma, pluginId);
88
+ await fn(handle);
89
+ });
90
+ }
91
+ }
@@ -0,0 +1,22 @@
1
+ import type { PrismaClient } from "@prisma/client";
2
+ import { type DatabaseDialect, type PluginTableSchema, type TransactionContext } from "@velora-cms/db-adapter";
3
+ import { StructuredQueryBuilder } from "./structured-query-builder.js";
4
+ export declare class PluginTableNotDefinedError extends Error {
5
+ constructor(pluginId: string, tableName: string);
6
+ }
7
+ export declare function resolvePluginTable(prisma: PrismaClient, pluginId: string, tableName: string): Promise<{
8
+ physicalTableName: string;
9
+ schema: PluginTableSchema;
10
+ }>;
11
+ export declare class StructuredStorageTransaction {
12
+ private readonly tx;
13
+ private readonly dialect;
14
+ private readonly prisma;
15
+ private readonly pluginId;
16
+ constructor(tx: TransactionContext, dialect: DatabaseDialect, prisma: PrismaClient, pluginId: string);
17
+ private resolveTable;
18
+ insert(tableName: string, data: Record<string, unknown>): Promise<void>;
19
+ update(tableName: string, where: Record<string, unknown>, data: Record<string, unknown>): Promise<number>;
20
+ delete(tableName: string, where: Record<string, unknown>): Promise<number>;
21
+ query<T = Record<string, unknown>>(tableName: string): StructuredQueryBuilder<T>;
22
+ }
@@ -0,0 +1,63 @@
1
+ import { buildDeleteStatement, buildInsertStatement, buildSelectStatement, buildUpdateStatement, deserializeRow, } from "@velora-cms/db-adapter";
2
+ import { StructuredQueryBuilder } from "./structured-query-builder.js";
3
+ export class PluginTableNotDefinedError extends Error {
4
+ constructor(pluginId, tableName) {
5
+ super(`Plugin "${pluginId}" has not defined a structured table named "${tableName}"`);
6
+ this.name = "PluginTableNotDefinedError";
7
+ }
8
+ }
9
+ // Shared by StructuredStorageAPI and StructuredStorageTransaction — both
10
+ // need to resolve a plugin's declared table name to its physical table
11
+ // name and recorded schema before touching the adapter. Kept here (rather
12
+ // than duplicated in both files) so the two paths can't drift.
13
+ export async function resolvePluginTable(prisma, pluginId, tableName) {
14
+ const row = await prisma.pluginStructuredTable.findUnique({
15
+ where: { pluginId_tableName: { pluginId, tableName } },
16
+ });
17
+ if (!row) {
18
+ throw new PluginTableNotDefinedError(pluginId, tableName);
19
+ }
20
+ return { physicalTableName: row.physicalTableName, schema: row.schema };
21
+ }
22
+ // Plugin-safe transaction handle passed into StructuredStorageAPI's
23
+ // transaction(pluginId, fn) callback. Deliberately does NOT expose the
24
+ // raw TransactionContext (no plugin ever writes SQL) —
25
+ // only insert/update/query, scoped to the plugin fixed for this
26
+ // transaction's lifetime, reusing the exact same statement builders the
27
+ // top-level (non-transactional) StructuredStorageAPI path uses.
28
+ export class StructuredStorageTransaction {
29
+ tx;
30
+ dialect;
31
+ prisma;
32
+ pluginId;
33
+ constructor(tx, dialect, prisma, pluginId) {
34
+ this.tx = tx;
35
+ this.dialect = dialect;
36
+ this.prisma = prisma;
37
+ this.pluginId = pluginId;
38
+ }
39
+ async resolveTable(tableName) {
40
+ return resolvePluginTable(this.prisma, this.pluginId, tableName);
41
+ }
42
+ async insert(tableName, data) {
43
+ const { physicalTableName, schema } = await this.resolveTable(tableName);
44
+ await this.tx.execute(buildInsertStatement(this.dialect, physicalTableName, schema, data));
45
+ }
46
+ async update(tableName, where, data) {
47
+ const { physicalTableName, schema } = await this.resolveTable(tableName);
48
+ const result = await this.tx.execute(buildUpdateStatement(this.dialect, physicalTableName, schema, where, data));
49
+ return result.rowCount;
50
+ }
51
+ async delete(tableName, where) {
52
+ const { physicalTableName, schema } = await this.resolveTable(tableName);
53
+ const result = await this.tx.execute(buildDeleteStatement(this.dialect, physicalTableName, schema, where));
54
+ return result.rowCount;
55
+ }
56
+ query(tableName) {
57
+ return new StructuredQueryBuilder(async (options) => {
58
+ const { physicalTableName, schema } = await this.resolveTable(tableName);
59
+ const rows = await this.tx.query(buildSelectStatement(this.dialect, physicalTableName, schema, options));
60
+ return rows.map((row) => deserializeRow(schema, row));
61
+ });
62
+ }
63
+ }
@@ -0,0 +1 @@
1
+ export declare function physicalTableName(pluginId: string, tableName: string, maxLength?: number): string;
@@ -0,0 +1,18 @@
1
+ import { createHash } from "node:crypto";
2
+ // Plugin ids are reverse-domain strings (e.g. "com.velora.commerce") — not
3
+ // safe as bare SQL identifiers — and unlike SimpleStorageAPI's shared
4
+ // content_field_values table, structured storage has no shared table with
5
+ // a plugin_id column to filter by: each plugin gets its own physical
6
+ // table, so isolation has to happen at the naming level instead.
7
+ //
8
+ // Single-path, deterministic scheme (a hash prefix always present, never
9
+ // a "short name passthrough vs. long name hash fallback" branch) — one
10
+ // invariant to prove, not two, since Session 87's snapshot logic depends
11
+ // on this staying stable forever.
12
+ export function physicalTableName(pluginId, tableName, maxLength = 63) {
13
+ const sanitize = (value) => value.replace(/[^a-z0-9]+/gi, "_").toLowerCase();
14
+ const digest = createHash("sha256").update(`${pluginId}:${tableName}`).digest("hex").slice(0, 10);
15
+ const fixed = `p_${digest}_`;
16
+ const human = `${sanitize(pluginId)}_${sanitize(tableName)}`.slice(0, maxLength - fixed.length);
17
+ return `${fixed}${human}`;
18
+ }
@@ -0,0 +1,11 @@
1
+ import type { TrustedKey } from "@velora-cms/plugin-package";
2
+ /**
3
+ * The trusted key set for this process: what shipped with the CMS, plus
4
+ * anything VELORA_TRUSTED_SIGNING_KEYS supplies (later entries win on a
5
+ * keyId collision, so an operator can override a shipped key).
6
+ *
7
+ * An empty result is a legitimate, non-error state meaning "trust nobody".
8
+ * A malformed override warns and is ignored — a config typo must never
9
+ * stop the server booting.
10
+ */
11
+ export declare function loadTrustedKeys(): TrustedKey[];
@@ -0,0 +1,67 @@
1
+ import { deriveKeyId } from "@velora-cms/plugin-package";
2
+ import { SHIPPED_TRUSTED_KEYS } from "./trusted-signing-keys.js";
3
+ /**
4
+ * keyId is otherwise an unauthenticated label: nothing stops a {keyId,
5
+ * publicKey} pair whose keyId does not actually belong to that publicKey
6
+ * from entering the trusted set, at which point describeVerifyResult could
7
+ * log "verified against trusted key <keyId>" for a signature made by a
8
+ * completely different key. Re-deriving and comparing here is what makes
9
+ * D2's "cannot drift" claim true rather than aspirational — applied to
10
+ * BOTH the shipped set and any override, since a future entry in either
11
+ * place could get this wrong. deriveKeyId THROWS on a wrong-length key
12
+ * (envelope.ts's rawPublicKey), so it must be guarded, not assumed safe.
13
+ */
14
+ function keyIdMatchesPublicKey(entry, source) {
15
+ let derived;
16
+ try {
17
+ derived = deriveKeyId(entry.publicKey);
18
+ }
19
+ catch (error) {
20
+ console.warn(`[plugin-verify] ${source} entry with keyId "${entry.keyId}" has a malformed publicKey — ignoring it: ${error instanceof Error ? error.message : String(error)}`);
21
+ return false;
22
+ }
23
+ if (derived !== entry.keyId) {
24
+ console.warn(`[plugin-verify] ${source} entry declares keyId "${entry.keyId}" but its publicKey derives to keyId "${derived}" — ignoring it`);
25
+ return false;
26
+ }
27
+ return true;
28
+ }
29
+ function parseOverride(raw) {
30
+ let parsed;
31
+ try {
32
+ parsed = JSON.parse(raw);
33
+ }
34
+ catch {
35
+ console.warn("[plugin-verify] VELORA_TRUSTED_SIGNING_KEYS is not valid JSON — ignoring it");
36
+ return [];
37
+ }
38
+ const keys = parsed?.keys;
39
+ if (!Array.isArray(keys)) {
40
+ console.warn('[plugin-verify] VELORA_TRUSTED_SIGNING_KEYS must be {"keys":[…]} — ignoring it');
41
+ return [];
42
+ }
43
+ return keys
44
+ .filter((entry) => {
45
+ const candidate = entry;
46
+ return typeof candidate?.keyId === "string" && typeof candidate?.publicKey === "string";
47
+ })
48
+ .filter((entry) => keyIdMatchesPublicKey(entry, "VELORA_TRUSTED_SIGNING_KEYS"));
49
+ }
50
+ /**
51
+ * The trusted key set for this process: what shipped with the CMS, plus
52
+ * anything VELORA_TRUSTED_SIGNING_KEYS supplies (later entries win on a
53
+ * keyId collision, so an operator can override a shipped key).
54
+ *
55
+ * An empty result is a legitimate, non-error state meaning "trust nobody".
56
+ * A malformed override warns and is ignored — a config typo must never
57
+ * stop the server booting.
58
+ */
59
+ export function loadTrustedKeys() {
60
+ const raw = process.env.VELORA_TRUSTED_SIGNING_KEYS;
61
+ const shipped = SHIPPED_TRUSTED_KEYS.filter((entry) => keyIdMatchesPublicKey(entry, "SHIPPED_TRUSTED_KEYS"));
62
+ const merged = new Map();
63
+ for (const key of [...shipped, ...(raw ? parseOverride(raw) : [])]) {
64
+ merged.set(key.keyId, key);
65
+ }
66
+ return [...merged.values()];
67
+ }
@@ -0,0 +1,27 @@
1
+ import type { TrustedKey } from "@velora-cms/plugin-package";
2
+ /**
3
+ * The signing keys this CMS build trusts, shipped with the CMS.
4
+ *
5
+ * SHIPPED AS OF SESSION 165: the production marketplace signing key,
6
+ * ROTATED in Session 180 — the active key's private half is held solely
7
+ * by the operator outside this repository; the production marketplace's
8
+ * environment receives it. It is never written to this repo, the dev
9
+ * `.env`, or any database — the marketplace's signing_keys table stores
10
+ * only public halves. The pre-rotation key was retired in Session 182 —
11
+ * see the active entry's own comment below.
12
+ *
13
+ * Consequence, stated plainly: a stock install now verifies any plugin
14
+ * signed by the real marketplace with zero configuration — the
15
+ * VELORA_TRUSTED_SIGNING_KEYS era of hand-fed keys is over for the
16
+ * default case. An operator can still add or override keys with that
17
+ * env var (e.g. a private/self-hosted marketplace signing under its own
18
+ * key).
19
+ *
20
+ * Rotation is add-new + re-sign current artifacts + retire old — never
21
+ * delete a key that signed an artifact any CMS may still need to verify
22
+ * (the one-version-one-artifact rule, Session 127).
23
+ *
24
+ * A SET rather than a single key from day one: the Trust model leaves the
25
+ * door open for v1.x enterprise org-signing.
26
+ */
27
+ export declare const SHIPPED_TRUSTED_KEYS: TrustedKey[];
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The signing keys this CMS build trusts, shipped with the CMS.
3
+ *
4
+ * SHIPPED AS OF SESSION 165: the production marketplace signing key,
5
+ * ROTATED in Session 180 — the active key's private half is held solely
6
+ * by the operator outside this repository; the production marketplace's
7
+ * environment receives it. It is never written to this repo, the dev
8
+ * `.env`, or any database — the marketplace's signing_keys table stores
9
+ * only public halves. The pre-rotation key was retired in Session 182 —
10
+ * see the active entry's own comment below.
11
+ *
12
+ * Consequence, stated plainly: a stock install now verifies any plugin
13
+ * signed by the real marketplace with zero configuration — the
14
+ * VELORA_TRUSTED_SIGNING_KEYS era of hand-fed keys is over for the
15
+ * default case. An operator can still add or override keys with that
16
+ * env var (e.g. a private/self-hosted marketplace signing under its own
17
+ * key).
18
+ *
19
+ * Rotation is add-new + re-sign current artifacts + retire old — never
20
+ * delete a key that signed an artifact any CMS may still need to verify
21
+ * (the one-version-one-artifact rule, Session 127).
22
+ *
23
+ * A SET rather than a single key from day one: the Trust model leaves the
24
+ * door open for v1.x enterprise org-signing.
25
+ */
26
+ export const SHIPPED_TRUSTED_KEYS = [
27
+ {
28
+ // Active production marketplace signing key, since the Session 180
29
+ // rotation (2026-08-30). Its private half is held solely by the
30
+ // operator outside this repository; the production marketplace's
31
+ // environment receives it. Rotation is add-new + re-sign current
32
+ // artifacts + retire old — never delete a key that signed an artifact
33
+ // any CMS may still need to verify (the one-version-one-artifact
34
+ // rule, Session 127).
35
+ // Pre-rotation key 91d5529a9f997602 removed in S182 — every listed
36
+ // marketplace version was re-signed with this key in S180.
37
+ keyId: "73e409f633dbbea3",
38
+ publicKey: "MCowBQYDK2VwAyEAcTQOhyCUsvGCWooiC4cp1idRWzXE3W8acCU/uKzBOro=",
39
+ },
40
+ ];