@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/revalidate.ts"],"sourcesContent":["import \"server-only\";\n\nimport { revalidatePath, revalidateTag } from \"next/cache\";\nimport { timingSafeEqual } from \"crypto\";\n\n/**\n * On-demand revalidation-handler (6.5).\n *\n * De backend (Cloud Function `publishPage`) roept deze route aan ná een publicatie, zodat een\n * wijziging binnen SECONDEN live staat — zónder de ISR-timer af te wachten en zónder een volledige\n * Vercel-rebuild (dus nul bouwminuten voor content). Werkt alleen zinvol samen met `XICMS_LIVE_READS=1`\n * (de reader leest dan vers uit Firestore; zie server/pages.ts).\n *\n * WAT DEZE ROUTE NIET KAN: een VERWIJDERDE pagina van de site halen. Gemeten op staging — de ping\n * komt aan en antwoordt 200, maar Next houdt bij een revalidatie die op `notFound()` uitkomt de\n * laatste goede render vast in plaats van hem door een 404 te vervangen. Een verwijderde pagina\n * bleef daardoor uren bereikbaar. Verwijderen leunt dus op de rebuild (`deletePage` zet de\n * dirty-vlag); de ping ruimt wél de lijst-tag op zodat overzichten en sitemaps meteen kloppen.\n *\n * Strategie: naast een eventueel meegegeven `path` invalideren we ALTIJD de root-layout\n * (`revalidatePath(\"/\", \"layout\")`). Dat cascadeert naar élke pagina van de site — settings/thema/nav\n * raken immers elke route. Publicatiefrequentie is laag, dus deze \"brede\" invalidatie is goedkoop:\n * de eerstvolgende bezoeker per pagina leest één keer vers uit Firestore, daarna weer ISR-gecached.\n *\n * Beveiliging: gedeeld geheim in env-var `XICMS_REVALIDATE_SECRET` (constant-tijd vergeleken).\n * Zonder ingesteld geheim staat de route UIT (404) — zo doet een per ongeluk gedeployede route zonder\n * config niets. Het geheim mag via header `x-revalidate-secret` of `?secret=` meekomen.\n */\n\n/** Constant-tijd-vergelijking (voorkomt timing-aanvallen op het geheim). */\nfunction secretsMatch(a: string, b: string): boolean {\n const ba = Buffer.from(a);\n const bb = Buffer.from(b);\n return ba.length === bb.length && timingSafeEqual(ba, bb);\n}\n\nfunction json(body: unknown, status: number): Response {\n return new Response(JSON.stringify(body), { status, headers: { \"Content-Type\": \"application/json\" } });\n}\n\n/**\n * POST-handler voor `app/api/revalidate/route.ts`. Elke klantsite re-exporteert deze functie zodat\n * de logica centraal in de engine leeft (één bron van waarheid, centraal upgradebaar).\n */\nexport async function revalidatePOST(req: Request): Promise<Response> {\n const secret = process.env.XICMS_REVALIDATE_SECRET;\n if (!secret) return json({ ok: false, error: \"disabled\" }, 404);\n\n const url = new URL(req.url);\n const provided = req.headers.get(\"x-revalidate-secret\") ?? url.searchParams.get(\"secret\") ?? \"\";\n if (!secretsMatch(provided, secret)) return json({ ok: false, error: \"unauthorized\" }, 401);\n\n // Optioneel gericht pad en/of cache-tags. Body is optioneel.\n let path: string | undefined;\n let tags: string[] = [];\n try {\n const body = (await req.json()) as { path?: unknown; tags?: unknown } | null;\n if (body && typeof body.path === \"string\" && body.path.startsWith(\"/\")) path = body.path;\n if (body && Array.isArray(body.tags)) {\n // Alleen onze eigen tags accepteren: een open lijst zou een derde in staat stellen\n // willekeurige caches leeg te trekken zodra hij het geheim ooit te pakken krijgt.\n tags = body.tags.filter((t): t is string => typeof t === \"string\" && t.startsWith(\"xicms:\")).slice(0, 50);\n }\n } catch {\n // Geen/ongeldige body → val terug op de brede invalidatie hieronder.\n }\n\n if (tags.length > 0) {\n // GERICHT: alleen wat werkelijk wijzigde. Een tekstwijziging op één pagina laat de caches van\n // alle andere pagina's, de instellingen en de symbolen ongemoeid — die kosten dan ook niets.\n // `expire: 0` en niet een profiel als \"seconds\": het tweede argument van revalidateTag bepaalt\n // hoe lang de oude ingang nog gESERVEERD mag worden. Met een profiel blijft de vorige versie\n // dus nog even staan — precies wat je bij publiceren NIET wilt. Nul betekent: per direct\n // verlopen, dus de eerstvolgende bezoeker haalt vers op.\n //\n // (Next kent `updateTag` voor read-your-own-writes, maar dat mag alleen vanuit een Server\n // Action; deze route is een Route Handler, dus dat is hier geen optie.)\n for (const t of tags) revalidateTag(t, { expire: 0 });\n if (path) revalidatePath(path);\n return json({ ok: true, revalidated: true, tags, path: path ?? null, now: Date.now() }, 200);\n }\n\n // TERUGVAL (oudere backend, of een aanroep zonder tags): het oude, brede gedrag. Correct maar\n // duur — het invalideert élke pagina van de site.\n if (path) revalidatePath(path);\n revalidatePath(\"/\", \"layout\");\n return json({ ok: true, revalidated: true, tags: [], path: path ?? null, now: Date.now() }, 200);\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,gBAAgB,qBAAqB;AAC9C,SAAS,uBAAuB;AA2BhC,SAAS,aAAa,GAAW,GAAoB;AACnD,QAAM,KAAK,OAAO,KAAK,CAAC;AACxB,QAAM,KAAK,OAAO,KAAK,CAAC;AACxB,SAAO,GAAG,WAAW,GAAG,UAAU,gBAAgB,IAAI,EAAE;AAC1D;AAEA,SAAS,KAAK,MAAe,QAA0B;AACrD,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG,EAAE,QAAQ,SAAS,EAAE,gBAAgB,mBAAmB,EAAE,CAAC;AACvG;AAMA,eAAsB,eAAe,KAAiC;AACpE,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,OAAQ,QAAO,KAAK,EAAE,IAAI,OAAO,OAAO,WAAW,GAAG,GAAG;AAE9D,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,WAAW,IAAI,QAAQ,IAAI,qBAAqB,KAAK,IAAI,aAAa,IAAI,QAAQ,KAAK;AAC7F,MAAI,CAAC,aAAa,UAAU,MAAM,EAAG,QAAO,KAAK,EAAE,IAAI,OAAO,OAAO,eAAe,GAAG,GAAG;AAG1F,MAAI;AACJ,MAAI,OAAiB,CAAC;AACtB,MAAI;AACF,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,QAAI,QAAQ,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,WAAW,GAAG,EAAG,QAAO,KAAK;AACpF,QAAI,QAAQ,MAAM,QAAQ,KAAK,IAAI,GAAG;AAGpC,aAAO,KAAK,KAAK,OAAO,CAAC,MAAmB,OAAO,MAAM,YAAY,EAAE,WAAW,QAAQ,CAAC,EAAE,MAAM,GAAG,EAAE;AAAA,IAC1G;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,MAAI,KAAK,SAAS,GAAG;AAUnB,eAAW,KAAK,KAAM,eAAc,GAAG,EAAE,QAAQ,EAAE,CAAC;AACpD,QAAI,KAAM,gBAAe,IAAI;AAC7B,WAAO,KAAK,EAAE,IAAI,MAAM,aAAa,MAAM,MAAM,MAAM,QAAQ,MAAM,KAAK,KAAK,IAAI,EAAE,GAAG,GAAG;AAAA,EAC7F;AAIA,MAAI,KAAM,gBAAe,IAAI;AAC7B,iBAAe,KAAK,QAAQ;AAC5B,SAAO,KAAK,EAAE,IAAI,MAAM,aAAa,MAAM,MAAM,CAAC,GAAG,MAAM,QAAQ,MAAM,KAAK,KAAK,IAAI,EAAE,GAAG,GAAG;AACjG;","names":[]}
|
package/dist/server/seo.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
import type { Metadata, MetadataRoute } from "next";
|
|
3
|
-
import type { Seo } from "../schema/seo";
|
|
4
|
-
import type { Page } from "../schema/pages";
|
|
5
|
-
import { type LocaleConfig } from "../i18n/overlay";
|
|
3
|
+
import type { Seo } from "../schema/seo.js";
|
|
4
|
+
import type { Page } from "../schema/pages.js";
|
|
5
|
+
import { type LocaleConfig } from "../i18n/overlay.js";
|
|
6
6
|
/** Absolute origin (https://domein) uit de site-config; "" als er geen domein is. */
|
|
7
7
|
export declare function siteOrigin(site: {
|
|
8
8
|
domain?: string;
|
package/dist/server/seo.js
CHANGED
package/dist/server/shop.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
import type { Metadata } from "next";
|
|
3
|
-
import { type Product, type ShopCategory } from "../schema/shop";
|
|
3
|
+
import { type Product, type ShopCategory } from "../schema/shop.js";
|
|
4
4
|
/**
|
|
5
5
|
* Server-readers voor de shop (5.6) — zelfde contract als server/pages.ts: Admin SDK als die
|
|
6
6
|
* er is, anders keyless Firestore-REST met de publieke web-apiKey (de rules staan publieke
|
|
@@ -17,8 +17,8 @@ export type ServerProduct = Product & {
|
|
|
17
17
|
export type ServerShopCategory = ShopCategory & {
|
|
18
18
|
id: string;
|
|
19
19
|
};
|
|
20
|
-
export type { ShopPageData } from "../modules/shop/types";
|
|
21
|
-
import type { ShopPageData } from "../modules/shop/types";
|
|
20
|
+
export type { ShopPageData } from "../modules/shop/types.js";
|
|
21
|
+
import type { ShopPageData } from "../modules/shop/types.js";
|
|
22
22
|
declare function getActiveProductsUncached(siteId: string): Promise<ServerProduct[]>;
|
|
23
23
|
declare function getShopCategoriesUncached(siteId: string): Promise<ServerShopCategory[]>;
|
|
24
24
|
declare function getProductBySlugUncached(siteId: string, slug: string): Promise<ServerProduct | null>;
|
package/dist/server/shop.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
import { cache } from "react";
|
|
3
|
-
import { getAdminDb } from "../firebase/admin";
|
|
3
|
+
import { getAdminDb } from "../firebase/admin.js";
|
|
4
4
|
import {
|
|
5
5
|
ProductSchema,
|
|
6
6
|
ShopCategorySchema,
|
|
7
7
|
productPriceCents
|
|
8
|
-
} from "../schema/shop";
|
|
8
|
+
} from "../schema/shop.js";
|
|
9
9
|
function parseProduct(id, data) {
|
|
10
10
|
const res = ProductSchema.safeParse(data);
|
|
11
11
|
if (!res.success) {
|
|
@@ -54,7 +54,10 @@ async function restQuery(siteId, collectionId, filters, limit) {
|
|
|
54
54
|
method: "POST",
|
|
55
55
|
headers: { "Content-Type": "application/json" },
|
|
56
56
|
body: JSON.stringify({ structuredQuery }),
|
|
57
|
-
|
|
57
|
+
// Zelfde vangnet-logica als pages.ts: de tag is het normale pad, de timer dekt een
|
|
58
|
+
// gemiste revalidate-push. Shop-content wijzigt zelden; elke 5 minuten opnieuw tellen was
|
|
59
|
+
// pure verspilling.
|
|
60
|
+
next: { revalidate: Number(process.env.XICMS_ISR_FALLBACK) || 3600, tags: [`xicms:${siteId}:${collectionId}`] }
|
|
58
61
|
});
|
|
59
62
|
if (!res.ok) return [];
|
|
60
63
|
const rows = await res.json();
|
package/dist/server/shop.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/shop.ts"],"sourcesContent":["import \"server-only\";\n\nimport { cache } from \"react\";\nimport type { Metadata } from \"next\";\n\nimport { getAdminDb } from \"../firebase/admin\";\nimport {\n ProductSchema,\n ShopCategorySchema,\n productPriceCents,\n type Product,\n type ShopCategory,\n} from \"../schema/shop\";\n\n/**\n * Server-readers voor de shop (5.6) — zelfde contract als server/pages.ts: Admin SDK als die\n * er is, anders keyless Firestore-REST met de publieke web-apiKey (de rules staan publieke\n * reads op actieve producten/categorieën toe). Reads zijn per-request gededupt via cache();\n * route-level ISR (`revalidate`) dempt de kosten tussen requests.\n *\n * Gebruik in de klantsite (catch-all):\n * const blockData = { shop: await getShopPageData(siteId, path, searchParams) };\n * <PublishedPage page={page} siteId={siteId} blockData={blockData} />\n */\n\nexport type ServerProduct = Product & { id: string };\nexport type ServerShopCategory = ShopCategory & { id: string };\n\n// Gedeelde vorm met de blocks (useBlockData(\"shop\")) — één bron van waarheid.\nexport type { ShopPageData } from \"../modules/shop/types\";\nimport type { ShopPageData } from \"../modules/shop/types\";\n\nfunction parseProduct(id: string, data: unknown): ServerProduct | null {\n const res = ProductSchema.safeParse(data);\n if (!res.success) {\n console.warn(`[xicms] product \"${id}\" faalt schema-validatie en wordt overgeslagen:`, res.error.issues[0]);\n return null;\n }\n return { id, ...res.data };\n}\n\nfunction parseCategory(id: string, data: unknown): ServerShopCategory | null {\n const res = ShopCategorySchema.safeParse(data);\n if (!res.success) {\n console.warn(`[xicms] categorie \"${id}\" faalt schema-validatie en wordt overgeslagen:`, res.error.issues[0]);\n return null;\n }\n return { id, ...res.data };\n}\n\n/* ── keyless REST (zelfde aanpak als server/pages.ts) ────────────────────── */\n\ntype FsValue = Record<string, unknown>;\nfunction decode(v: FsValue): unknown {\n if (v == null) return null;\n if (\"stringValue\" in v) return v.stringValue;\n if (\"integerValue\" in v) return Number(v.integerValue);\n if (\"doubleValue\" in v) return v.doubleValue;\n if (\"booleanValue\" in v) return v.booleanValue;\n if (\"nullValue\" in v) return null;\n if (\"timestampValue\" in v) return v.timestampValue;\n if (\"arrayValue\" in v) return (((v.arrayValue as FsValue)?.values as FsValue[]) ?? []).map(decode);\n if (\"mapValue\" in v) return decodeFields(((v.mapValue as FsValue)?.fields as Record<string, FsValue>) ?? {});\n return null;\n}\nfunction decodeFields(fields: Record<string, FsValue>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).map(([k, x]) => [k, decode(x)]));\n}\n\ninterface RestDoc {\n id: string;\n data: Record<string, unknown>;\n}\n\nasync function restQuery(\n siteId: string,\n collectionId: string,\n filters: { fieldPath: string; value: string }[],\n limit?: number,\n): Promise<RestDoc[]> {\n const apiKey = process.env.NEXT_PUBLIC_FIREBASE_API_KEY;\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;\n if (!apiKey || !projectId) return [];\n const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/sites/${siteId}:runQuery?key=${apiKey}`;\n const fieldFilters = filters.map((f) => ({\n fieldFilter: { field: { fieldPath: f.fieldPath }, op: \"EQUAL\", value: { stringValue: f.value } },\n }));\n const where =\n fieldFilters.length === 0\n ? undefined\n : fieldFilters.length === 1\n ? fieldFilters[0]\n : { compositeFilter: { op: \"AND\", filters: fieldFilters } };\n const structuredQuery: Record<string, unknown> = { from: [{ collectionId }] };\n if (where) structuredQuery.where = where;\n if (limit) structuredQuery.limit = limit;\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ structuredQuery }),\n next: { revalidate: 300 },\n });\n if (!res.ok) return [];\n const rows = (await res.json()) as { document?: { name: string; fields: Record<string, FsValue> } }[];\n return rows\n .filter((r) => r.document)\n .map((r) => ({ id: r.document!.name.split(\"/\").pop()!, data: decodeFields(r.document!.fields ?? {}) }));\n } catch (err) {\n console.error(\"[xicms] Firestore REST-query (shop) mislukt:\", err);\n return [];\n }\n}\n\n/* ── Readers ─────────────────────────────────────────────────────────────── */\n\nasync function getActiveProductsUncached(siteId: string): Promise<ServerProduct[]> {\n const adb = getAdminDb();\n let docs: RestDoc[];\n if (adb) {\n try {\n const snap = await adb.collection(`sites/${siteId}/products`).where(\"status\", \"==\", \"active\").limit(500).get();\n docs = snap.docs.map((d) => ({ id: d.id, data: d.data() }));\n } catch (e) {\n console.warn(\"[xicms] Admin-read producten mislukt — lege lijst:\", (e as Error)?.message ?? e);\n docs = [];\n }\n } else {\n docs = await restQuery(siteId, \"products\", [{ fieldPath: \"status\", value: \"active\" }], 500);\n }\n return docs\n .map((d) => parseProduct(d.id, d.data))\n .filter((p): p is ServerProduct => p !== null)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.name.localeCompare(b.name, \"nl\"));\n}\n\nasync function getShopCategoriesUncached(siteId: string): Promise<ServerShopCategory[]> {\n const adb = getAdminDb();\n let docs: RestDoc[];\n if (adb) {\n try {\n const snap = await adb.collection(`sites/${siteId}/shopCategories`).limit(100).get();\n docs = snap.docs.map((d) => ({ id: d.id, data: d.data() }));\n } catch (e) {\n console.warn(\"[xicms] Admin-read categorieën mislukt — lege lijst:\", (e as Error)?.message ?? e);\n docs = [];\n }\n } else {\n docs = await restQuery(siteId, \"shopCategories\", [], 100);\n }\n return docs\n .map((d) => parseCategory(d.id, d.data))\n .filter((c): c is ServerShopCategory => c !== null && c.visible !== false)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n}\n\nasync function getProductBySlugUncached(siteId: string, slug: string): Promise<ServerProduct | null> {\n if (!slug) return null;\n const adb = getAdminDb();\n let docs: RestDoc[];\n if (adb) {\n try {\n const snap = await adb\n .collection(`sites/${siteId}/products`)\n .where(\"status\", \"==\", \"active\")\n .where(\"slug\", \"==\", slug)\n .limit(1)\n .get();\n docs = snap.docs.map((d) => ({ id: d.id, data: d.data() }));\n } catch (e) {\n console.warn(\"[xicms] Admin-read product mislukt:\", (e as Error)?.message ?? e);\n docs = [];\n }\n } else {\n docs = await restQuery(\n siteId,\n \"products\",\n [\n { fieldPath: \"status\", value: \"active\" },\n { fieldPath: \"slug\", value: slug },\n ],\n 1,\n );\n }\n const d = docs[0];\n return d ? parseProduct(d.id, d.data) : null;\n}\n\nexport const getActiveProducts = cache(getActiveProductsUncached);\nexport const getShopCategories = cache(getShopCategoriesUncached);\nexport const getProductBySlug = cache(getProductBySlugUncached);\n\n/**\n * Verzamelt de shop-startdata voor één pagina. `searchParams.p` (detailpagina) bepaalt of er\n * ook een enkel product geladen wordt. Geef het resultaat door als\n * `<PublishedPage blockData={{ shop: data }}>`.\n */\nexport async function getShopPageData(\n siteId: string,\n searchParams?: { p?: string | string[]; [k: string]: unknown },\n): Promise<ShopPageData> {\n const slugParam = searchParams?.p;\n const slug = Array.isArray(slugParam) ? slugParam[0] : slugParam;\n const [products, categories, product] = await Promise.all([\n getActiveProducts(siteId),\n getShopCategories(siteId),\n slug ? getProductBySlug(siteId, slug) : Promise.resolve(undefined),\n ]);\n const data: ShopPageData = { products, categories };\n if (slug !== undefined) data.product = product ?? null;\n return data;\n}\n\n/* ── SEO ─────────────────────────────────────────────────────────────────── */\n\n/** Metadata voor een productdetailpagina (title/description/OG-beeld). */\nexport function buildProductMetadata(\n product: ServerProduct | null,\n site: { name: string; domain?: string },\n): Metadata {\n if (!product) return { title: `Product — ${site.name}` };\n const description = (product.description || `${product.name} — ${site.name}`)\n .replace(/\\s+/g, \" \")\n .slice(0, 300);\n const image = product.images?.[0];\n return {\n title: `${product.name} — ${site.name}`,\n description,\n openGraph: {\n title: product.name,\n description,\n type: \"website\",\n ...(image ? { images: [{ url: image }] } : {}),\n },\n };\n}\n\n/** schema.org Product/Offer-JSON-LD met echte prijs en beschikbaarheid. */\nexport function buildProductJsonLd(\n product: ServerProduct,\n site: { name: string; domain?: string },\n currency = \"EUR\",\n): Record<string, unknown> {\n const soldOut =\n product.trackStock === true &&\n (product.options ?? []).length === 0 &&\n product.stock !== undefined &&\n product.stock <= 0;\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Product\",\n name: product.name,\n ...(product.description ? { description: product.description.slice(0, 500) } : {}),\n ...(product.images?.length ? { image: product.images } : {}),\n offers: {\n \"@type\": \"Offer\",\n price: (productPriceCents(product) / 100).toFixed(2),\n priceCurrency: currency,\n availability: soldOut ? \"https://schema.org/OutOfStock\" : \"https://schema.org/InStock\",\n ...(site.domain ? { url: `https://${site.domain}/product?p=${encodeURIComponent(product.slug)}` } : {}),\n },\n brand: { \"@type\": \"Brand\", name: site.name },\n };\n}\n"],"mappings":"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","names":["d"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/shop.ts"],"sourcesContent":["import \"server-only\";\n\nimport { cache } from \"react\";\nimport type { Metadata } from \"next\";\n\nimport { getAdminDb } from \"../firebase/admin\";\nimport {\n ProductSchema,\n ShopCategorySchema,\n productPriceCents,\n type Product,\n type ShopCategory,\n} from \"../schema/shop\";\n\n/**\n * Server-readers voor de shop (5.6) — zelfde contract als server/pages.ts: Admin SDK als die\n * er is, anders keyless Firestore-REST met de publieke web-apiKey (de rules staan publieke\n * reads op actieve producten/categorieën toe). Reads zijn per-request gededupt via cache();\n * route-level ISR (`revalidate`) dempt de kosten tussen requests.\n *\n * Gebruik in de klantsite (catch-all):\n * const blockData = { shop: await getShopPageData(siteId, path, searchParams) };\n * <PublishedPage page={page} siteId={siteId} blockData={blockData} />\n */\n\nexport type ServerProduct = Product & { id: string };\nexport type ServerShopCategory = ShopCategory & { id: string };\n\n// Gedeelde vorm met de blocks (useBlockData(\"shop\")) — één bron van waarheid.\nexport type { ShopPageData } from \"../modules/shop/types\";\nimport type { ShopPageData } from \"../modules/shop/types\";\n\nfunction parseProduct(id: string, data: unknown): ServerProduct | null {\n const res = ProductSchema.safeParse(data);\n if (!res.success) {\n console.warn(`[xicms] product \"${id}\" faalt schema-validatie en wordt overgeslagen:`, res.error.issues[0]);\n return null;\n }\n return { id, ...res.data };\n}\n\nfunction parseCategory(id: string, data: unknown): ServerShopCategory | null {\n const res = ShopCategorySchema.safeParse(data);\n if (!res.success) {\n console.warn(`[xicms] categorie \"${id}\" faalt schema-validatie en wordt overgeslagen:`, res.error.issues[0]);\n return null;\n }\n return { id, ...res.data };\n}\n\n/* ── keyless REST (zelfde aanpak als server/pages.ts) ────────────────────── */\n\ntype FsValue = Record<string, unknown>;\nfunction decode(v: FsValue): unknown {\n if (v == null) return null;\n if (\"stringValue\" in v) return v.stringValue;\n if (\"integerValue\" in v) return Number(v.integerValue);\n if (\"doubleValue\" in v) return v.doubleValue;\n if (\"booleanValue\" in v) return v.booleanValue;\n if (\"nullValue\" in v) return null;\n if (\"timestampValue\" in v) return v.timestampValue;\n if (\"arrayValue\" in v) return (((v.arrayValue as FsValue)?.values as FsValue[]) ?? []).map(decode);\n if (\"mapValue\" in v) return decodeFields(((v.mapValue as FsValue)?.fields as Record<string, FsValue>) ?? {});\n return null;\n}\nfunction decodeFields(fields: Record<string, FsValue>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).map(([k, x]) => [k, decode(x)]));\n}\n\ninterface RestDoc {\n id: string;\n data: Record<string, unknown>;\n}\n\nasync function restQuery(\n siteId: string,\n collectionId: string,\n filters: { fieldPath: string; value: string }[],\n limit?: number,\n): Promise<RestDoc[]> {\n const apiKey = process.env.NEXT_PUBLIC_FIREBASE_API_KEY;\n const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID;\n if (!apiKey || !projectId) return [];\n const url = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/sites/${siteId}:runQuery?key=${apiKey}`;\n const fieldFilters = filters.map((f) => ({\n fieldFilter: { field: { fieldPath: f.fieldPath }, op: \"EQUAL\", value: { stringValue: f.value } },\n }));\n const where =\n fieldFilters.length === 0\n ? undefined\n : fieldFilters.length === 1\n ? fieldFilters[0]\n : { compositeFilter: { op: \"AND\", filters: fieldFilters } };\n const structuredQuery: Record<string, unknown> = { from: [{ collectionId }] };\n if (where) structuredQuery.where = where;\n if (limit) structuredQuery.limit = limit;\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ structuredQuery }),\n // Zelfde vangnet-logica als pages.ts: de tag is het normale pad, de timer dekt een\n // gemiste revalidate-push. Shop-content wijzigt zelden; elke 5 minuten opnieuw tellen was\n // pure verspilling.\n next: { revalidate: Number(process.env.XICMS_ISR_FALLBACK) || 3600, tags: [`xicms:${siteId}:${collectionId}`] },\n });\n if (!res.ok) return [];\n const rows = (await res.json()) as { document?: { name: string; fields: Record<string, FsValue> } }[];\n return rows\n .filter((r) => r.document)\n .map((r) => ({ id: r.document!.name.split(\"/\").pop()!, data: decodeFields(r.document!.fields ?? {}) }));\n } catch (err) {\n console.error(\"[xicms] Firestore REST-query (shop) mislukt:\", err);\n return [];\n }\n}\n\n/* ── Readers ─────────────────────────────────────────────────────────────── */\n\nasync function getActiveProductsUncached(siteId: string): Promise<ServerProduct[]> {\n const adb = getAdminDb();\n let docs: RestDoc[];\n if (adb) {\n try {\n const snap = await adb.collection(`sites/${siteId}/products`).where(\"status\", \"==\", \"active\").limit(500).get();\n docs = snap.docs.map((d) => ({ id: d.id, data: d.data() }));\n } catch (e) {\n console.warn(\"[xicms] Admin-read producten mislukt — lege lijst:\", (e as Error)?.message ?? e);\n docs = [];\n }\n } else {\n docs = await restQuery(siteId, \"products\", [{ fieldPath: \"status\", value: \"active\" }], 500);\n }\n return docs\n .map((d) => parseProduct(d.id, d.data))\n .filter((p): p is ServerProduct => p !== null)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.name.localeCompare(b.name, \"nl\"));\n}\n\nasync function getShopCategoriesUncached(siteId: string): Promise<ServerShopCategory[]> {\n const adb = getAdminDb();\n let docs: RestDoc[];\n if (adb) {\n try {\n const snap = await adb.collection(`sites/${siteId}/shopCategories`).limit(100).get();\n docs = snap.docs.map((d) => ({ id: d.id, data: d.data() }));\n } catch (e) {\n console.warn(\"[xicms] Admin-read categorieën mislukt — lege lijst:\", (e as Error)?.message ?? e);\n docs = [];\n }\n } else {\n docs = await restQuery(siteId, \"shopCategories\", [], 100);\n }\n return docs\n .map((d) => parseCategory(d.id, d.data))\n .filter((c): c is ServerShopCategory => c !== null && c.visible !== false)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n}\n\nasync function getProductBySlugUncached(siteId: string, slug: string): Promise<ServerProduct | null> {\n if (!slug) return null;\n const adb = getAdminDb();\n let docs: RestDoc[];\n if (adb) {\n try {\n const snap = await adb\n .collection(`sites/${siteId}/products`)\n .where(\"status\", \"==\", \"active\")\n .where(\"slug\", \"==\", slug)\n .limit(1)\n .get();\n docs = snap.docs.map((d) => ({ id: d.id, data: d.data() }));\n } catch (e) {\n console.warn(\"[xicms] Admin-read product mislukt:\", (e as Error)?.message ?? e);\n docs = [];\n }\n } else {\n docs = await restQuery(\n siteId,\n \"products\",\n [\n { fieldPath: \"status\", value: \"active\" },\n { fieldPath: \"slug\", value: slug },\n ],\n 1,\n );\n }\n const d = docs[0];\n return d ? parseProduct(d.id, d.data) : null;\n}\n\nexport const getActiveProducts = cache(getActiveProductsUncached);\nexport const getShopCategories = cache(getShopCategoriesUncached);\nexport const getProductBySlug = cache(getProductBySlugUncached);\n\n/**\n * Verzamelt de shop-startdata voor één pagina. `searchParams.p` (detailpagina) bepaalt of er\n * ook een enkel product geladen wordt. Geef het resultaat door als\n * `<PublishedPage blockData={{ shop: data }}>`.\n */\nexport async function getShopPageData(\n siteId: string,\n searchParams?: { p?: string | string[]; [k: string]: unknown },\n): Promise<ShopPageData> {\n const slugParam = searchParams?.p;\n const slug = Array.isArray(slugParam) ? slugParam[0] : slugParam;\n const [products, categories, product] = await Promise.all([\n getActiveProducts(siteId),\n getShopCategories(siteId),\n slug ? getProductBySlug(siteId, slug) : Promise.resolve(undefined),\n ]);\n const data: ShopPageData = { products, categories };\n if (slug !== undefined) data.product = product ?? null;\n return data;\n}\n\n/* ── SEO ─────────────────────────────────────────────────────────────────── */\n\n/** Metadata voor een productdetailpagina (title/description/OG-beeld). */\nexport function buildProductMetadata(\n product: ServerProduct | null,\n site: { name: string; domain?: string },\n): Metadata {\n if (!product) return { title: `Product — ${site.name}` };\n const description = (product.description || `${product.name} — ${site.name}`)\n .replace(/\\s+/g, \" \")\n .slice(0, 300);\n const image = product.images?.[0];\n return {\n title: `${product.name} — ${site.name}`,\n description,\n openGraph: {\n title: product.name,\n description,\n type: \"website\",\n ...(image ? { images: [{ url: image }] } : {}),\n },\n };\n}\n\n/** schema.org Product/Offer-JSON-LD met echte prijs en beschikbaarheid. */\nexport function buildProductJsonLd(\n product: ServerProduct,\n site: { name: string; domain?: string },\n currency = \"EUR\",\n): Record<string, unknown> {\n const soldOut =\n product.trackStock === true &&\n (product.options ?? []).length === 0 &&\n product.stock !== undefined &&\n product.stock <= 0;\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Product\",\n name: product.name,\n ...(product.description ? { description: product.description.slice(0, 500) } : {}),\n ...(product.images?.length ? { image: product.images } : {}),\n offers: {\n \"@type\": \"Offer\",\n price: (productPriceCents(product) / 100).toFixed(2),\n priceCurrency: currency,\n availability: soldOut ? \"https://schema.org/OutOfStock\" : \"https://schema.org/InStock\",\n ...(site.domain ? { url: `https://${site.domain}/product?p=${encodeURIComponent(product.slug)}` } : {}),\n },\n brand: { \"@type\": \"Brand\", name: site.name },\n };\n}\n"],"mappings":"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","names":["d"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import { type PublicSiteSettings } from "../schema/settings";
|
|
3
|
-
import type { SiteConfig } from "../schema/siteConfig";
|
|
2
|
+
import { type PublicSiteSettings } from "../schema/settings.js";
|
|
3
|
+
import type { SiteConfig } from "../schema/siteConfig.js";
|
|
4
4
|
/**
|
|
5
5
|
* Live, publiek-veilige site-instellingen (`sites/{siteId}/settings/public`) zodat dingen die de
|
|
6
6
|
* klant in /beheer wijzigt (footer, nav, thema, module-instellingen) direct op de site verschijnen.
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import { getDb } from "../firebase/client";
|
|
6
|
-
import { useSiteConfig } from "../config";
|
|
4
|
+
import { useSiteConfig } from "../config.js";
|
|
7
5
|
import {
|
|
8
6
|
PublicSiteSettingsSchema,
|
|
9
7
|
mergeThemeTokens
|
|
10
|
-
} from "../schema/settings";
|
|
8
|
+
} from "../schema/settings.js";
|
|
11
9
|
const SettingsContext = createContext({ settings: {}, loaded: false });
|
|
12
10
|
function SiteSettingsProvider({
|
|
13
11
|
siteId,
|
|
@@ -20,28 +18,38 @@ function SiteSettingsProvider({
|
|
|
20
18
|
const [loaded, setLoaded] = useState(hasInitial);
|
|
21
19
|
useEffect(() => {
|
|
22
20
|
if (hasInitial && !live) return;
|
|
23
|
-
const db = getDb();
|
|
24
|
-
if (!db) {
|
|
25
|
-
setLoaded(true);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const ref = doc(db, "sites", siteId, "settings", "public");
|
|
29
21
|
const apply = (data) => {
|
|
30
22
|
const res = PublicSiteSettingsSchema.safeParse(data ?? {});
|
|
31
23
|
if (res.success) setSettings(res.data);
|
|
32
24
|
setLoaded(true);
|
|
33
25
|
};
|
|
34
|
-
if (live) {
|
|
35
|
-
return onSnapshot(
|
|
36
|
-
ref,
|
|
37
|
-
(snap) => apply(snap.exists() ? snap.data() : {}),
|
|
38
|
-
() => setLoaded(true)
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
26
|
let cancelled = false;
|
|
42
|
-
|
|
27
|
+
let unsub;
|
|
28
|
+
void (async () => {
|
|
29
|
+
const [{ getDb }, { doc, getDoc, onSnapshot }] = await Promise.all([
|
|
30
|
+
import("../firebase/client.js"),
|
|
31
|
+
import("firebase/firestore")
|
|
32
|
+
]);
|
|
33
|
+
const db = getDb();
|
|
34
|
+
if (!db || cancelled) {
|
|
35
|
+
setLoaded(true);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const ref = doc(db, "sites", siteId, "settings", "public");
|
|
39
|
+
if (live) {
|
|
40
|
+
unsub = onSnapshot(ref, (snap) => apply(snap.exists() ? snap.data() : {}), () => setLoaded(true));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const snap = await getDoc(ref);
|
|
45
|
+
if (!cancelled) apply(snap.exists() ? snap.data() : {});
|
|
46
|
+
} catch {
|
|
47
|
+
if (!cancelled) setLoaded(true);
|
|
48
|
+
}
|
|
49
|
+
})();
|
|
43
50
|
return () => {
|
|
44
51
|
cancelled = true;
|
|
52
|
+
unsub?.();
|
|
45
53
|
};
|
|
46
54
|
}, [siteId, live, hasInitial]);
|
|
47
55
|
const value = useMemo(() => ({ settings, loaded, _set: setSettings }), [settings, loaded]);
|
|
@@ -54,16 +62,28 @@ function useSiteSettingsLiveSync(siteId, enabled) {
|
|
|
54
62
|
const { _set } = useContext(SettingsContext);
|
|
55
63
|
useEffect(() => {
|
|
56
64
|
if (!enabled || !_set) return;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
(
|
|
66
|
-
|
|
65
|
+
let unsub;
|
|
66
|
+
let afgebroken = false;
|
|
67
|
+
void (async () => {
|
|
68
|
+
const [{ getDb }, { doc, onSnapshot }] = await Promise.all([
|
|
69
|
+
import("../firebase/client.js"),
|
|
70
|
+
import("firebase/firestore")
|
|
71
|
+
]);
|
|
72
|
+
const db = getDb();
|
|
73
|
+
if (!db || afgebroken) return;
|
|
74
|
+
unsub = onSnapshot(
|
|
75
|
+
doc(db, "sites", siteId, "settings", "public"),
|
|
76
|
+
(snap) => {
|
|
77
|
+
const res = PublicSiteSettingsSchema.safeParse(snap.exists() ? snap.data() : {});
|
|
78
|
+
if (res.success) _set(res.data);
|
|
79
|
+
},
|
|
80
|
+
() => void 0
|
|
81
|
+
);
|
|
82
|
+
})();
|
|
83
|
+
return () => {
|
|
84
|
+
afgebroken = true;
|
|
85
|
+
unsub?.();
|
|
86
|
+
};
|
|
67
87
|
}, [enabled, siteId, _set]);
|
|
68
88
|
}
|
|
69
89
|
function useMergedSiteConfig() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/settings/useSiteSettings.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/settings/useSiteSettings.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport { useSiteConfig } from \"../config\";\nimport {\n PublicSiteSettingsSchema,\n mergeThemeTokens,\n type PublicSiteSettings,\n} from \"../schema/settings\";\nimport type { SiteConfig } from \"../schema/siteConfig\";\n\n/**\n * Live, publiek-veilige site-instellingen (`sites/{siteId}/settings/public`) zodat dingen die de\n * klant in /beheer wijzigt (footer, nav, thema, module-instellingen) direct op de site verschijnen.\n *\n * - Met SSR-seed (`initial`, via XiCMSProvider `settingsData`): géén client-read voor publiek —\n * de settings zitten al in de server-HTML (geen theme-flits) en verversen via ISR.\n * - Zonder seed (publiek): één `getDoc` (geen per-bezoeker listener).\n * - Editors: `useSiteSettingsLiveSync` (onSnapshot) zodat /beheer-wijzigingen meteen zichtbaar zijn.\n * Zonder Firebase of zonder doc valt alles terug op de statische `SiteConfig` (identiek aan vroeger).\n */\ninterface Ctx {\n settings: PublicSiteSettings;\n loaded: boolean;\n /** Interne setter voor de editor-live-sync (useSiteSettingsLiveSync). */\n _set?: (s: PublicSiteSettings) => void;\n}\nconst SettingsContext = createContext<Ctx>({ settings: {}, loaded: false });\n\nexport function SiteSettingsProvider({\n siteId,\n live = false,\n initial,\n children,\n}: {\n siteId: string;\n live?: boolean;\n /** SSR-startset (server-side gelezen via `getPublicSiteSettings`); slaat de client-read over. */\n initial?: PublicSiteSettings;\n children: ReactNode;\n}) {\n const hasInitial = initial !== undefined;\n const [settings, setSettings] = useState<PublicSiteSettings>(initial ?? {});\n const [loaded, setLoaded] = useState(hasInitial);\n\n useEffect(() => {\n // Met een SSR-seed doet de publieke site géén client-read meer: de layout-route is ISR-gecached\n // en levert verse settings per revalidate; editors krijgen apart een live sync.\n if (hasInitial && !live) return;\n const apply = (data: unknown) => {\n const res = PublicSiteSettingsSchema.safeParse(data ?? {});\n if (res.success) setSettings(res.data);\n setLoaded(true);\n };\n // Firestore DYNAMISCH: met een SSR-seed komt de publieke site hier niet eens, en een bezoeker\n // hoort de SDK nooit te downloaden. Statisch importeren zette hem in de bundel van elke pagina.\n let cancelled = false;\n let unsub: (() => void) | undefined;\n void (async () => {\n const [{ getDb }, { doc, getDoc, onSnapshot }] = await Promise.all([\n import(\"../firebase/client\"),\n import(\"firebase/firestore\"),\n ]);\n const db = getDb();\n if (!db || cancelled) {\n setLoaded(true);\n return;\n }\n const ref = doc(db, \"sites\", siteId, \"settings\", \"public\");\n if (live) {\n unsub = onSnapshot(ref, (snap) => apply(snap.exists() ? snap.data() : {}), () => setLoaded(true));\n return;\n }\n try {\n const snap = await getDoc(ref);\n if (!cancelled) apply(snap.exists() ? snap.data() : {});\n } catch {\n if (!cancelled) setLoaded(true);\n }\n })();\n return () => {\n cancelled = true;\n unsub?.();\n };\n }, [siteId, live, hasInitial]);\n\n const value = useMemo<Ctx>(() => ({ settings, loaded, _set: setSettings }), [settings, loaded]);\n return <SettingsContext.Provider value={value}>{children}</SettingsContext.Provider>;\n}\n\n/** Ruwe publieke settings + laadstatus. */\nexport function useSiteSettings(): Ctx {\n return useContext(SettingsContext);\n}\n\n/**\n * Editor-live-sync: onSnapshot op `settings/public` zolang `enabled` (ingelogde editor). Aparte\n * hook (i.p.v. `live` op de provider) zodat publiek nooit een listener krijgt — zelfde patroon\n * als CustomBlocksSync in XiCMSProvider. Een leesfout laat de bestaande settings staan.\n */\nexport function useSiteSettingsLiveSync(siteId: string, enabled: boolean) {\n const { _set } = useContext(SettingsContext);\n useEffect(() => {\n // Alleen editors (enabled) krijgen een listener, en pas dán laadt de SDK.\n if (!enabled || !_set) return;\n let unsub: (() => void) | undefined;\n let afgebroken = false;\n void (async () => {\n const [{ getDb }, { doc, onSnapshot }] = await Promise.all([\n import(\"../firebase/client\"),\n import(\"firebase/firestore\"),\n ]);\n const db = getDb();\n if (!db || afgebroken) return;\n unsub = onSnapshot(\n doc(db, \"sites\", siteId, \"settings\", \"public\"),\n (snap) => {\n const res = PublicSiteSettingsSchema.safeParse(snap.exists() ? snap.data() : {});\n if (res.success) _set(res.data);\n },\n () => undefined,\n );\n })();\n return () => {\n afgebroken = true;\n unsub?.();\n };\n }, [enabled, siteId, _set]);\n}\n\n/** SiteConfig met de live Firestore-settings eroverheen (Firestore wint waar gezet). */\nexport function useMergedSiteConfig(): SiteConfig {\n const base = useSiteConfig();\n const { settings } = useSiteSettings();\n return useMemo(() => {\n const nav = settings.nav && settings.nav.length > 0 ? settings.nav : base.nav;\n return {\n ...base,\n name: settings.name ?? base.name,\n nav,\n theme: mergeThemeTokens(base.theme, settings.theme),\n seoDefaults: settings.seoDefaults ?? base.seoDefaults,\n };\n }, [base, settings]);\n}\n"],"mappings":"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","names":[]}
|
package/dist/styles.css
CHANGED
|
@@ -1346,7 +1346,10 @@
|
|
|
1346
1346
|
text-decoration: underline;
|
|
1347
1347
|
}
|
|
1348
1348
|
.xicms-hours__badge {
|
|
1349
|
-
|
|
1349
|
+
/* inline-flex + gap: de statusstip is nu een eigen element naast de tekst, geen teken erin. */
|
|
1350
|
+
display: inline-flex;
|
|
1351
|
+
align-items: center;
|
|
1352
|
+
gap: 0.4rem;
|
|
1350
1353
|
margin: 0.15rem 0 0.6rem;
|
|
1351
1354
|
padding: 0.2rem 0.65rem;
|
|
1352
1355
|
border-radius: 999px;
|
package/dist/theme/presets.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ThemeTokens } from "../schema/settings";
|
|
1
|
+
import type { ThemeTokens } from "../schema/settings.js";
|
|
2
2
|
/**
|
|
3
3
|
* Kant-en-klare thema-presets (kleur + lettertype + afronding) waarmee een agent of editor in één
|
|
4
4
|
* keuze een verzorgde huisstijl neerzet. Pas daarna gerust losse tokens aan. De `fontUrl` is een
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiwebdesign/xicms-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "XiCMS engine: gestructureerde content, pagina's, draft/publish, admin (TipTap+Radix), SEO en beveiliging voor klantsites.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
"types": "./dist/server/shop.d.ts",
|
|
41
41
|
"import": "./dist/server/shop.js"
|
|
42
42
|
},
|
|
43
|
+
"./server/auth": {
|
|
44
|
+
"types": "./dist/server/auth.d.ts",
|
|
45
|
+
"import": "./dist/server/auth.js"
|
|
46
|
+
},
|
|
43
47
|
"./all": {
|
|
44
48
|
"types": "./dist/all.d.ts",
|
|
45
49
|
"import": "./dist/all.js"
|
|
@@ -76,13 +80,13 @@
|
|
|
76
80
|
"access": "public"
|
|
77
81
|
},
|
|
78
82
|
"scripts": {
|
|
79
|
-
"build": "tsup && tsc -p tsconfig.build.json && tailwindcss -
|
|
83
|
+
"build": "tsup && tsc -p tsconfig.build.json && tailwindcss -i src/admin/admin.css -o dist/admin.css --minify && node ./scripts/copy-css.mjs && node ./scripts/add-extensions.mjs",
|
|
80
84
|
"typecheck": "tsc --noEmit",
|
|
81
85
|
"test": "vitest run",
|
|
82
86
|
"prepublishOnly": "npm run build"
|
|
83
87
|
},
|
|
84
88
|
"peerDependencies": {
|
|
85
|
-
"firebase": "^
|
|
89
|
+
"firebase": "^12.0.0",
|
|
86
90
|
"next": "^15 || ^16",
|
|
87
91
|
"react": "^18 || ^19",
|
|
88
92
|
"react-dom": "^18 || ^19"
|
|
@@ -93,43 +97,40 @@
|
|
|
93
97
|
}
|
|
94
98
|
},
|
|
95
99
|
"dependencies": {
|
|
96
|
-
"@astryxdesign/core": "^0.
|
|
97
|
-
"@astryxdesign/theme-neutral": "^0.
|
|
98
|
-
"@radix-ui/react-
|
|
99
|
-
"@
|
|
100
|
-
"@
|
|
101
|
-
"@
|
|
102
|
-
"@
|
|
103
|
-
"@
|
|
104
|
-
"@
|
|
105
|
-
"@radix-ui/react-tooltip": "^1.2.12",
|
|
106
|
-
"@tiptap/extension-image": "^2.11.0",
|
|
107
|
-
"@tiptap/extension-link": "^2.11.0",
|
|
108
|
-
"@tiptap/extension-text-style": "^2.27.2",
|
|
109
|
-
"@tiptap/extension-underline": "^2.27.2",
|
|
110
|
-
"@tiptap/pm": "^2.11.0",
|
|
111
|
-
"@tiptap/react": "^2.11.0",
|
|
112
|
-
"@tiptap/starter-kit": "^2.11.0",
|
|
100
|
+
"@astryxdesign/core": "^0.4.2",
|
|
101
|
+
"@astryxdesign/theme-neutral": "^0.4.2",
|
|
102
|
+
"@radix-ui/react-tabs": "^1.1.21",
|
|
103
|
+
"@tiptap/extension-image": "^3.30.1",
|
|
104
|
+
"@tiptap/extension-link": "^3.30.1",
|
|
105
|
+
"@tiptap/extension-text-style": "^3.30.1",
|
|
106
|
+
"@tiptap/pm": "^3.30.1",
|
|
107
|
+
"@tiptap/react": "^3.30.1",
|
|
108
|
+
"@tiptap/starter-kit": "^3.30.1",
|
|
113
109
|
"clsx": "^2.1.1",
|
|
114
110
|
"dompurify": "^3.2.3",
|
|
115
|
-
"firebase-admin": "^
|
|
116
|
-
"html2canvas-pro": "^
|
|
117
|
-
"sanitize-html": "^2.
|
|
118
|
-
"tailwind-merge": "^
|
|
119
|
-
"zod": "^
|
|
111
|
+
"firebase-admin": "^14.2.0",
|
|
112
|
+
"html2canvas-pro": "^2.3.8",
|
|
113
|
+
"sanitize-html": "^2.17.7",
|
|
114
|
+
"tailwind-merge": "^3.6.0",
|
|
115
|
+
"zod": "^4.4.3"
|
|
120
116
|
},
|
|
121
117
|
"devDependencies": {
|
|
122
|
-
"@astryxdesign/cli": "^0.
|
|
123
|
-
"@
|
|
124
|
-
"@
|
|
118
|
+
"@astryxdesign/cli": "^0.4.2",
|
|
119
|
+
"@tailwindcss/cli": "^4.3.3",
|
|
120
|
+
"@testing-library/dom": "^10.4.1",
|
|
121
|
+
"@testing-library/react": "^16.3.2",
|
|
122
|
+
"@testing-library/user-event": "^14.6.4",
|
|
123
|
+
"@types/react": "^19.2.18",
|
|
124
|
+
"@types/react-dom": "^19.2.4",
|
|
125
125
|
"@types/sanitize-html": "^2.13.0",
|
|
126
|
-
"firebase": "^
|
|
126
|
+
"firebase": "^12.17.1",
|
|
127
|
+
"jsdom": "^30.0.1",
|
|
127
128
|
"next": "^16.0.0",
|
|
128
|
-
"react": "^19.
|
|
129
|
-
"react-dom": "^19.
|
|
130
|
-
"tailwindcss": "^3.
|
|
129
|
+
"react": "^19.2.8",
|
|
130
|
+
"react-dom": "^19.2.8",
|
|
131
|
+
"tailwindcss": "^4.3.3",
|
|
131
132
|
"tsup": "^8.5.0",
|
|
132
|
-
"typescript": "^
|
|
133
|
-
"vitest": "^
|
|
133
|
+
"typescript": "^7.0.2",
|
|
134
|
+
"vitest": "^4.1.10"
|
|
134
135
|
}
|
|
135
136
|
}
|