@xiwebdesign/xicms-core 6.4.7 → 7.0.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 (373) hide show
  1. package/dist/SiteChrome.d.ts +12 -0
  2. package/dist/SiteChrome.js +24 -0
  3. package/dist/SiteChrome.js.map +1 -0
  4. package/dist/XiCMSProvider.d.ts +16 -8
  5. package/dist/XiCMSProvider.js +83 -58
  6. package/dist/XiCMSProvider.js.map +1 -1
  7. package/dist/admin/AdminApp.d.ts +1 -1
  8. package/dist/admin/AdminApp.js +79 -35
  9. package/dist/admin/AdminApp.js.map +1 -1
  10. package/dist/admin/AdminHelp.d.ts +8 -1
  11. package/dist/admin/AdminHelp.js +2 -2
  12. package/dist/admin/AdminHelp.js.map +1 -1
  13. package/dist/admin/NewsEditor.js +9 -9
  14. package/dist/admin/SuperadminPanel.js +5 -5
  15. package/dist/admin/blockEditors/GenericEditor.d.ts +2 -2
  16. package/dist/admin/blockEditors/GenericEditor.js +12 -11
  17. package/dist/admin/blockEditors/GenericEditor.js.map +1 -1
  18. package/dist/admin/blockEditors/StyleEditor.d.ts +1 -1
  19. package/dist/admin/blockEditors/StyleEditor.js +2 -2
  20. package/dist/admin/blockEditors/SymbolRefEditor.d.ts +1 -1
  21. package/dist/admin/blockEditors/SymbolRefEditor.js +5 -5
  22. package/dist/admin/blockEditors/index.d.ts +1 -1
  23. package/dist/admin/blockEditors/index.js +10 -10
  24. package/dist/admin/customBlocks/CustomBlocksPanel.js +7 -7
  25. package/dist/admin/editors/RichTextEditor.js +8 -7
  26. package/dist/admin/editors/RichTextEditor.js.map +1 -1
  27. package/dist/admin/editors/fontSizeExtension.js +1 -1
  28. package/dist/admin/editors/fontSizeExtension.js.map +1 -1
  29. package/dist/admin/forms/FormsPanel.js +9 -9
  30. package/dist/admin/index.d.ts +16 -16
  31. package/dist/admin/index.js +16 -16
  32. package/dist/admin/lib/mediaUsage.js +1 -1
  33. package/dist/admin/lib/pagesClient.d.ts +1 -1
  34. package/dist/admin/lib/pagesClient.js +11 -7
  35. package/dist/admin/lib/pagesClient.js.map +1 -1
  36. package/dist/admin/lib/sanitizeDraft.d.ts +9 -0
  37. package/dist/admin/lib/sanitizeDraft.js +28 -0
  38. package/dist/admin/lib/sanitizeDraft.js.map +1 -0
  39. package/dist/admin/lib/symbolsClient.d.ts +1 -1
  40. package/dist/admin/lib/symbolsClient.js +3 -3
  41. package/dist/admin/lib/upload.js +3 -3
  42. package/dist/admin/lib/useNestedDialog.d.ts +15 -0
  43. package/dist/admin/lib/useNestedDialog.js +23 -0
  44. package/dist/admin/lib/useNestedDialog.js.map +1 -0
  45. package/dist/admin/lib/zodFields.d.ts +4 -18
  46. package/dist/admin/lib/zodFields.js +54 -35
  47. package/dist/admin/lib/zodFields.js.map +1 -1
  48. package/dist/admin/media/MediaManager.js +7 -7
  49. package/dist/admin/media/MediaPicker.js +7 -7
  50. package/dist/admin/media/MediaPicker.js.map +1 -1
  51. package/dist/admin/pages/PageEditor.d.ts +1 -1
  52. package/dist/admin/pages/PageEditor.js +13 -13
  53. package/dist/admin/pages/PagesPanel.js +30 -30
  54. package/dist/admin/pages/PagesPanel.js.map +1 -1
  55. package/dist/admin/panelRegistry.js +7 -7
  56. package/dist/admin/patterns/PatternInserter.d.ts +1 -1
  57. package/dist/admin/patterns/PatternInserter.js +5 -5
  58. package/dist/admin/patterns/PatternInserter.js.map +1 -1
  59. package/dist/admin/settings/MailSettings.js +5 -5
  60. package/dist/admin/settings/MailTemplates.d.ts +1 -1
  61. package/dist/admin/settings/MailTemplates.js +5 -5
  62. package/dist/admin/settings/SettingsPanel.js +20 -20
  63. package/dist/admin/settings/SettingsPanel.js.map +1 -1
  64. package/dist/admin/settings/SnapshotsCard.js +5 -5
  65. package/dist/admin/settings/moduleSettingsForm.d.ts +1 -1
  66. package/dist/admin/settings/moduleSettingsForm.js +4 -4
  67. package/dist/admin/settings/moduleSettingsForm.js.map +1 -1
  68. package/dist/admin/shop/CategoriesTab.js +8 -8
  69. package/dist/admin/shop/ConnectionsTab.js +3 -3
  70. package/dist/admin/shop/DiscountsTab.js +8 -8
  71. package/dist/admin/shop/OrdersTab.js +10 -10
  72. package/dist/admin/shop/OrdersTab.js.map +1 -1
  73. package/dist/admin/shop/ProductsTab.js +10 -10
  74. package/dist/admin/shop/ShopPanel.js +9 -9
  75. package/dist/admin/shop/ShopSettingsTab.js +4 -4
  76. package/dist/admin/shop/index.d.ts +2 -2
  77. package/dist/admin/shop/index.js +2 -2
  78. package/dist/admin/shop/lib.js +2 -2
  79. package/dist/admin/stats/StatsPanel.js +2 -2
  80. package/dist/admin/ui/ArrayEditor.js +1 -1
  81. package/dist/admin/ui/PanelIcon.d.ts +3 -0
  82. package/dist/admin/ui/PanelIcon.js +34 -0
  83. package/dist/admin/ui/PanelIcon.js.map +1 -0
  84. package/dist/admin/ui/dialog.d.ts +21 -6
  85. package/dist/admin/ui/dialog.js +55 -34
  86. package/dist/admin/ui/dialog.js.map +1 -1
  87. package/dist/admin/ui/index.d.ts +7 -7
  88. package/dist/admin/ui/index.js +7 -7
  89. package/dist/admin/ui/primitives.js +1 -1
  90. package/dist/admin/ui/tabs.js +1 -1
  91. package/dist/admin/visual/TranslationPanel.d.ts +1 -1
  92. package/dist/admin/visual/TranslationPanel.js +3 -3
  93. package/dist/admin/visual/VisualEditor.js +47 -25
  94. package/dist/admin/visual/VisualEditor.js.map +1 -1
  95. package/dist/admin.css +2 -1
  96. package/dist/agentFiles/catalog.d.ts +2 -2
  97. package/dist/agentFiles/catalog.js +6 -5
  98. package/dist/agentFiles/catalog.js.map +1 -1
  99. package/dist/agentFiles/files.d.ts +5 -5
  100. package/dist/agentFiles/files.js +17 -17
  101. package/dist/agentFiles/files.js.map +1 -1
  102. package/dist/all.d.ts +4 -4
  103. package/dist/all.js +6 -6
  104. package/dist/auth/AuthProvider.d.ts +2 -2
  105. package/dist/auth/AuthProvider.js +51 -28
  106. package/dist/auth/AuthProvider.js.map +1 -1
  107. package/dist/auth/LoginForm.js +1 -1
  108. package/dist/auth/roles.js +4 -1
  109. package/dist/auth/roles.js.map +1 -1
  110. package/dist/blocks/BlockRenderer.d.ts +1 -1
  111. package/dist/blocks/BlockRenderer.js +3 -3
  112. package/dist/blocks/blockStyle.d.ts +1 -1
  113. package/dist/blocks/builtins/Accordion.d.ts +1 -1
  114. package/dist/blocks/builtins/Accordion.js +13 -6
  115. package/dist/blocks/builtins/Accordion.js.map +1 -1
  116. package/dist/blocks/builtins/Agenda.d.ts +1 -1
  117. package/dist/blocks/builtins/Agenda.js +5 -5
  118. package/dist/blocks/builtins/Banner.d.ts +1 -1
  119. package/dist/blocks/builtins/Banner.js +2 -2
  120. package/dist/blocks/builtins/BeforeAfter.d.ts +1 -1
  121. package/dist/blocks/builtins/BeforeAfter.js +4 -4
  122. package/dist/blocks/builtins/BeforeAfter.js.map +1 -1
  123. package/dist/blocks/builtins/Booking.d.ts +1 -1
  124. package/dist/blocks/builtins/Booking.js +5 -5
  125. package/dist/blocks/builtins/Breadcrumbs.d.ts +1 -1
  126. package/dist/blocks/builtins/Breadcrumbs.js +3 -3
  127. package/dist/blocks/builtins/Cards.d.ts +1 -1
  128. package/dist/blocks/builtins/Cards.js +5 -5
  129. package/dist/blocks/builtins/Cards.js.map +1 -1
  130. package/dist/blocks/builtins/Columns.d.ts +1 -1
  131. package/dist/blocks/builtins/Columns.js +4 -4
  132. package/dist/blocks/builtins/Contact.d.ts +3 -3
  133. package/dist/blocks/builtins/Contact.js +4 -4
  134. package/dist/blocks/builtins/Countdown.d.ts +1 -1
  135. package/dist/blocks/builtins/Countdown.js +4 -4
  136. package/dist/blocks/builtins/Countdown.js.map +1 -1
  137. package/dist/blocks/builtins/Cta.d.ts +1 -1
  138. package/dist/blocks/builtins/Cta.js +3 -3
  139. package/dist/blocks/builtins/Downloads.d.ts +1 -1
  140. package/dist/blocks/builtins/Downloads.js +4 -4
  141. package/dist/blocks/builtins/Elements.d.ts +3 -3
  142. package/dist/blocks/builtins/Elements.js +3 -3
  143. package/dist/blocks/builtins/Embed.d.ts +1 -1
  144. package/dist/blocks/builtins/Embed.js +3 -3
  145. package/dist/blocks/builtins/Faq.d.ts +1 -1
  146. package/dist/blocks/builtins/Faq.js +14 -6
  147. package/dist/blocks/builtins/Faq.js.map +1 -1
  148. package/dist/blocks/builtins/FormBlock.d.ts +1 -1
  149. package/dist/blocks/builtins/FormBlock.js +28 -18
  150. package/dist/blocks/builtins/FormBlock.js.map +1 -1
  151. package/dist/blocks/builtins/Gallery.d.ts +2 -2
  152. package/dist/blocks/builtins/Gallery.js +5 -5
  153. package/dist/blocks/builtins/Hero.d.ts +1 -1
  154. package/dist/blocks/builtins/Hero.js +4 -4
  155. package/dist/blocks/builtins/ImageBlock.d.ts +1 -1
  156. package/dist/blocks/builtins/ImageBlock.js +3 -3
  157. package/dist/blocks/builtins/Jobs.d.ts +2 -2
  158. package/dist/blocks/builtins/Jobs.js +5 -5
  159. package/dist/blocks/builtins/Layout.d.ts +2 -2
  160. package/dist/blocks/builtins/Layout.js +4 -4
  161. package/dist/blocks/builtins/MapBlock.d.ts +1 -1
  162. package/dist/blocks/builtins/MapBlock.js +3 -3
  163. package/dist/blocks/builtins/NewsListBlock.d.ts +1 -1
  164. package/dist/blocks/builtins/NewsListBlock.js +4 -4
  165. package/dist/blocks/builtins/Newsletter.d.ts +1 -1
  166. package/dist/blocks/builtins/Newsletter.js +7 -7
  167. package/dist/blocks/builtins/Newsletter.js.map +1 -1
  168. package/dist/blocks/builtins/OpenNowBadge.d.ts +1 -1
  169. package/dist/blocks/builtins/OpenNowBadge.js +8 -3
  170. package/dist/blocks/builtins/OpenNowBadge.js.map +1 -1
  171. package/dist/blocks/builtins/OpeningHours.d.ts +1 -1
  172. package/dist/blocks/builtins/OpeningHours.js +6 -6
  173. package/dist/blocks/builtins/PdfBlock.d.ts +1 -1
  174. package/dist/blocks/builtins/PdfBlock.js +3 -3
  175. package/dist/blocks/builtins/Portfolio.d.ts +1 -1
  176. package/dist/blocks/builtins/Portfolio.js +5 -5
  177. package/dist/blocks/builtins/Pricing.d.ts +2 -2
  178. package/dist/blocks/builtins/Pricing.js +5 -5
  179. package/dist/blocks/builtins/Pricing.js.map +1 -1
  180. package/dist/blocks/builtins/RichTextBlock.d.ts +1 -1
  181. package/dist/blocks/builtins/RichTextBlock.js +2 -2
  182. package/dist/blocks/builtins/RichTextBlock.js.map +1 -1
  183. package/dist/blocks/builtins/Search.d.ts +1 -1
  184. package/dist/blocks/builtins/Search.js +9 -7
  185. package/dist/blocks/builtins/Search.js.map +1 -1
  186. package/dist/blocks/builtins/Sections.d.ts +4 -4
  187. package/dist/blocks/builtins/Sections.js +4 -4
  188. package/dist/blocks/builtins/Slider.d.ts +1 -1
  189. package/dist/blocks/builtins/Slider.js +3 -3
  190. package/dist/blocks/builtins/Spacer.d.ts +1 -1
  191. package/dist/blocks/builtins/Spacer.js +2 -2
  192. package/dist/blocks/builtins/SymbolRef.d.ts +1 -1
  193. package/dist/blocks/builtins/SymbolRef.js +31 -17
  194. package/dist/blocks/builtins/SymbolRef.js.map +1 -1
  195. package/dist/blocks/builtins/Team.d.ts +1 -1
  196. package/dist/blocks/builtins/Team.js +4 -4
  197. package/dist/blocks/builtins/Testimonials.d.ts +2 -2
  198. package/dist/blocks/builtins/Testimonials.js +4 -4
  199. package/dist/blocks/builtins/Video.d.ts +2 -2
  200. package/dist/blocks/builtins/Video.js +4 -4
  201. package/dist/blocks/builtins/Video.js.map +1 -1
  202. package/dist/blocks/builtins/WhatsappChat.d.ts +1 -1
  203. package/dist/blocks/builtins/WhatsappChat.js +2 -2
  204. package/dist/blocks/builtins/Widgets.d.ts +12 -12
  205. package/dist/blocks/builtins/Widgets.js +19 -19
  206. package/dist/blocks/builtins/Widgets.js.map +1 -1
  207. package/dist/blocks/builtins/consentEmbed.js +2 -2
  208. package/dist/blocks/builtins/index.d.ts +40 -40
  209. package/dist/blocks/builtins/index.js +40 -40
  210. package/dist/blocks/builtins/lib/submitForm.js +5 -3
  211. package/dist/blocks/builtins/lib/submitForm.js.map +1 -1
  212. package/dist/blocks/context.d.ts +1 -1
  213. package/dist/blocks/customTemplate/definition.d.ts +3 -3
  214. package/dist/blocks/customTemplate/definition.js +6 -6
  215. package/dist/blocks/customTemplate/definition.js.map +1 -1
  216. package/dist/blocks/customTemplate/interpret.js +1 -1
  217. package/dist/blocks/index.d.ts +5 -5
  218. package/dist/blocks/index.js +5 -5
  219. package/dist/blocks/registry.d.ts +17 -7
  220. package/dist/blocks/registry.js +2 -1
  221. package/dist/blocks/registry.js.map +1 -1
  222. package/dist/components/EditBar.js +3 -3
  223. package/dist/components/FeedbackWidget.js +4 -4
  224. package/dist/components/News.d.ts +1 -1
  225. package/dist/components/News.js +50 -37
  226. package/dist/components/News.js.map +1 -1
  227. package/dist/components/PageView.js +11 -8
  228. package/dist/components/PageView.js.map +1 -1
  229. package/dist/components/ResponsiveImage.d.ts +1 -1
  230. package/dist/components/site/SiteFooter.d.ts +1 -1
  231. package/dist/components/site/SiteFooter.js +2 -2
  232. package/dist/config.d.ts +1 -1
  233. package/dist/demo/DemoStudio.js +3 -3
  234. package/dist/demo/demoStore.d.ts +3 -3
  235. package/dist/demo/index.d.ts +2 -2
  236. package/dist/demo/index.js +2 -2
  237. package/dist/editor/EditorProvider.js +1 -1
  238. package/dist/firebase/admin.d.ts +0 -2
  239. package/dist/firebase/admin.js +0 -6
  240. package/dist/firebase/admin.js.map +1 -1
  241. package/dist/firebase/appcheck.d.ts +1 -1
  242. package/dist/firebase/appcheck.js +5 -3
  243. package/dist/firebase/appcheck.js.map +1 -1
  244. package/dist/firebase/client.d.ts +2 -1
  245. package/dist/firebase/client.js +2 -14
  246. package/dist/firebase/client.js.map +1 -1
  247. package/dist/firebase/config.d.ts +31 -0
  248. package/dist/firebase/config.js +39 -0
  249. package/dist/firebase/config.js.map +1 -0
  250. package/dist/firebase/feedback.js +5 -3
  251. package/dist/firebase/feedback.js.map +1 -1
  252. package/dist/i18n/overlay.d.ts +2 -2
  253. package/dist/i18n/overlay.js +6 -4
  254. package/dist/i18n/overlay.js.map +1 -1
  255. package/dist/index.d.ts +45 -44
  256. package/dist/index.js +35 -33
  257. package/dist/index.js.map +1 -1
  258. package/dist/lock/DemoLock.js +1 -1
  259. package/dist/modules/builtins.d.ts +1 -1
  260. package/dist/modules/builtins.js +53 -55
  261. package/dist/modules/builtins.js.map +1 -1
  262. package/dist/modules/consent/ConsentBanner.js +1 -1
  263. package/dist/modules/consent/useConsent.js +1 -1
  264. package/dist/modules/context.d.ts +1 -1
  265. package/dist/modules/contract.d.ts +1 -1
  266. package/dist/modules/defineModule.d.ts +1 -1
  267. package/dist/modules/index.d.ts +9 -9
  268. package/dist/modules/index.js +8 -8
  269. package/dist/modules/overrides.d.ts +1 -1
  270. package/dist/modules/registry.d.ts +2 -7
  271. package/dist/modules/registry.js +2 -1
  272. package/dist/modules/registry.js.map +1 -1
  273. package/dist/modules/shop/blocks/Cart.d.ts +1 -1
  274. package/dist/modules/shop/blocks/Cart.js +6 -6
  275. package/dist/modules/shop/blocks/Checkout.d.ts +1 -1
  276. package/dist/modules/shop/blocks/Checkout.js +6 -6
  277. package/dist/modules/shop/blocks/Donation.d.ts +1 -1
  278. package/dist/modules/shop/blocks/Donation.js +5 -5
  279. package/dist/modules/shop/blocks/OrderStatus.d.ts +1 -1
  280. package/dist/modules/shop/blocks/OrderStatus.js +5 -5
  281. package/dist/modules/shop/blocks/ProductDetail.d.ts +1 -1
  282. package/dist/modules/shop/blocks/ProductDetail.js +8 -8
  283. package/dist/modules/shop/blocks/ProductGrid.d.ts +3 -3
  284. package/dist/modules/shop/blocks/ProductGrid.js +7 -7
  285. package/dist/modules/shop/blocks/ShopCategories.d.ts +1 -1
  286. package/dist/modules/shop/blocks/ShopCategories.js +6 -6
  287. package/dist/modules/shop/index.d.ts +5 -5
  288. package/dist/modules/shop/index.js +5 -5
  289. package/dist/modules/shop/lib.d.ts +2 -2
  290. package/dist/modules/shop/lib.js +18 -13
  291. package/dist/modules/shop/lib.js.map +1 -1
  292. package/dist/modules/shop/module.d.ts +1 -1
  293. package/dist/modules/shop/module.js +9 -9
  294. package/dist/modules/shop/settings.d.ts +18 -50
  295. package/dist/modules/shop/settings.js +16 -16
  296. package/dist/modules/shop/settings.js.map +1 -1
  297. package/dist/modules/shop/strings.js +1 -1
  298. package/dist/modules/shop/types.d.ts +1 -1
  299. package/dist/modules/types.d.ts +2 -2
  300. package/dist/patterns/builtins.d.ts +1 -1
  301. package/dist/patterns/builtins.js +1 -1
  302. package/dist/patterns/index.d.ts +2 -2
  303. package/dist/patterns/index.js +2 -2
  304. package/dist/render/PageRenderer.d.ts +2 -2
  305. package/dist/render/PageRenderer.js +1 -1
  306. package/dist/render/PublishedPage.d.ts +1 -1
  307. package/dist/render/PublishedPage.js +2 -2
  308. package/dist/schema/blocks.d.ts +6793 -15095
  309. package/dist/schema/blocks.js +335 -355
  310. package/dist/schema/blocks.js.map +1 -1
  311. package/dist/schema/customBlocks.d.ts +10 -29
  312. package/dist/schema/customBlocks.js +14 -13
  313. package/dist/schema/customBlocks.js.map +1 -1
  314. package/dist/schema/fields.d.ts +10 -16
  315. package/dist/schema/fields.js +37 -37
  316. package/dist/schema/fields.js.map +1 -1
  317. package/dist/schema/forms.d.ts +62 -111
  318. package/dist/schema/forms.js +17 -17
  319. package/dist/schema/forms.js.map +1 -1
  320. package/dist/schema/index.d.ts +7 -7
  321. package/dist/schema/index.js +7 -7
  322. package/dist/schema/mail.d.ts +39 -87
  323. package/dist/schema/mail.js +4 -4
  324. package/dist/schema/mail.js.map +1 -1
  325. package/dist/schema/pages.d.ts +152 -674
  326. package/dist/schema/pages.js +26 -15
  327. package/dist/schema/pages.js.map +1 -1
  328. package/dist/schema/seo.d.ts +9 -27
  329. package/dist/schema/seo.js +7 -7
  330. package/dist/schema/seo.js.map +1 -1
  331. package/dist/schema/settings.d.ts +218 -1074
  332. package/dist/schema/settings.js +55 -55
  333. package/dist/schema/settings.js.map +1 -1
  334. package/dist/schema/shop.d.ts +470 -1214
  335. package/dist/schema/shop.js +124 -124
  336. package/dist/schema/shop.js.map +1 -1
  337. package/dist/schema/siteConfig.d.ts +73 -597
  338. package/dist/schema/siteConfig.js +17 -17
  339. package/dist/schema/siteConfig.js.map +1 -1
  340. package/dist/schema/symbols.d.ts +34 -191
  341. package/dist/schema/symbols.js +8 -8
  342. package/dist/schema/symbols.js.map +1 -1
  343. package/dist/server/auth.d.ts +17 -0
  344. package/dist/server/auth.js +11 -0
  345. package/dist/server/auth.js.map +1 -0
  346. package/dist/server/bundle.d.ts +15 -6
  347. package/dist/server/bundle.js +5 -1
  348. package/dist/server/bundle.js.map +1 -1
  349. package/dist/server/draft.d.ts +16 -0
  350. package/dist/server/draft.js +12 -0
  351. package/dist/server/draft.js.map +1 -0
  352. package/dist/server/env.js +8 -8
  353. package/dist/server/env.js.map +1 -1
  354. package/dist/server/index.d.ts +10 -8
  355. package/dist/server/index.js +12 -9
  356. package/dist/server/index.js.map +1 -1
  357. package/dist/server/pages.d.ts +14 -7
  358. package/dist/server/pages.js +183 -17
  359. package/dist/server/pages.js.map +1 -1
  360. package/dist/server/revalidate.d.ts +6 -0
  361. package/dist/server/revalidate.js +40 -0
  362. package/dist/server/revalidate.js.map +1 -0
  363. package/dist/server/seo.d.ts +3 -3
  364. package/dist/server/seo.js +1 -1
  365. package/dist/server/shop.d.ts +3 -3
  366. package/dist/server/shop.js +6 -3
  367. package/dist/server/shop.js.map +1 -1
  368. package/dist/settings/useSiteSettings.d.ts +2 -2
  369. package/dist/settings/useSiteSettings.js +48 -28
  370. package/dist/settings/useSiteSettings.js.map +1 -1
  371. package/dist/styles.css +4 -1
  372. package/dist/theme/presets.d.ts +1 -1
  373. package/package.json +35 -34
@@ -1,8 +1,8 @@
1
- import { type Page } from "../schema/pages";
2
- import { type SymbolDefinition } from "../schema/symbols";
3
- import { type CustomBlock } from "../schema/customBlocks";
4
- import { type FormDefinition } from "../schema/forms";
5
- import type { NewsArticle } from "../content/types";
1
+ import { type Page } from "../schema/pages.js";
2
+ import { type SymbolDefinition } from "../schema/symbols.js";
3
+ import { type CustomBlock } from "../schema/customBlocks.js";
4
+ import { type FormDefinition } from "../schema/forms.js";
5
+ import type { NewsArticle } from "../content/types.js";
6
6
  /** Stabiele JSON-representatie (gesorteerde sleutels, zonder undefined) voor hashing. */
7
7
  export declare function stableStringify(value: unknown): string;
8
8
  /** FNV-1a 64-bit (hex) — snel, dependency-vrij en client-safe (geen node:crypto). Dit is een
@@ -1,8 +1,8 @@
1
- import { z } from "zod";
2
- import { PageSchema } from "../schema/pages";
3
- import { SymbolSchema } from "../schema/symbols";
4
- import { CustomBlockSchema } from "../schema/customBlocks";
5
- import { FormSchema } from "../schema/forms";
1
+ import * as z from "zod/mini";
2
+ import { PageSchema } from "../schema/pages.js";
3
+ import { SymbolSchema } from "../schema/symbols.js";
4
+ import { CustomBlockSchema } from "../schema/customBlocks.js";
5
+ import { FormSchema } from "../schema/forms.js";
6
6
  function stableStringify(value) {
7
7
  if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
8
8
  if (value && typeof value === "object") {
@@ -52,7 +52,7 @@ function serializePage(page) {
52
52
  ...page.translations && Object.keys(page.translations).length > 0 ? { translations: page.translations } : {}
53
53
  };
54
54
  }
55
- const PageFileSchema = PageSchema.extend({ id: z.string().min(1).max(64) });
55
+ const PageFileSchema = z.extend(PageSchema, { id: z.string().check(z.minLength(1)).check(z.maxLength(64)) });
56
56
  function parsePageFile(obj) {
57
57
  const res = PageFileSchema.safeParse(obj);
58
58
  if (!res.success) {
@@ -69,7 +69,7 @@ function serializeSymbol(symbol) {
69
69
  ...symbol.translations && Object.keys(symbol.translations).length > 0 ? { translations: symbol.translations } : {}
70
70
  };
71
71
  }
72
- const SymbolFileSchema = SymbolSchema.extend({ id: z.string().min(1).max(120) });
72
+ const SymbolFileSchema = z.extend(SymbolSchema, { id: z.string().check(z.minLength(1)).check(z.maxLength(120)) });
73
73
  function parseSymbolFile(obj) {
74
74
  const res = SymbolFileSchema.safeParse(obj);
75
75
  if (!res.success) {
@@ -94,15 +94,15 @@ function parseCustomBlockFiles(meta, template, css) {
94
94
  return res.data;
95
95
  }
96
96
  const NewsFileSchema = z.object({
97
- id: z.string().min(1).max(120),
98
- title: z.string().min(1).max(200),
99
- slug: z.string().min(1).max(200),
100
- excerpt: z.string().max(1e3).optional(),
101
- body: z.string().max(5e4).default(""),
102
- bodyHtml: z.string().max(2e5).optional(),
103
- coverUrl: z.string().max(2e3).optional(),
104
- date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "datum als yyyy-mm-dd"),
105
- published: z.boolean().default(false)
97
+ id: z.string().check(z.minLength(1)).check(z.maxLength(120)),
98
+ title: z.string().check(z.minLength(1)).check(z.maxLength(200)),
99
+ slug: z.string().check(z.minLength(1)).check(z.maxLength(200)),
100
+ excerpt: z.optional(z.string().check(z.maxLength(1e3))),
101
+ body: z._default(z.string().check(z.maxLength(5e4)), ""),
102
+ bodyHtml: z.optional(z.string().check(z.maxLength(2e5))),
103
+ coverUrl: z.optional(z.string().check(z.maxLength(2e3))),
104
+ date: z.string().check(z.regex(/^\d{4}-\d{2}-\d{2}$/, "datum als yyyy-mm-dd")),
105
+ published: z._default(z.boolean(), false)
106
106
  });
107
107
  function serializeNews(a) {
108
108
  return {
@@ -136,7 +136,7 @@ function serializeForm(form) {
136
136
  fields: form.fields
137
137
  };
138
138
  }
139
- const FormFileSchema = FormSchema.extend({ id: z.string().min(1).max(120) });
139
+ const FormFileSchema = z.extend(FormSchema, { id: z.string().check(z.minLength(1)).check(z.maxLength(120)) });
140
140
  function parseFormFile(obj) {
141
141
  const res = FormFileSchema.safeParse(obj);
142
142
  if (!res.success) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/agentFiles/files.ts"],"sourcesContent":["/**\n * Agent-bestandslaag (5.0, primo-concept \"DB ⇄ files\"): pure serialisatie-, hash- en\n * sync-logica voor de `pull-site`/`push-site`-scripts. Een site wordt een map met YAML/HTML-\n * bestanden die een coding-agent direct kan bewerken; `.xicms/manifest.json` onthoudt per\n * entiteit een inhouds-hash van het pull-moment zodat push conflicten kan detecteren\n * (drie-wegen: basis/lokaal/remote) in plaats van stil werk te overschrijven.\n *\n * Alleen pure functies — geen I/O, geen Firebase — zodat dit unit-testbaar is en zowel de\n * scripts als toekomstige tooling (bv. een cloud-agent) er op kunnen bouwen.\n */\nimport { z } from \"zod\";\nimport { PageSchema, type Page } from \"../schema/pages\";\nimport { SymbolSchema, type SymbolDefinition } from \"../schema/symbols\";\nimport { CustomBlockSchema, type CustomBlock } from \"../schema/customBlocks\";\nimport { FormSchema, type FormDefinition } from \"../schema/forms\";\nimport type { NewsArticle } from \"../content/types\";\n\n/* ---------------- hashing ---------------- */\n\n/** Stabiele JSON-representatie (gesorteerde sleutels, zonder undefined) voor hashing. */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : 1));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\n/** FNV-1a 64-bit (hex) — snel, dependency-vrij en client-safe (geen node:crypto). Dit is een\n * wijzigingsdetector, geen beveiligingshash. */\nexport function contentHash(value: unknown): string {\n const s = stableStringify(value);\n let h = 0xcbf29ce484222325n;\n const prime = 0x100000001b3n;\n for (let i = 0; i < s.length; i++) {\n h ^= BigInt(s.charCodeAt(i));\n h = (h * prime) & 0xffffffffffffffffn;\n }\n return h.toString(16).padStart(16, \"0\");\n}\n\n/* ---------------- manifest + sync-beslissing ---------------- */\n\nexport interface ManifestEntry {\n /** Relatief bestandspad binnen de site-map (bv. \"pages/diensten/web.yaml\"). */\n file: string;\n /** contentHash van de geserialiseerde entiteit op pull-moment (de \"basis\"). */\n hash: string;\n}\n\nexport interface Manifest {\n format: \"xicms-files\";\n version: 1;\n siteId: string;\n pulledAt: string;\n /** Sleutels: \"<soort>:<id>\", bv. \"page:abc123\", \"symbol:cta\", \"customBlock:custom-usps\". */\n entities: Record<string, ManifestEntry>;\n}\n\nexport function emptyManifest(siteId: string, pulledAt: string): Manifest {\n return { format: \"xicms-files\", version: 1, siteId, pulledAt, entities: {} };\n}\n\nexport type SyncAction = \"push\" | \"skip\" | \"conflict\" | \"create\" | \"remote-deleted\";\n\n/**\n * Drie-wegen-beslissing per entiteit:\n * - basis (manifest-hash van pull), lokaal (bestand nu), remote (database nu).\n * - lokaal ongewijzigd → skip (niets te pushen), ook als remote intussen veranderde.\n * - lokaal gewijzigd + remote ongewijzigd → push.\n * - beide gewijzigd → conflict (push weigert zonder --force).\n * - geen basis (nieuw bestand) → create; bestaat remote al ook → conflict.\n * - basis wel, remote weg → remote-deleted (conflict-achtig: iemand verwijderde de entiteit).\n */\nexport function decideSync(base: string | undefined, local: string, remote: string | undefined): SyncAction {\n if (base === undefined) return remote === undefined ? \"create\" : remote === local ? \"skip\" : \"conflict\";\n if (remote === undefined) return \"remote-deleted\";\n if (remote === local) return \"skip\"; // al identiek — niets te doen\n if (local === base) return \"skip\"; // lokaal niets gewijzigd — remote-wijziging laten staan\n if (remote === base) return \"push\"; // alleen lokaal gewijzigd\n return \"conflict\"; // beide kanten gewijzigd\n}\n\n/* ---------------- bestandsnamen ---------------- */\n\n/** Pagina-pad → relatief YAML-pad: \"/\" → \"pages/index.yaml\", \"/diensten/web\" → \"pages/diensten/web.yaml\". */\nexport function pagePathToFile(path: string): string {\n const clean = path.replace(/^\\/+|\\/+$/g, \"\");\n return clean === \"\" ? \"pages/index.yaml\" : `pages/${clean}.yaml`;\n}\n\n/** Veilige bestandsnaam voor een id (symbolen kunnen elk Firestore-id hebben). */\nexport function idToFilename(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\n/* ---------------- serialisatie (entiteit ⇄ plain object) ---------------- */\n\n/** Pagina-doc → bestandsobject met vaste, leesbare sleutelvolgorde. */\nexport function serializePage(page: Page & { id: string }): Record<string, unknown> {\n return {\n id: page.id,\n title: page.title,\n slug: page.slug,\n path: page.path,\n parentId: page.parentId ?? null,\n order: page.order ?? 0,\n ...(page.seo && Object.keys(page.seo).length > 0 ? { seo: page.seo } : {}),\n blocks: page.blocks ?? [],\n ...(page.translations && Object.keys(page.translations).length > 0 ? { translations: page.translations } : {}),\n };\n}\n\nconst PageFileSchema = PageSchema.extend({ id: z.string().min(1).max(64) });\n\n/** Bestandsobject → gevalideerde pagina. Gooit met een leesbare melding bij ongeldige inhoud. */\nexport function parsePageFile(obj: unknown): Page & { id: string } {\n const res = PageFileSchema.safeParse(obj);\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldige pagina: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n\nexport function serializeSymbol(symbol: SymbolDefinition & { id: string }): Record<string, unknown> {\n return {\n id: symbol.id,\n name: symbol.name,\n blocks: symbol.blocks ?? [],\n ...(symbol.translations && Object.keys(symbol.translations).length > 0 ? { translations: symbol.translations } : {}),\n };\n}\n\nconst SymbolFileSchema = SymbolSchema.extend({ id: z.string().min(1).max(120) });\n\nexport function parseSymbolFile(obj: unknown): SymbolDefinition & { id: string } {\n const res = SymbolFileSchema.safeParse(obj);\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldig symbool: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n\n/** Custom block → drie bestanden: block.yaml (meta + Field-IR), template.html, style.css. */\nexport function serializeCustomBlock(def: CustomBlock): { meta: Record<string, unknown>; template: string; css: string } {\n return {\n meta: { type: def.type, label: def.label, icon: def.icon, version: def.version, fields: def.fields },\n template: def.template,\n css: def.css,\n };\n}\n\nexport function parseCustomBlockFiles(meta: unknown, template: string, css: string): CustomBlock {\n const res = CustomBlockSchema.safeParse({ ...(meta as Record<string, unknown>), template, css });\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldige bouwsteen: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n\n/* ---------------- nieuws & formulieren (round-trip in pull/push) ---------------- */\n\n/**\n * Runtime-schema voor nieuwsartikelen in de bestandslaag. `NewsArticle` is elders alleen een\n * TS-interface (het beheer schrijft rechtstreeks naar Firestore); voor pull/push valideren we\n * wél — een agent-geschreven YAML met een kapotte datum of ontbrekende titel hoort luid te\n * falen, niet stil een kapot artikel te maken. `createdAt`/`updatedAt` (servertimestamps)\n * blijven bewust buiten de bestanden: die beheert Firestore.\n */\nconst NewsFileSchema = z.object({\n id: z.string().min(1).max(120),\n title: z.string().min(1).max(200),\n slug: z.string().min(1).max(200),\n excerpt: z.string().max(1000).optional(),\n body: z.string().max(50_000).default(\"\"),\n bodyHtml: z.string().max(200_000).optional(),\n coverUrl: z.string().max(2000).optional(),\n date: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"datum als yyyy-mm-dd\"),\n published: z.boolean().default(false),\n});\n\n/** Nieuws-doc → bestandsobject (vaste sleutelvolgorde, zonder servertimestamps/lege velden). */\nexport function serializeNews(a: NewsArticle): Record<string, unknown> {\n return {\n id: a.id,\n title: a.title,\n slug: a.slug,\n date: a.date,\n published: a.published,\n ...(a.excerpt ? { excerpt: a.excerpt } : {}),\n ...(a.coverUrl ? { coverUrl: a.coverUrl } : {}),\n body: a.body ?? \"\",\n ...(a.bodyHtml ? { bodyHtml: a.bodyHtml } : {}),\n };\n}\n\n/** Bestandsobject → gevalideerd nieuwsartikel. Gooit met een leesbare melding. */\nexport function parseNewsFile(obj: unknown): NewsArticle {\n const res = NewsFileSchema.safeParse(obj);\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldig nieuwsartikel: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n\nexport function serializeForm(form: FormDefinition & { id: string }): Record<string, unknown> {\n return {\n id: form.id,\n name: form.name,\n submitLabel: form.submitLabel,\n successMessage: form.successMessage,\n ...(form.successRedirect ? { successRedirect: form.successRedirect } : {}),\n ...(form.confirmSubmitter !== undefined ? { confirmSubmitter: form.confirmSubmitter } : {}),\n fields: form.fields,\n };\n}\n\nconst FormFileSchema = FormSchema.extend({ id: z.string().min(1).max(120) });\n\nexport function parseFormFile(obj: unknown): FormDefinition & { id: string } {\n const res = FormFileSchema.safeParse(obj);\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldig formulier: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/agentFiles/files.ts"],"sourcesContent":["/**\n * Agent-bestandslaag (5.0, primo-concept \"DB ⇄ files\"): pure serialisatie-, hash- en\n * sync-logica voor de `pull-site`/`push-site`-scripts. Een site wordt een map met YAML/HTML-\n * bestanden die een coding-agent direct kan bewerken; `.xicms/manifest.json` onthoudt per\n * entiteit een inhouds-hash van het pull-moment zodat push conflicten kan detecteren\n * (drie-wegen: basis/lokaal/remote) in plaats van stil werk te overschrijven.\n *\n * Alleen pure functies — geen I/O, geen Firebase — zodat dit unit-testbaar is en zowel de\n * scripts als toekomstige tooling (bv. een cloud-agent) er op kunnen bouwen.\n */\nimport * as z from \"zod/mini\";\nimport { PageSchema, type Page } from \"../schema/pages\";\nimport { SymbolSchema, type SymbolDefinition } from \"../schema/symbols\";\nimport { CustomBlockSchema, type CustomBlock } from \"../schema/customBlocks\";\nimport { FormSchema, type FormDefinition } from \"../schema/forms\";\nimport type { NewsArticle } from \"../content/types\";\n\n/* ---------------- hashing ---------------- */\n\n/** Stabiele JSON-representatie (gesorteerde sleutels, zonder undefined) voor hashing. */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : 1));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\n/** FNV-1a 64-bit (hex) — snel, dependency-vrij en client-safe (geen node:crypto). Dit is een\n * wijzigingsdetector, geen beveiligingshash. */\nexport function contentHash(value: unknown): string {\n const s = stableStringify(value);\n let h = 0xcbf29ce484222325n;\n const prime = 0x100000001b3n;\n for (let i = 0; i < s.length; i++) {\n h ^= BigInt(s.charCodeAt(i));\n h = (h * prime) & 0xffffffffffffffffn;\n }\n return h.toString(16).padStart(16, \"0\");\n}\n\n/* ---------------- manifest + sync-beslissing ---------------- */\n\nexport interface ManifestEntry {\n /** Relatief bestandspad binnen de site-map (bv. \"pages/diensten/web.yaml\"). */\n file: string;\n /** contentHash van de geserialiseerde entiteit op pull-moment (de \"basis\"). */\n hash: string;\n}\n\nexport interface Manifest {\n format: \"xicms-files\";\n version: 1;\n siteId: string;\n pulledAt: string;\n /** Sleutels: \"<soort>:<id>\", bv. \"page:abc123\", \"symbol:cta\", \"customBlock:custom-usps\". */\n entities: Record<string, ManifestEntry>;\n}\n\nexport function emptyManifest(siteId: string, pulledAt: string): Manifest {\n return { format: \"xicms-files\", version: 1, siteId, pulledAt, entities: {} };\n}\n\nexport type SyncAction = \"push\" | \"skip\" | \"conflict\" | \"create\" | \"remote-deleted\";\n\n/**\n * Drie-wegen-beslissing per entiteit:\n * - basis (manifest-hash van pull), lokaal (bestand nu), remote (database nu).\n * - lokaal ongewijzigd → skip (niets te pushen), ook als remote intussen veranderde.\n * - lokaal gewijzigd + remote ongewijzigd → push.\n * - beide gewijzigd → conflict (push weigert zonder --force).\n * - geen basis (nieuw bestand) → create; bestaat remote al ook → conflict.\n * - basis wel, remote weg → remote-deleted (conflict-achtig: iemand verwijderde de entiteit).\n */\nexport function decideSync(base: string | undefined, local: string, remote: string | undefined): SyncAction {\n if (base === undefined) return remote === undefined ? \"create\" : remote === local ? \"skip\" : \"conflict\";\n if (remote === undefined) return \"remote-deleted\";\n if (remote === local) return \"skip\"; // al identiek — niets te doen\n if (local === base) return \"skip\"; // lokaal niets gewijzigd — remote-wijziging laten staan\n if (remote === base) return \"push\"; // alleen lokaal gewijzigd\n return \"conflict\"; // beide kanten gewijzigd\n}\n\n/* ---------------- bestandsnamen ---------------- */\n\n/** Pagina-pad → relatief YAML-pad: \"/\" → \"pages/index.yaml\", \"/diensten/web\" → \"pages/diensten/web.yaml\". */\nexport function pagePathToFile(path: string): string {\n const clean = path.replace(/^\\/+|\\/+$/g, \"\");\n return clean === \"\" ? \"pages/index.yaml\" : `pages/${clean}.yaml`;\n}\n\n/** Veilige bestandsnaam voor een id (symbolen kunnen elk Firestore-id hebben). */\nexport function idToFilename(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\n/* ---------------- serialisatie (entiteit ⇄ plain object) ---------------- */\n\n/** Pagina-doc → bestandsobject met vaste, leesbare sleutelvolgorde. */\nexport function serializePage(page: Page & { id: string }): Record<string, unknown> {\n return {\n id: page.id,\n title: page.title,\n slug: page.slug,\n path: page.path,\n parentId: page.parentId ?? null,\n order: page.order ?? 0,\n ...(page.seo && Object.keys(page.seo).length > 0 ? { seo: page.seo } : {}),\n blocks: page.blocks ?? [],\n ...(page.translations && Object.keys(page.translations).length > 0 ? { translations: page.translations } : {}),\n };\n}\n\nconst PageFileSchema = z.extend(PageSchema, { id: z.string().check(z.minLength(1)).check(z.maxLength(64)) });\n\n/** Bestandsobject → gevalideerde pagina. Gooit met een leesbare melding bij ongeldige inhoud. */\nexport function parsePageFile(obj: unknown): Page & { id: string } {\n const res = PageFileSchema.safeParse(obj);\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldige pagina: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n\nexport function serializeSymbol(symbol: SymbolDefinition & { id: string }): Record<string, unknown> {\n return {\n id: symbol.id,\n name: symbol.name,\n blocks: symbol.blocks ?? [],\n ...(symbol.translations && Object.keys(symbol.translations).length > 0 ? { translations: symbol.translations } : {}),\n };\n}\n\nconst SymbolFileSchema = z.extend(SymbolSchema, { id: z.string().check(z.minLength(1)).check(z.maxLength(120)) });\n\nexport function parseSymbolFile(obj: unknown): SymbolDefinition & { id: string } {\n const res = SymbolFileSchema.safeParse(obj);\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldig symbool: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n\n/** Custom block → drie bestanden: block.yaml (meta + Field-IR), template.html, style.css. */\nexport function serializeCustomBlock(def: CustomBlock): { meta: Record<string, unknown>; template: string; css: string } {\n return {\n meta: { type: def.type, label: def.label, icon: def.icon, version: def.version, fields: def.fields },\n template: def.template,\n css: def.css,\n };\n}\n\nexport function parseCustomBlockFiles(meta: unknown, template: string, css: string): CustomBlock {\n const res = CustomBlockSchema.safeParse({ ...(meta as Record<string, unknown>), template, css });\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldige bouwsteen: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n\n/* ---------------- nieuws & formulieren (round-trip in pull/push) ---------------- */\n\n/**\n * Runtime-schema voor nieuwsartikelen in de bestandslaag. `NewsArticle` is elders alleen een\n * TS-interface (het beheer schrijft rechtstreeks naar Firestore); voor pull/push valideren we\n * wél — een agent-geschreven YAML met een kapotte datum of ontbrekende titel hoort luid te\n * falen, niet stil een kapot artikel te maken. `createdAt`/`updatedAt` (servertimestamps)\n * blijven bewust buiten de bestanden: die beheert Firestore.\n */\nconst NewsFileSchema = z.object({\n id: z.string().check(z.minLength(1)).check(z.maxLength(120)),\n title: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n slug: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n excerpt: z.optional(z.string().check(z.maxLength(1000))),\n body: z._default(z.string().check(z.maxLength(50_000)), \"\"),\n bodyHtml: z.optional(z.string().check(z.maxLength(200_000))),\n coverUrl: z.optional(z.string().check(z.maxLength(2000))),\n date: z.string().check(z.regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"datum als yyyy-mm-dd\")),\n published: z._default(z.boolean(), false),\n});\n\n/** Nieuws-doc → bestandsobject (vaste sleutelvolgorde, zonder servertimestamps/lege velden). */\nexport function serializeNews(a: NewsArticle): Record<string, unknown> {\n return {\n id: a.id,\n title: a.title,\n slug: a.slug,\n date: a.date,\n published: a.published,\n ...(a.excerpt ? { excerpt: a.excerpt } : {}),\n ...(a.coverUrl ? { coverUrl: a.coverUrl } : {}),\n body: a.body ?? \"\",\n ...(a.bodyHtml ? { bodyHtml: a.bodyHtml } : {}),\n };\n}\n\n/** Bestandsobject → gevalideerd nieuwsartikel. Gooit met een leesbare melding. */\nexport function parseNewsFile(obj: unknown): NewsArticle {\n const res = NewsFileSchema.safeParse(obj);\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldig nieuwsartikel: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n\nexport function serializeForm(form: FormDefinition & { id: string }): Record<string, unknown> {\n return {\n id: form.id,\n name: form.name,\n submitLabel: form.submitLabel,\n successMessage: form.successMessage,\n ...(form.successRedirect ? { successRedirect: form.successRedirect } : {}),\n ...(form.confirmSubmitter !== undefined ? { confirmSubmitter: form.confirmSubmitter } : {}),\n fields: form.fields,\n };\n}\n\nconst FormFileSchema = z.extend(FormSchema, { id: z.string().check(z.minLength(1)).check(z.maxLength(120)) });\n\nexport function parseFormFile(obj: unknown): FormDefinition & { id: string } {\n const res = FormFileSchema.safeParse(obj);\n if (!res.success) {\n const i = res.error.issues[0];\n throw new Error(`ongeldig formulier: ${i?.path.join(\".\") || \"(root)\"}: ${i?.message}`);\n }\n return res.data;\n}\n"],"mappings":"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","names":[]}
package/dist/all.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /** Alle modules incl. shop (5.x-gedrag). */
2
- export declare const allModules: import("./modules").ModuleDefinition[];
2
+ export declare const allModules: import("./modules/index.js").ModuleDefinition[];
3
3
  /** Alle admin-panelen incl. het shop-paneel (5.x-gedrag). */
4
- export declare const allPanels: import("./admin").PanelDefinition[];
5
- export { shopModule } from "./modules/shop";
6
- export { shopPanel } from "./admin/shop";
4
+ export declare const allPanels: import("./admin/index.js").PanelDefinition[];
5
+ export { shopModule } from "./modules/shop/index.js";
6
+ export { shopPanel } from "./admin/shop/index.js";
package/dist/all.js CHANGED
@@ -1,11 +1,11 @@
1
- import { builtinModules } from "./modules/builtins";
2
- import { shopModule } from "./modules/shop";
3
- import { builtinPanels } from "./admin/panelRegistry";
4
- import { shopPanel } from "./admin/shop";
1
+ import { builtinModules } from "./modules/builtins.js";
2
+ import { shopModule } from "./modules/shop/index.js";
3
+ import { builtinPanels } from "./admin/panelRegistry.js";
4
+ import { shopPanel } from "./admin/shop/index.js";
5
5
  const allModules = [...builtinModules, shopModule];
6
6
  const allPanels = [...builtinPanels, shopPanel];
7
- import { shopModule as shopModule2 } from "./modules/shop";
8
- import { shopPanel as shopPanel2 } from "./admin/shop";
7
+ import { shopModule as shopModule2 } from "./modules/shop/index.js";
8
+ import { shopPanel as shopPanel2 } from "./admin/shop/index.js";
9
9
  export {
10
10
  allModules,
11
11
  allPanels,
@@ -1,6 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
- import { type User } from "firebase/auth";
3
- import { type Role, type XiClaims } from "./roles";
2
+ import type { User } from "firebase/auth";
3
+ import { type Role, type XiClaims } from "./roles.js";
4
4
  interface AuthState {
5
5
  /** Of Firebase überhaupt geconfigureerd is. */
6
6
  configured: boolean;
@@ -7,16 +7,9 @@ import {
7
7
  useMemo,
8
8
  useState
9
9
  } from "react";
10
- import {
11
- GoogleAuthProvider,
12
- onAuthStateChanged,
13
- signInWithEmailAndPassword,
14
- signInWithPopup,
15
- signOut as fbSignOut
16
- } from "firebase/auth";
17
- import { getFirebaseAuth, isFirebaseConfigured } from "../firebase/client";
18
- import { roleForSite } from "./roles";
19
- import { DEMO_EVENT, isDemoLoggedIn, setDemoLoggedIn } from "../demo/demoStore";
10
+ import { hasAuthHint, isFirebaseConfigured, setAuthHint } from "../firebase/config.js";
11
+ import { roleForSite } from "./roles.js";
12
+ import { DEMO_EVENT, isDemoLoggedIn, setDemoLoggedIn } from "../demo/demoStore.js";
20
13
  const AuthContext = createContext(null);
21
14
  function AuthProvider({
22
15
  siteId,
@@ -35,6 +28,12 @@ function AuthProvider({
35
28
  window.addEventListener(DEMO_EVENT, sync);
36
29
  return () => window.removeEventListener(DEMO_EVENT, sync);
37
30
  }, [demo]);
31
+ async function authSdk() {
32
+ const [client, sdk] = await Promise.all([import("../firebase/client.js"), import("firebase/auth")]);
33
+ const auth = client.getFirebaseAuth();
34
+ if (!auth) throw new Error("Firebase is niet geconfigureerd.");
35
+ return { auth, sdk };
36
+ }
38
37
  async function loadClaims(u, forceRefresh = false) {
39
38
  if (!u) {
40
39
  setClaims(null);
@@ -47,18 +46,33 @@ function AuthProvider({
47
46
  });
48
47
  }
49
48
  useEffect(() => {
50
- const auth = getFirebaseAuth();
51
- if (!auth) {
49
+ if (!configured || !hasAuthHint()) {
52
50
  setLoading(false);
53
51
  return;
54
52
  }
55
- const unsub = onAuthStateChanged(auth, async (u) => {
56
- setUser(u);
57
- await loadClaims(u);
58
- setLoading(false);
59
- });
60
- return () => unsub();
61
- }, []);
53
+ let unsub;
54
+ let afgebroken = false;
55
+ void (async () => {
56
+ const { auth, sdk } = await authSdk().catch(() => ({ auth: null, sdk: null }));
57
+ if (!auth || afgebroken) {
58
+ setLoading(false);
59
+ return;
60
+ }
61
+ void sdk.getRedirectResult(auth).catch((e) => {
62
+ console.error("[xicms] inloggen via redirect mislukt:", e);
63
+ });
64
+ unsub = sdk.onAuthStateChanged(auth, async (u) => {
65
+ setUser(u);
66
+ setAuthHint(Boolean(u));
67
+ await loadClaims(u);
68
+ setLoading(false);
69
+ });
70
+ })();
71
+ return () => {
72
+ afgebroken = true;
73
+ unsub?.();
74
+ };
75
+ }, [configured]);
62
76
  const value = useMemo(() => {
63
77
  const demoActiveLogin = demo && demoLoggedIn;
64
78
  const effectiveUser = demoActiveLogin ? { uid: "demo", email: "demo@voorbeeld.nl", displayName: "Demo-beheerder" } : user;
@@ -70,25 +84,34 @@ function AuthProvider({
70
84
  claims: effectiveClaims,
71
85
  role: roleForSite(effectiveClaims, siteId),
72
86
  async signIn(email, password) {
73
- const auth = getFirebaseAuth();
74
- if (!auth) throw new Error("Firebase is niet geconfigureerd.");
75
- await signInWithEmailAndPassword(auth, email, password);
87
+ const { auth, sdk } = await authSdk();
88
+ await sdk.signInWithEmailAndPassword(auth, email, password);
89
+ setAuthHint(true);
76
90
  },
77
91
  async signInWithGoogle() {
78
- const auth = getFirebaseAuth();
79
- if (!auth) throw new Error("Firebase is niet geconfigureerd.");
80
- await signInWithPopup(auth, new GoogleAuthProvider());
92
+ const { auth, sdk } = await authSdk();
93
+ const provider = new sdk.GoogleAuthProvider();
94
+ try {
95
+ await sdk.signInWithPopup(auth, provider);
96
+ setAuthHint(true);
97
+ } catch (e) {
98
+ const code = e?.code ?? "";
99
+ if (code === "auth/cancelled-popup-request" || code === "auth/popup-closed-by-user") throw e;
100
+ await sdk.signInWithRedirect(auth, provider);
101
+ }
81
102
  },
82
103
  async signOut() {
83
104
  if (demo) {
84
105
  setDemoLoggedIn(false);
85
106
  return;
86
107
  }
87
- const auth = getFirebaseAuth();
88
- if (auth) await fbSignOut(auth);
108
+ const { auth, sdk } = await authSdk();
109
+ await sdk.signOut(auth);
110
+ setAuthHint(false);
89
111
  },
90
112
  async refresh() {
91
- await loadClaims(getFirebaseAuth()?.currentUser ?? user, true);
113
+ const { auth } = await authSdk();
114
+ await loadClaims(auth.currentUser ?? user, true);
92
115
  },
93
116
  demo,
94
117
  demoSignIn() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/AuthProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n GoogleAuthProvider,\n onAuthStateChanged,\n signInWithEmailAndPassword,\n signInWithPopup,\n signOut as fbSignOut,\n type User,\n} from \"firebase/auth\";\nimport { getFirebaseAuth, isFirebaseConfigured } from \"../firebase/client\";\nimport { roleForSite, type Role, type XiClaims } from \"./roles\";\nimport { DEMO_EVENT, isDemoLoggedIn, setDemoLoggedIn } from \"../demo/demoStore\";\n\ninterface AuthState {\n /** Of Firebase überhaupt geconfigureerd is. */\n configured: boolean;\n /** Bezig met laden van de auth-status. */\n loading: boolean;\n user: User | null;\n claims: XiClaims | null;\n /** Effectieve rol op de huidige site (uit `siteId`). */\n role: Role | null;\n signIn: (email: string, password: string) => Promise<void>;\n /** Inloggen met Google (popup). */\n signInWithGoogle: () => Promise<void>;\n signOut: () => Promise<void>;\n /** Forceer het opnieuw inlezen van claims (na een rolwijziging). */\n refresh: () => Promise<void>;\n /** Demo-bewerkmodus actief (config.demoEditing) — pseudo-login, niets wordt opgeslagen. */\n demo: boolean;\n /** Pseudo-login voor de demo: geeft lokaal een siteadmin-rol zonder Firebase. */\n demoSignIn: () => void;\n}\n\nconst AuthContext = createContext<AuthState | null>(null);\n\nexport function AuthProvider({\n siteId,\n demo = false,\n children,\n}: {\n siteId: string;\n demo?: boolean;\n children: ReactNode;\n}) {\n const configured = isFirebaseConfigured();\n const [loading, setLoading] = useState(configured);\n const [user, setUser] = useState<User | null>(null);\n const [claims, setClaims] = useState<XiClaims | null>(null);\n const [demoLoggedIn, setDemoLoggedInState] = useState(false);\n\n // Demo-login uit de (sessie)opslag inlezen + meeluisteren met wijzigingen (login/logout/reset).\n useEffect(() => {\n if (!demo) return;\n const sync = () => setDemoLoggedInState(isDemoLoggedIn());\n sync();\n window.addEventListener(DEMO_EVENT, sync);\n return () => window.removeEventListener(DEMO_EVENT, sync);\n }, [demo]);\n\n async function loadClaims(u: User | null, forceRefresh = false) {\n if (!u) {\n setClaims(null);\n return;\n }\n // Standaard GEEN geforceerde token-refresh: dat is een netwerk-roundtrip op élke\n // auth-state-change (incl. eerste load van elke ingelogde editor). Alleen refresh() —\n // na een rolwijziging — forceert, want dan is het gecachte token per definitie verouderd.\n const token = await u.getIdTokenResult(forceRefresh);\n setClaims({\n superadmin: token.claims.superadmin === true,\n roles: (token.claims.roles as XiClaims[\"roles\"]) ?? {},\n });\n }\n\n useEffect(() => {\n const auth = getFirebaseAuth();\n if (!auth) {\n setLoading(false);\n return;\n }\n const unsub = onAuthStateChanged(auth, async (u) => {\n setUser(u);\n await loadClaims(u);\n setLoading(false);\n });\n return () => unsub();\n }, []);\n\n const value = useMemo<AuthState>(() => {\n // In de demo met actieve pseudo-login: doe alsof een siteadmin is ingelogd (lokaal, geen Firebase).\n const demoActiveLogin = demo && demoLoggedIn;\n const effectiveUser = demoActiveLogin\n ? ({ uid: \"demo\", email: \"demo@voorbeeld.nl\", displayName: \"Demo-beheerder\" } as unknown as User)\n : user;\n const effectiveClaims: XiClaims | null = demoActiveLogin ? { roles: { [siteId]: \"siteadmin\" } } : claims;\n\n return {\n configured,\n loading,\n user: effectiveUser,\n claims: effectiveClaims,\n role: roleForSite(effectiveClaims, siteId),\n async signIn(email, password) {\n const auth = getFirebaseAuth();\n if (!auth) throw new Error(\"Firebase is niet geconfigureerd.\");\n await signInWithEmailAndPassword(auth, email, password);\n },\n async signInWithGoogle() {\n const auth = getFirebaseAuth();\n if (!auth) throw new Error(\"Firebase is niet geconfigureerd.\");\n await signInWithPopup(auth, new GoogleAuthProvider());\n },\n async signOut() {\n if (demo) {\n setDemoLoggedIn(false);\n return;\n }\n const auth = getFirebaseAuth();\n if (auth) await fbSignOut(auth);\n },\n async refresh() {\n // Altijd de actuele Firebase-user (niet de gememoïseerde) — na een snelle\n // uitlog/inlog-wissel zou de closure anders claims van de vorige user verversen.\n // Force-refresh: na een rolwijziging is het gecachte token verouderd.\n await loadClaims(getFirebaseAuth()?.currentUser ?? user, true);\n },\n demo,\n demoSignIn() {\n setDemoLoggedIn(true);\n },\n };\n }, [configured, loading, user, claims, siteId, demo, demoLoggedIn]);\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\nexport function useAuth(): AuthState {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error(\"useAuth moet binnen <XiCMSProvider> worden gebruikt.\");\n return ctx;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/auth/AuthProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\nimport type { User } from \"firebase/auth\";\nimport { hasAuthHint, isFirebaseConfigured, setAuthHint } from \"../firebase/config\";\nimport { roleForSite, type Role, type XiClaims } from \"./roles\";\nimport { DEMO_EVENT, isDemoLoggedIn, setDemoLoggedIn } from \"../demo/demoStore\";\n\ninterface AuthState {\n /** Of Firebase überhaupt geconfigureerd is. */\n configured: boolean;\n /** Bezig met laden van de auth-status. */\n loading: boolean;\n user: User | null;\n claims: XiClaims | null;\n /** Effectieve rol op de huidige site (uit `siteId`). */\n role: Role | null;\n signIn: (email: string, password: string) => Promise<void>;\n /** Inloggen met Google (popup). */\n signInWithGoogle: () => Promise<void>;\n signOut: () => Promise<void>;\n /** Forceer het opnieuw inlezen van claims (na een rolwijziging). */\n refresh: () => Promise<void>;\n /** Demo-bewerkmodus actief (config.demoEditing) — pseudo-login, niets wordt opgeslagen. */\n demo: boolean;\n /** Pseudo-login voor de demo: geeft lokaal een siteadmin-rol zonder Firebase. */\n demoSignIn: () => void;\n}\n\nconst AuthContext = createContext<AuthState | null>(null);\n\nexport function AuthProvider({\n siteId,\n demo = false,\n children,\n}: {\n siteId: string;\n demo?: boolean;\n children: ReactNode;\n}) {\n const configured = isFirebaseConfigured();\n const [loading, setLoading] = useState(configured);\n const [user, setUser] = useState<User | null>(null);\n const [claims, setClaims] = useState<XiClaims | null>(null);\n const [demoLoggedIn, setDemoLoggedInState] = useState(false);\n\n // Demo-login uit de (sessie)opslag inlezen + meeluisteren met wijzigingen (login/logout/reset).\n useEffect(() => {\n if (!demo) return;\n const sync = () => setDemoLoggedInState(isDemoLoggedIn());\n sync();\n window.addEventListener(DEMO_EVENT, sync);\n return () => window.removeEventListener(DEMO_EVENT, sync);\n }, [demo]);\n\n /** Laadt de auth-SDK pas wanneer er iets mee moet gebeuren (inloggen, uitloggen, verversen). */\n async function authSdk() {\n const [client, sdk] = await Promise.all([import(\"../firebase/client\"), import(\"firebase/auth\")]);\n const auth = client.getFirebaseAuth();\n if (!auth) throw new Error(\"Firebase is niet geconfigureerd.\");\n return { auth, sdk };\n }\n\n async function loadClaims(u: User | null, forceRefresh = false) {\n if (!u) {\n setClaims(null);\n return;\n }\n // Standaard GEEN geforceerde token-refresh: dat is een netwerk-roundtrip op élke\n // auth-state-change (incl. eerste load van elke ingelogde editor). Alleen refresh() —\n // na een rolwijziging — forceert, want dan is het gecachte token per definitie verouderd.\n const token = await u.getIdTokenResult(forceRefresh);\n setClaims({\n superadmin: token.claims.superadmin === true,\n roles: (token.claims.roles as XiClaims[\"roles\"]) ?? {},\n });\n }\n\n useEffect(() => {\n // Zonder sessie-hint laden we de auth-SDK NIET: een anonieme bezoeker hoeft ~1 MB niet te\n // downloaden om te ontdekken dat hij niet is ingelogd. Zodra hij inlogt of het beheer opent\n // komt de SDK alsnog, en vindt onAuthStateChanged een bestaande sessie meteen terug.\n if (!configured || !hasAuthHint()) {\n setLoading(false);\n return;\n }\n let unsub: (() => void) | undefined;\n let afgebroken = false;\n void (async () => {\n const { auth, sdk } = await authSdk().catch(() => ({ auth: null, sdk: null }) as never);\n if (!auth || afgebroken) {\n setLoading(false);\n return;\n }\n // Terugkeer van een redirect-login afronden (zie signInWithGoogle). `onAuthStateChanged`\n // pikt de sessie zelf al op; deze aanroep is er om een FOUT bij terugkomst niet stil te\n // laten verdwijnen — anders sta je weer op het inlogscherm zonder te weten waarom.\n void sdk.getRedirectResult(auth).catch((e: unknown) => {\n console.error(\"[xicms] inloggen via redirect mislukt:\", e);\n });\n\n unsub = sdk.onAuthStateChanged(auth, async (u: User | null) => {\n setUser(u);\n setAuthHint(Boolean(u));\n await loadClaims(u);\n setLoading(false);\n });\n })();\n return () => {\n afgebroken = true;\n unsub?.();\n };\n }, [configured]);\n\n const value = useMemo<AuthState>(() => {\n // In de demo met actieve pseudo-login: doe alsof een siteadmin is ingelogd (lokaal, geen Firebase).\n const demoActiveLogin = demo && demoLoggedIn;\n const effectiveUser = demoActiveLogin\n ? ({ uid: \"demo\", email: \"demo@voorbeeld.nl\", displayName: \"Demo-beheerder\" } as unknown as User)\n : user;\n const effectiveClaims: XiClaims | null = demoActiveLogin ? { roles: { [siteId]: \"siteadmin\" } } : claims;\n\n return {\n configured,\n loading,\n user: effectiveUser,\n claims: effectiveClaims,\n role: roleForSite(effectiveClaims, siteId),\n async signIn(email, password) {\n const { auth, sdk } = await authSdk();\n await sdk.signInWithEmailAndPassword(auth, email, password);\n setAuthHint(true);\n },\n async signInWithGoogle() {\n const { auth, sdk } = await authSdk();\n const provider = new sdk.GoogleAuthProvider();\n try {\n await sdk.signInWithPopup(auth, provider);\n setAuthHint(true);\n } catch (e) {\n // TERUGVAL OP REDIRECT. `signInWithPopup` faalt in de praktijk vaker dan je denkt: een\n // popup-blokkade, een browser die de popup als tab opent (dan raakt Firebase het\n // venster kwijt), of strengere cookie-instellingen. Gemeten op localhost: de popup ging\n // dicht, de gebruiker bleef uitgelogd, en er kwam GEEN foutcode — waardoor het\n // inlogscherm alleen \"mislukt of geannuleerd\" kon melden. Doodlopend voor de klant.\n //\n // De redirect-variant heeft geen venster nodig en werkt daarom overal; het resultaat\n // wordt bij terugkomst opgepikt (zie de getRedirectResult-effect hieronder). Alleen bij\n // een échte annulering door de gebruiker gooien we door — dan opnieuw beginnen is\n // precies niet wat hij wil.\n const code = (e as { code?: string })?.code ?? \"\";\n if (code === \"auth/cancelled-popup-request\" || code === \"auth/popup-closed-by-user\") throw e;\n await sdk.signInWithRedirect(auth, provider);\n }\n },\n async signOut() {\n if (demo) {\n setDemoLoggedIn(false);\n return;\n }\n const { auth, sdk } = await authSdk();\n await sdk.signOut(auth);\n setAuthHint(false);\n },\n async refresh() {\n // Altijd de actuele Firebase-user (niet de gememoïseerde) — na een snelle\n // uitlog/inlog-wissel zou de closure anders claims van de vorige user verversen.\n // Force-refresh: na een rolwijziging is het gecachte token verouderd.\n const { auth } = await authSdk();\n await loadClaims(auth.currentUser ?? user, true);\n },\n demo,\n demoSignIn() {\n setDemoLoggedIn(true);\n },\n };\n }, [configured, loading, user, claims, siteId, demo, demoLoggedIn]);\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\nexport function useAuth(): AuthState {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error(\"useAuth moet binnen <XiCMSProvider> worden gebruikt.\");\n return ctx;\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import { useAuth } from "./AuthProvider";
4
+ import { useAuth } from "./AuthProvider.js";
5
5
  function authErrorMessage(e, fallback) {
6
6
  const code = e?.code ?? "";
7
7
  switch (code) {
@@ -1,7 +1,10 @@
1
1
  function roleForSite(claims, siteId) {
2
2
  if (!claims) return null;
3
3
  if (claims.superadmin) return "superadmin";
4
- return claims.roles?.[siteId] ?? null;
4
+ const roles = claims.roles;
5
+ if (!roles || !Object.hasOwn(roles, siteId)) return null;
6
+ const role = roles[siteId];
7
+ return role === "siteadmin" || role === "editor" ? role : null;
5
8
  }
6
9
  function canEdit(role) {
7
10
  return role === "superadmin" || role === "siteadmin" || role === "editor";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/roles.ts"],"sourcesContent":["/** Rollen binnen XiCMS. */\nexport type Role = \"superadmin\" | \"siteadmin\" | \"editor\";\n\n/** Vorm van de custom claims op het Firebase ID-token. */\nexport interface XiClaims {\n superadmin?: boolean;\n roles?: Record<string, \"siteadmin\" | \"editor\">;\n}\n\n/** De effectieve rol van een gebruiker op één site. */\nexport function roleForSite(claims: XiClaims | null | undefined, siteId: string): Role | null {\n if (!claims) return null;\n if (claims.superadmin) return \"superadmin\";\n return claims.roles?.[siteId] ?? null;\n}\n\n/** Mag deze rol content bewerken? */\nexport function canEdit(role: Role | null): boolean {\n return role === \"superadmin\" || role === \"siteadmin\" || role === \"editor\";\n}\n\n/** Mag deze rol de site beheren (instellingen, accounts voor zover toegestaan)? */\nexport function canManageSite(role: Role | null): boolean {\n return role === \"superadmin\" || role === \"siteadmin\";\n}\n\n/** Alleen de superadmin mag sites en accounts aanmaken. */\nexport function isSuperadmin(claims: XiClaims | null | undefined): boolean {\n return Boolean(claims?.superadmin);\n}\n"],"mappings":"AAUO,SAAS,YAAY,QAAqC,QAA6B;AAC5F,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,WAAY,QAAO;AAC9B,SAAO,OAAO,QAAQ,MAAM,KAAK;AACnC;AAGO,SAAS,QAAQ,MAA4B;AAClD,SAAO,SAAS,gBAAgB,SAAS,eAAe,SAAS;AACnE;AAGO,SAAS,cAAc,MAA4B;AACxD,SAAO,SAAS,gBAAgB,SAAS;AAC3C;AAGO,SAAS,aAAa,QAA8C;AACzE,SAAO,QAAQ,QAAQ,UAAU;AACnC;","names":[]}
1
+ {"version":3,"sources":["../../src/auth/roles.ts"],"sourcesContent":["/** Rollen binnen XiCMS. */\nexport type Role = \"superadmin\" | \"siteadmin\" | \"editor\";\n\n/** Vorm van de custom claims op het Firebase ID-token. */\nexport interface XiClaims {\n superadmin?: boolean;\n roles?: Record<string, \"siteadmin\" | \"editor\">;\n}\n\n/** De effectieve rol van een gebruiker op één site. */\nexport function roleForSite(claims: XiClaims | null | undefined, siteId: string): Role | null {\n if (!claims) return null;\n if (claims.superadmin) return \"superadmin\";\n const roles = claims.roles;\n // `roles[siteId]` zou zonder eigen-sleutel-check doorlezen naar Object.prototype: een siteId als\n // \"constructor\" of \"toString\" leverde dan een functie op i.p.v. null — het retourtype loog. Niet\n // uitbuitbaar (canEdit/canManageSite vergelijken met ===), maar `role` is publiek geëxporteerd,\n // dus een klantrepo die `if (role)` schrijft kreeg een truthy waarde. Meteen ook de waarde\n // narrowen: claims komen van Cloud Functions, maar het type moet waar zijn zonder dat vertrouwen.\n if (!roles || !Object.hasOwn(roles, siteId)) return null;\n const role = roles[siteId];\n return role === \"siteadmin\" || role === \"editor\" ? role : null;\n}\n\n/** Mag deze rol content bewerken? */\nexport function canEdit(role: Role | null): boolean {\n return role === \"superadmin\" || role === \"siteadmin\" || role === \"editor\";\n}\n\n/** Mag deze rol de site beheren (instellingen, accounts voor zover toegestaan)? */\nexport function canManageSite(role: Role | null): boolean {\n return role === \"superadmin\" || role === \"siteadmin\";\n}\n\n/** Alleen de superadmin mag sites en accounts aanmaken. */\nexport function isSuperadmin(claims: XiClaims | null | undefined): boolean {\n return Boolean(claims?.superadmin);\n}\n"],"mappings":"AAUO,SAAS,YAAY,QAAqC,QAA6B;AAC5F,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,WAAY,QAAO;AAC9B,QAAM,QAAQ,OAAO;AAMrB,MAAI,CAAC,SAAS,CAAC,OAAO,OAAO,OAAO,MAAM,EAAG,QAAO;AACpD,QAAM,OAAO,MAAM,MAAM;AACzB,SAAO,SAAS,eAAe,SAAS,WAAW,OAAO;AAC5D;AAGO,SAAS,QAAQ,MAA4B;AAClD,SAAO,SAAS,gBAAgB,SAAS,eAAe,SAAS;AACnE;AAGO,SAAS,cAAc,MAA4B;AACxD,SAAO,SAAS,gBAAgB,SAAS;AAC3C;AAGO,SAAS,aAAa,QAA8C;AACzE,SAAO,QAAQ,QAAQ,UAAU;AACnC;","names":[]}
@@ -1,4 +1,4 @@
1
- import { type BlockRegistry } from "./registry";
1
+ import { type BlockRegistry } from "./registry.js";
2
2
  /**
3
3
  * Rendert een blocks-array via de registry. Server-safe (geen "use client"): bruikbaar in
4
4
  * React Server Components én client. Geef de registry expliciet mee (zo werkt server-rendering);
@@ -1,7 +1,7 @@
1
1
  import { Fragment, jsx } from "react/jsx-runtime";
2
- import { validateBlocks } from "./registry";
3
- import { BlockErrorBoundary } from "./BlockErrorBoundary";
4
- import { blockStyleAttrs } from "./blockStyle";
2
+ import { validateBlocks } from "./registry.js";
3
+ import { BlockErrorBoundary } from "./BlockErrorBoundary.js";
4
+ import { blockStyleAttrs } from "./blockStyle.js";
5
5
  function BlockRenderer({
6
6
  blocks,
7
7
  registry,
@@ -1,5 +1,5 @@
1
1
  import type { CSSProperties } from "react";
2
- import type { BlockStyle } from "../schema/blocks";
2
+ import type { BlockStyle } from "../schema/blocks.js";
3
3
  export interface BlockStyleAttrs {
4
4
  id?: string;
5
5
  className: string;
@@ -1,2 +1,2 @@
1
1
  import type { z } from "zod";
2
- export declare const accordionBlock: import("..").BlockDefinition<z.ZodTypeAny>;
2
+ export declare const accordionBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import { AccordionBlockSchema } from "../../schema/blocks";
5
- import { defineBlock } from "../registry";
4
+ import { AccordionBlockSchema } from "../../schema/blocks.js";
5
+ import { defineBlock } from "../registry.js";
6
6
  import { Collapsible } from "@astryxdesign/core/Collapsible";
7
- import { VStack, Heading, Text } from "../ui";
7
+ import { VStack, Heading, Text } from "../ui/index.js";
8
8
  function AccordionRender({ block }) {
9
9
  const items = (block.items ?? []).filter((i) => i.title);
10
10
  const [openIdx, setOpenIdx] = useState(null);
@@ -15,9 +15,16 @@ function AccordionRender({ block }) {
15
15
  const body = it.bodyHtml ? (
16
16
  // Server-side gesanitized bij publiceren (*Html-velden) — zie functions/lib/sanitize.
17
17
  // eslint-disable-next-line react/no-danger
18
- /* @__PURE__ */ jsx("div", { className: "xicms-richtext", dangerouslySetInnerHTML: { __html: it.bodyHtml } })
19
- ) : /* @__PURE__ */ jsx(Text, { color: "secondary", children: it.body });
20
- const trigger = /* @__PURE__ */ jsx(Text, { weight: "bold", children: it.title });
18
+ /* @__PURE__ */ jsx(
19
+ "div",
20
+ {
21
+ className: "xicms-richtext",
22
+ "data-xicms-edit": `items.${i}.bodyHtml`,
23
+ dangerouslySetInnerHTML: { __html: it.bodyHtml }
24
+ }
25
+ )
26
+ ) : /* @__PURE__ */ jsx(Text, { color: "secondary", "data-xicms-edit": `items.${i}.body`, children: it.body });
27
+ const trigger = /* @__PURE__ */ jsx(Text, { weight: "bold", "data-xicms-edit": `items.${i}.title`, children: it.title });
21
28
  return block.multiple ? /* @__PURE__ */ jsx(Collapsible, { trigger, defaultIsOpen: false, children: body }, i) : /* @__PURE__ */ jsx(
22
29
  Collapsible,
23
30
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/builtins/Accordion.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { z } from \"zod\";\nimport { AccordionBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { Collapsible } from \"@astryxdesign/core/Collapsible\";\nimport { VStack, Heading, Text } from \"../ui\";\n\ntype Accordion = z.infer<typeof AccordionBlockSchema>;\n\n/** Accordeon op Astryx Collapsible. Bij `multiple: false` is er max. één paneel tegelijk open. */\nfunction AccordionRender({ block }: BlockRenderProps<Accordion>) {\n const items = (block.items ?? []).filter((i) => i.title);\n const [openIdx, setOpenIdx] = useState<number | null>(null);\n if (items.length === 0) return null;\n return (\n <VStack gap={3} as=\"section\" className=\"xicms-accordion\">\n {block.heading && <Heading level={2} data-xicms-edit=\"heading\">{block.heading}</Heading>}\n <VStack gap={1.5}>\n {items.map((it, i) => {\n const body = it.bodyHtml ? (\n // Server-side gesanitized bij publiceren (*Html-velden) — zie functions/lib/sanitize.\n // eslint-disable-next-line react/no-danger\n <div className=\"xicms-richtext\" dangerouslySetInnerHTML={{ __html: it.bodyHtml }} />\n ) : (\n <Text color=\"secondary\">{it.body}</Text>\n );\n const trigger = <Text weight=\"bold\">{it.title}</Text>;\n return block.multiple ? (\n <Collapsible key={i} trigger={trigger} defaultIsOpen={false}>{body}</Collapsible>\n ) : (\n <Collapsible\n key={i}\n trigger={trigger}\n isOpen={openIdx === i}\n onOpenChange={(o) => setOpenIdx(o ? i : null)}\n >\n {body}\n </Collapsible>\n );\n })}\n </VStack>\n </VStack>\n );\n}\n\nexport const accordionBlock = defineBlock({\n type: \"accordion\",\n schema: AccordionBlockSchema,\n Render: AccordionRender,\n label: \"Accordeon\",\n icon: \"🪗\",\n defaults: () => ({ items: [{ title: \"Titel\", body: \"Inhoud…\" }], multiple: false }),\n});\n"],"mappings":";AAiBI,SACoB,KADpB;AAfJ,SAAS,gBAAgB;AAEzB,SAAS,4BAA4B;AACrC,SAAS,mBAA0C;AACnD,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,SAAS,YAAY;AAKtC,SAAS,gBAAgB,EAAE,MAAM,GAAgC;AAC/D,QAAM,SAAS,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK;AACvD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAwB,IAAI;AAC1D,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SACE,qBAAC,UAAO,KAAK,GAAG,IAAG,WAAU,WAAU,mBACpC;AAAA,UAAM,WAAW,oBAAC,WAAQ,OAAO,GAAG,mBAAgB,WAAW,gBAAM,SAAQ;AAAA,IAC9E,oBAAC,UAAO,KAAK,KACV,gBAAM,IAAI,CAAC,IAAI,MAAM;AACpB,YAAM,OAAO,GAAG;AAAA;AAAA;AAAA,QAGd,oBAAC,SAAI,WAAU,kBAAiB,yBAAyB,EAAE,QAAQ,GAAG,SAAS,GAAG;AAAA,UAElF,oBAAC,QAAK,OAAM,aAAa,aAAG,MAAK;AAEnC,YAAM,UAAU,oBAAC,QAAK,QAAO,QAAQ,aAAG,OAAM;AAC9C,aAAO,MAAM,WACX,oBAAC,eAAoB,SAAkB,eAAe,OAAQ,kBAA5C,CAAiD,IAEnE;AAAA,QAAC;AAAA;AAAA,UAEC;AAAA,UACA,QAAQ,YAAY;AAAA,UACpB,cAAc,CAAC,MAAM,WAAW,IAAI,IAAI,IAAI;AAAA,UAE3C;AAAA;AAAA,QALI;AAAA,MAMP;AAAA,IAEJ,CAAC,GACH;AAAA,KACF;AAEJ;AAEO,MAAM,iBAAiB,YAAY;AAAA,EACxC,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,SAAS,MAAM,eAAU,CAAC,GAAG,UAAU,MAAM;AACnF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/builtins/Accordion.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { z } from \"zod\";\nimport { AccordionBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\nimport { Collapsible } from \"@astryxdesign/core/Collapsible\";\nimport { VStack, Heading, Text } from \"../ui\";\n\ntype Accordion = z.infer<typeof AccordionBlockSchema>;\n\n/** Accordeon op Astryx Collapsible. Bij `multiple: false` is er max. één paneel tegelijk open. */\nfunction AccordionRender({ block }: BlockRenderProps<Accordion>) {\n const items = (block.items ?? []).filter((i) => i.title);\n const [openIdx, setOpenIdx] = useState<number | null>(null);\n if (items.length === 0) return null;\n return (\n <VStack gap={3} as=\"section\" className=\"xicms-accordion\">\n {block.heading && <Heading level={2} data-xicms-edit=\"heading\">{block.heading}</Heading>}\n <VStack gap={1.5}>\n {items.map((it, i) => {\n const body = it.bodyHtml ? (\n // Server-side gesanitized bij publiceren (*Html-velden) — zie functions/lib/sanitize.\n // eslint-disable-next-line react/no-danger\n <div\n className=\"xicms-richtext\"\n data-xicms-edit={`items.${i}.bodyHtml`}\n dangerouslySetInnerHTML={{ __html: it.bodyHtml }}\n />\n ) : (\n <Text color=\"secondary\" data-xicms-edit={`items.${i}.body`}>{it.body}</Text>\n );\n const trigger = <Text weight=\"bold\" data-xicms-edit={`items.${i}.title`}>{it.title}</Text>;\n return block.multiple ? (\n <Collapsible key={i} trigger={trigger} defaultIsOpen={false}>{body}</Collapsible>\n ) : (\n <Collapsible\n key={i}\n trigger={trigger}\n isOpen={openIdx === i}\n onOpenChange={(o) => setOpenIdx(o ? i : null)}\n >\n {body}\n </Collapsible>\n );\n })}\n </VStack>\n </VStack>\n );\n}\n\nexport const accordionBlock = defineBlock({\n type: \"accordion\",\n schema: AccordionBlockSchema,\n Render: AccordionRender,\n label: \"Accordeon\",\n icon: \"🪗\",\n defaults: () => ({ items: [{ title: \"Titel\", body: \"Inhoud…\" }], multiple: false }),\n});\n"],"mappings":";AAiBI,SACoB,KADpB;AAfJ,SAAS,gBAAgB;AAEzB,SAAS,4BAA4B;AACrC,SAAS,mBAA0C;AACnD,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,SAAS,YAAY;AAKtC,SAAS,gBAAgB,EAAE,MAAM,GAAgC;AAC/D,QAAM,SAAS,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK;AACvD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAwB,IAAI;AAC1D,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SACE,qBAAC,UAAO,KAAK,GAAG,IAAG,WAAU,WAAU,mBACpC;AAAA,UAAM,WAAW,oBAAC,WAAQ,OAAO,GAAG,mBAAgB,WAAW,gBAAM,SAAQ;AAAA,IAC9E,oBAAC,UAAO,KAAK,KACV,gBAAM,IAAI,CAAC,IAAI,MAAM;AACpB,YAAM,OAAO,GAAG;AAAA;AAAA;AAAA,QAGd;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,mBAAiB,SAAS,CAAC;AAAA,YAC3B,yBAAyB,EAAE,QAAQ,GAAG,SAAS;AAAA;AAAA,QACjD;AAAA,UAEA,oBAAC,QAAK,OAAM,aAAY,mBAAiB,SAAS,CAAC,SAAU,aAAG,MAAK;AAEvE,YAAM,UAAU,oBAAC,QAAK,QAAO,QAAO,mBAAiB,SAAS,CAAC,UAAW,aAAG,OAAM;AACnF,aAAO,MAAM,WACX,oBAAC,eAAoB,SAAkB,eAAe,OAAQ,kBAA5C,CAAiD,IAEnE;AAAA,QAAC;AAAA;AAAA,UAEC;AAAA,UACA,QAAQ,YAAY;AAAA,UACpB,cAAc,CAAC,MAAM,WAAW,IAAI,IAAI,IAAI;AAAA,UAE3C;AAAA;AAAA,QALI;AAAA,MAMP;AAAA,IAEJ,CAAC,GACH;AAAA,KACF;AAEJ;AAEO,MAAM,iBAAiB,YAAY;AAAA,EACxC,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,SAAS,MAAM,eAAU,CAAC,GAAG,UAAU,MAAM;AACnF,CAAC;","names":[]}
@@ -26,4 +26,4 @@ export declare function selectAgendaEvents<T extends AgendaEvent>(events: T[] |
26
26
  hidePast?: boolean;
27
27
  limit?: number;
28
28
  }): T[];
29
- export declare const agendaBlock: import("..").BlockDefinition<z.ZodTypeAny>;
29
+ export declare const agendaBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { jsonLdHtml } from "./jsonld";
3
- import { AgendaBlockSchema } from "../../schema/blocks";
4
- import { defineBlock } from "../registry";
5
- import { Heading } from "../ui";
6
- import { icsDataUrl } from "./lib/ics";
2
+ import { jsonLdHtml } from "./jsonld.js";
3
+ import { AgendaBlockSchema } from "../../schema/blocks.js";
4
+ import { defineBlock } from "../registry.js";
5
+ import { Heading } from "../ui/index.js";
6
+ import { icsDataUrl } from "./lib/ics.js";
7
7
  function parseEventDate(value) {
8
8
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
9
9
  if (m) return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
@@ -1,2 +1,2 @@
1
1
  import type { z } from "zod";
2
- export declare const bannerBlock: import("..").BlockDefinition<z.ZodTypeAny>;
2
+ export declare const bannerBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from "react";
4
- import { BannerBlockSchema } from "../../schema/blocks";
5
- import { defineBlock } from "../registry";
4
+ import { BannerBlockSchema } from "../../schema/blocks.js";
5
+ import { defineBlock } from "../registry.js";
6
6
  function storageKey(block) {
7
7
  return `xicms-banner:${block._key}${block.dismissVersion ? `:${block.dismissVersion}` : ""}`;
8
8
  }
@@ -1,2 +1,2 @@
1
1
  import type { z } from "zod";
2
- export declare const beforeAfterBlock: import("..").BlockDefinition<z.ZodTypeAny>;
2
+ export declare const beforeAfterBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import { BeforeAfterBlockSchema } from "../../schema/blocks";
5
- import { defineBlock } from "../registry";
4
+ import { BeforeAfterBlockSchema } from "../../schema/blocks.js";
5
+ import { defineBlock } from "../registry.js";
6
6
  function BeforeAfterRender({ block }) {
7
7
  const [pos, setPos] = useState(50);
8
8
  if (!block.beforeUrl || !block.afterUrl) return null;
@@ -12,8 +12,8 @@ function BeforeAfterRender({ block }) {
12
12
  /* @__PURE__ */ jsx("img", { className: "xicms-beforeafter__img", src: block.beforeUrl, alt: block.alt || block.beforeLabel, loading: "lazy" }),
13
13
  /* @__PURE__ */ jsx("div", { className: "xicms-beforeafter__clip", style: { clipPath: `inset(0 0 0 ${pos}%)` }, "aria-hidden": true, children: /* @__PURE__ */ jsx("img", { className: "xicms-beforeafter__img", src: block.afterUrl, alt: "", loading: "lazy" }) }),
14
14
  /* @__PURE__ */ jsx("div", { className: "xicms-beforeafter__divider", style: { left: `${pos}%` }, "aria-hidden": true, children: /* @__PURE__ */ jsx("span", { className: "xicms-beforeafter__handle", children: "\u27E8\u27E9" }) }),
15
- /* @__PURE__ */ jsx("span", { className: "xicms-beforeafter__badge xicms-beforeafter__badge--before", children: block.beforeLabel }),
16
- /* @__PURE__ */ jsx("span", { className: "xicms-beforeafter__badge xicms-beforeafter__badge--after", children: block.afterLabel }),
15
+ /* @__PURE__ */ jsx("span", { className: "xicms-beforeafter__badge xicms-beforeafter__badge--before", "data-xicms-edit": "beforeLabel", children: block.beforeLabel }),
16
+ /* @__PURE__ */ jsx("span", { className: "xicms-beforeafter__badge xicms-beforeafter__badge--after", "data-xicms-edit": "afterLabel", children: block.afterLabel }),
17
17
  /* @__PURE__ */ jsx(
18
18
  "input",
19
19
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/blocks/builtins/BeforeAfter.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { z } from \"zod\";\nimport { BeforeAfterBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\n\ntype BeforeAfter = z.infer<typeof BeforeAfterBlockSchema>;\n\n/**\n * Voor/na-vergelijker: het \"na\"-beeld wordt geclipt op de sleppositie. De greep is een échte\n * range-input (over de hele breedte, transparant) — slepen, klikken én pijltjestoetsen werken\n * daarmee gratis en toegankelijk (aria-label + waarde in %).\n */\nfunction BeforeAfterRender({ block }: BlockRenderProps<BeforeAfter>) {\n const [pos, setPos] = useState(50);\n if (!block.beforeUrl || !block.afterUrl) return null;\n return (\n <figure className=\"xicms-beforeafter\" style={{ aspectRatio: block.aspectRatio.replace(\"/\", \" / \") }}>\n {block.heading && <figcaption className=\"xicms-beforeafter__heading\" data-xicms-edit=\"heading\">{block.heading}</figcaption>}\n <div className=\"xicms-beforeafter__stage\">\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img className=\"xicms-beforeafter__img\" src={block.beforeUrl} alt={block.alt || block.beforeLabel} loading=\"lazy\" />\n <div className=\"xicms-beforeafter__clip\" style={{ clipPath: `inset(0 0 0 ${pos}%)` }} aria-hidden>\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img className=\"xicms-beforeafter__img\" src={block.afterUrl} alt=\"\" loading=\"lazy\" />\n </div>\n <div className=\"xicms-beforeafter__divider\" style={{ left: `${pos}%` }} aria-hidden>\n <span className=\"xicms-beforeafter__handle\">⟨⟩</span>\n </div>\n <span className=\"xicms-beforeafter__badge xicms-beforeafter__badge--before\">{block.beforeLabel}</span>\n <span className=\"xicms-beforeafter__badge xicms-beforeafter__badge--after\">{block.afterLabel}</span>\n <input\n className=\"xicms-beforeafter__range\"\n type=\"range\"\n min={0}\n max={100}\n value={pos}\n onChange={(e) => setPos(Number(e.target.value))}\n aria-label={`Vergelijk ${block.beforeLabel} en ${block.afterLabel}`}\n aria-valuetext={`${pos}% ${block.afterLabel}`}\n />\n </div>\n </figure>\n );\n}\n\nexport const beforeAfterBlock = defineBlock({\n type: \"beforeAfter\",\n fields: { beforeUrl: { type: \"image\" }, afterUrl: { type: \"image\" } },\n schema: BeforeAfterBlockSchema,\n Render: BeforeAfterRender,\n label: \"Voor / na\",\n icon: \"🔀\",\n defaults: () => ({ beforeUrl: \"\", afterUrl: \"\", beforeLabel: \"Voor\", afterLabel: \"Na\", aspectRatio: \"4/3\" }),\n});\n"],"mappings":";AAmBwB,cAClB,YADkB;AAjBxB,SAAS,gBAAgB;AAEzB,SAAS,8BAA8B;AACvC,SAAS,mBAA0C;AASnD,SAAS,kBAAkB,EAAE,MAAM,GAAkC;AACnE,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,EAAE;AACjC,MAAI,CAAC,MAAM,aAAa,CAAC,MAAM,SAAU,QAAO;AAChD,SACE,qBAAC,YAAO,WAAU,qBAAoB,OAAO,EAAE,aAAa,MAAM,YAAY,QAAQ,KAAK,KAAK,EAAE,GAC/F;AAAA,UAAM,WAAW,oBAAC,gBAAW,WAAU,8BAA6B,mBAAgB,WAAW,gBAAM,SAAQ;AAAA,IAC9G,qBAAC,SAAI,WAAU,4BAEb;AAAA,0BAAC,SAAI,WAAU,0BAAyB,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO,MAAM,aAAa,SAAQ,QAAO;AAAA,MAClH,oBAAC,SAAI,WAAU,2BAA0B,OAAO,EAAE,UAAU,eAAe,GAAG,KAAK,GAAG,eAAW,MAE/F,8BAAC,SAAI,WAAU,0BAAyB,KAAK,MAAM,UAAU,KAAI,IAAG,SAAQ,QAAO,GACrF;AAAA,MACA,oBAAC,SAAI,WAAU,8BAA6B,OAAO,EAAE,MAAM,GAAG,GAAG,IAAI,GAAG,eAAW,MACjF,8BAAC,UAAK,WAAU,6BAA4B,0BAAE,GAChD;AAAA,MACA,oBAAC,UAAK,WAAU,6DAA6D,gBAAM,aAAY;AAAA,MAC/F,oBAAC,UAAK,WAAU,4DAA4D,gBAAM,YAAW;AAAA,MAC7F;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,MAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,UAC9C,cAAY,aAAa,MAAM,WAAW,OAAO,MAAM,UAAU;AAAA,UACjE,kBAAgB,GAAG,GAAG,KAAK,MAAM,UAAU;AAAA;AAAA,MAC7C;AAAA,OACF;AAAA,KACF;AAEJ;AAEO,MAAM,mBAAmB,YAAY;AAAA,EAC1C,MAAM;AAAA,EACN,QAAQ,EAAE,WAAW,EAAE,MAAM,QAAQ,GAAG,UAAU,EAAE,MAAM,QAAQ,EAAE;AAAA,EACpE,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO,EAAE,WAAW,IAAI,UAAU,IAAI,aAAa,QAAQ,YAAY,MAAM,aAAa,MAAM;AAC5G,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../../src/blocks/builtins/BeforeAfter.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { z } from \"zod\";\nimport { BeforeAfterBlockSchema } from \"../../schema/blocks\";\nimport { defineBlock, type BlockRenderProps } from \"../registry\";\n\ntype BeforeAfter = z.infer<typeof BeforeAfterBlockSchema>;\n\n/**\n * Voor/na-vergelijker: het \"na\"-beeld wordt geclipt op de sleppositie. De greep is een échte\n * range-input (over de hele breedte, transparant) — slepen, klikken én pijltjestoetsen werken\n * daarmee gratis en toegankelijk (aria-label + waarde in %).\n */\nfunction BeforeAfterRender({ block }: BlockRenderProps<BeforeAfter>) {\n const [pos, setPos] = useState(50);\n if (!block.beforeUrl || !block.afterUrl) return null;\n return (\n <figure className=\"xicms-beforeafter\" style={{ aspectRatio: block.aspectRatio.replace(\"/\", \" / \") }}>\n {block.heading && <figcaption className=\"xicms-beforeafter__heading\" data-xicms-edit=\"heading\">{block.heading}</figcaption>}\n <div className=\"xicms-beforeafter__stage\">\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img className=\"xicms-beforeafter__img\" src={block.beforeUrl} alt={block.alt || block.beforeLabel} loading=\"lazy\" />\n <div className=\"xicms-beforeafter__clip\" style={{ clipPath: `inset(0 0 0 ${pos}%)` }} aria-hidden>\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img className=\"xicms-beforeafter__img\" src={block.afterUrl} alt=\"\" loading=\"lazy\" />\n </div>\n <div className=\"xicms-beforeafter__divider\" style={{ left: `${pos}%` }} aria-hidden>\n <span className=\"xicms-beforeafter__handle\">⟨⟩</span>\n </div>\n <span className=\"xicms-beforeafter__badge xicms-beforeafter__badge--before\" data-xicms-edit=\"beforeLabel\">{block.beforeLabel}</span>\n <span className=\"xicms-beforeafter__badge xicms-beforeafter__badge--after\" data-xicms-edit=\"afterLabel\">{block.afterLabel}</span>\n <input\n className=\"xicms-beforeafter__range\"\n type=\"range\"\n min={0}\n max={100}\n value={pos}\n onChange={(e) => setPos(Number(e.target.value))}\n aria-label={`Vergelijk ${block.beforeLabel} en ${block.afterLabel}`}\n aria-valuetext={`${pos}% ${block.afterLabel}`}\n />\n </div>\n </figure>\n );\n}\n\nexport const beforeAfterBlock = defineBlock({\n type: \"beforeAfter\",\n fields: { beforeUrl: { type: \"image\" }, afterUrl: { type: \"image\" } },\n schema: BeforeAfterBlockSchema,\n Render: BeforeAfterRender,\n label: \"Voor / na\",\n icon: \"🔀\",\n defaults: () => ({ beforeUrl: \"\", afterUrl: \"\", beforeLabel: \"Voor\", afterLabel: \"Na\", aspectRatio: \"4/3\" }),\n});\n"],"mappings":";AAmBwB,cAClB,YADkB;AAjBxB,SAAS,gBAAgB;AAEzB,SAAS,8BAA8B;AACvC,SAAS,mBAA0C;AASnD,SAAS,kBAAkB,EAAE,MAAM,GAAkC;AACnE,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,EAAE;AACjC,MAAI,CAAC,MAAM,aAAa,CAAC,MAAM,SAAU,QAAO;AAChD,SACE,qBAAC,YAAO,WAAU,qBAAoB,OAAO,EAAE,aAAa,MAAM,YAAY,QAAQ,KAAK,KAAK,EAAE,GAC/F;AAAA,UAAM,WAAW,oBAAC,gBAAW,WAAU,8BAA6B,mBAAgB,WAAW,gBAAM,SAAQ;AAAA,IAC9G,qBAAC,SAAI,WAAU,4BAEb;AAAA,0BAAC,SAAI,WAAU,0BAAyB,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO,MAAM,aAAa,SAAQ,QAAO;AAAA,MAClH,oBAAC,SAAI,WAAU,2BAA0B,OAAO,EAAE,UAAU,eAAe,GAAG,KAAK,GAAG,eAAW,MAE/F,8BAAC,SAAI,WAAU,0BAAyB,KAAK,MAAM,UAAU,KAAI,IAAG,SAAQ,QAAO,GACrF;AAAA,MACA,oBAAC,SAAI,WAAU,8BAA6B,OAAO,EAAE,MAAM,GAAG,GAAG,IAAI,GAAG,eAAW,MACjF,8BAAC,UAAK,WAAU,6BAA4B,0BAAE,GAChD;AAAA,MACA,oBAAC,UAAK,WAAU,6DAA4D,mBAAgB,eAAe,gBAAM,aAAY;AAAA,MAC7H,oBAAC,UAAK,WAAU,4DAA2D,mBAAgB,cAAc,gBAAM,YAAW;AAAA,MAC1H;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,MAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,UAC9C,cAAY,aAAa,MAAM,WAAW,OAAO,MAAM,UAAU;AAAA,UACjE,kBAAgB,GAAG,GAAG,KAAK,MAAM,UAAU;AAAA;AAAA,MAC7C;AAAA,OACF;AAAA,KACF;AAEJ;AAEO,MAAM,mBAAmB,YAAY;AAAA,EAC1C,MAAM;AAAA,EACN,QAAQ,EAAE,WAAW,EAAE,MAAM,QAAQ,GAAG,UAAU,EAAE,MAAM,QAAQ,EAAE;AAAA,EACpE,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO,EAAE,WAAW,IAAI,UAAU,IAAI,aAAa,QAAQ,YAAY,MAAM,aAAa,MAAM;AAC5G,CAAC;","names":[]}
@@ -1,2 +1,2 @@
1
1
  import type { z } from "zod";
2
- export declare const bookingBlock: import("..").BlockDefinition<z.ZodTypeAny>;
2
+ export declare const bookingBlock: import("../index.js").BlockDefinition<z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { BookingBlockSchema } from "../../schema/blocks";
4
- import { defineBlock } from "../registry";
5
- import { Heading } from "../ui";
6
- import { ConsentEmbed } from "./consentEmbed";
7
- import { buildBookingUrl } from "./lib/bookingUrl";
3
+ import { BookingBlockSchema } from "../../schema/blocks.js";
4
+ import { defineBlock } from "../registry.js";
5
+ import { Heading } from "../ui/index.js";
6
+ import { ConsentEmbed } from "./consentEmbed.js";
7
+ import { buildBookingUrl } from "./lib/bookingUrl.js";
8
8
  function BookingRender({ block, mode }) {
9
9
  const src = buildBookingUrl(block.provider ?? "custom", block.embedUrl);
10
10
  if (!src) return null;