@endora-commerce/mod-cms 0.0.0-stage → 0.100.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 (264) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -2
  3. package/dist/admin/api/cms-client.d.ts +77 -0
  4. package/dist/admin/api/cms-client.d.ts.map +1 -0
  5. package/dist/admin/api/cms-client.js +162 -0
  6. package/dist/admin/api/cms-client.js.map +1 -0
  7. package/dist/admin/components/AdminCatalogPreviewProvider.d.ts +6 -0
  8. package/dist/admin/components/AdminCatalogPreviewProvider.d.ts.map +1 -0
  9. package/dist/admin/components/AdminCatalogPreviewProvider.js +74 -0
  10. package/dist/admin/components/AdminCatalogPreviewProvider.js.map +1 -0
  11. package/dist/admin/components/AdminCmsAssetProvider.d.ts +9 -0
  12. package/dist/admin/components/AdminCmsAssetProvider.d.ts.map +1 -0
  13. package/dist/admin/components/AdminCmsAssetProvider.js +50 -0
  14. package/dist/admin/components/AdminCmsAssetProvider.js.map +1 -0
  15. package/dist/admin/components/BackgroundFields.d.ts +4 -0
  16. package/dist/admin/components/BackgroundFields.d.ts.map +1 -0
  17. package/dist/admin/components/BackgroundFields.js +40 -0
  18. package/dist/admin/components/BackgroundFields.js.map +1 -0
  19. package/dist/admin/components/ButtonLinkFields.d.ts +6 -0
  20. package/dist/admin/components/ButtonLinkFields.d.ts.map +1 -0
  21. package/dist/admin/components/ButtonLinkFields.js +127 -0
  22. package/dist/admin/components/ButtonLinkFields.js.map +1 -0
  23. package/dist/admin/components/CarouselPreviewNav.d.ts +12 -0
  24. package/dist/admin/components/CarouselPreviewNav.d.ts.map +1 -0
  25. package/dist/admin/components/CarouselPreviewNav.js +31 -0
  26. package/dist/admin/components/CarouselPreviewNav.js.map +1 -0
  27. package/dist/admin/components/CmsContentEditorLayout.d.ts +12 -0
  28. package/dist/admin/components/CmsContentEditorLayout.d.ts.map +1 -0
  29. package/dist/admin/components/CmsContentEditorLayout.js +9 -0
  30. package/dist/admin/components/CmsContentEditorLayout.js.map +1 -0
  31. package/dist/admin/components/ComponentDragHandle.d.ts +6 -0
  32. package/dist/admin/components/ComponentDragHandle.d.ts.map +1 -0
  33. package/dist/admin/components/ComponentDragHandle.js +119 -0
  34. package/dist/admin/components/ComponentDragHandle.js.map +1 -0
  35. package/dist/admin/components/ContentSliderActionBarExtras.d.ts +6 -0
  36. package/dist/admin/components/ContentSliderActionBarExtras.d.ts.map +1 -0
  37. package/dist/admin/components/ContentSliderActionBarExtras.js +109 -0
  38. package/dist/admin/components/ContentSliderActionBarExtras.js.map +1 -0
  39. package/dist/admin/components/HookBlockAttachmentsPanel.d.ts +9 -0
  40. package/dist/admin/components/HookBlockAttachmentsPanel.d.ts.map +1 -0
  41. package/dist/admin/components/HookBlockAttachmentsPanel.js +137 -0
  42. package/dist/admin/components/HookBlockAttachmentsPanel.js.map +1 -0
  43. package/dist/admin/components/PageBuilderActionBar.d.ts +10 -0
  44. package/dist/admin/components/PageBuilderActionBar.d.ts.map +1 -0
  45. package/dist/admin/components/PageBuilderActionBar.js +118 -0
  46. package/dist/admin/components/PageBuilderActionBar.js.map +1 -0
  47. package/dist/admin/components/PageBuilderDrawer.d.ts +16 -0
  48. package/dist/admin/components/PageBuilderDrawer.d.ts.map +1 -0
  49. package/dist/admin/components/PageBuilderDrawer.js +53 -0
  50. package/dist/admin/components/PageBuilderDrawer.js.map +1 -0
  51. package/dist/admin/components/PageBuilderEditor.d.ts +45 -0
  52. package/dist/admin/components/PageBuilderEditor.d.ts.map +1 -0
  53. package/dist/admin/components/PageBuilderEditor.js +632 -0
  54. package/dist/admin/components/PageBuilderEditor.js.map +1 -0
  55. package/dist/admin/components/PuckActionGuard.d.ts +12 -0
  56. package/dist/admin/components/PuckActionGuard.d.ts.map +1 -0
  57. package/dist/admin/components/PuckActionGuard.js +30 -0
  58. package/dist/admin/components/PuckActionGuard.js.map +1 -0
  59. package/dist/admin/components/RowLayoutPicker.d.ts +8 -0
  60. package/dist/admin/components/RowLayoutPicker.d.ts.map +1 -0
  61. package/dist/admin/components/RowLayoutPicker.js +17 -0
  62. package/dist/admin/components/RowLayoutPicker.js.map +1 -0
  63. package/dist/admin/components/SliderPreviewActionBarExtras.d.ts +6 -0
  64. package/dist/admin/components/SliderPreviewActionBarExtras.d.ts.map +1 -0
  65. package/dist/admin/components/SliderPreviewActionBarExtras.js +32 -0
  66. package/dist/admin/components/SliderPreviewActionBarExtras.js.map +1 -0
  67. package/dist/admin/components/admin-catalog-preview-map.d.ts +17 -0
  68. package/dist/admin/components/admin-catalog-preview-map.d.ts.map +1 -0
  69. package/dist/admin/components/admin-catalog-preview-map.js +18 -0
  70. package/dist/admin/components/admin-catalog-preview-map.js.map +1 -0
  71. package/dist/admin/components/build-viewports.d.ts +23 -0
  72. package/dist/admin/components/build-viewports.d.ts.map +1 -0
  73. package/dist/admin/components/build-viewports.js +23 -0
  74. package/dist/admin/components/build-viewports.js.map +1 -0
  75. package/dist/admin/components/cms-template-layout.d.ts +16 -0
  76. package/dist/admin/components/cms-template-layout.d.ts.map +1 -0
  77. package/dist/admin/components/cms-template-layout.js +53 -0
  78. package/dist/admin/components/cms-template-layout.js.map +1 -0
  79. package/dist/admin/components/page-builder-i18n.d.ts +27 -0
  80. package/dist/admin/components/page-builder-i18n.d.ts.map +1 -0
  81. package/dist/admin/components/page-builder-i18n.js +34 -0
  82. package/dist/admin/components/page-builder-i18n.js.map +1 -0
  83. package/dist/admin/components/puck-safe.d.ts +10 -0
  84. package/dist/admin/components/puck-safe.d.ts.map +1 -0
  85. package/dist/admin/components/puck-safe.js +22 -0
  86. package/dist/admin/components/puck-safe.js.map +1 -0
  87. package/dist/admin/components/scope-utils.d.ts +4 -0
  88. package/dist/admin/components/scope-utils.d.ts.map +1 -0
  89. package/dist/admin/components/scope-utils.js +7 -0
  90. package/dist/admin/components/scope-utils.js.map +1 -0
  91. package/dist/admin/editors/BlockEditor.d.ts +9 -0
  92. package/dist/admin/editors/BlockEditor.d.ts.map +1 -0
  93. package/dist/admin/editors/BlockEditor.js +217 -0
  94. package/dist/admin/editors/BlockEditor.js.map +1 -0
  95. package/dist/admin/editors/PageEditor.d.ts +9 -0
  96. package/dist/admin/editors/PageEditor.d.ts.map +1 -0
  97. package/dist/admin/editors/PageEditor.js +316 -0
  98. package/dist/admin/editors/PageEditor.js.map +1 -0
  99. package/dist/admin/editors/TemplateEditor.d.ts +9 -0
  100. package/dist/admin/editors/TemplateEditor.d.ts.map +1 -0
  101. package/dist/admin/editors/TemplateEditor.js +180 -0
  102. package/dist/admin/editors/TemplateEditor.js.map +1 -0
  103. package/dist/admin/index.d.ts +38 -0
  104. package/dist/admin/index.d.ts.map +1 -0
  105. package/dist/admin/index.js +144 -0
  106. package/dist/admin/index.js.map +1 -0
  107. package/dist/admin/pages/BlocksListPage.d.ts +9 -0
  108. package/dist/admin/pages/BlocksListPage.d.ts.map +1 -0
  109. package/dist/admin/pages/BlocksListPage.js +47 -0
  110. package/dist/admin/pages/BlocksListPage.js.map +1 -0
  111. package/dist/admin/pages/HooksPage.d.ts +9 -0
  112. package/dist/admin/pages/HooksPage.d.ts.map +1 -0
  113. package/dist/admin/pages/HooksPage.js +41 -0
  114. package/dist/admin/pages/HooksPage.js.map +1 -0
  115. package/dist/admin/pages/PagesListPage.d.ts +9 -0
  116. package/dist/admin/pages/PagesListPage.d.ts.map +1 -0
  117. package/dist/admin/pages/PagesListPage.js +51 -0
  118. package/dist/admin/pages/PagesListPage.js.map +1 -0
  119. package/dist/admin/pages/TemplatesListPage.d.ts +9 -0
  120. package/dist/admin/pages/TemplatesListPage.d.ts.map +1 -0
  121. package/dist/admin/pages/TemplatesListPage.js +47 -0
  122. package/dist/admin/pages/TemplatesListPage.js.map +1 -0
  123. package/dist/admin-ui/index.d.ts +90 -0
  124. package/dist/admin-ui/index.d.ts.map +1 -0
  125. package/dist/admin-ui/index.js +89 -0
  126. package/dist/admin-ui/index.js.map +1 -0
  127. package/dist/backend/cli/block-names.d.ts +92 -0
  128. package/dist/backend/cli/block-names.d.ts.map +1 -0
  129. package/dist/backend/cli/block-names.js +132 -0
  130. package/dist/backend/cli/block-names.js.map +1 -0
  131. package/dist/backend/entities/cms-block.entity.d.ts +20 -0
  132. package/dist/backend/entities/cms-block.entity.d.ts.map +1 -0
  133. package/dist/backend/entities/cms-block.entity.js +77 -0
  134. package/dist/backend/entities/cms-block.entity.js.map +1 -0
  135. package/dist/backend/entities/cms-hook-block-attachment.entity.d.ts +15 -0
  136. package/dist/backend/entities/cms-hook-block-attachment.entity.d.ts.map +1 -0
  137. package/dist/backend/entities/cms-hook-block-attachment.entity.js +46 -0
  138. package/dist/backend/entities/cms-hook-block-attachment.entity.js.map +1 -0
  139. package/dist/backend/entities/cms-hook.entity.d.ts +20 -0
  140. package/dist/backend/entities/cms-hook.entity.d.ts.map +1 -0
  141. package/dist/backend/entities/cms-hook.entity.js +73 -0
  142. package/dist/backend/entities/cms-hook.entity.js.map +1 -0
  143. package/dist/backend/entities/cms-page.entity.d.ts +43 -0
  144. package/dist/backend/entities/cms-page.entity.d.ts.map +1 -0
  145. package/dist/backend/entities/cms-page.entity.js +140 -0
  146. package/dist/backend/entities/cms-page.entity.js.map +1 -0
  147. package/dist/backend/entities/cms-template.entity.d.ts +20 -0
  148. package/dist/backend/entities/cms-template.entity.d.ts.map +1 -0
  149. package/dist/backend/entities/cms-template.entity.js +73 -0
  150. package/dist/backend/entities/cms-template.entity.js.map +1 -0
  151. package/dist/backend/index.d.ts +157 -0
  152. package/dist/backend/index.d.ts.map +1 -0
  153. package/dist/backend/index.js +317 -0
  154. package/dist/backend/index.js.map +1 -0
  155. package/dist/backend/plugin.d.ts +81 -0
  156. package/dist/backend/plugin.d.ts.map +1 -0
  157. package/dist/backend/plugin.js +81 -0
  158. package/dist/backend/plugin.js.map +1 -0
  159. package/dist/backend/routes.admin.d.ts +19 -0
  160. package/dist/backend/routes.admin.d.ts.map +1 -0
  161. package/dist/backend/routes.admin.js +164 -0
  162. package/dist/backend/routes.admin.js.map +1 -0
  163. package/dist/backend/routes.storefront.d.ts +6 -0
  164. package/dist/backend/routes.storefront.d.ts.map +1 -0
  165. package/dist/backend/routes.storefront.js +89 -0
  166. package/dist/backend/routes.storefront.js.map +1 -0
  167. package/dist/backend/services/asset-embed-resolver.d.ts +17 -0
  168. package/dist/backend/services/asset-embed-resolver.d.ts.map +1 -0
  169. package/dist/backend/services/asset-embed-resolver.js +40 -0
  170. package/dist/backend/services/asset-embed-resolver.js.map +1 -0
  171. package/dist/backend/services/asset-references.d.ts +4 -0
  172. package/dist/backend/services/asset-references.d.ts.map +1 -0
  173. package/dist/backend/services/asset-references.js +55 -0
  174. package/dist/backend/services/asset-references.js.map +1 -0
  175. package/dist/backend/services/cms-block-read-port.d.ts +27 -0
  176. package/dist/backend/services/cms-block-read-port.d.ts.map +1 -0
  177. package/dist/backend/services/cms-block-read-port.js +50 -0
  178. package/dist/backend/services/cms-block-read-port.js.map +1 -0
  179. package/dist/backend/services/cms-block-seed-port.d.ts +31 -0
  180. package/dist/backend/services/cms-block-seed-port.d.ts.map +1 -0
  181. package/dist/backend/services/cms-block-seed-port.js +68 -0
  182. package/dist/backend/services/cms-block-seed-port.js.map +1 -0
  183. package/dist/backend/services/cms-block-service.d.ts +38 -0
  184. package/dist/backend/services/cms-block-service.d.ts.map +1 -0
  185. package/dist/backend/services/cms-block-service.js +218 -0
  186. package/dist/backend/services/cms-block-service.js.map +1 -0
  187. package/dist/backend/services/cms-cache.d.ts +54 -0
  188. package/dist/backend/services/cms-cache.d.ts.map +1 -0
  189. package/dist/backend/services/cms-cache.js +113 -0
  190. package/dist/backend/services/cms-cache.js.map +1 -0
  191. package/dist/backend/services/cms-hook-service.d.ts +35 -0
  192. package/dist/backend/services/cms-hook-service.d.ts.map +1 -0
  193. package/dist/backend/services/cms-hook-service.js +216 -0
  194. package/dist/backend/services/cms-hook-service.js.map +1 -0
  195. package/dist/backend/services/cms-language-reference.d.ts +11 -0
  196. package/dist/backend/services/cms-language-reference.d.ts.map +1 -0
  197. package/dist/backend/services/cms-language-reference.js +26 -0
  198. package/dist/backend/services/cms-language-reference.js.map +1 -0
  199. package/dist/backend/services/cms-page-read-port.d.ts +23 -0
  200. package/dist/backend/services/cms-page-read-port.d.ts.map +1 -0
  201. package/dist/backend/services/cms-page-read-port.js +54 -0
  202. package/dist/backend/services/cms-page-read-port.js.map +1 -0
  203. package/dist/backend/services/cms-page-service.d.ts +104 -0
  204. package/dist/backend/services/cms-page-service.d.ts.map +1 -0
  205. package/dist/backend/services/cms-page-service.js +468 -0
  206. package/dist/backend/services/cms-page-service.js.map +1 -0
  207. package/dist/backend/services/cms-reference-registry.d.ts +30 -0
  208. package/dist/backend/services/cms-reference-registry.d.ts.map +1 -0
  209. package/dist/backend/services/cms-reference-registry.js +98 -0
  210. package/dist/backend/services/cms-reference-registry.js.map +1 -0
  211. package/dist/backend/services/cms-template-service.d.ts +37 -0
  212. package/dist/backend/services/cms-template-service.d.ts.map +1 -0
  213. package/dist/backend/services/cms-template-service.js +205 -0
  214. package/dist/backend/services/cms-template-service.js.map +1 -0
  215. package/dist/backend/services/content-tree-walker.d.ts +7 -0
  216. package/dist/backend/services/content-tree-walker.d.ts.map +1 -0
  217. package/dist/backend/services/content-tree-walker.js +89 -0
  218. package/dist/backend/services/content-tree-walker.js.map +1 -0
  219. package/dist/backend/services/page-builder-registry.d.ts +120 -0
  220. package/dist/backend/services/page-builder-registry.d.ts.map +1 -0
  221. package/dist/backend/services/page-builder-registry.js +220 -0
  222. package/dist/backend/services/page-builder-registry.js.map +1 -0
  223. package/dist/backend/services/seed-hooks.d.ts +22 -0
  224. package/dist/backend/services/seed-hooks.d.ts.map +1 -0
  225. package/dist/backend/services/seed-hooks.js +94 -0
  226. package/dist/backend/services/seed-hooks.js.map +1 -0
  227. package/dist/backend/services/storefront-resolver.d.ts +71 -0
  228. package/dist/backend/services/storefront-resolver.d.ts.map +1 -0
  229. package/dist/backend/services/storefront-resolver.js +329 -0
  230. package/dist/backend/services/storefront-resolver.js.map +1 -0
  231. package/dist/manifest.d.ts +225 -0
  232. package/dist/manifest.d.ts.map +1 -0
  233. package/dist/manifest.js +695 -0
  234. package/dist/manifest.js.map +1 -0
  235. package/dist/migrations/20260425T162418_cms_pages_init.d.ts +10 -0
  236. package/dist/migrations/20260425T162418_cms_pages_init.d.ts.map +1 -0
  237. package/dist/migrations/20260425T162418_cms_pages_init.js +30 -0
  238. package/dist/migrations/20260425T162418_cms_pages_init.js.map +1 -0
  239. package/dist/migrations/20260505T130214_cms_init.d.ts +28 -0
  240. package/dist/migrations/20260505T130214_cms_init.d.ts.map +1 -0
  241. package/dist/migrations/20260505T130214_cms_init.js +332 -0
  242. package/dist/migrations/20260505T130214_cms_init.js.map +1 -0
  243. package/dist/migrations/20260903T101741_cms_namespace_block_names.d.ts +6 -0
  244. package/dist/migrations/20260903T101741_cms_namespace_block_names.d.ts.map +1 -0
  245. package/dist/migrations/20260903T101741_cms_namespace_block_names.js +60 -0
  246. package/dist/migrations/20260903T101741_cms_namespace_block_names.js.map +1 -0
  247. package/dist/migrations/20260912T094733_cms_sales_channel_cms_pages.d.ts +27 -0
  248. package/dist/migrations/20260912T094733_cms_sales_channel_cms_pages.d.ts.map +1 -0
  249. package/dist/migrations/20260912T094733_cms_sales_channel_cms_pages.js +44 -0
  250. package/dist/migrations/20260912T094733_cms_sales_channel_cms_pages.js.map +1 -0
  251. package/dist/migrations/20260912T125709_cms_page_body_asset_ref_index.d.ts +32 -0
  252. package/dist/migrations/20260912T125709_cms_page_body_asset_ref_index.d.ts.map +1 -0
  253. package/dist/migrations/20260912T125709_cms_page_body_asset_ref_index.js +36 -0
  254. package/dist/migrations/20260912T125709_cms_page_body_asset_ref_index.js.map +1 -0
  255. package/dist/migrations/index.d.ts +31 -0
  256. package/dist/migrations/index.d.ts.map +1 -0
  257. package/dist/migrations/index.js +37 -0
  258. package/dist/migrations/index.js.map +1 -0
  259. package/docs/cms/extending-page-builder.md +256 -0
  260. package/docs/cms/index.md +233 -0
  261. package/i18n/en.json +231 -0
  262. package/i18n/pl.json +231 -0
  263. package/package.json +125 -3
  264. package/tailwind.css +16 -0
@@ -0,0 +1,32 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * The GIN index on `cms_pages.body` that keeps the assets-library
4
+ * reference-protection scan fast (feature 013, research R10 — the reference
5
+ * registry's CMS descriptor runs `jsonb_path_exists` over `body`).
6
+ *
7
+ * It was created by `assets_library`' frozen
8
+ * `Migration20260505T102206AssetsLibraryInit` until
9
+ * `specs/120-migration-closure-bridge-ownership/` Phase 3. Under D-226 a
10
+ * migration may name a table only if its own module, its transitive
11
+ * `dependencies` closure or the platform creates it, and `assets_library`
12
+ * does not declare `cms` — nor could it sensibly, `cms` declaring
13
+ * `assets_library`. An instance that omits `cms` had `assets_library`'s init
14
+ * indexing a table nothing builds.
15
+ *
16
+ * It belongs here because `cms_pages` is this module's own table, and the
17
+ * closure holds in the direction the SQL runs: this module declares
18
+ * `assets_library`, not the other way round. The index exists for a
19
+ * consumer in another module and lives with the table it is on, which is
20
+ * what the ownership rule says.
21
+ *
22
+ * `if not exists`, because every database that has already applied the
23
+ * frozen migration has this index. The storage keys on the class name and
24
+ * holds no checksum, so the reduced frozen body is not re-offered there.
25
+ * The statement is otherwise the frozen one verbatim — same name, same
26
+ * operator class — so the two paths reach one schema.
27
+ */
28
+ export declare class Migration20260912T125709CmsPageBodyAssetRefIndex extends Migration {
29
+ up(): Promise<void>;
30
+ down(): Promise<void>;
31
+ }
32
+ //# sourceMappingURL=20260912T125709_cms_page_body_asset_ref_index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260912T125709_cms_page_body_asset_ref_index.d.ts","sourceRoot":"","sources":["../../src/migrations/20260912T125709_cms_page_body_asset_ref_index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,gDAAiD,SAAQ,SAAS;IAC9D,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAMnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
@@ -0,0 +1,36 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * The GIN index on `cms_pages.body` that keeps the assets-library
4
+ * reference-protection scan fast (feature 013, research R10 — the reference
5
+ * registry's CMS descriptor runs `jsonb_path_exists` over `body`).
6
+ *
7
+ * It was created by `assets_library`' frozen
8
+ * `Migration20260505T102206AssetsLibraryInit` until
9
+ * `specs/120-migration-closure-bridge-ownership/` Phase 3. Under D-226 a
10
+ * migration may name a table only if its own module, its transitive
11
+ * `dependencies` closure or the platform creates it, and `assets_library`
12
+ * does not declare `cms` — nor could it sensibly, `cms` declaring
13
+ * `assets_library`. An instance that omits `cms` had `assets_library`'s init
14
+ * indexing a table nothing builds.
15
+ *
16
+ * It belongs here because `cms_pages` is this module's own table, and the
17
+ * closure holds in the direction the SQL runs: this module declares
18
+ * `assets_library`, not the other way round. The index exists for a
19
+ * consumer in another module and lives with the table it is on, which is
20
+ * what the ownership rule says.
21
+ *
22
+ * `if not exists`, because every database that has already applied the
23
+ * frozen migration has this index. The storage keys on the class name and
24
+ * holds no checksum, so the reduced frozen body is not re-offered there.
25
+ * The statement is otherwise the frozen one verbatim — same name, same
26
+ * operator class — so the two paths reach one schema.
27
+ */
28
+ export class Migration20260912T125709CmsPageBodyAssetRefIndex extends Migration {
29
+ async up() {
30
+ this.addSql('create index if not exists "idx_cms_pages_body_asset_refs" on "cms_pages" using gin ("body" jsonb_path_ops);');
31
+ }
32
+ async down() {
33
+ this.addSql('drop index if exists "idx_cms_pages_body_asset_refs";');
34
+ }
35
+ }
36
+ //# sourceMappingURL=20260912T125709_cms_page_body_asset_ref_index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260912T125709_cms_page_body_asset_ref_index.js","sourceRoot":"","sources":["../../src/migrations/20260912T125709_cms_page_body_asset_ref_index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,OAAO,gDAAiD,SAAQ,SAAS;IACpE,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CACT,8GAA8G,CAC/G,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,uDAAuD,CAAC,CAAC;IACvE,CAAC;CACF"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which is the order of this module's own
10
+ * migrations and of nothing else (feature 081): a manifest `dependencies` array
11
+ * is the only thing ordering this block against another module's.
12
+ *
13
+ * The **named** exports stay beside the array, and the asymmetry with
14
+ * `./backend` — which publishes an array and no named class (D-168) — is
15
+ * deliberate. `db/migrations-registry.generated.ts` imports each class by name
16
+ * from this specifier, and a migration class name is contract in a way an entity
17
+ * class name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom is
22
+ * a query against a table nobody created.
23
+ */
24
+ import { Migration20260425T162418CmsPagesInit } from './20260425T162418_cms_pages_init.js';
25
+ import { Migration20260505T130214CmsInit } from './20260505T130214_cms_init.js';
26
+ import { Migration20260903T101741CmsNamespaceBlockNames } from './20260903T101741_cms_namespace_block_names.js';
27
+ import { Migration20260912T094733CmsSalesChannelCmsPages } from './20260912T094733_cms_sales_channel_cms_pages.js';
28
+ import { Migration20260912T125709CmsPageBodyAssetRefIndex } from './20260912T125709_cms_page_body_asset_ref_index.js';
29
+ export declare const migrations: (typeof Migration20260425T162418CmsPagesInit)[];
30
+ export { Migration20260425T162418CmsPagesInit, Migration20260505T130214CmsInit, Migration20260903T101741CmsNamespaceBlockNames, Migration20260912T094733CmsSalesChannelCmsPages, Migration20260912T125709CmsPageBodyAssetRefIndex, };
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,oCAAoC,EAAE,MAAM,qCAAqC,CAAC;AAC3F,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,8CAA8C,EAAE,MAAM,gDAAgD,CAAC;AAChH,OAAO,EAAE,+CAA+C,EAAE,MAAM,kDAAkD,CAAC;AACnH,OAAO,EAAE,gDAAgD,EAAE,MAAM,oDAAoD,CAAC;AAEtH,eAAO,MAAM,UAAU,iDAMtB,CAAC;AAEF,OAAO,EACL,oCAAoC,EACpC,+BAA+B,EAC/B,8CAA8C,EAC9C,+CAA+C,EAC/C,gDAAgD,GACjD,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which is the order of this module's own
10
+ * migrations and of nothing else (feature 081): a manifest `dependencies` array
11
+ * is the only thing ordering this block against another module's.
12
+ *
13
+ * The **named** exports stay beside the array, and the asymmetry with
14
+ * `./backend` — which publishes an array and no named class (D-168) — is
15
+ * deliberate. `db/migrations-registry.generated.ts` imports each class by name
16
+ * from this specifier, and a migration class name is contract in a way an entity
17
+ * class name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom is
22
+ * a query against a table nobody created.
23
+ */
24
+ import { Migration20260425T162418CmsPagesInit } from './20260425T162418_cms_pages_init.js';
25
+ import { Migration20260505T130214CmsInit } from './20260505T130214_cms_init.js';
26
+ import { Migration20260903T101741CmsNamespaceBlockNames } from './20260903T101741_cms_namespace_block_names.js';
27
+ import { Migration20260912T094733CmsSalesChannelCmsPages } from './20260912T094733_cms_sales_channel_cms_pages.js';
28
+ import { Migration20260912T125709CmsPageBodyAssetRefIndex } from './20260912T125709_cms_page_body_asset_ref_index.js';
29
+ export const migrations = [
30
+ Migration20260425T162418CmsPagesInit,
31
+ Migration20260505T130214CmsInit,
32
+ Migration20260903T101741CmsNamespaceBlockNames,
33
+ Migration20260912T094733CmsSalesChannelCmsPages,
34
+ Migration20260912T125709CmsPageBodyAssetRefIndex,
35
+ ];
36
+ export { Migration20260425T162418CmsPagesInit, Migration20260505T130214CmsInit, Migration20260903T101741CmsNamespaceBlockNames, Migration20260912T094733CmsSalesChannelCmsPages, Migration20260912T125709CmsPageBodyAssetRefIndex, };
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,oCAAoC,EAAE,MAAM,qCAAqC,CAAC;AAC3F,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,8CAA8C,EAAE,MAAM,gDAAgD,CAAC;AAChH,OAAO,EAAE,+CAA+C,EAAE,MAAM,kDAAkD,CAAC;AACnH,OAAO,EAAE,gDAAgD,EAAE,MAAM,oDAAoD,CAAC;AAEtH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,oCAAoC;IACpC,+BAA+B;IAC/B,8CAA8C;IAC9C,+CAA+C;IAC/C,gDAAgD;CACjD,CAAC;AAEF,OAAO,EACL,oCAAoC,EACpC,+BAA+B,EAC/B,8CAA8C,EAC9C,+CAA+C,EAC/C,gDAAgD,GACjD,CAAC"}
@@ -0,0 +1,256 @@
1
+ ---
2
+ title: Extending the Page Builder
3
+ sidebar_position: 2
4
+ ---
5
+
6
+ # Extending the Page Builder
7
+
8
+ The CMS module ships built-in components — `Row`, `Columns`, `Text`,
9
+ `Heading`, `Button`, `InsertBlock`, and others — but every other
10
+ backend module can contribute its own components through an in-process
11
+ service-provider interface (SPI). (`InsertTemplate` remains registered for
12
+ legacy content trees but is no longer listed in the drawer palette.)
13
+ A contributing module participates in three places:
14
+
15
+ 1. **Backend descriptor**: register field metadata at composition time so
16
+ the admin's component palette can render the editor controls.
17
+ 2. **Shared renderer**: ship a React component to a workspace package both
18
+ admin and storefront depend on (typically `@endora-commerce/cms-components` itself
19
+ or a per-module package re-exporting from it).
20
+ 3. **Composition wiring**: pass the contributing module's registration
21
+ helper to the platform's composition root before the CMS plugin is
22
+ instantiated.
23
+
24
+ ## 1. Declare the descriptor
25
+
26
+ In your module's `plugin.ts` (or a dedicated `register-page-builder.ts`),
27
+ declare a function that takes the registry and registers your components:
28
+
29
+ ```ts
30
+ // the promotions module: src/backend/services/register-page-builder.ts
31
+ import type { PageBuilderRegistry } from '../../cms/services/page-builder-registry.js';
32
+
33
+ export function registerPromotionsPageBuilderComponents(
34
+ registry: PageBuilderRegistry,
35
+ ): void {
36
+ registry.register('promotions', {
37
+ components: {
38
+ PromoBanner: {
39
+ fields: {
40
+ headline: { type: 'text', label: 'Headline', required: true },
41
+ codeInput: { type: 'text', label: 'Promo code' },
42
+ tone: {
43
+ type: 'select',
44
+ label: 'Tone',
45
+ options: [
46
+ { label: 'Info', value: 'info' },
47
+ { label: 'Urgent', value: 'urgent' },
48
+ ],
49
+ },
50
+ },
51
+ previewIcon: 'ticket',
52
+ /** Omit email/invoice — this component is storefront-only. */
53
+ contexts: ['cms'],
54
+ },
55
+ },
56
+ });
57
+ }
58
+ ```
59
+
60
+ Field types are taken from the closed enum declared in
61
+ `packages/contracts/src/cms.ts`: `text | textarea | number | select |
62
+ radio | array | object | external | uuid | richtext`. This metadata is
63
+ React-free; the backend never imports a renderer.
64
+
65
+ ## 2. Ship the renderer
66
+
67
+ Add a React `ComponentConfig` in `packages/cms-components/src/components/`
68
+ (or in your module's own admin/storefront package) and export it from the
69
+ package's entry point:
70
+
71
+ ```tsx
72
+ // packages/cms-components/src/components/PromoBanner.tsx
73
+ import type { ComponentConfig } from '@measured/puck';
74
+
75
+ interface Props {
76
+ headline: string;
77
+ codeInput?: string;
78
+ tone: 'info' | 'urgent';
79
+ }
80
+
81
+ export const PromoBanner: ComponentConfig<Props> = {
82
+ fields: {
83
+ headline: { type: 'text' },
84
+ codeInput: { type: 'text' },
85
+ tone: { type: 'select', options: [
86
+ { label: 'Info', value: 'info' },
87
+ { label: 'Urgent', value: 'urgent' },
88
+ ] },
89
+ },
90
+ defaultProps: { headline: 'Spring sale', tone: 'info' },
91
+ contexts: ['cms'],
92
+ render: ({ headline, codeInput, tone }) => (
93
+ <div className={`promo-banner promo-${tone}`}>
94
+ <strong>{headline}</strong>
95
+ {codeInput ? <code>{codeInput}</code> : null}
96
+ </div>
97
+ ),
98
+ };
99
+ ```
100
+
101
+ Wire it into the `defaultPageBuilderConfig` so both the admin editor and
102
+ the storefront `<Render>` call sites pick it up:
103
+
104
+ ```ts
105
+ // packages/cms-components/src/index.ts
106
+ import { PromoBanner } from './components/PromoBanner.js';
107
+
108
+ export * from './components/PromoBanner.js';
109
+
110
+ export const defaultPageBuilderConfig = {
111
+ // ...existing categories
112
+ components: {
113
+ Row,
114
+ Columns,
115
+ Text,
116
+ Heading,
117
+ Button,
118
+ InsertBlock,
119
+ InsertTemplate,
120
+ PromoBanner,
121
+ },
122
+ };
123
+ ```
124
+
125
+ A renderer omitted from the bundle does not break the admin: the
126
+ `PageBuilderEditor` merges the descriptor with the local config and
127
+ substitutes a `MissingComponentPlaceholder` for any component the
128
+ descriptor names but the bundle does not export. The placeholder renders
129
+ nothing on the storefront unless the page is loaded with the
130
+ `?cms_admin=1` query parameter (preview mode).
131
+
132
+ ## 3. Wire composition
133
+
134
+ Call your registration helper from `composition.ts` before the CMS module
135
+ is instantiated:
136
+
137
+ ```ts
138
+ // backend/src/composition.ts
139
+ const cms = cmsModule({ emFactory, requireAdmin });
140
+ registerPromotionsPageBuilderComponents(cms.handle.pageBuilderRegistry);
141
+ ```
142
+
143
+ The CMS module's plugin reads the registry on every
144
+ `GET /api/v1/admin/cms/page-builder/config` call, so registering after the
145
+ module instance is constructed is fine — the editor picks the new
146
+ component up on the next admin reload.
147
+
148
+ ## Component name namespacing
149
+
150
+ Component names are unique platform-wide. The registry warns and
151
+ overwrites on collisions; reviewers should reject changes that produce a
152
+ warning. By convention, prefix names with the contributing module's
153
+ domain when ambiguity is likely (`PromoBanner`, `CatalogProductCard`,
154
+ etc.).
155
+
156
+ ## Context availability (`contexts`)
157
+
158
+ Every component declares which Page Builder surfaces may expose it via
159
+ `contexts`:
160
+
161
+ | Context | Used by |
162
+ |---------|---------|
163
+ | `cms` | CMS pages, blocks, templates, blog |
164
+ | `email` | Transactional email editor |
165
+ | `newsletter` | Newsletter campaigns (alias of email-safe set) |
166
+ | `invoice` | Invoice PDF template editor |
167
+
168
+ Register on the backend descriptor:
169
+
170
+ ```ts
171
+ contexts: ['cms'], // default when omitted in registry — CMS-only
172
+ ```
173
+
174
+ In `@endora-commerce/cms-components`, wrap the Puck config with
175
+ `definePageBuilderComponent` from `@endora-commerce/page-builder-core` so the admin
176
+ palette filter stays in sync. Components without `email` / `invoice` in
177
+ `contexts` never appear in those editors (e.g. a product carousel).
178
+
179
+ ## Responsive props and visibility (CMS only)
180
+
181
+ The CMS Page Builder supports per-breakpoint overrides with inheritance
182
+ (mobile ← tablet ← desktop). Use `createResponsiveField` from
183
+ `@endora-commerce/page-builder-core` for individual props and
184
+ `withResponsiveVisibility` for a per-component **Visibility** control.
185
+
186
+ Default breakpoints (configurable via Settings
187
+ `cms.page_builder.breakpoint.*` or env `CMS_PB_BREAKPOINT_*`):
188
+
189
+ - Mobile: &lt; 768px
190
+ - Tablet: 768–1023px
191
+ - Desktop: ≥ 1024px
192
+
193
+ Email and invoice builders use a single layout width and do not expose
194
+ responsive fields.
195
+
196
+ **Email builders** (transactional + newsletter) do **not** use CMS Mobile /
197
+ Tablet / Desktop viewports. The authoring canvas is fixed at **600px** (mail
198
+ width). A **Preview** modal offers **600px** / **320px** HTML frames. Color
199
+ fields reuse the CMS color palette.
200
+
201
+ ## Nesting (slots)
202
+
203
+ Layout components (`Row`, `Columns`) use Puck **slot** fields — nested
204
+ content is stored in `props`, not the legacy `zones` map.
205
+
206
+ ## Box model (CMS layout + content)
207
+
208
+ Layout and content components expose **outer spacing** (margin), **inner
209
+ spacing** (padding), and **border** fields on the Responsive tab. Values
210
+ support uniform or per-side editing. **Columns** use a responsive column
211
+ count (1–12 per breakpoint) with equal-width tracks.
212
+
213
+ Use `createSpacingField`, `createBorderField`, and `createColorField` from
214
+ `@endora-commerce/page-builder-core` when adding new CMS components.
215
+
216
+ ## Built-in Icons / Social
217
+
218
+ - **`Icons`** — curated Lucide allowlist (~100 icons) in
219
+ `packages/cms-components/src/components/icon-catalog.ts` with a **visual
220
+ grid picker** (`IconPickerField`). Do not expose the full Lucide catalog.
221
+ - **`Social`** — brand icons via `react-icons/fa6` (Facebook, X, Instagram,
222
+ LinkedIn, YouTube, TikTok, …). Array items use `getItemSummary` so the
223
+ selected **network name** appears in the Links list. Layouts:
224
+ `icons-only` | `icons-with-labels` | `vertical-list` | `pills`.
225
+
226
+ ## Additional landing components
227
+
228
+ | Component | Purpose |
229
+ | --------- | ------- |
230
+ | `Spacer` | Vertical space + optional divider |
231
+ | `FeatureList` | Icon + title + description columns |
232
+ | `Hero` | Background + heading + CTA first-fold |
233
+ | `LogoStrip` | Partner / trust logos |
234
+ | `Testimonial` | Quote + author |
235
+ | `Stats` | KPI strip |
236
+ | `AnnouncementBar` | Thin promo strip |
237
+ | `SimpleTable` | Pipe-separated headers/rows |
238
+ | `NewsletterSignup` | Email form (wire `actionUrl` to newsletter) |
239
+ | `ContactFormEmbed` | Iframe embed or mailto fallback |
240
+
241
+ ## Suggested follow-up (deeper integrations)
242
+
243
+ | Priority | Idea | Why |
244
+ | -------- | ---- | --- |
245
+ | Medium | Newsletter ↔ module 048 | Auto-wire channel subscribe endpoint |
246
+ | Low | Contact ↔ forms module | Pick an existing form instead of iframe URL |
247
+
248
+ `Accordion` / `Tabs` already cover FAQ-style sections; prefer those before a
249
+ dedicated FAQ component.
250
+
251
+ ## Testing
252
+
253
+ The platform ships a TDD scaffold for the SPI in
254
+ `backend/test/integration/cms/page-builder-extension.test.ts`. Use the
255
+ fixture in `backend/test/fixtures/cms/test-extension-module.ts` as a
256
+ template when adding tests for your own contributions.
@@ -0,0 +1,233 @@
1
+ ---
2
+ title: CMS
3
+ sidebar_position: 1
4
+ description: Page Builder authoring surface — Pages, Blocks, Templates, Hooks — per channel + language
5
+ ---
6
+
7
+ # CMS
8
+
9
+ The CMS module is the platform's editorial surface. It owns four entities
10
+ authored through a drag-and-drop **Page Builder** and surfaced on the
11
+ storefront with full sales-channel and language scoping.
12
+
13
+ | Entity | Identifier | Lifecycle | Embedded by |
14
+ | ----------- | ----------------- | ------------------------------------ | --------------------------------------------- |
15
+ | **Page** | `slug` (per channel) | `draft → published → archived` | URL on the storefront |
16
+ | **Block** | `code` (per channel) | `active` flag | Pages (`InsertBlock`) and Hooks (attachment) |
17
+ | **Template**| `code` (per channel) | always-visible (no flag) | Blueprints for pages/blocks (**Save as template** / **Apply template**). Legacy `InsertTemplate` embeds still resolve at runtime. |
18
+ | **Hook** | `code` (global) | `active` flag, system-protected seed | Storefront layouts (`<Hook code="..." />`) |
19
+
20
+ ## Entities and the reference graph
21
+
22
+ The entity graph at runtime:
23
+
24
+ ```
25
+ Hook ──── attachment ────┐
26
+ ▼
27
+ Block ─── InsertBlock ───┐
28
+ ▼
29
+ Template (blueprint) ──> Page / Block canvas (Save as / Apply template)
30
+ Template ── InsertTemplate (legacy) ──> Page (slug-routed)
31
+ ```
32
+
33
+ Content templates (`cms_templates`) are reusable Page Builder layouts (Save as template / Apply template). Email and invoice templates stay in their own admin lists and storage. Embedding via `InsertTemplate` is withdrawn from the component drawer; existing trees still render.
34
+
35
+ Reference protection runs on every delete:
36
+
37
+ - A **Block** referenced by a Page, a Template, or a Hook attachment cannot be deleted (HTTP `409 CMS_REFERENCED`).
38
+ - A **Template** referenced by a Page or a Block cannot be deleted.
39
+ - A **Hook** flagged `is_system=true` cannot be deleted (`409 CMS_HOOK_SYSTEM_PROTECTED`); admin-created Hooks are deletable.
40
+
41
+ Every reference scan is a JSONB walk over the entity's `content` envelope (`page→block`, `page→template`, `block→template`, `template→block`) plus a foreign-key check on `cms_hook_block_attachments` for `hook→block`.
42
+
43
+ The same `content` JSONB is scanned by the **Assets Library reference registry** — deleting an Asset embedded in a CMS component's `props` is similarly refused.
44
+
45
+ ## Page Builder authoring
46
+
47
+ The Page Builder is built on **Puck** (`@measured/puck`) and ships components
48
+ in `@endora-commerce/cms-components`. Core layout/content defaults:
49
+
50
+ | Component | Purpose |
51
+ | --------------- | ---------------------------------------------------------------------- |
52
+ | `Row` | Flex-column layout container. |
53
+ | `Heading` | `h1`–`h6` with align + level select. |
54
+ | `Text` | Simple body text with typography controls. |
55
+ | `RichContent` | TipTap rich text (links modal, colors, images). |
56
+ | `Button` | Label + link target + variant select. |
57
+ | `Image` | URL or asset library; width modes + align. |
58
+ | `Icons` | Visual Lucide icon picker (~100 curated icons). |
59
+ | `Social` | Brand social icons (`react-icons`); link list shows network names. |
60
+ | `Spacer` | Vertical spacing + optional divider. |
61
+ | `FeatureList` | Icon + title + description columns. |
62
+ | `Hero` | CTA banner with background, heading, button. |
63
+ | `LogoStrip` | Partner / trust logos. |
64
+ | `Testimonial` | Quote + author (+ optional avatar). |
65
+ | `Stats` | KPI / counter strip. |
66
+ | `AnnouncementBar` | Thin promo strip. |
67
+ | `SimpleTable` | Simple pipe-separated table. |
68
+ | `NewsletterSignup` | Email signup form (configurable action URL). |
69
+ | `ContactFormEmbed` | Form iframe embed or mailto. |
70
+ | `InsertBlock` | Embeds a Block by `code`. Storefront inlines the resolved Block. |
71
+ | `InsertTemplate`| Legacy: embeds a Template by `code`. Kept for existing trees; **not** in the drawer palette. Prefer Save as / Apply template. |
72
+
73
+ Additional categories (catalog, media, interactive, forms, advanced) ship
74
+ Product*, Video, Map, sliders, Tabs, Accordion, RawHtml/RawJs, etc.
75
+
76
+ ### Preview viewports
77
+
78
+ Puck Mobile / Tablet / Desktop frames use logical widths **360 / tabletMin /
79
+ max(desktopMin, 1280)**. Zoom is Puck’s built-in `transform: scale` inside an
80
+ iframe (`waitForStyles: true`). Prefer the viewport switcher over browser
81
+ resize when checking responsive props — CSS `@media` rules resolve against the
82
+ iframe width, while editing-tier JS uses the selected viewport width.
83
+
84
+ The content tree is persisted in a JSONB envelope:
85
+
86
+ ```jsonc
87
+ {
88
+ "schema_version": 1,
89
+ "languages": {
90
+ "pl-PL": { /* Puck Data tree */ },
91
+ "en-US": { /* Puck Data tree */ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ `schema_version` is bumped only when the storage shape of a node changes. The boot-time `content-schema-upgrader` walks every saved tree and rewrites nodes in place; current components start at `schema_version=1`.
97
+
98
+ ## Sales-channel + language scoping
99
+
100
+ Every Page, Block, Template, and Hook is bound to one or more sales
101
+ channels (M:N join with the `code`/`slug` denormalised onto the join row
102
+ to enforce per-channel uniqueness at the DB level). The same `slug` may
103
+ exist in two channels — they're independent rows. Languages live as a
104
+ JSONB array on each entity; the storefront resolver follows the standard
105
+ platform fallback rule (requested → channel default → 404).
106
+
107
+ The admin's `ScopePicker` restricts the per-channel language list to
108
+ each channel's configured language set; saving a Page whose `languages`
109
+ includes a code unsupported by any assigned channel returns
110
+ `400 CMS_LANGUAGE_NOT_IN_CHANNEL_SCOPE`.
111
+
112
+ ## Seeded Hooks
113
+
114
+ 23 base Hook codes are seeded with `is_system=true` at boot via an
115
+ idempotent reconciler. They cover the standard storefront insertion
116
+ points:
117
+
118
+ ```
119
+ header.top
120
+ homepage.top, homepage.bottom
121
+ footer.before, footer.top, footer.bottom, footer.after, footer.copyright
122
+ category.top, category.bottom
123
+ product.top, product.bottom, product.buttons.after
124
+ search.top, search.bottom
125
+ page.top, page.bottom
126
+ cms.page.top, cms.page.bottom
127
+ login.top, login.bottom
128
+ register.top, register.bottom
129
+ ```
130
+
131
+ Each `<Hook code="…" />` server component fetches
132
+ `/api/v1/cms/hooks/by-code` with the resolved channel + language and
133
+ renders every active attached Block in `position` order. Empty
134
+ attachments + failed fetches both render nothing — Hooks must not break a
135
+ page render.
136
+
137
+ ## HTTP surface
138
+
139
+ ### Admin (`/api/v1/admin/cms`)
140
+
141
+ | Method | Path | Purpose |
142
+ | ------- | --------------------------------------------------- | ----------------------------------------------- |
143
+ | GET | `/pages` | Paginated Page list with filters. |
144
+ | POST | `/pages` | Create a Page (always starts as `draft`). |
145
+ | GET | `/pages/:id` | Detail with full content envelope + version. |
146
+ | PATCH | `/pages/:id` | Edit metadata; honours `If-Match` via `version`.|
147
+ | PUT | `/pages/:id/content/:language` | Save the Page Builder tree per language. |
148
+ | POST | `/pages/:id/{publish,archive,unarchive}` | Lifecycle transitions. |
149
+ | DELETE | `/pages/:id` | Hard delete (channels are cascade-unbound). |
150
+ | Same | `/blocks/*` | Same shape; per-channel-unique `code`; reference-protected on delete. |
151
+ | Same | `/templates/*` | Same shape minus `active` flag. |
152
+ | GET | `/hooks`, `/hooks/:id` | List + detail with attachment count. |
153
+ | POST | `/hooks` | Create an admin (non-system) Hook. |
154
+ | PATCH | `/hooks/:id` | Edit name / active / scope; `code` is immutable.|
155
+ | DELETE | `/hooks/:id` | Refused with 409 when `is_system=true`. |
156
+ | GET / POST / PATCH / DELETE | `/hooks/:id/attachments[/:blockId]` | Attach / reorder / detach Blocks on a Hook. |
157
+ | GET | `/page-builder/config` | Merged Page Builder descriptor (metadata only). |
158
+
159
+ ### Storefront (`/api/v1/cms`)
160
+
161
+ | Method | Path | Returns |
162
+ | ------ | --------------------------------- | -------------------------------------------------------------- |
163
+ | GET | `/pages/by-slug?slug=…&language=…`| Resolved Page with `embeds.blocks`, `embeds.templates`, assets.|
164
+ | GET | `/blocks/by-code?code=…&language=…`| Resolved Block (single, channel-filtered, active-only). |
165
+ | GET | `/hooks/by-code?code=…&language=…`| Ordered active Block list for the named Hook. |
166
+
167
+ Channel resolution prefers the `X-Sales-Channel` header, then falls back
168
+ to the system-default channel. Language resolution prefers `?language=`,
169
+ then `Accept-Language`, then the channel's configured default.
170
+
171
+ ## Storefront resolution + Redis cache
172
+
173
+ The single `StorefrontResolver` service answers all three storefront
174
+ read operations with one query for the root entity, one batched query
175
+ per embed type (`InsertBlock` / `InsertTemplate`) per recursion level,
176
+ and one batched call to `assetsLibrary.resolveUrl` for every embedded
177
+ asset. Recursion is capped at depth 3; cycles or deeper graphs degrade
178
+ to a `MissingComponentPlaceholder` rendered admin-side.
179
+
180
+ Resolved payloads are cached in Redis with a 5-minute TTL under three
181
+ key families:
182
+
183
+ ```
184
+ cms:v1:page:<slug>:<channel>:<language>
185
+ cms:v1:block:<code>:<channel>:<language>
186
+ cms:v1:hook:<code>:<channel>:<language>
187
+ ```
188
+
189
+ Invalidation runs on every Page / Block / Template / Hook write:
190
+
191
+ - Page write → drops `cms:v1:page:<slug>:*` for every slug the page is bound to.
192
+ - Block write → drops the block's own keys + every page-keyed entry (we don't yet track which pages embed which block; coarse drop is acceptable at platform scale).
193
+ - Template write → drops every CMS-namespace key.
194
+ - Hook / attachment write → drops the hook's keys.
195
+
196
+ Performance target: page resolution with 5 embedded Blocks + 3 embedded
197
+ Templates returns in &lt; 200 ms p95 cold; warm path returns in &lt; 5 ms.
198
+
199
+ ## Migration from legacy `cms_pages`
200
+
201
+ Previously, `cms_pages` carried `path` + `body` (per-language HTML) and a
202
+ `status` enum. Migration `035_cms_init.ts` adds the new column set
203
+ (`slug`, `name`, `active`, `content`, `languages`, `version`, `meta_*`)
204
+ and backfills every row idempotently:
205
+
206
+ - `slug = path`
207
+ - `name = title['en-US']` (best effort)
208
+ - `active = (status = 'published')`
209
+ - `content` envelope built from `body` with each language's HTML wrapped in a single `Text` node carrying `tiptapHtml`
210
+ - `languages` array = non-empty body keys
211
+ - bound to the platform's default sales channel via `cms_page_sales_channels`
212
+
213
+ Re-running the backfill against a partially-migrated state is a no-op.
214
+ The legacy `path`, `title`, and `body` columns survive for one release as
215
+ mirrors; the asset-ref scan covers `body` for backward compatibility.
216
+
217
+ ## Extending the Page Builder
218
+
219
+ Other backend modules contribute components via the SPI in
220
+ `packages/modules/cms/src/backend/services/page-builder-registry.ts`. See the
221
+ [Extending the Page Builder](./extending-page-builder) guide for the
222
+ end-to-end workflow: descriptor declaration, renderer shipping, and
223
+ composition wiring.
224
+
225
+ ## Error codes
226
+
227
+ `CMS_PAGE_NOT_FOUND`, `CMS_BLOCK_NOT_FOUND`, `CMS_TEMPLATE_NOT_FOUND`,
228
+ `CMS_HOOK_NOT_FOUND`, `CMS_SLUG_CONFLICT`, `CMS_CODE_CONFLICT`,
229
+ `CMS_REFERENCED`, `CMS_HOOK_SYSTEM_PROTECTED`,
230
+ `CMS_LANGUAGE_NOT_IN_CHANNEL_SCOPE`, `CMS_SCHEMA_UPGRADE_FAILED`.
231
+
232
+ All envelopes follow the platform-wide error contract in
233
+ `packages/contracts/src/errors.ts`.