@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.
- package/dist/SiteChrome.d.ts +12 -0
- package/dist/SiteChrome.js +24 -0
- package/dist/SiteChrome.js.map +1 -0
- package/dist/XiCMSProvider.d.ts +16 -8
- package/dist/XiCMSProvider.js +83 -58
- package/dist/XiCMSProvider.js.map +1 -1
- package/dist/admin/AdminApp.d.ts +1 -1
- package/dist/admin/AdminApp.js +79 -35
- package/dist/admin/AdminApp.js.map +1 -1
- package/dist/admin/AdminHelp.d.ts +8 -1
- package/dist/admin/AdminHelp.js +2 -2
- package/dist/admin/AdminHelp.js.map +1 -1
- package/dist/admin/NewsEditor.js +9 -9
- package/dist/admin/SuperadminPanel.js +5 -5
- package/dist/admin/blockEditors/GenericEditor.d.ts +2 -2
- package/dist/admin/blockEditors/GenericEditor.js +12 -11
- package/dist/admin/blockEditors/GenericEditor.js.map +1 -1
- package/dist/admin/blockEditors/StyleEditor.d.ts +1 -1
- package/dist/admin/blockEditors/StyleEditor.js +2 -2
- package/dist/admin/blockEditors/SymbolRefEditor.d.ts +1 -1
- package/dist/admin/blockEditors/SymbolRefEditor.js +5 -5
- package/dist/admin/blockEditors/index.d.ts +1 -1
- package/dist/admin/blockEditors/index.js +10 -10
- package/dist/admin/customBlocks/CustomBlocksPanel.js +7 -7
- package/dist/admin/editors/RichTextEditor.js +8 -7
- package/dist/admin/editors/RichTextEditor.js.map +1 -1
- package/dist/admin/editors/fontSizeExtension.js +1 -1
- package/dist/admin/editors/fontSizeExtension.js.map +1 -1
- package/dist/admin/forms/FormsPanel.js +9 -9
- package/dist/admin/index.d.ts +16 -16
- package/dist/admin/index.js +16 -16
- package/dist/admin/lib/mediaUsage.js +1 -1
- package/dist/admin/lib/pagesClient.d.ts +1 -1
- package/dist/admin/lib/pagesClient.js +11 -7
- package/dist/admin/lib/pagesClient.js.map +1 -1
- package/dist/admin/lib/sanitizeDraft.d.ts +9 -0
- package/dist/admin/lib/sanitizeDraft.js +28 -0
- package/dist/admin/lib/sanitizeDraft.js.map +1 -0
- package/dist/admin/lib/symbolsClient.d.ts +1 -1
- package/dist/admin/lib/symbolsClient.js +3 -3
- package/dist/admin/lib/upload.js +3 -3
- package/dist/admin/lib/useNestedDialog.d.ts +15 -0
- package/dist/admin/lib/useNestedDialog.js +23 -0
- package/dist/admin/lib/useNestedDialog.js.map +1 -0
- package/dist/admin/lib/zodFields.d.ts +4 -18
- package/dist/admin/lib/zodFields.js +54 -35
- package/dist/admin/lib/zodFields.js.map +1 -1
- package/dist/admin/media/MediaManager.js +7 -7
- package/dist/admin/media/MediaPicker.js +7 -7
- package/dist/admin/media/MediaPicker.js.map +1 -1
- package/dist/admin/pages/PageEditor.d.ts +1 -1
- package/dist/admin/pages/PageEditor.js +13 -13
- package/dist/admin/pages/PagesPanel.js +30 -30
- package/dist/admin/pages/PagesPanel.js.map +1 -1
- package/dist/admin/panelRegistry.js +7 -7
- package/dist/admin/patterns/PatternInserter.d.ts +1 -1
- package/dist/admin/patterns/PatternInserter.js +5 -5
- package/dist/admin/patterns/PatternInserter.js.map +1 -1
- package/dist/admin/settings/MailSettings.js +5 -5
- package/dist/admin/settings/MailTemplates.d.ts +1 -1
- package/dist/admin/settings/MailTemplates.js +5 -5
- package/dist/admin/settings/SettingsPanel.js +20 -20
- package/dist/admin/settings/SettingsPanel.js.map +1 -1
- package/dist/admin/settings/SnapshotsCard.js +5 -5
- package/dist/admin/settings/moduleSettingsForm.d.ts +1 -1
- package/dist/admin/settings/moduleSettingsForm.js +4 -4
- package/dist/admin/settings/moduleSettingsForm.js.map +1 -1
- package/dist/admin/shop/CategoriesTab.js +8 -8
- package/dist/admin/shop/ConnectionsTab.js +3 -3
- package/dist/admin/shop/DiscountsTab.js +8 -8
- package/dist/admin/shop/OrdersTab.js +10 -10
- package/dist/admin/shop/OrdersTab.js.map +1 -1
- package/dist/admin/shop/ProductsTab.js +10 -10
- package/dist/admin/shop/ShopPanel.js +9 -9
- package/dist/admin/shop/ShopSettingsTab.js +4 -4
- package/dist/admin/shop/index.d.ts +2 -2
- package/dist/admin/shop/index.js +2 -2
- package/dist/admin/shop/lib.js +2 -2
- package/dist/admin/stats/StatsPanel.js +2 -2
- package/dist/admin/ui/ArrayEditor.js +1 -1
- package/dist/admin/ui/PanelIcon.d.ts +3 -0
- package/dist/admin/ui/PanelIcon.js +34 -0
- package/dist/admin/ui/PanelIcon.js.map +1 -0
- package/dist/admin/ui/dialog.d.ts +21 -6
- package/dist/admin/ui/dialog.js +55 -34
- package/dist/admin/ui/dialog.js.map +1 -1
- package/dist/admin/ui/index.d.ts +7 -7
- package/dist/admin/ui/index.js +7 -7
- package/dist/admin/ui/primitives.js +1 -1
- package/dist/admin/ui/tabs.js +1 -1
- package/dist/admin/visual/TranslationPanel.d.ts +1 -1
- package/dist/admin/visual/TranslationPanel.js +3 -3
- package/dist/admin/visual/VisualEditor.js +47 -25
- package/dist/admin/visual/VisualEditor.js.map +1 -1
- package/dist/admin.css +2 -1
- package/dist/agentFiles/catalog.d.ts +2 -2
- package/dist/agentFiles/catalog.js +6 -5
- package/dist/agentFiles/catalog.js.map +1 -1
- package/dist/agentFiles/files.d.ts +5 -5
- package/dist/agentFiles/files.js +17 -17
- package/dist/agentFiles/files.js.map +1 -1
- package/dist/all.d.ts +4 -4
- package/dist/all.js +6 -6
- package/dist/auth/AuthProvider.d.ts +2 -2
- package/dist/auth/AuthProvider.js +51 -28
- package/dist/auth/AuthProvider.js.map +1 -1
- package/dist/auth/LoginForm.js +1 -1
- package/dist/auth/roles.js +4 -1
- package/dist/auth/roles.js.map +1 -1
- package/dist/blocks/BlockRenderer.d.ts +1 -1
- package/dist/blocks/BlockRenderer.js +3 -3
- package/dist/blocks/blockStyle.d.ts +1 -1
- package/dist/blocks/builtins/Accordion.d.ts +1 -1
- package/dist/blocks/builtins/Accordion.js +13 -6
- package/dist/blocks/builtins/Accordion.js.map +1 -1
- package/dist/blocks/builtins/Agenda.d.ts +1 -1
- package/dist/blocks/builtins/Agenda.js +5 -5
- package/dist/blocks/builtins/Banner.d.ts +1 -1
- package/dist/blocks/builtins/Banner.js +2 -2
- package/dist/blocks/builtins/BeforeAfter.d.ts +1 -1
- package/dist/blocks/builtins/BeforeAfter.js +4 -4
- package/dist/blocks/builtins/BeforeAfter.js.map +1 -1
- package/dist/blocks/builtins/Booking.d.ts +1 -1
- package/dist/blocks/builtins/Booking.js +5 -5
- package/dist/blocks/builtins/Breadcrumbs.d.ts +1 -1
- package/dist/blocks/builtins/Breadcrumbs.js +3 -3
- package/dist/blocks/builtins/Cards.d.ts +1 -1
- package/dist/blocks/builtins/Cards.js +5 -5
- package/dist/blocks/builtins/Cards.js.map +1 -1
- package/dist/blocks/builtins/Columns.d.ts +1 -1
- package/dist/blocks/builtins/Columns.js +4 -4
- package/dist/blocks/builtins/Contact.d.ts +3 -3
- package/dist/blocks/builtins/Contact.js +4 -4
- package/dist/blocks/builtins/Countdown.d.ts +1 -1
- package/dist/blocks/builtins/Countdown.js +4 -4
- package/dist/blocks/builtins/Countdown.js.map +1 -1
- package/dist/blocks/builtins/Cta.d.ts +1 -1
- package/dist/blocks/builtins/Cta.js +3 -3
- package/dist/blocks/builtins/Downloads.d.ts +1 -1
- package/dist/blocks/builtins/Downloads.js +4 -4
- package/dist/blocks/builtins/Elements.d.ts +3 -3
- package/dist/blocks/builtins/Elements.js +3 -3
- package/dist/blocks/builtins/Embed.d.ts +1 -1
- package/dist/blocks/builtins/Embed.js +3 -3
- package/dist/blocks/builtins/Faq.d.ts +1 -1
- package/dist/blocks/builtins/Faq.js +14 -6
- package/dist/blocks/builtins/Faq.js.map +1 -1
- package/dist/blocks/builtins/FormBlock.d.ts +1 -1
- package/dist/blocks/builtins/FormBlock.js +28 -18
- package/dist/blocks/builtins/FormBlock.js.map +1 -1
- package/dist/blocks/builtins/Gallery.d.ts +2 -2
- package/dist/blocks/builtins/Gallery.js +5 -5
- package/dist/blocks/builtins/Hero.d.ts +1 -1
- package/dist/blocks/builtins/Hero.js +4 -4
- package/dist/blocks/builtins/ImageBlock.d.ts +1 -1
- package/dist/blocks/builtins/ImageBlock.js +3 -3
- package/dist/blocks/builtins/Jobs.d.ts +2 -2
- package/dist/blocks/builtins/Jobs.js +5 -5
- package/dist/blocks/builtins/Layout.d.ts +2 -2
- package/dist/blocks/builtins/Layout.js +4 -4
- package/dist/blocks/builtins/MapBlock.d.ts +1 -1
- package/dist/blocks/builtins/MapBlock.js +3 -3
- package/dist/blocks/builtins/NewsListBlock.d.ts +1 -1
- package/dist/blocks/builtins/NewsListBlock.js +4 -4
- package/dist/blocks/builtins/Newsletter.d.ts +1 -1
- package/dist/blocks/builtins/Newsletter.js +7 -7
- package/dist/blocks/builtins/Newsletter.js.map +1 -1
- package/dist/blocks/builtins/OpenNowBadge.d.ts +1 -1
- package/dist/blocks/builtins/OpenNowBadge.js +8 -3
- package/dist/blocks/builtins/OpenNowBadge.js.map +1 -1
- package/dist/blocks/builtins/OpeningHours.d.ts +1 -1
- package/dist/blocks/builtins/OpeningHours.js +6 -6
- package/dist/blocks/builtins/PdfBlock.d.ts +1 -1
- package/dist/blocks/builtins/PdfBlock.js +3 -3
- package/dist/blocks/builtins/Portfolio.d.ts +1 -1
- package/dist/blocks/builtins/Portfolio.js +5 -5
- package/dist/blocks/builtins/Pricing.d.ts +2 -2
- package/dist/blocks/builtins/Pricing.js +5 -5
- package/dist/blocks/builtins/Pricing.js.map +1 -1
- package/dist/blocks/builtins/RichTextBlock.d.ts +1 -1
- package/dist/blocks/builtins/RichTextBlock.js +2 -2
- package/dist/blocks/builtins/RichTextBlock.js.map +1 -1
- package/dist/blocks/builtins/Search.d.ts +1 -1
- package/dist/blocks/builtins/Search.js +9 -7
- package/dist/blocks/builtins/Search.js.map +1 -1
- package/dist/blocks/builtins/Sections.d.ts +4 -4
- package/dist/blocks/builtins/Sections.js +4 -4
- package/dist/blocks/builtins/Slider.d.ts +1 -1
- package/dist/blocks/builtins/Slider.js +3 -3
- package/dist/blocks/builtins/Spacer.d.ts +1 -1
- package/dist/blocks/builtins/Spacer.js +2 -2
- package/dist/blocks/builtins/SymbolRef.d.ts +1 -1
- package/dist/blocks/builtins/SymbolRef.js +31 -17
- package/dist/blocks/builtins/SymbolRef.js.map +1 -1
- package/dist/blocks/builtins/Team.d.ts +1 -1
- package/dist/blocks/builtins/Team.js +4 -4
- package/dist/blocks/builtins/Testimonials.d.ts +2 -2
- package/dist/blocks/builtins/Testimonials.js +4 -4
- package/dist/blocks/builtins/Video.d.ts +2 -2
- package/dist/blocks/builtins/Video.js +4 -4
- package/dist/blocks/builtins/Video.js.map +1 -1
- package/dist/blocks/builtins/WhatsappChat.d.ts +1 -1
- package/dist/blocks/builtins/WhatsappChat.js +2 -2
- package/dist/blocks/builtins/Widgets.d.ts +12 -12
- package/dist/blocks/builtins/Widgets.js +19 -19
- package/dist/blocks/builtins/Widgets.js.map +1 -1
- package/dist/blocks/builtins/consentEmbed.js +2 -2
- package/dist/blocks/builtins/index.d.ts +40 -40
- package/dist/blocks/builtins/index.js +40 -40
- package/dist/blocks/builtins/lib/submitForm.js +5 -3
- package/dist/blocks/builtins/lib/submitForm.js.map +1 -1
- package/dist/blocks/context.d.ts +1 -1
- package/dist/blocks/customTemplate/definition.d.ts +3 -3
- package/dist/blocks/customTemplate/definition.js +6 -6
- package/dist/blocks/customTemplate/definition.js.map +1 -1
- package/dist/blocks/customTemplate/interpret.js +1 -1
- package/dist/blocks/index.d.ts +5 -5
- package/dist/blocks/index.js +5 -5
- package/dist/blocks/registry.d.ts +17 -7
- package/dist/blocks/registry.js +2 -1
- package/dist/blocks/registry.js.map +1 -1
- package/dist/components/EditBar.js +3 -3
- package/dist/components/FeedbackWidget.js +4 -4
- package/dist/components/News.d.ts +1 -1
- package/dist/components/News.js +50 -37
- package/dist/components/News.js.map +1 -1
- package/dist/components/PageView.js +11 -8
- package/dist/components/PageView.js.map +1 -1
- package/dist/components/ResponsiveImage.d.ts +1 -1
- package/dist/components/site/SiteFooter.d.ts +1 -1
- package/dist/components/site/SiteFooter.js +2 -2
- package/dist/config.d.ts +1 -1
- package/dist/demo/DemoStudio.js +3 -3
- package/dist/demo/demoStore.d.ts +3 -3
- package/dist/demo/index.d.ts +2 -2
- package/dist/demo/index.js +2 -2
- package/dist/editor/EditorProvider.js +1 -1
- package/dist/firebase/admin.d.ts +0 -2
- package/dist/firebase/admin.js +0 -6
- package/dist/firebase/admin.js.map +1 -1
- package/dist/firebase/appcheck.d.ts +1 -1
- package/dist/firebase/appcheck.js +5 -3
- package/dist/firebase/appcheck.js.map +1 -1
- package/dist/firebase/client.d.ts +2 -1
- package/dist/firebase/client.js +2 -14
- package/dist/firebase/client.js.map +1 -1
- package/dist/firebase/config.d.ts +31 -0
- package/dist/firebase/config.js +39 -0
- package/dist/firebase/config.js.map +1 -0
- package/dist/firebase/feedback.js +5 -3
- package/dist/firebase/feedback.js.map +1 -1
- package/dist/i18n/overlay.d.ts +2 -2
- package/dist/i18n/overlay.js +6 -4
- package/dist/i18n/overlay.js.map +1 -1
- package/dist/index.d.ts +45 -44
- package/dist/index.js +35 -33
- package/dist/index.js.map +1 -1
- package/dist/lock/DemoLock.js +1 -1
- package/dist/modules/builtins.d.ts +1 -1
- package/dist/modules/builtins.js +53 -55
- package/dist/modules/builtins.js.map +1 -1
- package/dist/modules/consent/ConsentBanner.js +1 -1
- package/dist/modules/consent/useConsent.js +1 -1
- package/dist/modules/context.d.ts +1 -1
- package/dist/modules/contract.d.ts +1 -1
- package/dist/modules/defineModule.d.ts +1 -1
- package/dist/modules/index.d.ts +9 -9
- package/dist/modules/index.js +8 -8
- package/dist/modules/overrides.d.ts +1 -1
- package/dist/modules/registry.d.ts +2 -7
- package/dist/modules/registry.js +2 -1
- package/dist/modules/registry.js.map +1 -1
- package/dist/modules/shop/blocks/Cart.d.ts +1 -1
- package/dist/modules/shop/blocks/Cart.js +6 -6
- package/dist/modules/shop/blocks/Checkout.d.ts +1 -1
- package/dist/modules/shop/blocks/Checkout.js +6 -6
- package/dist/modules/shop/blocks/Donation.d.ts +1 -1
- package/dist/modules/shop/blocks/Donation.js +5 -5
- package/dist/modules/shop/blocks/OrderStatus.d.ts +1 -1
- package/dist/modules/shop/blocks/OrderStatus.js +5 -5
- package/dist/modules/shop/blocks/ProductDetail.d.ts +1 -1
- package/dist/modules/shop/blocks/ProductDetail.js +8 -8
- package/dist/modules/shop/blocks/ProductGrid.d.ts +3 -3
- package/dist/modules/shop/blocks/ProductGrid.js +7 -7
- package/dist/modules/shop/blocks/ShopCategories.d.ts +1 -1
- package/dist/modules/shop/blocks/ShopCategories.js +6 -6
- package/dist/modules/shop/index.d.ts +5 -5
- package/dist/modules/shop/index.js +5 -5
- package/dist/modules/shop/lib.d.ts +2 -2
- package/dist/modules/shop/lib.js +18 -13
- package/dist/modules/shop/lib.js.map +1 -1
- package/dist/modules/shop/module.d.ts +1 -1
- package/dist/modules/shop/module.js +9 -9
- package/dist/modules/shop/settings.d.ts +18 -50
- package/dist/modules/shop/settings.js +16 -16
- package/dist/modules/shop/settings.js.map +1 -1
- package/dist/modules/shop/strings.js +1 -1
- package/dist/modules/shop/types.d.ts +1 -1
- package/dist/modules/types.d.ts +2 -2
- package/dist/patterns/builtins.d.ts +1 -1
- package/dist/patterns/builtins.js +1 -1
- package/dist/patterns/index.d.ts +2 -2
- package/dist/patterns/index.js +2 -2
- package/dist/render/PageRenderer.d.ts +2 -2
- package/dist/render/PageRenderer.js +1 -1
- package/dist/render/PublishedPage.d.ts +1 -1
- package/dist/render/PublishedPage.js +2 -2
- package/dist/schema/blocks.d.ts +6793 -15095
- package/dist/schema/blocks.js +335 -355
- package/dist/schema/blocks.js.map +1 -1
- package/dist/schema/customBlocks.d.ts +10 -29
- package/dist/schema/customBlocks.js +14 -13
- package/dist/schema/customBlocks.js.map +1 -1
- package/dist/schema/fields.d.ts +10 -16
- package/dist/schema/fields.js +37 -37
- package/dist/schema/fields.js.map +1 -1
- package/dist/schema/forms.d.ts +62 -111
- package/dist/schema/forms.js +17 -17
- package/dist/schema/forms.js.map +1 -1
- package/dist/schema/index.d.ts +7 -7
- package/dist/schema/index.js +7 -7
- package/dist/schema/mail.d.ts +39 -87
- package/dist/schema/mail.js +4 -4
- package/dist/schema/mail.js.map +1 -1
- package/dist/schema/pages.d.ts +152 -674
- package/dist/schema/pages.js +26 -15
- package/dist/schema/pages.js.map +1 -1
- package/dist/schema/seo.d.ts +9 -27
- package/dist/schema/seo.js +7 -7
- package/dist/schema/seo.js.map +1 -1
- package/dist/schema/settings.d.ts +218 -1074
- package/dist/schema/settings.js +55 -55
- package/dist/schema/settings.js.map +1 -1
- package/dist/schema/shop.d.ts +470 -1214
- package/dist/schema/shop.js +124 -124
- package/dist/schema/shop.js.map +1 -1
- package/dist/schema/siteConfig.d.ts +73 -597
- package/dist/schema/siteConfig.js +17 -17
- package/dist/schema/siteConfig.js.map +1 -1
- package/dist/schema/symbols.d.ts +34 -191
- package/dist/schema/symbols.js +8 -8
- package/dist/schema/symbols.js.map +1 -1
- package/dist/server/auth.d.ts +17 -0
- package/dist/server/auth.js +11 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/bundle.d.ts +15 -6
- package/dist/server/bundle.js +5 -1
- package/dist/server/bundle.js.map +1 -1
- package/dist/server/draft.d.ts +16 -0
- package/dist/server/draft.js +12 -0
- package/dist/server/draft.js.map +1 -0
- package/dist/server/env.js +8 -8
- package/dist/server/env.js.map +1 -1
- package/dist/server/index.d.ts +10 -8
- package/dist/server/index.js +12 -9
- package/dist/server/index.js.map +1 -1
- package/dist/server/pages.d.ts +14 -7
- package/dist/server/pages.js +183 -17
- package/dist/server/pages.js.map +1 -1
- package/dist/server/revalidate.d.ts +6 -0
- package/dist/server/revalidate.js +40 -0
- package/dist/server/revalidate.js.map +1 -0
- package/dist/server/seo.d.ts +3 -3
- package/dist/server/seo.js +1 -1
- package/dist/server/shop.d.ts +3 -3
- package/dist/server/shop.js +6 -3
- package/dist/server/shop.js.map +1 -1
- package/dist/settings/useSiteSettings.d.ts +2 -2
- package/dist/settings/useSiteSettings.js +48 -28
- package/dist/settings/useSiteSettings.js.map +1 -1
- package/dist/styles.css +4 -1
- package/dist/theme/presets.d.ts +1 -1
- 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
|
package/dist/agentFiles/files.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
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 =
|
|
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 =
|
|
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().
|
|
98
|
-
title: z.string().
|
|
99
|
-
slug: z.string().
|
|
100
|
-
excerpt: z.string().
|
|
101
|
-
body: z.string().
|
|
102
|
-
bodyHtml: z.string().
|
|
103
|
-
coverUrl: z.string().
|
|
104
|
-
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "datum als yyyy-mm-dd"),
|
|
105
|
-
published: z.boolean()
|
|
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 =
|
|
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 {
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
51
|
-
if (!auth) {
|
|
49
|
+
if (!configured || !hasAuthHint()) {
|
|
52
50
|
setLoading(false);
|
|
53
51
|
return;
|
|
54
52
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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 =
|
|
74
|
-
|
|
75
|
-
|
|
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 =
|
|
79
|
-
|
|
80
|
-
|
|
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 =
|
|
88
|
-
|
|
108
|
+
const { auth, sdk } = await authSdk();
|
|
109
|
+
await sdk.signOut(auth);
|
|
110
|
+
setAuthHint(false);
|
|
89
111
|
},
|
|
90
112
|
async refresh() {
|
|
91
|
-
|
|
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":";AA+IS;AA7IT;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,OAEN;AACP,SAAS,iBAAiB,4BAA4B;AACtD,SAAS,mBAA6C;AACtD,SAAS,YAAY,gBAAgB,uBAAuB;AAuB5D,MAAM,cAAc,cAAgC,IAAI;AAEjD,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,OAAO;AAAA,EACP;AACF,GAIG;AACD,QAAM,aAAa,qBAAqB;AACxC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,UAAU;AACjD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAsB,IAAI;AAClD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA0B,IAAI;AAC1D,QAAM,CAAC,cAAc,oBAAoB,IAAI,SAAS,KAAK;AAG3D,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,MAAM,qBAAqB,eAAe,CAAC;AACxD,SAAK;AACL,WAAO,iBAAiB,YAAY,IAAI;AACxC,WAAO,MAAM,OAAO,oBAAoB,YAAY,IAAI;AAAA,EAC1D,GAAG,CAAC,IAAI,CAAC;AAET,iBAAe,WAAW,GAAgB,eAAe,OAAO;AAC9D,QAAI,CAAC,GAAG;AACN,gBAAU,IAAI;AACd;AAAA,IACF;AAIA,UAAM,QAAQ,MAAM,EAAE,iBAAiB,YAAY;AACnD,cAAU;AAAA,MACR,YAAY,MAAM,OAAO,eAAe;AAAA,MACxC,OAAQ,MAAM,OAAO,SAA+B,CAAC;AAAA,IACvD,CAAC;AAAA,EACH;AAEA,YAAU,MAAM;AACd,UAAM,OAAO,gBAAgB;AAC7B,QAAI,CAAC,MAAM;AACT,iBAAW,KAAK;AAChB;AAAA,IACF;AACA,UAAM,QAAQ,mBAAmB,MAAM,OAAO,MAAM;AAClD,cAAQ,CAAC;AACT,YAAM,WAAW,CAAC;AAClB,iBAAW,KAAK;AAAA,IAClB,CAAC;AACD,WAAO,MAAM,MAAM;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQ,QAAmB,MAAM;AAErC,UAAM,kBAAkB,QAAQ;AAChC,UAAM,gBAAgB,kBACjB,EAAE,KAAK,QAAQ,OAAO,qBAAqB,aAAa,iBAAiB,IAC1E;AACJ,UAAM,kBAAmC,kBAAkB,EAAE,OAAO,EAAE,CAAC,MAAM,GAAG,YAAY,EAAE,IAAI;AAElG,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,MAAM,YAAY,iBAAiB,MAAM;AAAA,MACzC,MAAM,OAAO,OAAO,UAAU;AAC5B,cAAM,OAAO,gBAAgB;AAC7B,YAAI,CAAC,KAAM,OAAM,IAAI,MAAM,kCAAkC;AAC7D,cAAM,2BAA2B,MAAM,OAAO,QAAQ;AAAA,MACxD;AAAA,MACA,MAAM,mBAAmB;AACvB,cAAM,OAAO,gBAAgB;AAC7B,YAAI,CAAC,KAAM,OAAM,IAAI,MAAM,kCAAkC;AAC7D,cAAM,gBAAgB,MAAM,IAAI,mBAAmB,CAAC;AAAA,MACtD;AAAA,MACA,MAAM,UAAU;AACd,YAAI,MAAM;AACR,0BAAgB,KAAK;AACrB;AAAA,QACF;AACA,cAAM,OAAO,gBAAgB;AAC7B,YAAI,KAAM,OAAM,UAAU,IAAI;AAAA,MAChC;AAAA,MACA,MAAM,UAAU;AAId,cAAM,WAAW,gBAAgB,GAAG,eAAe,MAAM,IAAI;AAAA,MAC/D;AAAA,MACA;AAAA,MACA,aAAa;AACX,wBAAgB,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,YAAY,SAAS,MAAM,QAAQ,QAAQ,MAAM,YAAY,CAAC;AAElE,SAAO,oBAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AACvD;AAEO,SAAS,UAAqB;AACnC,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sDAAsD;AAChF,SAAO;AACT;","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":[]}
|
package/dist/auth/LoginForm.js
CHANGED
|
@@ -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) {
|
package/dist/auth/roles.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
function roleForSite(claims, siteId) {
|
|
2
2
|
if (!claims) return null;
|
|
3
3
|
if (claims.superadmin) return "superadmin";
|
|
4
|
-
|
|
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";
|
package/dist/auth/roles.js.map
CHANGED
|
@@ -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
|
|
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,2 +1,2 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
|
-
export declare const accordionBlock: import("
|
|
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(
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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("
|
|
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("
|
|
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("
|
|
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,
|
|
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("
|
|
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;
|