@xiwebdesign/xicms-core 7.10.0 → 7.12.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/admin/media/MediaPicker.d.ts +1 -1
- package/dist/admin/media/MediaPicker.js +1 -0
- package/dist/admin/media/MediaPicker.js.map +1 -1
- package/dist/admin/patterns/PatternInserter.js +1 -1
- package/dist/admin/patterns/PatternInserter.js.map +1 -1
- package/dist/admin/visual/VisualEditor.js +92 -15
- package/dist/admin/visual/VisualEditor.js.map +1 -1
- package/dist/admin/visual/wijzigingen.js +18 -2
- package/dist/admin/visual/wijzigingen.js.map +1 -1
- package/dist/blocks/builtins/FormBlock.js +6 -2
- package/dist/blocks/builtins/FormBlock.js.map +1 -1
- package/dist/blocks/builtins/Search.js +6 -24
- package/dist/blocks/builtins/Search.js.map +1 -1
- package/dist/blocks/builtins/SymbolRef.js +23 -9
- package/dist/blocks/builtins/SymbolRef.js.map +1 -1
- package/dist/blocks/searchText.d.ts +19 -0
- package/dist/blocks/searchText.js +27 -0
- package/dist/blocks/searchText.js.map +1 -0
- package/dist/demo/demoStore.d.ts +8 -0
- package/dist/demo/demoStore.js +2 -1
- package/dist/demo/demoStore.js.map +1 -1
- package/dist/editor/EditorProvider.js +15 -3
- package/dist/editor/EditorProvider.js.map +1 -1
- package/dist/server/blockData.d.ts +17 -0
- package/dist/server/blockData.js +86 -0
- package/dist/server/blockData.js.map +1 -0
- package/dist/server/cached.d.ts +2 -0
- package/dist/server/cached.js +10 -0
- package/dist/server/cached.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +4 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/pages.d.ts +14 -0
- package/dist/server/pages.js +20 -10
- package/dist/server/pages.js.map +1 -1
- package/dist/server/shop.d.ts +8 -6
- package/dist/server/shop.js +10 -3
- package/dist/server/shop.js.map +1 -1
- package/dist/styles.css +44 -19
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/editor/EditorProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useState, lazy, Suspense, type ReactNode } from \"react\";\n// Het bloktype van de PAGINA zelf, zodat wat hier langskomt per definitie past in wat\n// `PublishedPage` rendert. Een eigen definitie zou hier stil uit de pas kunnen lopen.\nimport type { Page } from \"../schema/pages\";\n\ntype PaginaBlokken = Page[\"blocks\"];\n\n/**\n * Lichte runtime-context voor het inline (visuele) bewerken op de live pagina. De echte editor\n * (TipTap/Radix, zwaar) wordt PAS geladen als een editor hem opent — publieke bezoekers downloaden\n * hem nooit. De editor leeft in de admin-entry; we laden 'm via een dynamische import (eigen chunk).\n *\n * ── WAAROM DE BLOKKEN HIER LANGSKOMEN ─────────────────────────────────────────────────────\n *\n * De editor tekende de pagina vroeger zélf, in een eigen canvas binnen een schermvullende overlay.\n * Je klikte \"Pagina bewerken\" en je site verdween achter een donker paneel met een nabootsing erin.\n * Dat voelde niet als je site bewerken maar als een ander programma openen.\n *\n * Nu blijft de PAGINA de pagina en wordt alleen de DATA vervangen: de editor laadt de werkversie en\n * zet die hier neer, waarna `PublishedPage` de werkversie rendert in plaats van de gepubliceerde\n * blokken. Dezelfde componenten, dezelfde huisstijl, dezelfde layout — alleen andere inhoud.\n *\n * Die omweg via de data is wezenlijk, geen implementatiedetail. Alleen potloodjes over de live\n * pagina leggen zou betekenen dat je de WERKVERSIE bewerkt terwijl je de GEPUBLICEERDE tekst ziet.\n * Zodra je iets wijzigt en nog niet publiceert, lopen die twee uit elkaar en bewerk je iets anders\n * dan er staat. Wat je ziet moet zijn wat je bewerkt.\n *\n * `editPath` hoort erbij: een site kan meerdere gerenderde pagina's in beeld hebben (een\n * ingekaderde pagina, een preview). Alleen het pad dat de editor open heeft mag zijn blokken\n * verwisselen.\n *\n * ── WAAROM DE MODUS EEN REFRESH MOET OVERLEVEN (7.8) ──────────────────────────────────────\n *\n * Bewerken was een toestand die alleen in het geheugen van één render-boom bestond. Een per ongeluk\n * ingedrukte F5 — of een klik op een link in je eigen site, want dat is een volledige navigatie —\n * zette je terug op de live site. Je wijzigingen waren niet weg (die staan in de werkversie in\n * Firestore), maar je zág ze niet meer, en dat is niet te onderscheiden van kwijt zijn.\n *\n * Daarom staat de modus in `sessionStorage`: hij overleeft een refresh en het navigeren door de\n * site, en verdwijnt vanzelf zodra het tabblad dichtgaat. Bewust niet `localStorage` — dan zit je\n * morgen in een nieuw venster nog steeds in de bewerkmodus zonder dat je erom vroeg.\n */\nconst VisualEditor = lazy(() => import(\"../admin/visual/VisualEditor\"));\n\n/** \"live\" = precies wat een bezoeker ziet. \"bewerken\" = dezelfde pagina, met werkversie en gereedschap. */\nexport type Bewerkmodus = \"live\" | \"bewerken\";\n\nconst OPSLAGSLEUTEL = \"xicms-bewerkmodus\";\n\ninterface EditorCtx {\n modus: Bewerkmodus;\n /** Blijft bestaan naast `modus` omdat het door de hele codebase heen gelezen wordt. */\n open: boolean;\n zetModus: (modus: Bewerkmodus) => void;\n openEditor: () => void;\n closeEditor: () => void;\n /** De blokken van de werkversie die de pagina moet tonen; `null` = toon gewoon het gepubliceerde. */\n draftBlocks: PaginaBlokken | null;\n /** Het pad waarvoor `draftBlocks` geldt. */\n editPath: string | null;\n /** Door de editor aangeroepen zodra de werkversie laadt of wijzigt. */\n publishDraft: (path: string, blocks: PaginaBlokken | null) => void;\n /**\n * \"Vervang de foto in veld X van blok Y\" — gezet door de VisualEditor zolang hij open is.\n *\n * De 📷-knopjes op de foto's zitten in de RENDER van de pagina (Img), de mediakiezer zit in de\n * EDITOR. Die twee delen geen boom meer sinds het canvas weg is; dit is de brug. `null` buiten\n * de bewerkmodus, en dan tekent Img geen knopjes.\n */\n fotoVervanger: ((field: string, blockKey?: string) => void) | null;\n zetFotoVervanger: (fn: ((field: string, blockKey?: string) => void) | null) => void;\n}\n\nconst Ctx = createContext<EditorCtx>({\n modus: \"live\",\n open: false,\n zetModus: () => {},\n openEditor: () => {},\n closeEditor: () => {},\n draftBlocks: null,\n editPath: null,\n publishDraft: () => {},\n fotoVervanger: null,\n zetFotoVervanger: () => {},\n});\n\nexport function EditorProvider({ children }: { children: ReactNode }) {\n /**\n * Start altijd op \"live\", ook als de opslag iets anders zegt.\n *\n * De server rendert deze boom zonder toegang tot `sessionStorage`. Zou de eerste client-render\n * meteen \"bewerken\" teruggeven, dan wijkt hij af van de HTML die er al staat en gooit React de\n * boom weg (hydration mismatch). Vandaar: eerst hetzelfde als de server, daarna in een effect\n * bijstellen. Eén frame \"live\" is niet zichtbaar; een kapotte hydration wel.\n */\n const [modus, setModusState] = useState<Bewerkmodus>(\"live\");\n const [draft, setDraft] = useState<{ path: string; blocks: PaginaBlokken } | null>(null);\n const [fotoVervanger, setFotoVervanger] = useState<((field: string, blockKey?: string) => void) | null>(null);\n // In state gewikkeld: een functie rechtstreeks aan een setState geven zou hem AANROEPEN\n // (functionele updater) in plaats van opslaan.\n const zetFotoVervanger = useCallback((fn: ((field: string, blockKey?: string) => void) | null) => {\n setFotoVervanger(() => fn);\n }, []);\n\n useEffect(() => {\n try {\n if (sessionStorage.getItem(OPSLAGSLEUTEL) === \"bewerken\") setModusState(\"bewerken\");\n } catch {\n /* privémodus of geblokkeerde opslag: dan gewoon op de live site beginnen */\n }\n }, []);\n\n const zetModus = useCallback((next: Bewerkmodus) => {\n setModusState(next);\n try {\n if (next === \"bewerken\") sessionStorage.setItem(OPSLAGSLEUTEL, \"bewerken\");\n else sessionStorage.removeItem(OPSLAGSLEUTEL);\n } catch {\n /* de modus werkt ook zonder opslag; alleen een refresh valt dan terug op live */\n }\n // De werkversie hoort bij de bewerkmodus. Blijft hij staan na het sluiten, dan ziet de bezoeker\n // ongepubliceerde tekst terwijl het gereedschap weg is — een pagina die iets toont wat niet\n // live is. De werkversie zelf blijft in Firestore staan; alleen deze weergave stopt.\n if (next === \"live\") setDraft(null);\n }, []);\n\n const publishDraft = useCallback((path: string, blocks: PaginaBlokken | null) => {\n setDraft(blocks ? { path, blocks } : null);\n }, []);\n\n const value = useMemo<EditorCtx>(() => ({\n modus,\n open: modus === \"bewerken\",\n zetModus,\n openEditor: () => zetModus(\"bewerken\"),\n closeEditor: () => zetModus(\"live\"),\n draftBlocks: draft?.blocks ?? null,\n editPath: draft?.path ?? null,\n publishDraft,\n fotoVervanger,\n zetFotoVervanger,\n }), [modus, draft, zetModus, publishDraft, fotoVervanger, zetFotoVervanger]);\n\n return (\n <Ctx.Provider value={value}>\n {children}\n {modus === \"bewerken\" && (\n <Suspense fallback={null}>\n <VisualEditor onClose={() => zetModus(\"live\")} />\n </Suspense>\n )}\n </Ctx.Provider>\n );\n}\n\n/** Open/sluit de visuele pagina-editor. */\nexport function useEditor(): EditorCtx {\n return useContext(Ctx);\n}\n"],"mappings":";AAkJI,SAIM,KAJN;AAhJJ,SAAS,eAAe,aAAa,YAAY,WAAW,SAAS,UAAU,MAAM,gBAAgC;AA0CrH,MAAM,eAAe,KAAK,MAAM,OAAO,8BAA8B,CAAC;AAKtE,MAAM,gBAAgB;AA0BtB,MAAM,MAAM,cAAyB;AAAA,EACnC,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,MAAM;AAAA,EAAC;AAAA,EACjB,YAAY,MAAM;AAAA,EAAC;AAAA,EACnB,aAAa,MAAM;AAAA,EAAC;AAAA,EACpB,aAAa;AAAA,EACb,UAAU;AAAA,EACV,cAAc,MAAM;AAAA,EAAC;AAAA,EACrB,eAAe;AAAA,EACf,kBAAkB,MAAM;AAAA,EAAC;AAC3B,CAAC;AAEM,SAAS,eAAe,EAAE,SAAS,GAA4B;AASpE,QAAM,CAAC,OAAO,aAAa,IAAI,SAAsB,MAAM;AAC3D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAyD,IAAI;AACvF,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAA8D,IAAI;AAG5G,QAAM,mBAAmB,YAAY,CAAC,OAA4D;AAChG,qBAAiB,MAAM,EAAE;AAAA,EAC3B,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,QAAI;AACF,UAAI,eAAe,QAAQ,aAAa,MAAM,WAAY,eAAc,UAAU;AAAA,IACpF,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,WAAW,YAAY,CAAC,SAAsB;AAClD,kBAAc,IAAI;AAClB,QAAI;AACF,UAAI,SAAS,WAAY,gBAAe,QAAQ,eAAe,UAAU;AAAA,UACpE,gBAAe,WAAW,aAAa;AAAA,IAC9C,QAAQ;AAAA,IAER;AAIA,QAAI,SAAS,OAAQ,UAAS,IAAI;AAAA,EACpC,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,YAAY,CAAC,MAAc,WAAiC;AAC/E,aAAS,SAAS,EAAE,MAAM,OAAO,IAAI,IAAI;AAAA,EAC3C,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQ,QAAmB,OAAO;AAAA,IACtC;AAAA,IACA,MAAM,UAAU;AAAA,IAChB;AAAA,IACA,YAAY,MAAM,SAAS,UAAU;AAAA,IACrC,aAAa,MAAM,SAAS,MAAM;AAAA,IAClC,aAAa,OAAO,UAAU;AAAA,IAC9B,UAAU,OAAO,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,CAAC,OAAO,OAAO,UAAU,cAAc,eAAe,gBAAgB,CAAC;AAE3E,SACE,qBAAC,IAAI,UAAJ,EAAa,OACX;AAAA;AAAA,IACA,UAAU,cACT,oBAAC,YAAS,UAAU,MAClB,8BAAC,gBAAa,SAAS,MAAM,SAAS,MAAM,GAAG,GACjD;AAAA,KAEJ;AAEJ;AAGO,SAAS,YAAuB;AACrC,SAAO,WAAW,GAAG;AACvB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/editor/EditorProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useState, type ComponentType, type ReactNode } from \"react\";\n// Het bloktype van de PAGINA zelf, zodat wat hier langskomt per definitie past in wat\n// `PublishedPage` rendert. Een eigen definitie zou hier stil uit de pas kunnen lopen.\nimport type { Page } from \"../schema/pages\";\n\ntype PaginaBlokken = Page[\"blocks\"];\n\n/**\n * Lichte runtime-context voor het inline (visuele) bewerken op de live pagina. De echte editor\n * (TipTap/Radix, zwaar) wordt PAS geladen als een editor hem opent — publieke bezoekers downloaden\n * hem nooit. De editor leeft in de admin-entry; we laden 'm via een dynamische import (eigen chunk).\n *\n * ── WAAROM DE BLOKKEN HIER LANGSKOMEN ─────────────────────────────────────────────────────\n *\n * De editor tekende de pagina vroeger zélf, in een eigen canvas binnen een schermvullende overlay.\n * Je klikte \"Pagina bewerken\" en je site verdween achter een donker paneel met een nabootsing erin.\n * Dat voelde niet als je site bewerken maar als een ander programma openen.\n *\n * Nu blijft de PAGINA de pagina en wordt alleen de DATA vervangen: de editor laadt de werkversie en\n * zet die hier neer, waarna `PublishedPage` de werkversie rendert in plaats van de gepubliceerde\n * blokken. Dezelfde componenten, dezelfde huisstijl, dezelfde layout — alleen andere inhoud.\n *\n * Die omweg via de data is wezenlijk, geen implementatiedetail. Alleen potloodjes over de live\n * pagina leggen zou betekenen dat je de WERKVERSIE bewerkt terwijl je de GEPUBLICEERDE tekst ziet.\n * Zodra je iets wijzigt en nog niet publiceert, lopen die twee uit elkaar en bewerk je iets anders\n * dan er staat. Wat je ziet moet zijn wat je bewerkt.\n *\n * `editPath` hoort erbij: een site kan meerdere gerenderde pagina's in beeld hebben (een\n * ingekaderde pagina, een preview). Alleen het pad dat de editor open heeft mag zijn blokken\n * verwisselen.\n *\n * ── WAAROM DE MODUS EEN REFRESH MOET OVERLEVEN (7.8) ──────────────────────────────────────\n *\n * Bewerken was een toestand die alleen in het geheugen van één render-boom bestond. Een per ongeluk\n * ingedrukte F5 — of een klik op een link in je eigen site, want dat is een volledige navigatie —\n * zette je terug op de live site. Je wijzigingen waren niet weg (die staan in de werkversie in\n * Firestore), maar je zág ze niet meer, en dat is niet te onderscheiden van kwijt zijn.\n *\n * Daarom staat de modus in `sessionStorage`: hij overleeft een refresh en het navigeren door de\n * site, en verdwijnt vanzelf zodra het tabblad dichtgaat. Bewust niet `localStorage` — dan zit je\n * morgen in een nieuw venster nog steeds in de bewerkmodus zonder dat je erom vroeg.\n */\n/**\n * BEWUST GEEN `lazy()` + `<Suspense>`. De chunk-splitsing is dezelfde (de dynamische import\n * hieronder maakt zijn eigen chunk), maar Suspense heeft een gedraging die ons hier de kop kostte:\n * als de boom suspendt, koppelt React de al-gemonteerde children tijdelijk los en weer vast\n * (disconnect/reconnect van de passive effects). In dat her-aansluitpad draaide Astryx'\n * `useScrollLock`-effect dubbel zonder tussenliggende cleanup, waardoor zijn module-brede teller\n * nooit meer op nul kwam — en een teller boven nul betekent: `body { position: fixed }`. De hele\n * site stond stil: niet meer scrollen, dus ook geen bewerkknoppen bij blokken onder de vouw.\n * Zelf laden in een effect en pas renderen als de module er is, heeft dat Suspense-pad niet.\n */\ntype VisualEditorComponent = ComponentType<{ onClose: () => void }>;\nlet visualEditorModule: VisualEditorComponent | null = null;\n\n/** \"live\" = precies wat een bezoeker ziet. \"bewerken\" = dezelfde pagina, met werkversie en gereedschap. */\nexport type Bewerkmodus = \"live\" | \"bewerken\";\n\nconst OPSLAGSLEUTEL = \"xicms-bewerkmodus\";\n\ninterface EditorCtx {\n modus: Bewerkmodus;\n /** Blijft bestaan naast `modus` omdat het door de hele codebase heen gelezen wordt. */\n open: boolean;\n zetModus: (modus: Bewerkmodus) => void;\n openEditor: () => void;\n closeEditor: () => void;\n /** De blokken van de werkversie die de pagina moet tonen; `null` = toon gewoon het gepubliceerde. */\n draftBlocks: PaginaBlokken | null;\n /** Het pad waarvoor `draftBlocks` geldt. */\n editPath: string | null;\n /** Door de editor aangeroepen zodra de werkversie laadt of wijzigt. */\n publishDraft: (path: string, blocks: PaginaBlokken | null) => void;\n /**\n * \"Vervang de foto in veld X van blok Y\" — gezet door de VisualEditor zolang hij open is.\n *\n * De 📷-knopjes op de foto's zitten in de RENDER van de pagina (Img), de mediakiezer zit in de\n * EDITOR. Die twee delen geen boom meer sinds het canvas weg is; dit is de brug. `null` buiten\n * de bewerkmodus, en dan tekent Img geen knopjes.\n */\n fotoVervanger: ((field: string, blockKey?: string) => void) | null;\n zetFotoVervanger: (fn: ((field: string, blockKey?: string) => void) | null) => void;\n}\n\nconst Ctx = createContext<EditorCtx>({\n modus: \"live\",\n open: false,\n zetModus: () => {},\n openEditor: () => {},\n closeEditor: () => {},\n draftBlocks: null,\n editPath: null,\n publishDraft: () => {},\n fotoVervanger: null,\n zetFotoVervanger: () => {},\n});\n\nexport function EditorProvider({ children }: { children: ReactNode }) {\n /**\n * Start altijd op \"live\", ook als de opslag iets anders zegt.\n *\n * De server rendert deze boom zonder toegang tot `sessionStorage`. Zou de eerste client-render\n * meteen \"bewerken\" teruggeven, dan wijkt hij af van de HTML die er al staat en gooit React de\n * boom weg (hydration mismatch). Vandaar: eerst hetzelfde als de server, daarna in een effect\n * bijstellen. Eén frame \"live\" is niet zichtbaar; een kapotte hydration wel.\n */\n const [modus, setModusState] = useState<Bewerkmodus>(\"live\");\n // De editor-component zodra zijn chunk binnen is; module-cache zodat een tweede keer openen\n // (of een andere pagina in dezelfde sessie) niet opnieuw op het netwerk wacht.\n const [VisualEditor, setVisualEditor] = useState<VisualEditorComponent | null>(() => visualEditorModule);\n const [draft, setDraft] = useState<{ path: string; blocks: PaginaBlokken } | null>(null);\n const [fotoVervanger, setFotoVervanger] = useState<((field: string, blockKey?: string) => void) | null>(null);\n // In state gewikkeld: een functie rechtstreeks aan een setState geven zou hem AANROEPEN\n // (functionele updater) in plaats van opslaan.\n const zetFotoVervanger = useCallback((fn: ((field: string, blockKey?: string) => void) | null) => {\n setFotoVervanger(() => fn);\n }, []);\n\n useEffect(() => {\n try {\n if (sessionStorage.getItem(OPSLAGSLEUTEL) === \"bewerken\") setModusState(\"bewerken\");\n } catch {\n /* privémodus of geblokkeerde opslag: dan gewoon op de live site beginnen */\n }\n }, []);\n\n // De editor-chunk laden zodra de bewerkmodus aangaat (zie het commentaar bij\n // `visualEditorModule` waarom dit met de hand gebeurt en niet via lazy/Suspense).\n useEffect(() => {\n if (modus !== \"bewerken\" || VisualEditor) return;\n let losgekoppeld = false;\n void import(\"../admin/visual/VisualEditor\").then((m) => {\n visualEditorModule = m.default;\n if (!losgekoppeld) setVisualEditor(() => m.default);\n });\n return () => {\n losgekoppeld = true;\n };\n }, [modus, VisualEditor]);\n\n const zetModus = useCallback((next: Bewerkmodus) => {\n setModusState(next);\n try {\n if (next === \"bewerken\") sessionStorage.setItem(OPSLAGSLEUTEL, \"bewerken\");\n else sessionStorage.removeItem(OPSLAGSLEUTEL);\n } catch {\n /* de modus werkt ook zonder opslag; alleen een refresh valt dan terug op live */\n }\n // De werkversie hoort bij de bewerkmodus. Blijft hij staan na het sluiten, dan ziet de bezoeker\n // ongepubliceerde tekst terwijl het gereedschap weg is — een pagina die iets toont wat niet\n // live is. De werkversie zelf blijft in Firestore staan; alleen deze weergave stopt.\n if (next === \"live\") setDraft(null);\n }, []);\n\n const publishDraft = useCallback((path: string, blocks: PaginaBlokken | null) => {\n setDraft(blocks ? { path, blocks } : null);\n }, []);\n\n const value = useMemo<EditorCtx>(() => ({\n modus,\n open: modus === \"bewerken\",\n zetModus,\n openEditor: () => zetModus(\"bewerken\"),\n closeEditor: () => zetModus(\"live\"),\n draftBlocks: draft?.blocks ?? null,\n editPath: draft?.path ?? null,\n publishDraft,\n fotoVervanger,\n zetFotoVervanger,\n }), [modus, draft, zetModus, publishDraft, fotoVervanger, zetFotoVervanger]);\n\n return (\n <Ctx.Provider value={value}>\n {children}\n {modus === \"bewerken\" && VisualEditor && <VisualEditor onClose={() => zetModus(\"live\")} />}\n </Ctx.Provider>\n );\n}\n\n/** Open/sluit de visuele pagina-editor. */\nexport function useEditor(): EditorCtx {\n return useContext(Ctx);\n}\n"],"mappings":";AA8KI,SAE2C,KAF3C;AA5KJ,SAAS,eAAe,aAAa,YAAY,WAAW,SAAS,gBAAoD;AAqDzH,IAAI,qBAAmD;AAKvD,MAAM,gBAAgB;AA0BtB,MAAM,MAAM,cAAyB;AAAA,EACnC,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,MAAM;AAAA,EAAC;AAAA,EACjB,YAAY,MAAM;AAAA,EAAC;AAAA,EACnB,aAAa,MAAM;AAAA,EAAC;AAAA,EACpB,aAAa;AAAA,EACb,UAAU;AAAA,EACV,cAAc,MAAM;AAAA,EAAC;AAAA,EACrB,eAAe;AAAA,EACf,kBAAkB,MAAM;AAAA,EAAC;AAC3B,CAAC;AAEM,SAAS,eAAe,EAAE,SAAS,GAA4B;AASpE,QAAM,CAAC,OAAO,aAAa,IAAI,SAAsB,MAAM;AAG3D,QAAM,CAAC,cAAc,eAAe,IAAI,SAAuC,MAAM,kBAAkB;AACvG,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAyD,IAAI;AACvF,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAA8D,IAAI;AAG5G,QAAM,mBAAmB,YAAY,CAAC,OAA4D;AAChG,qBAAiB,MAAM,EAAE;AAAA,EAC3B,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,QAAI;AACF,UAAI,eAAe,QAAQ,aAAa,MAAM,WAAY,eAAc,UAAU;AAAA,IACpF,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAAC,CAAC;AAIL,YAAU,MAAM;AACd,QAAI,UAAU,cAAc,aAAc;AAC1C,QAAI,eAAe;AACnB,SAAK,OAAO,8BAA8B,EAAE,KAAK,CAAC,MAAM;AACtD,2BAAqB,EAAE;AACvB,UAAI,CAAC,aAAc,iBAAgB,MAAM,EAAE,OAAO;AAAA,IACpD,CAAC;AACD,WAAO,MAAM;AACX,qBAAe;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,OAAO,YAAY,CAAC;AAExB,QAAM,WAAW,YAAY,CAAC,SAAsB;AAClD,kBAAc,IAAI;AAClB,QAAI;AACF,UAAI,SAAS,WAAY,gBAAe,QAAQ,eAAe,UAAU;AAAA,UACpE,gBAAe,WAAW,aAAa;AAAA,IAC9C,QAAQ;AAAA,IAER;AAIA,QAAI,SAAS,OAAQ,UAAS,IAAI;AAAA,EACpC,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,YAAY,CAAC,MAAc,WAAiC;AAC/E,aAAS,SAAS,EAAE,MAAM,OAAO,IAAI,IAAI;AAAA,EAC3C,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQ,QAAmB,OAAO;AAAA,IACtC;AAAA,IACA,MAAM,UAAU;AAAA,IAChB;AAAA,IACA,YAAY,MAAM,SAAS,UAAU;AAAA,IACrC,aAAa,MAAM,SAAS,MAAM;AAAA,IAClC,aAAa,OAAO,UAAU;AAAA,IAC9B,UAAU,OAAO,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,CAAC,OAAO,OAAO,UAAU,cAAc,eAAe,gBAAgB,CAAC;AAE3E,SACE,qBAAC,IAAI,UAAJ,EAAa,OACX;AAAA;AAAA,IACA,UAAU,cAAc,gBAAgB,oBAAC,gBAAa,SAAS,MAAM,SAAS,MAAM,GAAG;AAAA,KAC1F;AAEJ;AAGO,SAAS,YAAuB;AACrC,SAAO,WAAW,GAAG;AACvB;","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import type { Page } from "../schema/pages.js";
|
|
3
|
+
export interface PageBlockDataOptions {
|
|
4
|
+
/** `searchParams` van de route — alleen nodig voor productdetailpagina's (`?p=<slug>`). */
|
|
5
|
+
searchParams?: {
|
|
6
|
+
p?: string | string[];
|
|
7
|
+
[k: string]: unknown;
|
|
8
|
+
};
|
|
9
|
+
/** De taal van de render — de zoekindex indexeert dan de vertaalde paden. (Nog ongebruikt.) */
|
|
10
|
+
locale?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Server-side blockData voor één pagina, op basis van zijn bloktypes.
|
|
14
|
+
* Geef het resultaat door als `<PublishedPage blockData={...}>`. `undefined`-namespaces blijven
|
|
15
|
+
* weg zodat de blokken hun eigen fallback houden waar deze functie niets voor verzamelde.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getPageBlockData(siteId: string, page: Pick<Page, "blocks">, opts?: PageBlockDataOptions): Promise<Record<string, unknown> | undefined>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { listPublishedPages, listPublishedNews, listFormDefinitions } from "./pages.js";
|
|
3
|
+
import { blocksToText } from "../blocks/searchText.js";
|
|
4
|
+
const SHOP_TYPES = /* @__PURE__ */ new Set(["productGrid", "shopCategories", "productDetail", "cart", "checkout", "donation", "orderStatus"]);
|
|
5
|
+
const NEWS_TYPES = /* @__PURE__ */ new Set(["newsList", "newsWidget"]);
|
|
6
|
+
function collectTypes(v, out, depth) {
|
|
7
|
+
if (depth > 6 || v == null || typeof v !== "object") return;
|
|
8
|
+
if (Array.isArray(v)) {
|
|
9
|
+
for (const item of v) collectTypes(item, out, depth + 1);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const o = v;
|
|
13
|
+
if (typeof o._type === "string") out.add(o._type);
|
|
14
|
+
for (const val of Object.values(o)) collectTypes(val, out, depth + 1);
|
|
15
|
+
}
|
|
16
|
+
function collectFormIds(v, out, depth) {
|
|
17
|
+
if (depth > 6 || v == null || typeof v !== "object") return;
|
|
18
|
+
if (Array.isArray(v)) {
|
|
19
|
+
for (const item of v) collectFormIds(item, out, depth + 1);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const o = v;
|
|
23
|
+
if (o._type === "form" && typeof o.formId === "string" && o.formId) out.add(o.formId);
|
|
24
|
+
for (const val of Object.values(o)) collectFormIds(val, out, depth + 1);
|
|
25
|
+
}
|
|
26
|
+
function searchWantsNews(v, depth) {
|
|
27
|
+
if (depth > 6 || v == null || typeof v !== "object") return false;
|
|
28
|
+
if (Array.isArray(v)) return v.some((item) => searchWantsNews(item, depth + 1));
|
|
29
|
+
const o = v;
|
|
30
|
+
if (o._type === "search" && o.includeNews === true) return true;
|
|
31
|
+
return Object.values(o).some((val) => searchWantsNews(val, depth + 1));
|
|
32
|
+
}
|
|
33
|
+
async function getPageBlockData(siteId, page, opts) {
|
|
34
|
+
const types = /* @__PURE__ */ new Set();
|
|
35
|
+
collectTypes(page.blocks ?? [], types, 0);
|
|
36
|
+
const wilShop = [...SHOP_TYPES].some((t) => types.has(t));
|
|
37
|
+
const wilNews = [...NEWS_TYPES].some((t) => types.has(t));
|
|
38
|
+
const wilSearch = types.has("search");
|
|
39
|
+
const formIds = /* @__PURE__ */ new Set();
|
|
40
|
+
collectFormIds(page.blocks ?? [], formIds, 0);
|
|
41
|
+
if (!wilShop && !wilNews && !wilSearch && formIds.size === 0) return void 0;
|
|
42
|
+
const data = {};
|
|
43
|
+
await Promise.all([
|
|
44
|
+
(async () => {
|
|
45
|
+
if (!wilShop) return;
|
|
46
|
+
const { getShopPageData } = await import("./shop.js");
|
|
47
|
+
data.shop = await getShopPageData(siteId, opts?.searchParams);
|
|
48
|
+
})(),
|
|
49
|
+
(async () => {
|
|
50
|
+
if (!wilNews) return;
|
|
51
|
+
data.news = await listPublishedNews(siteId);
|
|
52
|
+
})(),
|
|
53
|
+
(async () => {
|
|
54
|
+
if (formIds.size === 0) return;
|
|
55
|
+
const alle = await listFormDefinitions(siteId);
|
|
56
|
+
const map = {};
|
|
57
|
+
for (const f of alle) if (formIds.has(f.id)) map[f.id] = f;
|
|
58
|
+
data.forms = map;
|
|
59
|
+
})(),
|
|
60
|
+
(async () => {
|
|
61
|
+
if (!wilSearch) return;
|
|
62
|
+
const [pages, news] = await Promise.all([
|
|
63
|
+
listPublishedPages(siteId),
|
|
64
|
+
searchWantsNews(page.blocks ?? [], 0) ? listPublishedNews(siteId) : Promise.resolve([])
|
|
65
|
+
]);
|
|
66
|
+
const index = pages.map((p) => ({
|
|
67
|
+
title: String(p.title ?? ""),
|
|
68
|
+
path: String(p.path ?? "/"),
|
|
69
|
+
text: blocksToText(p.blocks)
|
|
70
|
+
}));
|
|
71
|
+
for (const n of news) {
|
|
72
|
+
index.push({
|
|
73
|
+
title: n.title,
|
|
74
|
+
path: `/nieuws/${n.slug || n.id}`,
|
|
75
|
+
text: `${n.excerpt ?? ""} ${(n.bodyHtml ?? n.body ?? "").replace(/<[^>]*>/g, " ")}`
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
data.searchIndex = index;
|
|
79
|
+
})()
|
|
80
|
+
]);
|
|
81
|
+
return data;
|
|
82
|
+
}
|
|
83
|
+
export {
|
|
84
|
+
getPageBlockData
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=blockData.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/blockData.ts"],"sourcesContent":["import \"server-only\";\n\nimport type { Page } from \"../schema/pages\";\nimport { listPublishedPages, listPublishedNews, listFormDefinitions } from \"./pages\";\nimport { blocksToText, type SearchEntry } from \"../blocks/searchText\";\n\n/**\n * SSR-seeds voor blokken die data buiten de pagina zelf nodig hebben (7.12).\n *\n * Het duurste patroon in XiCMS was niet een gemiste servercache maar de BROWSER: blokken als de\n * productgrid, het zoekblok en het formulier haalden hun data client-side uit Firestore — per\n * bezoeker, buiten elke cache om. Eén winkelpagina kostte zo honderden reads per bezoeker, en het\n * zoekblok las de complete pagina-collectie. De server-readers bestonden (server/shop.ts), maar\n * elke site moest er zelf aan denken ze aan te roepen — en geen enkele deed dat. SITE-AUDIT noemt\n * ontbrekende SSR-seeds \"de #1 prestatieval\"; dit bestand maakt die val structureel onmogelijk.\n *\n * `getPageBlockData` kijkt naar de blóktypes van de pagina en verzamelt precies de data die deze\n * pagina nodig heeft — niets meer. Een pagina zonder shop-blokken doet nul product-reads; een\n * pagina zonder zoekblok bouwt geen index. Alle onderliggende readers zitten in de tagged\n * servercache (server/cached.ts), dus dit kost Firestore éénmaal per interval, niet per bezoeker.\n *\n * Gebruik in de catch-all van de klantsite (ná symbol-expansie, zodat blokken ín symbolen meetellen):\n *\n * const page = await getPublishedPageByPathExpanded(siteId, path, locale);\n * const blockData = page ? await getPageBlockData(siteId, page, { searchParams }) : undefined;\n * <PublishedPage page={page} siteId={siteId} blockData={blockData} />\n *\n * De blokken zelf houden hun client-fallback: een site die dit (nog) niet doorgeeft blijft werken,\n * alleen duurder en trager — precies het oude gedrag.\n */\n\n/** Bloktypes die de shop-seed nodig hebben (productDetail leest daarnaast `?p=` voor het product). */\nconst SHOP_TYPES = new Set([\"productGrid\", \"shopCategories\", \"productDetail\", \"cart\", \"checkout\", \"donation\", \"orderStatus\"]);\n/** Bloktypes die de nieuws-seed nodig hebben. */\nconst NEWS_TYPES = new Set([\"newsList\", \"newsWidget\"]);\n\n/** Verzamel alle `_type`-waarden, recursief — kolommen/tabs/secties kunnen blokken bevatten. */\nfunction collectTypes(v: unknown, out: Set<string>, depth: number): void {\n if (depth > 6 || v == null || typeof v !== \"object\") return;\n if (Array.isArray(v)) {\n for (const item of v) collectTypes(item, out, depth + 1);\n return;\n }\n const o = v as Record<string, unknown>;\n if (typeof o._type === \"string\") out.add(o._type);\n for (const val of Object.values(o)) collectTypes(val, out, depth + 1);\n}\n\n/** Verzamel de `formId`s van alle formulierblokken (ook genest). */\nfunction collectFormIds(v: unknown, out: Set<string>, depth: number): void {\n if (depth > 6 || v == null || typeof v !== \"object\") return;\n if (Array.isArray(v)) {\n for (const item of v) collectFormIds(item, out, depth + 1);\n return;\n }\n const o = v as Record<string, unknown>;\n if (o._type === \"form\" && typeof o.formId === \"string\" && o.formId) out.add(o.formId);\n for (const val of Object.values(o)) collectFormIds(val, out, depth + 1);\n}\n\n/** Wil één van de zoekblokken op de pagina ook nieuws doorzoeken? */\nfunction searchWantsNews(v: unknown, depth: number): boolean {\n if (depth > 6 || v == null || typeof v !== \"object\") return false;\n if (Array.isArray(v)) return v.some((item) => searchWantsNews(item, depth + 1));\n const o = v as Record<string, unknown>;\n if (o._type === \"search\" && o.includeNews === true) return true;\n return Object.values(o).some((val) => searchWantsNews(val, depth + 1));\n}\n\nexport interface PageBlockDataOptions {\n /** `searchParams` van de route — alleen nodig voor productdetailpagina's (`?p=<slug>`). */\n searchParams?: { p?: string | string[]; [k: string]: unknown };\n /** De taal van de render — de zoekindex indexeert dan de vertaalde paden. (Nog ongebruikt.) */\n locale?: string;\n}\n\n/**\n * Server-side blockData voor één pagina, op basis van zijn bloktypes.\n * Geef het resultaat door als `<PublishedPage blockData={...}>`. `undefined`-namespaces blijven\n * weg zodat de blokken hun eigen fallback houden waar deze functie niets voor verzamelde.\n */\nexport async function getPageBlockData(\n siteId: string,\n page: Pick<Page, \"blocks\">,\n opts?: PageBlockDataOptions,\n): Promise<Record<string, unknown> | undefined> {\n const types = new Set<string>();\n collectTypes(page.blocks ?? [], types, 0);\n\n const wilShop = [...SHOP_TYPES].some((t) => types.has(t));\n const wilNews = [...NEWS_TYPES].some((t) => types.has(t));\n const wilSearch = types.has(\"search\");\n const formIds = new Set<string>();\n collectFormIds(page.blocks ?? [], formIds, 0);\n\n if (!wilShop && !wilNews && !wilSearch && formIds.size === 0) return undefined;\n\n const data: Record<string, unknown> = {};\n\n // Parallel: elke seed leest uit de tagged servercache, dus dit zijn meestal nul echte reads.\n await Promise.all([\n (async () => {\n if (!wilShop) return;\n // Dynamische import: shop-servercode alleen in de graph van pagina's/sites die hem gebruiken.\n const { getShopPageData } = await import(\"./shop\");\n data.shop = await getShopPageData(siteId, opts?.searchParams);\n })(),\n (async () => {\n if (!wilNews) return;\n data.news = await listPublishedNews(siteId);\n })(),\n (async () => {\n if (formIds.size === 0) return;\n const alle = await listFormDefinitions(siteId);\n const map: Record<string, unknown> = {};\n for (const f of alle) if (formIds.has(f.id)) map[f.id] = f;\n data.forms = map;\n })(),\n (async () => {\n if (!wilSearch) return;\n const [pages, news] = await Promise.all([\n listPublishedPages(siteId),\n searchWantsNews(page.blocks ?? [], 0) ? listPublishedNews(siteId) : Promise.resolve([]),\n ]);\n const index: SearchEntry[] = pages.map((p) => ({\n title: String(p.title ?? \"\"),\n path: String(p.path ?? \"/\"),\n text: blocksToText(p.blocks),\n }));\n for (const n of news) {\n index.push({\n title: n.title,\n path: `/nieuws/${n.slug || n.id}`,\n text: `${n.excerpt ?? \"\"} ${(n.bodyHtml ?? n.body ?? \"\").replace(/<[^>]*>/g, \" \")}`,\n });\n }\n data.searchIndex = index;\n })(),\n ]);\n\n return data;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { unstable_cache } from "next/cache";
|
|
3
|
+
const VANGNET = Number(process.env.XICMS_ISR_FALLBACK) || 3600;
|
|
4
|
+
function cachedRead(keyParts, tags, fn) {
|
|
5
|
+
return unstable_cache(fn, keyParts, { tags, revalidate: VANGNET })();
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
cachedRead
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=cached.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/cached.ts"],"sourcesContent":["import \"server-only\";\n\nimport { unstable_cache } from \"next/cache\";\n\n/**\n * Tagged servercache om ELKE publieke Firestore-read (7.12).\n *\n * Waarom dit bestaat: de Next-datacache dekte maar één van onze leespaden. Firestore-REST-queries\n * zijn POST-fetches en die cachet Next niet; Admin-SDK-reads zijn geen fetch en cachen dus ook\n * niet. Het enige dat de kosten dempte was route-level ISR — een botte timer die élke render\n * opnieuw liet lezen, en waarop `revalidateTag` maar half werkte. Gevolg: de hele ping-\n * infrastructuur (functions → /api/revalidate → tags) bestond, maar luisterde alleen op het\n * settings-GET-pad.\n *\n * `unstable_cache` legt de uitkomst van de reader zelf in de servercache, mét onze tags. Daarmee:\n * - werken de bestaande publicatie-pings op élk leespad (Admin én REST), binnen seconden;\n * - kost een read Firestore éénmaal per interval in plaats van éénmaal per render;\n * - mag het route-vangnet omhoog (3600), want versheid komt van de push, niet van de timer.\n *\n * `keyParts` moet de read uniek identificeren (siteId + wat er gelezen wordt): de binnenfunctie is\n * een closure zonder argumenten, dus de sleutel komt volledig uit `keyParts`.\n *\n * NIET gebruiken voor: drafts, preview (`skipBundle`), of iets wat per gebruiker verschilt —\n * dit is een gedeelde cache voor publiek-identieke data.\n */\nconst VANGNET = Number(process.env.XICMS_ISR_FALLBACK) || 3600;\n\nexport function cachedRead<T>(keyParts: string[], tags: string[], fn: () => Promise<T>): Promise<T> {\n return unstable_cache(fn, keyParts, { tags, revalidate: VANGNET })();\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,sBAAsB;AAuB/B,MAAM,UAAU,OAAO,QAAQ,IAAI,kBAAkB,KAAK;AAEnD,SAAS,WAAc,UAAoB,MAAgB,IAAkC;AAClG,SAAO,eAAe,IAAI,UAAU,EAAE,MAAM,YAAY,QAAQ,CAAC,EAAE;AACrE;","names":[]}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export { sanitizeRichText } from "./sanitize.js";
|
|
|
8
8
|
export { jsonLdHtml } from "../blocks/builtins/jsonld.js";
|
|
9
9
|
export { buildMetadata, buildJsonLd, buildSitemap, buildRobots, siteOrigin, businessJsonLd, breadcrumbListJsonLd, articleJsonLd, productJsonLd, openingHoursJsonLd, galleryJsonLd, videoObjectJsonLd, type BusinessInfo, } from "./seo.js";
|
|
10
10
|
export { validateEnv, assertEnv, type EnvReport } from "./env.js";
|
|
11
|
+
export { getPageBlockData, type PageBlockDataOptions } from "./blockData.js";
|
|
12
|
+
export { listFormDefinitions } from "./pages.js";
|
|
11
13
|
export { revalidatePOST } from "./revalidate.js";
|
|
12
14
|
export { draftPOST } from "./draft.js";
|
|
13
15
|
export { versionGET } from "./version.js";
|
package/dist/server/index.js
CHANGED
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
videoObjectJsonLd
|
|
18
18
|
} from "./seo.js";
|
|
19
19
|
import { validateEnv, assertEnv } from "./env.js";
|
|
20
|
+
import { getPageBlockData } from "./blockData.js";
|
|
21
|
+
import { listFormDefinitions } from "./pages.js";
|
|
20
22
|
import { revalidatePOST } from "./revalidate.js";
|
|
21
23
|
import { draftPOST } from "./draft.js";
|
|
22
24
|
import { versionGET } from "./version.js";
|
|
@@ -69,12 +71,14 @@ export {
|
|
|
69
71
|
getDraftPageById,
|
|
70
72
|
getDraftPageByPath,
|
|
71
73
|
getNewsPageData,
|
|
74
|
+
getPageBlockData,
|
|
72
75
|
getPublicSiteSettings,
|
|
73
76
|
getPublishedPageByPath,
|
|
74
77
|
getPublishedPageByPathExpanded,
|
|
75
78
|
getSiteCustomBlocks,
|
|
76
79
|
getSiteSymbols,
|
|
77
80
|
jsonLdHtml,
|
|
81
|
+
listFormDefinitions,
|
|
78
82
|
listPublishedNews,
|
|
79
83
|
listPublishedPages,
|
|
80
84
|
localizeBlocks,
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/index.ts"],"sourcesContent":["/**\n * @xiwebdesign/xicms-core/server — server-only helpers (firebase-admin, sanitization, SEO,\n * pagina-reads). NOOIT importeren in client-componenten.\n */\nimport \"server-only\";\n\n// `getAdminAuth` zit BEWUST niet hier maar achter de subpath `@xiwebdesign/xicms-core/server/auth`\n// (zie ./auth.ts): firebase-admin/auth sloopt via jwks-rsa/jose élke server-render op Vercel, en\n// hoort dus niet in de bundel van sites die alleen pagina's lezen.\nexport { getAdminApp, getAdminDb } from \"../firebase/admin\";\nexport { sanitizeRichText } from \"./sanitize\";\n// Veilige JSON-LD-serialisatie voor <script type=\"application/ld+json\"> in consumer-routes\n// (escapet `<` zodat klant-tekst met </script> de pagina niet kan breken).\nexport { jsonLdHtml } from \"../blocks/builtins/jsonld\";\nexport {\n buildMetadata,\n buildJsonLd,\n buildSitemap,\n buildRobots,\n siteOrigin,\n businessJsonLd,\n breadcrumbListJsonLd,\n articleJsonLd,\n productJsonLd,\n openingHoursJsonLd,\n galleryJsonLd,\n videoObjectJsonLd,\n type BusinessInfo,\n} from \"./seo\";\nexport { validateEnv, assertEnv, type EnvReport } from \"./env\";\n// On-demand revalidation (6.5): POST-handler voor `app/api/revalidate/route.ts` in de klantsite.\n// De backend roept die route na een publicatie aan → wijziging binnen seconden live, zonder rebuild.\nexport { revalidatePOST } from \"./revalidate\";\n// Preview (Draft Mode): POST-handler voor `app/api/draft/route.ts` in de klantsite. Zet de\n// bypass-cookie zodat een ingelogde editor de content-bundel + ISR-cache overslaat, terwijl de\n// pagina-route voor publiek statisch blijft.\nexport { draftPOST } from \"./draft\";\n// Versie-endpoint: GET-handler voor `app/api/version/route.ts`, zodat het superbeheer kan zien op\n// welke engine een DEPLOYMENT draait (en niet alleen wanneer er voor het laatst is ingelogd).\nexport { versionGET } from \"./version\";\n// Shop-SSR (6.0): opt-in via subpath `@xiwebdesign/xicms-core/server/shop` (readers + product-SEO).\n// Niet meer onvoorwaardelijk her-geëxporteerd, zodat shop-servercode niet in elke site landt.\nexport {\n pathFromSlugs,\n listPublishedPages,\n listPublishedNews,\n getPublishedPageByPath,\n getPublishedPageByPathExpanded,\n getSiteSymbols,\n expandSymbols,\n getSiteCustomBlocks,\n getPublicSiteSettings,\n getDraftPageById,\n getDraftPageByPath,\n type StoredPage,\n} from \"./pages\";\n// Content-bundle (5.7): build-time reader van `content/published.bundle.json`. De pagina-/settings-/\n// symbol-/customBlock-readers hierboven zijn al bundle-first; deze exports zijn voor directe\n// consumptie (bv. nieuws-blockData in de klant-routes).\nexport {\n getBundle,\n getBundlePage,\n getBundleNews,\n getNewsPageData,\n type PublishedBundle,\n type BundleItem,\n} from \"./bundle\";\n// Localisatie (5.0) — server-safe overlay-helpers, ook bruikbaar in route-handlers/layouts.\nexport {\n siteLocales,\n parseLocaleSlugs,\n localizedPath,\n localizePage,\n localizeBlocks,\n pruneTranslations,\n TranslationsSchema,\n type Translations,\n type LocaleConfig,\n} from \"../i18n/overlay\";\n"],"mappings":"AAIA,OAAO;AAKP,SAAS,aAAa,kBAAkB;AACxC,SAAS,wBAAwB;AAGjC,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,aAAa,iBAAiC;
|
|
1
|
+
{"version":3,"sources":["../../src/server/index.ts"],"sourcesContent":["/**\n * @xiwebdesign/xicms-core/server — server-only helpers (firebase-admin, sanitization, SEO,\n * pagina-reads). NOOIT importeren in client-componenten.\n */\nimport \"server-only\";\n\n// `getAdminAuth` zit BEWUST niet hier maar achter de subpath `@xiwebdesign/xicms-core/server/auth`\n// (zie ./auth.ts): firebase-admin/auth sloopt via jwks-rsa/jose élke server-render op Vercel, en\n// hoort dus niet in de bundel van sites die alleen pagina's lezen.\nexport { getAdminApp, getAdminDb } from \"../firebase/admin\";\nexport { sanitizeRichText } from \"./sanitize\";\n// Veilige JSON-LD-serialisatie voor <script type=\"application/ld+json\"> in consumer-routes\n// (escapet `<` zodat klant-tekst met </script> de pagina niet kan breken).\nexport { jsonLdHtml } from \"../blocks/builtins/jsonld\";\nexport {\n buildMetadata,\n buildJsonLd,\n buildSitemap,\n buildRobots,\n siteOrigin,\n businessJsonLd,\n breadcrumbListJsonLd,\n articleJsonLd,\n productJsonLd,\n openingHoursJsonLd,\n galleryJsonLd,\n videoObjectJsonLd,\n type BusinessInfo,\n} from \"./seo\";\nexport { validateEnv, assertEnv, type EnvReport } from \"./env\";\n// On-demand revalidation (6.5): POST-handler voor `app/api/revalidate/route.ts` in de klantsite.\n// De backend roept die route na een publicatie aan → wijziging binnen seconden live, zonder rebuild.\n// SSR-seeds (7.12): verzamelt server-side de blockData die de blokken van déze pagina nodig\n// hebben (shop, nieuws, formulieren, zoekindex) — één aanroep in de catch-all, en geen enkel blok\n// hoeft meer per bezoeker uit Firestore te lezen.\nexport { getPageBlockData, type PageBlockDataOptions } from \"./blockData\";\nexport { listFormDefinitions } from \"./pages\";\nexport { revalidatePOST } from \"./revalidate\";\n// Preview (Draft Mode): POST-handler voor `app/api/draft/route.ts` in de klantsite. Zet de\n// bypass-cookie zodat een ingelogde editor de content-bundel + ISR-cache overslaat, terwijl de\n// pagina-route voor publiek statisch blijft.\nexport { draftPOST } from \"./draft\";\n// Versie-endpoint: GET-handler voor `app/api/version/route.ts`, zodat het superbeheer kan zien op\n// welke engine een DEPLOYMENT draait (en niet alleen wanneer er voor het laatst is ingelogd).\nexport { versionGET } from \"./version\";\n// Shop-SSR (6.0): opt-in via subpath `@xiwebdesign/xicms-core/server/shop` (readers + product-SEO).\n// Niet meer onvoorwaardelijk her-geëxporteerd, zodat shop-servercode niet in elke site landt.\nexport {\n pathFromSlugs,\n listPublishedPages,\n listPublishedNews,\n getPublishedPageByPath,\n getPublishedPageByPathExpanded,\n getSiteSymbols,\n expandSymbols,\n getSiteCustomBlocks,\n getPublicSiteSettings,\n getDraftPageById,\n getDraftPageByPath,\n type StoredPage,\n} from \"./pages\";\n// Content-bundle (5.7): build-time reader van `content/published.bundle.json`. De pagina-/settings-/\n// symbol-/customBlock-readers hierboven zijn al bundle-first; deze exports zijn voor directe\n// consumptie (bv. nieuws-blockData in de klant-routes).\nexport {\n getBundle,\n getBundlePage,\n getBundleNews,\n getNewsPageData,\n type PublishedBundle,\n type BundleItem,\n} from \"./bundle\";\n// Localisatie (5.0) — server-safe overlay-helpers, ook bruikbaar in route-handlers/layouts.\nexport {\n siteLocales,\n parseLocaleSlugs,\n localizedPath,\n localizePage,\n localizeBlocks,\n pruneTranslations,\n TranslationsSchema,\n type Translations,\n type LocaleConfig,\n} from \"../i18n/overlay\";\n"],"mappings":"AAIA,OAAO;AAKP,SAAS,aAAa,kBAAkB;AACxC,SAAS,wBAAwB;AAGjC,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,aAAa,iBAAiC;AAMvD,SAAS,wBAAmD;AAC5D,SAAS,2BAA2B;AACpC,SAAS,sBAAsB;AAI/B,SAAS,iBAAiB;AAG1B,SAAS,kBAAkB;AAG3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAIP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;","names":[]}
|
package/dist/server/pages.d.ts
CHANGED
|
@@ -60,6 +60,20 @@ declare function listPublishedNewsUncached(siteId: string): Promise<NewsArticle[
|
|
|
60
60
|
* bij een statische build), maar het is niet de verse bron.
|
|
61
61
|
*/
|
|
62
62
|
export declare const listPublishedNews: typeof listPublishedNewsUncached;
|
|
63
|
+
/**
|
|
64
|
+
* Alle formulierdefinities van een site, server-side — voor de `forms`-blockData-seed
|
|
65
|
+
* (server/blockData.ts). Tot 7.12 las het formulierblok zijn definitie per BEZOEKER client-side
|
|
66
|
+
* (`getDoc` in de browser): één read per bezoeker per formulier, en het formulier verscheen pas
|
|
67
|
+
* na hydratie. Server-side gelezen zit het formulier in de SSR-HTML en kost het één read per
|
|
68
|
+
* cache-interval voor de hele site.
|
|
69
|
+
*
|
|
70
|
+
* Formulieren zitten niet in de content-bundle; bij een Firestore-storing levert dit gewoon een
|
|
71
|
+
* lege lijst en valt het blok terug op zijn eigen client-pad (dat dan ook faalt, maar met een
|
|
72
|
+
* nette "neem contact op"-melding).
|
|
73
|
+
*/
|
|
74
|
+
export declare function listFormDefinitions(siteId: string): Promise<Array<{
|
|
75
|
+
id: string;
|
|
76
|
+
} & Record<string, unknown>>>;
|
|
63
77
|
/** Alle symbolen van een site (gevalideerd; ongeldige overgeslagen met waarschuwing). Live-first of bundle-first. */
|
|
64
78
|
declare function getSiteSymbolsUncached(siteId: string): Promise<StoredSymbol[]>;
|
|
65
79
|
/**
|
package/dist/server/pages.js
CHANGED
|
@@ -7,6 +7,7 @@ import { CustomBlockSchema } from "../schema/customBlocks.js";
|
|
|
7
7
|
import { PublicSiteSettingsSchema } from "../schema/settings.js";
|
|
8
8
|
import { localizePage, localizeBlocks } from "../i18n/overlay.js";
|
|
9
9
|
import { getBundle, getBundlePage } from "./bundle.js";
|
|
10
|
+
import { cachedRead } from "./cached.js";
|
|
10
11
|
import { sanitizeRichText } from "./sanitize.js";
|
|
11
12
|
const ISR_FALLBACK = Number(process.env.XICMS_ISR_FALLBACK) || 3600;
|
|
12
13
|
const ISR_NO_PUSH = Number(process.env.XICMS_ISR_FALLBACK_NOPUSH) || 300;
|
|
@@ -158,7 +159,7 @@ function bundlePages(siteId) {
|
|
|
158
159
|
}
|
|
159
160
|
async function listPublishedPages(siteId) {
|
|
160
161
|
if (LIVE_READS) {
|
|
161
|
-
return liveOrBundle(
|
|
162
|
+
return cachedRead(["xicms", "pages", siteId], [cacheTags.pages(siteId)], () => liveOrBundle(
|
|
162
163
|
`listPublishedPages ${siteId}`,
|
|
163
164
|
async () => {
|
|
164
165
|
const adb2 = getAdminDb();
|
|
@@ -169,7 +170,7 @@ async function listPublishedPages(siteId) {
|
|
|
169
170
|
return restQueryThrowing(siteId, [eq("status", "published")]);
|
|
170
171
|
},
|
|
171
172
|
() => bundlePages(siteId)
|
|
172
|
-
);
|
|
173
|
+
));
|
|
173
174
|
}
|
|
174
175
|
const bundle = getBundle();
|
|
175
176
|
if (bundle && bundle.siteId === siteId) {
|
|
@@ -190,7 +191,7 @@ const getSiteCustomBlocks = cache(getSiteCustomBlocksUncached);
|
|
|
190
191
|
const getPublicSiteSettings = cache(getPublicSiteSettingsUncached);
|
|
191
192
|
async function getPublishedPageByPathUncached(siteId, path, opts) {
|
|
192
193
|
if (LIVE_READS && !opts?.skipBundle) {
|
|
193
|
-
return liveOrBundle(
|
|
194
|
+
return cachedRead(["xicms", "page", siteId, path], [cacheTags.page(siteId, path)], () => liveOrBundle(
|
|
194
195
|
`getPublishedPageByPath ${siteId}${path}`,
|
|
195
196
|
async () => {
|
|
196
197
|
const adb2 = getAdminDb();
|
|
@@ -208,7 +209,7 @@ async function getPublishedPageByPathUncached(siteId, path, opts) {
|
|
|
208
209
|
const hit = getBundlePage(b, path);
|
|
209
210
|
return hit ? parse(hit.id, hit) : null;
|
|
210
211
|
}
|
|
211
|
-
);
|
|
212
|
+
));
|
|
212
213
|
}
|
|
213
214
|
const bundle = opts?.skipBundle ? null : getBundle();
|
|
214
215
|
if (bundle && bundle.siteId === siteId) {
|
|
@@ -303,11 +304,11 @@ function bundleNewsRows(siteId) {
|
|
|
303
304
|
async function listPublishedNewsUncached(siteId) {
|
|
304
305
|
let rows;
|
|
305
306
|
if (LIVE_READS) {
|
|
306
|
-
rows = await liveOrBundle(
|
|
307
|
+
rows = await cachedRead(["xicms", "news", siteId], [cacheTags.collection(siteId, "news")], () => liveOrBundle(
|
|
307
308
|
`listPublishedNews ${siteId}`,
|
|
308
309
|
() => readCollectionThrowing(siteId, "news"),
|
|
309
310
|
() => bundleNewsRows(siteId)
|
|
310
|
-
);
|
|
311
|
+
));
|
|
311
312
|
} else {
|
|
312
313
|
const bundle = getBundle();
|
|
313
314
|
rows = bundle && bundle.siteId === siteId ? bundleNewsRows(siteId) : await readCollection(siteId, "news");
|
|
@@ -315,6 +316,14 @@ async function listPublishedNewsUncached(siteId) {
|
|
|
315
316
|
return toPublishedNews(rows);
|
|
316
317
|
}
|
|
317
318
|
const listPublishedNews = cache(listPublishedNewsUncached);
|
|
319
|
+
async function listFormDefinitions(siteId) {
|
|
320
|
+
const rows = await cachedRead(
|
|
321
|
+
["xicms", "forms", siteId],
|
|
322
|
+
[cacheTags.collection(siteId, "forms")],
|
|
323
|
+
() => readCollection(siteId, "forms")
|
|
324
|
+
);
|
|
325
|
+
return rows.map((r) => ({ id: r.id, ...r.data }));
|
|
326
|
+
}
|
|
318
327
|
function bundleSymbolRows(siteId) {
|
|
319
328
|
const bundle = getBundle();
|
|
320
329
|
return bundle && bundle.siteId === siteId ? bundle.symbols.map((s) => ({ id: s.id, data: s })) : [];
|
|
@@ -322,7 +331,7 @@ function bundleSymbolRows(siteId) {
|
|
|
322
331
|
async function getSiteSymbolsUncached(siteId) {
|
|
323
332
|
let rows;
|
|
324
333
|
if (LIVE_READS) {
|
|
325
|
-
rows = await liveOrBundle(`getSiteSymbols ${siteId}`, () => readCollectionThrowing(siteId, "symbols"), () => bundleSymbolRows(siteId));
|
|
334
|
+
rows = await cachedRead(["xicms", "symbols", siteId], [cacheTags.collection(siteId, "symbols")], () => liveOrBundle(`getSiteSymbols ${siteId}`, () => readCollectionThrowing(siteId, "symbols"), () => bundleSymbolRows(siteId)));
|
|
326
335
|
} else {
|
|
327
336
|
const bundle = getBundle();
|
|
328
337
|
rows = bundle && bundle.siteId === siteId ? bundleSymbolRows(siteId) : await readCollection(siteId, "symbols");
|
|
@@ -368,7 +377,7 @@ async function getPublicSiteSettingsUncached(siteId) {
|
|
|
368
377
|
return parsed2.success ? parsed2.data : {};
|
|
369
378
|
};
|
|
370
379
|
if (LIVE_READS) {
|
|
371
|
-
return liveOrBundle(
|
|
380
|
+
return cachedRead(["xicms", "settings", siteId], [cacheTags.collection(siteId, "settings")], () => liveOrBundle(
|
|
372
381
|
`getPublicSiteSettings ${siteId}`,
|
|
373
382
|
async () => {
|
|
374
383
|
const adb2 = getAdminDb();
|
|
@@ -391,7 +400,7 @@ async function getPublicSiteSettingsUncached(siteId) {
|
|
|
391
400
|
return parsed2.success ? parsed2.data : {};
|
|
392
401
|
},
|
|
393
402
|
bundleSettings
|
|
394
|
-
);
|
|
403
|
+
));
|
|
395
404
|
}
|
|
396
405
|
const bundle = getBundle();
|
|
397
406
|
if (bundle && bundle.siteId === siteId) {
|
|
@@ -430,7 +439,7 @@ function bundleCustomBlockRows(siteId) {
|
|
|
430
439
|
async function getSiteCustomBlocksUncached(siteId) {
|
|
431
440
|
let rows;
|
|
432
441
|
if (LIVE_READS) {
|
|
433
|
-
rows = await liveOrBundle(`getSiteCustomBlocks ${siteId}`, () => readCollectionThrowing(siteId, "customBlocks"), () => bundleCustomBlockRows(siteId));
|
|
442
|
+
rows = await cachedRead(["xicms", "customBlocks", siteId], [cacheTags.collection(siteId, "customBlocks")], () => liveOrBundle(`getSiteCustomBlocks ${siteId}`, () => readCollectionThrowing(siteId, "customBlocks"), () => bundleCustomBlockRows(siteId)));
|
|
434
443
|
} else {
|
|
435
444
|
const bundle = getBundle();
|
|
436
445
|
rows = bundle && bundle.siteId === siteId ? bundleCustomBlockRows(siteId) : await readCollection(siteId, "customBlocks");
|
|
@@ -453,6 +462,7 @@ export {
|
|
|
453
462
|
getPublishedPageByPathExpanded,
|
|
454
463
|
getSiteCustomBlocks,
|
|
455
464
|
getSiteSymbols,
|
|
465
|
+
listFormDefinitions,
|
|
456
466
|
listPublishedNews,
|
|
457
467
|
listPublishedPages,
|
|
458
468
|
liveReadsActive,
|