@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,164 @@
1
+ import { createCmsBlockRequestSchema, createCmsHookRequestSchema, createCmsPageRequestSchema, createCmsTemplateRequestSchema, cmsHookAttachmentRequestSchema, patchCmsBlockRequestSchema, patchCmsHookRequestSchema, patchCmsPageRequestSchema, patchCmsTemplateRequestSchema, putCmsColorPaletteRequestSchema, putCmsPageContentRequestSchema, } from '@endora-commerce/contracts';
2
+ export async function registerCmsAdminRoutes(app, deps) {
3
+ const requireRead = deps.requireAdmin('cms.read');
4
+ const requireWrite = deps.requireAdmin('cms.write');
5
+ app.get('/api/v1/admin/cms/pages', { preHandler: requireRead }, async (request) => {
6
+ const query = (request.query ?? {});
7
+ return deps.pageService.list({
8
+ ...(query['salesChannelId'] ? { salesChannelId: query['salesChannelId'] } : {}),
9
+ ...(query['status'] ? { status: query['status'] } : {}),
10
+ ...(query['q'] ? { q: query['q'] } : {}),
11
+ });
12
+ });
13
+ /**
14
+ * The deployment's reserved first path segments, for the page editor's inline
15
+ * warning (feature 105, FR-033; `contracts/cms-page-url.md` §5.3).
16
+ *
17
+ * **One source, two readers.** This answers from `CmsPageService`, off the
18
+ * same Setting the save-time refusal enforces, so the warning an operator
19
+ * reads while typing and the refusal they meet on Save cannot disagree.
20
+ *
21
+ * It is this module's endpoint rather than `GET /api/v1/admin/settings/:code`
22
+ * for a reason that is about the operator and not about layering: that route
23
+ * is gated `settings:read`, and a content editor holding `cms.write` without
24
+ * it would get a warning that silently never fires — the exact silence this
25
+ * phase exists to remove, reintroduced one permission over.
26
+ *
27
+ * `/pages/:id` below is no hazard: Fastify's radix router prefers a static
28
+ * segment over a parametric one whatever the registration order, so
29
+ * `reserved-segments` is never read as a page id.
30
+ */
31
+ app.get('/api/v1/admin/cms/pages/reserved-segments', { preHandler: requireRead }, async () => ({ data: { segments: await deps.pageService.reservedSegments() } }));
32
+ app.get('/api/v1/admin/cms/page-builder/config', { preHandler: requireRead }, async () => ({
33
+ data: await deps.pageBuilderRegistry.describe(),
34
+ }));
35
+ app.put('/api/v1/admin/cms/page-builder/color-palette', { preHandler: requireWrite }, async (request) => {
36
+ const writer = deps.getColorPaletteWriter?.();
37
+ if (!writer) {
38
+ throw new Error('Color palette writer is not configured.');
39
+ }
40
+ const body = putCmsColorPaletteRequestSchema.parse(request.body);
41
+ const rid = request.headers['x-request-id'];
42
+ const actor = deps.resolveAdminAuditContext?.(request) ?? {
43
+ actorAdminUserId: null,
44
+ requestId: typeof rid === 'string' ? rid : null,
45
+ };
46
+ const entries = await writer(body.entries, body.expectedVersion ?? null, actor);
47
+ return { data: { entries } };
48
+ });
49
+ app.post('/api/v1/admin/cms/pages', { preHandler: requireWrite }, async (request, reply) => {
50
+ const body = createCmsPageRequestSchema.parse(request.body);
51
+ const page = await deps.pageService.create(body);
52
+ return reply.code(201).send({ data: page });
53
+ });
54
+ app.get('/api/v1/admin/cms/pages/:id', { preHandler: requireRead }, async (request) => ({ data: await deps.pageService.get(request.params.id) }));
55
+ app.patch('/api/v1/admin/cms/pages/:id', { preHandler: requireWrite }, async (request) => {
56
+ const body = patchCmsPageRequestSchema.parse(request.body);
57
+ return { data: await deps.pageService.patch(request.params.id, body) };
58
+ });
59
+ app.put('/api/v1/admin/cms/pages/:id/content/:language', { preHandler: requireWrite }, async (request) => {
60
+ const body = putCmsPageContentRequestSchema.parse(request.body);
61
+ return {
62
+ data: await deps.pageService.setContent(request.params.id, request.params.language, body.data, body.version),
63
+ };
64
+ });
65
+ app.post('/api/v1/admin/cms/pages/:id/publish', { preHandler: requireWrite }, async (request) => ({ data: await deps.pageService.publish(request.params.id) }));
66
+ app.post('/api/v1/admin/cms/pages/:id/archive', { preHandler: requireWrite }, async (request) => ({ data: await deps.pageService.archive(request.params.id) }));
67
+ app.post('/api/v1/admin/cms/pages/:id/unarchive', { preHandler: requireWrite }, async (request) => ({ data: await deps.pageService.unarchive(request.params.id) }));
68
+ app.delete('/api/v1/admin/cms/pages/:id', { preHandler: requireWrite }, async (request, reply) => {
69
+ await deps.pageService.delete(request.params.id);
70
+ return reply.code(204).send();
71
+ });
72
+ app.get('/api/v1/admin/cms/blocks', { preHandler: requireRead }, async (request) => {
73
+ const query = (request.query ?? {});
74
+ return deps.blockService.list({
75
+ ...(query['salesChannelId'] ? { salesChannelId: query['salesChannelId'] } : {}),
76
+ });
77
+ });
78
+ app.post('/api/v1/admin/cms/blocks', { preHandler: requireWrite }, async (request, reply) => {
79
+ const body = createCmsBlockRequestSchema.parse(request.body);
80
+ const block = await deps.blockService.create(body);
81
+ return reply.code(201).send({ data: block });
82
+ });
83
+ app.get('/api/v1/admin/cms/blocks/:id', { preHandler: requireRead }, async (request) => ({ data: await deps.blockService.get(request.params.id) }));
84
+ app.patch('/api/v1/admin/cms/blocks/:id', { preHandler: requireWrite }, async (request) => {
85
+ const body = patchCmsBlockRequestSchema.parse(request.body);
86
+ return { data: await deps.blockService.patch(request.params.id, body) };
87
+ });
88
+ app.put('/api/v1/admin/cms/blocks/:id/content/:language', { preHandler: requireWrite }, async (request) => {
89
+ const body = putCmsPageContentRequestSchema.parse(request.body);
90
+ return {
91
+ data: await deps.blockService.setContent(request.params.id, request.params.language, body.data, body.version),
92
+ };
93
+ });
94
+ app.delete('/api/v1/admin/cms/blocks/:id', { preHandler: requireWrite }, async (request, reply) => {
95
+ await deps.blockService.delete(request.params.id);
96
+ return reply.code(204).send();
97
+ });
98
+ app.get('/api/v1/admin/cms/templates', { preHandler: requireRead }, async (request) => {
99
+ const query = (request.query ?? {});
100
+ return deps.templateService.list({
101
+ ...(query['salesChannelId'] ? { salesChannelId: query['salesChannelId'] } : {}),
102
+ });
103
+ });
104
+ app.post('/api/v1/admin/cms/templates', { preHandler: requireWrite }, async (request, reply) => {
105
+ const body = createCmsTemplateRequestSchema.parse(request.body);
106
+ const template = await deps.templateService.create(body);
107
+ return reply.code(201).send({ data: template });
108
+ });
109
+ app.get('/api/v1/admin/cms/templates/:id', { preHandler: requireRead }, async (request) => ({ data: await deps.templateService.get(request.params.id) }));
110
+ app.patch('/api/v1/admin/cms/templates/:id', { preHandler: requireWrite }, async (request) => {
111
+ const body = patchCmsTemplateRequestSchema.parse(request.body);
112
+ return { data: await deps.templateService.patch(request.params.id, body) };
113
+ });
114
+ app.put('/api/v1/admin/cms/templates/:id/content/:language', { preHandler: requireWrite }, async (request) => {
115
+ const body = putCmsPageContentRequestSchema.parse(request.body);
116
+ return {
117
+ data: await deps.templateService.setContent(request.params.id, request.params.language, body.data, body.version),
118
+ };
119
+ });
120
+ app.delete('/api/v1/admin/cms/templates/:id', { preHandler: requireWrite }, async (request, reply) => {
121
+ await deps.templateService.delete(request.params.id);
122
+ return reply.code(204).send();
123
+ });
124
+ app.get('/api/v1/admin/cms/hooks', { preHandler: requireRead }, async (request) => {
125
+ const query = (request.query ?? {});
126
+ return deps.hookService.list({
127
+ ...(query['salesChannelId'] ? { salesChannelId: query['salesChannelId'] } : {}),
128
+ });
129
+ });
130
+ app.post('/api/v1/admin/cms/hooks', { preHandler: requireWrite }, async (request, reply) => {
131
+ const body = createCmsHookRequestSchema.parse(request.body);
132
+ const hook = await deps.hookService.create(body);
133
+ return reply.code(201).send({ data: hook });
134
+ });
135
+ app.get('/api/v1/admin/cms/hooks/:id', { preHandler: requireRead }, async (request) => ({ data: await deps.hookService.get(request.params.id) }));
136
+ app.patch('/api/v1/admin/cms/hooks/:id', { preHandler: requireWrite }, async (request) => {
137
+ const body = patchCmsHookRequestSchema.parse(request.body);
138
+ return { data: await deps.hookService.patch(request.params.id, body) };
139
+ });
140
+ app.delete('/api/v1/admin/cms/hooks/:id', { preHandler: requireWrite }, async (request, reply) => {
141
+ await deps.hookService.delete(request.params.id);
142
+ return reply.code(204).send();
143
+ });
144
+ app.get('/api/v1/admin/cms/hooks/:id/attachments', { preHandler: requireRead }, async (request) => ({ data: await deps.hookService.listAttachments(request.params.id) }));
145
+ app.post('/api/v1/admin/cms/hooks/:id/attachments', { preHandler: requireWrite }, async (request, reply) => {
146
+ const body = cmsHookAttachmentRequestSchema.parse(request.body);
147
+ const attachments = await deps.hookService.addAttachment(request.params.id, body.blockId, body.position);
148
+ return reply.code(201).send({ data: attachments });
149
+ });
150
+ app.patch('/api/v1/admin/cms/hooks/:id/attachments/:blockId', { preHandler: requireWrite }, async (request) => {
151
+ const body = cmsHookAttachmentRequestSchema.parse({
152
+ ...request.body,
153
+ blockId: request.params.blockId,
154
+ });
155
+ return {
156
+ data: await deps.hookService.reorderAttachment(request.params.id, request.params.blockId, body.position),
157
+ };
158
+ });
159
+ app.delete('/api/v1/admin/cms/hooks/:id/attachments/:blockId', { preHandler: requireWrite }, async (request, reply) => {
160
+ await deps.hookService.removeAttachment(request.params.id, request.params.blockId);
161
+ return reply.code(204).send();
162
+ });
163
+ }
164
+ //# sourceMappingURL=routes.admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { StorefrontResolver } from './services/storefront-resolver.js';
3
+ export declare function registerCmsStorefrontRoutes(app: FastifyInstance, deps: {
4
+ storefrontResolver: StorefrontResolver;
5
+ }): Promise<void>;
6
+ //# sourceMappingURL=routes.storefront.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.storefront.d.ts","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAI/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAgB5E,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE;IAAE,kBAAkB,EAAE,kBAAkB,CAAA;CAAE,GAC/C,OAAO,CAAC,IAAI,CAAC,CA8Ef"}
@@ -0,0 +1,89 @@
1
+ import { ERROR_CODES } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { getResolvedChannel } from '@endora-commerce/platform/kernel';
4
+ /**
5
+ * The request's sales channel, resolved once by the canonical resolver
6
+ * middleware (feature 053 / FR-002). Storefront modules read this instead of
7
+ * re-parsing the `x-sales-channel` header.
8
+ */
9
+ function resolvedChannel(request) {
10
+ const ch = getResolvedChannel(request);
11
+ return { id: ch.id, code: ch.code, defaultLanguage: ch.defaultLanguage };
12
+ }
13
+ export async function registerCmsStorefrontRoutes(app, deps) {
14
+ app.get('/api/v1/cms/pages/by-slug', async (request) => {
15
+ const query = (request.query ?? {});
16
+ const slug = query['slug'];
17
+ if (!slug) {
18
+ throw new HttpError(400, ERROR_CODES.VALIDATION_FAILED, 'Missing CMS page slug.');
19
+ }
20
+ const page = await deps.storefrontResolver.resolvePageBySlug({
21
+ slug,
22
+ resolvedChannel: resolvedChannel(request),
23
+ language: query['language'] ?? readHeader(request.headers['accept-language'])?.split(',')[0],
24
+ });
25
+ if (!page) {
26
+ throw new HttpError(404, ERROR_CODES.CMS_PAGE_NOT_FOUND, 'CMS Page not found.');
27
+ }
28
+ return { data: page };
29
+ });
30
+ /**
31
+ * The published pages of this request's sales channel, so the shop can tell a
32
+ * crawler they exist (feature 105, FR-021; `contracts/cms-page-url.md` §4.1).
33
+ *
34
+ * Until this endpoint the storefront advertised **no CMS URL at all**: the
35
+ * sitemap's `SITEMAP_DYNAMIC_ROUTES` names `/[...slug]` as a route *pattern*
36
+ * for the check's reconciliation, and nothing enumerated the rows behind it.
37
+ *
38
+ * The admin listing is not the answer to that and could not be: it is
39
+ * `cms.read`-gated, is not scoped to the request's channel, and returns
40
+ * drafts and archived pages, so a sitemap built from it would advertise
41
+ * unpublished content (`research.md` D-6).
42
+ *
43
+ * It takes no language, unlike the three lookups around it. A sitemap carries
44
+ * addresses, and a page's address is the same in every language it is served
45
+ * in; the language is resolved when the document is fetched, by `by-slug`.
46
+ */
47
+ app.get('/api/v1/cms/pages/by-channel', async (request) => {
48
+ const pages = await deps.storefrontResolver.listPublishedPages({
49
+ resolvedChannel: resolvedChannel(request),
50
+ });
51
+ return { data: { pages } };
52
+ });
53
+ app.get('/api/v1/cms/blocks/by-code', async (request) => {
54
+ const query = (request.query ?? {});
55
+ const code = query['code'];
56
+ if (!code) {
57
+ throw new HttpError(400, ERROR_CODES.VALIDATION_FAILED, 'Missing CMS block code.');
58
+ }
59
+ const block = await deps.storefrontResolver.resolveBlockByCode({
60
+ code,
61
+ resolvedChannel: resolvedChannel(request),
62
+ language: query['language'] ?? readHeader(request.headers['accept-language'])?.split(',')[0],
63
+ });
64
+ if (!block) {
65
+ throw new HttpError(404, ERROR_CODES.CMS_BLOCK_NOT_FOUND, 'CMS Block not found.');
66
+ }
67
+ return { data: block };
68
+ });
69
+ app.get('/api/v1/cms/hooks/by-code', async (request) => {
70
+ const query = (request.query ?? {});
71
+ const code = query['code'];
72
+ if (!code) {
73
+ throw new HttpError(400, ERROR_CODES.VALIDATION_FAILED, 'Missing CMS hook code.');
74
+ }
75
+ const hook = await deps.storefrontResolver.resolveHookByCode({
76
+ code,
77
+ resolvedChannel: resolvedChannel(request),
78
+ language: query['language'] ?? readHeader(request.headers['accept-language'])?.split(',')[0],
79
+ });
80
+ if (!hook) {
81
+ throw new HttpError(404, ERROR_CODES.CMS_HOOK_NOT_FOUND, 'CMS Hook not found.');
82
+ }
83
+ return { data: hook };
84
+ });
85
+ }
86
+ function readHeader(value) {
87
+ return Array.isArray(value) ? value[0] : value;
88
+ }
89
+ //# sourceMappingURL=routes.storefront.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.storefront.js","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { AssetsLibraryPort } from '@endora-commerce/contracts';
2
+ import type { CmsAssetResolver } from './storefront-resolver.js';
3
+ /**
4
+ * How an asset id a page, block or template embeds becomes the detail the
5
+ * storefront response carries (`specs/110-instance-repository/` T118c).
6
+ *
7
+ * A function of the port rather than of a `ModuleContext`, so the mapping and its
8
+ * one tolerance are testable over a stubbed `AssetsLibraryPort` with no container
9
+ * composed. `backend/index.ts` supplies the real one as
10
+ * `lazyPort<AssetsLibraryPort>(ctx, 'assetsLibraryPort')`, whose proxy resolves
11
+ * per call, so nothing here captures a registration.
12
+ *
13
+ * `getAsset` is the whole demand: the four fields a storefront embed renders plus
14
+ * the visibility a consumer branches on.
15
+ */
16
+ export declare function createAssetEmbedResolver(assets: Pick<AssetsLibraryPort, 'getAsset'>): CmsAssetResolver;
17
+ //# sourceMappingURL=asset-embed-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-embed-resolver.d.ts","sourceRoot":"","sources":["../../../src/backend/services/asset-embed-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,GAC1C,gBAAgB,CAuBlB"}
@@ -0,0 +1,40 @@
1
+ import { rethrowIfModuleDisabled } from '@endora-commerce/platform/kernel';
2
+ /**
3
+ * How an asset id a page, block or template embeds becomes the detail the
4
+ * storefront response carries (`specs/110-instance-repository/` T118c).
5
+ *
6
+ * A function of the port rather than of a `ModuleContext`, so the mapping and its
7
+ * one tolerance are testable over a stubbed `AssetsLibraryPort` with no container
8
+ * composed. `backend/index.ts` supplies the real one as
9
+ * `lazyPort<AssetsLibraryPort>(ctx, 'assetsLibraryPort')`, whose proxy resolves
10
+ * per call, so nothing here captures a registration.
11
+ *
12
+ * `getAsset` is the whole demand: the four fields a storefront embed renders plus
13
+ * the visibility a consumer branches on.
14
+ */
15
+ export function createAssetEmbedResolver(assets) {
16
+ return async (assetId) => {
17
+ try {
18
+ const detail = await assets.getAsset(assetId);
19
+ return {
20
+ url: detail.url,
21
+ mimeType: detail.mimeType,
22
+ filename: detail.filename,
23
+ label: detail.label,
24
+ visibility: detail.visibility,
25
+ };
26
+ }
27
+ catch (err) {
28
+ // The narrow tolerance the composition root's closure already carried, kept
29
+ // deliberately: `getAsset` throws 404 for a row that is gone, and a page
30
+ // embedding a deleted asset renders without it rather than failing the whole
31
+ // response. `rethrowIfModuleDisabled` is what stops that tolerance from also
32
+ // absorbing an owner's refusal (composition checklist item 7) — unreachable
33
+ // while `assets_library` declares `nonDeactivatable`, and the line that keeps
34
+ // this fail-closed on the day that changes.
35
+ rethrowIfModuleDisabled(err);
36
+ return null;
37
+ }
38
+ };
39
+ }
40
+ //# sourceMappingURL=asset-embed-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-embed-resolver.js","sourceRoot":"","sources":["../../../src/backend/services/asset-embed-resolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAG3E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAA2C;IAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC9C,OAAO;gBACL,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,4EAA4E;YAC5E,yEAAyE;YACzE,6EAA6E;YAC7E,6EAA6E;YAC7E,4EAA4E;YAC5E,8EAA8E;YAC9E,4CAA4C;YAC5C,uBAAuB,CAAC,GAAG,CAAC,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { AssetReferenceRegistryPort } from '@endora-commerce/contracts';
3
+ export declare function registerCmsAssetReferences(registry: AssetReferenceRegistryPort, emFactory: () => EntityManager): void;
4
+ //# sourceMappingURL=asset-references.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-references.d.ts","sourceRoot":"","sources":["../../../src/backend/services/asset-references.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAGV,0BAA0B,EAC3B,MAAM,4BAA4B,CAAC;AAEpC,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,0BAA0B,EACpC,SAAS,EAAE,MAAM,aAAa,GAC7B,IAAI,CAEN"}
@@ -0,0 +1,55 @@
1
+ // CMS → AssetReferenceRegistry descriptor — feature 014 / R12 / T031.
2
+ //
3
+ // Replaces the feature-013 cms_pages descriptor. Scans every CMS column
4
+ // that can carry asset embeds:
5
+ // - cms_pages.content
6
+ // - cms_blocks.content
7
+ // - cms_templates.content
8
+ //
9
+ // Match shape: any node whose `assetId` (or any `*assetId` key under
10
+ // `props`) equals the requested id. Caught by jsonb_path_exists with a
11
+ // wildcard-key match — handles components like LibraryImage,
12
+ // Button.iconAssetId, Card.mainImageAssetId, etc.
13
+ //
14
+ // Asset id is validated upstream by Zod (UUID), so inlining it in the
15
+ // jsonpath literal is safe; `?::text` parameter coercion confuses Knex
16
+ // under MikroORM in some cases (carried over from feature 013).
17
+ export function registerCmsAssetReferences(registry, emFactory) {
18
+ registry.register(cmsAssetReferenceDescriptor(emFactory));
19
+ }
20
+ function cmsAssetReferenceDescriptor(emFactory) {
21
+ return {
22
+ ownerModuleId: 'cms',
23
+ async findReferences(assetIds) {
24
+ if (assetIds.length === 0)
25
+ return [];
26
+ const em = emFactory();
27
+ const out = [];
28
+ for (const aidRaw of assetIds) {
29
+ const aid = aidRaw.replace(/[^0-9a-fA-F-]/g, '');
30
+ const path = `'$.** ? (@ == "${aid}")'::jsonpath`;
31
+ const rows = (await em.execute(`select id::text as id, name, 'cms_page' as kind
32
+ from cms_pages
33
+ where jsonb_path_exists(content, ${path})
34
+ union all
35
+ select id::text as id, name, 'cms_block' as kind
36
+ from cms_blocks
37
+ where jsonb_path_exists(content, ${path})
38
+ union all
39
+ select id::text as id, name, 'cms_template' as kind
40
+ from cms_templates
41
+ where jsonb_path_exists(content, ${path})`));
42
+ for (const r of rows) {
43
+ const label = r.kind === 'cms_page'
44
+ ? `CMS page "${r.name}"`
45
+ : r.kind === 'cms_block'
46
+ ? `CMS block "${r.name}"`
47
+ : `CMS template "${r.name}"`;
48
+ out.push({ kind: 'cms_body_embed', entityId: r.id, label });
49
+ }
50
+ }
51
+ return out;
52
+ },
53
+ };
54
+ }
55
+ //# sourceMappingURL=asset-references.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-references.js","sourceRoot":"","sources":["../../../src/backend/services/asset-references.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,wEAAwE;AACxE,+BAA+B;AAC/B,wBAAwB;AACxB,yBAAyB;AACzB,4BAA4B;AAC5B,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,6DAA6D;AAC7D,kDAAkD;AAClD,EAAE;AACF,sEAAsE;AACtE,uEAAuE;AACvE,gEAAgE;AAShE,MAAM,UAAU,0BAA0B,CACxC,QAAoC,EACpC,SAA8B;IAE9B,QAAQ,CAAC,QAAQ,CAAC,2BAA2B,CAAC,SAAS,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,2BAA2B,CAClC,SAA8B;IAE9B,OAAO;QACL,aAAa,EAAE,KAAK;QACpB,KAAK,CAAC,cAAc,CAAC,QAAkB;YACrC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YACrC,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;YACvB,MAAM,GAAG,GAAqB,EAAE,CAAC;YACjC,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;gBAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;gBACjD,MAAM,IAAI,GAAG,kBAAkB,GAAG,eAAe,CAAC;gBAElD,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAC5B;;8CAEoC,IAAI;;;;8CAIJ,IAAI;;;;8CAIJ,IAAI,GAAG,CAC5C,CAAsD,CAAC;gBAExD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;oBACrB,MAAM,KAAK,GACT,CAAC,CAAC,IAAI,KAAK,UAAU;wBACnB,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,GAAG;wBACxB,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW;4BACtB,CAAC,CAAC,cAAc,CAAC,CAAC,IAAI,GAAG;4BACzB,CAAC,CAAC,iBAAiB,CAAC,CAAC,IAAI,GAAG,CAAC;oBACnC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC9D,CAAC;YACH,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { CmsBlockReadPort, CmsBlockRecord, CmsLocalizedBlockRecord } from '@endora-commerce/contracts';
3
+ /**
4
+ * The block read model `cms` publishes (`specs/110-instance-repository/` T118c).
5
+ *
6
+ * `megamenu` is the only consumer and it asks two questions: *does this block
7
+ * exist* (the admin-side target validator, which refuses a menu item pointing at
8
+ * a block that is not there) and *what does it render as in this language* (the
9
+ * storefront resolver, which inlines the tree into the menu payload). Both were
10
+ * a `select … from cms_blocks` written twice in composition roots — once in
11
+ * `backend/src/composition.ts` and once in `backend/test/helpers/test-server.ts`
12
+ * — so this module's table and this module's envelope layout were read by a file
13
+ * this module does not own.
14
+ *
15
+ * **The envelope stops here.** `content.languages[<code>]` is a storage decision:
16
+ * which key a language hangs off, that a legacy `schema_version` may ride along,
17
+ * and that an absent key means "nothing authored" are all facts about the column,
18
+ * and the localized record is what lets a consumer have the tree without them.
19
+ * The root closure knew every one of them.
20
+ */
21
+ export declare class CmsBlockReadService implements CmsBlockReadPort {
22
+ private readonly emFactory;
23
+ constructor(emFactory: () => EntityManager);
24
+ findById(id: string): Promise<CmsBlockRecord | null>;
25
+ findLocalizedById(id: string, language: string): Promise<CmsLocalizedBlockRecord | null>;
26
+ }
27
+ //# sourceMappingURL=cms-block-read-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cms-block-read-port.d.ts","sourceRoot":"","sources":["../../../src/backend/services/cms-block-read-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,uBAAuB,EACxB,MAAM,4BAA4B,CAAC;AAGpC;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,mBAAoB,YAAW,gBAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAKpD,iBAAiB,CACrB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;CAoB3C"}
@@ -0,0 +1,50 @@
1
+ import { CmsBlock } from '../entities/cms-block.entity.js';
2
+ /**
3
+ * The block read model `cms` publishes (`specs/110-instance-repository/` T118c).
4
+ *
5
+ * `megamenu` is the only consumer and it asks two questions: *does this block
6
+ * exist* (the admin-side target validator, which refuses a menu item pointing at
7
+ * a block that is not there) and *what does it render as in this language* (the
8
+ * storefront resolver, which inlines the tree into the menu payload). Both were
9
+ * a `select … from cms_blocks` written twice in composition roots — once in
10
+ * `backend/src/composition.ts` and once in `backend/test/helpers/test-server.ts`
11
+ * — so this module's table and this module's envelope layout were read by a file
12
+ * this module does not own.
13
+ *
14
+ * **The envelope stops here.** `content.languages[<code>]` is a storage decision:
15
+ * which key a language hangs off, that a legacy `schema_version` may ride along,
16
+ * and that an absent key means "nothing authored" are all facts about the column,
17
+ * and the localized record is what lets a consumer have the tree without them.
18
+ * The root closure knew every one of them.
19
+ */
20
+ export class CmsBlockReadService {
21
+ emFactory;
22
+ constructor(emFactory) {
23
+ this.emFactory = emFactory;
24
+ }
25
+ async findById(id) {
26
+ const block = await this.emFactory().findOne(CmsBlock, { id });
27
+ return block ? { id: block.id, code: block.code, active: block.active } : null;
28
+ }
29
+ async findLocalizedById(id, language) {
30
+ /**
31
+ * `active: true` in the predicate rather than after it, which is the
32
+ * narrowing the storefront closure carried and the validator's did not: a
33
+ * deactivated block is content an operator has withdrawn, and loading its
34
+ * tree to throw it away is a read nobody asked for.
35
+ */
36
+ const block = await this.emFactory().findOne(CmsBlock, { id, active: true });
37
+ if (!block)
38
+ return null;
39
+ const languages = block.content.languages;
40
+ const data = languages?.[language];
41
+ // No fallback language, deliberately. This module's *own* hook resolution
42
+ // falls back to `{}` so a page still renders; a consumer inlining a block
43
+ // into somebody else's payload wants to know there is nothing to inline, and
44
+ // both composition roots answered `null` here.
45
+ if (data === undefined)
46
+ return null;
47
+ return { id: block.id, code: block.code, language, data };
48
+ }
49
+ }
50
+ //# sourceMappingURL=cms-block-read-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cms-block-read-port.js","sourceRoot":"","sources":["../../../src/backend/services/cms-block-read-port.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,mBAAmB;IACD;IAA7B,YAA6B,SAA8B;QAA9B,cAAS,GAAT,SAAS,CAAqB;IAAG,CAAC;IAE/D,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/D,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,iBAAiB,CACrB,EAAU,EACV,QAAgB;QAEhB;;;;;WAKG;QACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,SAAS,GAAI,KAAK,CAAC,OAAmD,CAAC,SAAS,CAAC;QACvF,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC,QAAQ,CAAC,CAAC;QACnC,0EAA0E;QAC1E,0EAA0E;QAC1E,6EAA6E;QAC7E,+CAA+C;QAC/C,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAEpC,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5D,CAAC;CACF"}
@@ -0,0 +1,31 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { CmsBlockSeedPort, CmsSeededBlock } from '@endora-commerce/contracts';
3
+ /**
4
+ * `cmsBlockSeedPort` — feature 075 / D-87.
5
+ *
6
+ * `newsletter` and `google_analytics` each ship a predefined block: the
7
+ * registration-form consent label and the cookie-banner message. Both used to
8
+ * insert it themselves, with raw SQL naming `cms_blocks` and
9
+ * `cms_block_sales_channels` — four ledgered reaches that no import specifier
10
+ * made visible, so the boundary they crossed compiled and returned rows.
11
+ *
12
+ * The statements are unchanged; only their owner is. That is deliberate: this
13
+ * cut is a boundary repair, and rewriting the SQL at the same time would put a
14
+ * behavioural change behind a move nobody would look at twice. So both halves
15
+ * stay `insert … where not exists`, which is what makes a re-run insert nothing
16
+ * and an operator's edit of the seeded text survive every restart.
17
+ *
18
+ * `em.execute`, not `em.getConnection().execute` (issue #200). A connection is
19
+ * not a transaction: these statements used to take their own pooled connection
20
+ * and commit immediately, so a caller holding a transaction open could neither
21
+ * roll them back nor have them see its own uncommitted rows — a channel created
22
+ * in that transaction was invisible here and binding it failed on
23
+ * `cms_block_sales_channels_channel_fk`. Identical outside a transaction, which
24
+ * is where the seeding caller normally runs.
25
+ */
26
+ export declare class CmsBlockSeedService implements CmsBlockSeedPort {
27
+ private readonly emFactory;
28
+ constructor(emFactory: () => EntityManager);
29
+ ensureSeededBlock(block: CmsSeededBlock): Promise<void>;
30
+ }
31
+ //# sourceMappingURL=cms-block-seed-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cms-block-seed-port.d.ts","sourceRoot":"","sources":["../../../src/backend/services/cms-block-seed-port.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAGnF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,mBAAoB,YAAW,gBAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CA4C9D"}
@@ -0,0 +1,68 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { SalesChannel } from '@endora-commerce/platform/kernel';
3
+ /**
4
+ * `cmsBlockSeedPort` — feature 075 / D-87.
5
+ *
6
+ * `newsletter` and `google_analytics` each ship a predefined block: the
7
+ * registration-form consent label and the cookie-banner message. Both used to
8
+ * insert it themselves, with raw SQL naming `cms_blocks` and
9
+ * `cms_block_sales_channels` — four ledgered reaches that no import specifier
10
+ * made visible, so the boundary they crossed compiled and returned rows.
11
+ *
12
+ * The statements are unchanged; only their owner is. That is deliberate: this
13
+ * cut is a boundary repair, and rewriting the SQL at the same time would put a
14
+ * behavioural change behind a move nobody would look at twice. So both halves
15
+ * stay `insert … where not exists`, which is what makes a re-run insert nothing
16
+ * and an operator's edit of the seeded text survive every restart.
17
+ *
18
+ * `em.execute`, not `em.getConnection().execute` (issue #200). A connection is
19
+ * not a transaction: these statements used to take their own pooled connection
20
+ * and commit immediately, so a caller holding a transaction open could neither
21
+ * roll them back nor have them see its own uncommitted rows — a channel created
22
+ * in that transaction was invisible here and binding it failed on
23
+ * `cms_block_sales_channels_channel_fk`. Identical outside a transaction, which
24
+ * is where the seeding caller normally runs.
25
+ */
26
+ export class CmsBlockSeedService {
27
+ emFactory;
28
+ constructor(emFactory) {
29
+ this.emFactory = emFactory;
30
+ }
31
+ async ensureSeededBlock(block) {
32
+ const em = this.emFactory();
33
+ await em.execute(`insert into cms_blocks (id, name, code, active, description, content, languages, version, created_at, updated_at)
34
+ select ?, ?, ?, true, null, ?::jsonb, ?::jsonb, 1, now(), now()
35
+ where not exists (select 1 from cms_blocks where code = ?)`, [
36
+ randomUUID(),
37
+ block.name,
38
+ block.code,
39
+ JSON.stringify(block.content),
40
+ JSON.stringify([...block.languages]),
41
+ block.code,
42
+ ]);
43
+ const rows = (await em.execute(`select id from cms_blocks where code = ? limit 1`, [
44
+ block.code,
45
+ ]));
46
+ const blockId = rows[0]?.id;
47
+ if (blockId === undefined)
48
+ return;
49
+ /**
50
+ * The channel ids come from the kernel's own entity, not from
51
+ * `select … from sales_channels sc` (feature 075, D-87). `sales_channels`
52
+ * is the kernel's table since feature 072 moved the resolution machinery
53
+ * there, and a module relating into the kernel by ORM is the sanctioned
54
+ * access. The binding insert stays one `not exists`-guarded statement, with
55
+ * one ordinary binding per channel id, so a re-run binds nothing new.
56
+ */
57
+ const channelIds = (await em.find(SalesChannel, {}, { fields: ['id'] })).map((c) => c.id);
58
+ if (channelIds.length === 0)
59
+ return;
60
+ const channelValues = channelIds.map(() => '(?::uuid)').join(', ');
61
+ await em.execute(`insert into cms_block_sales_channels (block_id, sales_channel_id, code)
62
+ select ?, sc.id, ? from (values ${channelValues}) as sc(id)
63
+ where not exists (
64
+ select 1 from cms_block_sales_channels c where c.sales_channel_id = sc.id and c.code = ?
65
+ )`, [blockId, block.code, ...channelIds, block.code]);
66
+ }
67
+ }
68
+ //# sourceMappingURL=cms-block-seed-port.js.map