From d558d18d9a978d82e9446023dcb8f74911855e1d Mon Sep 17 00:00:00 2001 From: rasm-its Date: Sun, 2 Aug 2026 20:11:20 +0200 Subject: [PATCH] Initial commit --- .gitignore | 9 + dist/exports/client.d.ts | 12 -- dist/exports/client.js | 12 -- dist/exports/client.js.map | 1 - dist/exports/next-middleware.d.ts | 9 - dist/exports/next-middleware.js | 9 - dist/exports/next-middleware.js.map | 1 - dist/exports/rsc.d.ts | 9 - dist/exports/rsc.js | 9 - dist/exports/rsc.js.map | 1 - dist/exports/server.d.ts | 17 -- dist/exports/server.js | 16 -- dist/exports/server.js.map | 1 - dist/globals/CookieSettings/fields.d.ts | 7 - dist/globals/CookieSettings/fields.js | 87 --------- dist/globals/CookieSettings/fields.js.map | 1 - dist/globals/CookieSettings/index.d.ts | 6 - dist/globals/CookieSettings/index.js | 19 -- dist/globals/CookieSettings/index.js.map | 1 - .../SiteIntegrations/fields/analytics.d.ts | 6 - .../SiteIntegrations/fields/analytics.js | 23 --- .../SiteIntegrations/fields/analytics.js.map | 1 - .../globals/SiteIntegrations/fields/smtp.d.ts | 9 - dist/globals/SiteIntegrations/fields/smtp.js | 59 ------ .../SiteIntegrations/fields/smtp.js.map | 1 - .../SiteIntegrations/fields/storage.d.ts | 10 - .../SiteIntegrations/fields/storage.js | 39 ---- .../SiteIntegrations/fields/storage.js.map | 1 - .../SiteIntegrations/fields/turnstile.d.ts | 10 - .../SiteIntegrations/fields/turnstile.js | 25 --- .../SiteIntegrations/fields/turnstile.js.map | 1 - dist/globals/SiteIntegrations/index.d.ts | 19 -- dist/globals/SiteIntegrations/index.js | 62 ------ dist/globals/SiteIntegrations/index.js.map | 1 - dist/globals/SiteSettings/fields/general.d.ts | 6 - dist/globals/SiteSettings/fields/general.js | 88 --------- .../SiteSettings/fields/general.js.map | 1 - .../SiteSettings/fields/navigation.d.ts | 1 - .../globals/SiteSettings/fields/navigation.js | 2 - .../SiteSettings/fields/navigation.js.map | 1 - dist/globals/SiteSettings/fields/theme.d.ts | 9 - dist/globals/SiteSettings/fields/theme.js | 37 ---- dist/globals/SiteSettings/fields/theme.js.map | 1 - dist/globals/SiteSettings/index.d.ts | 27 --- dist/globals/SiteSettings/index.js | 55 ------ dist/globals/SiteSettings/index.js.map | 1 - dist/index.d.ts | 29 --- dist/index.js | 20 -- dist/index.js.map | 1 - dist/modules/access/access.d.ts | 21 -- dist/modules/access/access.js | 29 --- dist/modules/access/access.js.map | 1 - dist/modules/access/index.d.ts | 6 - dist/modules/access/index.js | 7 - dist/modules/access/index.js.map | 1 - dist/modules/access/injectRoles.d.ts | 10 - dist/modules/access/injectRoles.js | 27 --- dist/modules/access/injectRoles.js.map | 1 - dist/modules/access/predicates.d.ts | 20 -- dist/modules/access/predicates.js | 32 ---- dist/modules/access/predicates.js.map | 1 - dist/modules/access/rolesField.d.ts | 9 - dist/modules/access/rolesField.js | 32 ---- dist/modules/access/rolesField.js.map | 1 - dist/modules/access/types.d.ts | 19 -- dist/modules/access/types.js | 10 - dist/modules/access/types.js.map | 1 - dist/modules/analytics/Analytics.d.ts | 19 -- dist/modules/analytics/Analytics.js | 63 ------ dist/modules/analytics/Analytics.js.map | 1 - dist/modules/analytics/client.d.ts | 1 - dist/modules/analytics/client.js | 4 - dist/modules/analytics/client.js.map | 1 - .../modules/analytics/getAnalyticsConfig.d.ts | 9 - dist/modules/analytics/getAnalyticsConfig.js | 15 -- .../analytics/getAnalyticsConfig.js.map | 1 - dist/modules/analytics/index.d.ts | 2 - dist/modules/analytics/index.js | 5 - dist/modules/analytics/index.js.map | 1 - dist/modules/analytics/types.d.ts | 9 - dist/modules/analytics/types.js | 5 - dist/modules/analytics/types.js.map | 1 - dist/modules/blocks/RenderBlocks.d.ts | 32 ---- dist/modules/blocks/RenderBlocks.js | 44 ----- dist/modules/blocks/RenderBlocks.js.map | 1 - dist/modules/blocks/index.d.ts | 3 - dist/modules/blocks/index.js | 3 - dist/modules/blocks/index.js.map | 1 - dist/modules/blocks/types.d.ts | 26 --- dist/modules/blocks/types.js | 9 - dist/modules/blocks/types.js.map | 1 - dist/modules/consent/ConsentContext.d.ts | 12 -- dist/modules/consent/ConsentContext.js | 24 --- dist/modules/consent/ConsentContext.js.map | 1 - dist/modules/consent/CookieBanner.d.ts | 12 -- dist/modules/consent/CookieBanner.js | 179 ------------------ dist/modules/consent/CookieBanner.js.map | 1 - dist/modules/consent/CookieButton.d.ts | 13 -- dist/modules/consent/CookieButton.js | 32 ---- dist/modules/consent/CookieButton.js.map | 1 - dist/modules/consent/categories.d.ts | 10 - dist/modules/consent/categories.js | 26 --- dist/modules/consent/categories.js.map | 1 - dist/modules/consent/client.d.ts | 5 - dist/modules/consent/client.js | 8 - dist/modules/consent/client.js.map | 1 - dist/modules/consent/getConsentTexts.d.ts | 25 --- dist/modules/consent/getConsentTexts.js | 79 -------- dist/modules/consent/getConsentTexts.js.map | 1 - dist/modules/consent/googleConsent.d.ts | 23 --- dist/modules/consent/googleConsent.js | 42 ---- dist/modules/consent/googleConsent.js.map | 1 - dist/modules/consent/index.d.ts | 6 - dist/modules/consent/index.js | 9 - dist/modules/consent/index.js.map | 1 - dist/modules/consent/storage.d.ts | 11 -- dist/modules/consent/storage.js | 42 ---- dist/modules/consent/storage.js.map | 1 - dist/modules/consent/texts.d.ts | 21 -- dist/modules/consent/texts.js | 3 - dist/modules/consent/texts.js.map | 1 - dist/modules/consent/useConsent.d.ts | 9 - dist/modules/consent/useConsent.js | 51 ----- dist/modules/consent/useConsent.js.map | 1 - dist/modules/content/archiveFields.d.ts | 13 -- dist/modules/content/archiveFields.js | 27 --- dist/modules/content/archiveFields.js.map | 1 - dist/modules/content/getArchiveEntries.d.ts | 39 ---- dist/modules/content/getArchiveEntries.js | 33 ---- dist/modules/content/getArchiveEntries.js.map | 1 - dist/modules/content/index.d.ts | 8 - dist/modules/content/index.js | 7 - dist/modules/content/index.js.map | 1 - dist/modules/content/paths.d.ts | 35 ---- dist/modules/content/paths.js | 28 --- dist/modules/content/paths.js.map | 1 - dist/modules/content/resolveRoute.d.ts | 68 ------- dist/modules/content/resolveRoute.js | 111 ----------- dist/modules/content/resolveRoute.js.map | 1 - dist/modules/content/types.d.ts | 29 --- dist/modules/content/types.js | 17 -- dist/modules/content/types.js.map | 1 - dist/modules/email/index.d.ts | 2 - dist/modules/email/index.js | 5 - dist/modules/email/index.js.map | 1 - dist/modules/email/panelSmtpAdapter.d.ts | 32 ---- dist/modules/email/panelSmtpAdapter.js | 83 -------- dist/modules/email/panelSmtpAdapter.js.map | 1 - dist/modules/email/sendEmail.d.ts | 34 ---- dist/modules/email/sendEmail.js | 70 ------- dist/modules/email/sendEmail.js.map | 1 - dist/modules/forms/formsPluginConfig.d.ts | 11 -- dist/modules/forms/formsPluginConfig.js | 37 ---- dist/modules/forms/formsPluginConfig.js.map | 1 - dist/modules/forms/index.d.ts | 8 - dist/modules/forms/index.js | 8 - dist/modules/forms/index.js.map | 1 - dist/modules/forms/rateLimit.d.ts | 14 -- dist/modules/forms/rateLimit.js | 51 ----- dist/modules/forms/rateLimit.js.map | 1 - dist/modules/forms/submitForm.d.ts | 72 ------- dist/modules/forms/submitForm.js | 91 --------- dist/modules/forms/submitForm.js.map | 1 - dist/modules/forms/types.d.ts | 49 ----- dist/modules/forms/types.js | 6 - dist/modules/forms/types.js.map | 1 - dist/modules/forms/validateSubmission.d.ts | 48 ----- dist/modules/forms/validateSubmission.js | 79 -------- dist/modules/forms/validateSubmission.js.map | 1 - .../frontend/createContentHelpers.d.ts | 64 ------- dist/modules/frontend/createContentHelpers.js | 111 ----------- .../frontend/createContentHelpers.js.map | 1 - dist/modules/frontend/index.d.ts | 1 - dist/modules/frontend/index.js | 3 - dist/modules/frontend/index.js.map | 1 - dist/modules/i18n/getLocalizedSlugs.d.ts | 34 ---- dist/modules/i18n/getLocalizedSlugs.js | 39 ---- dist/modules/i18n/getLocalizedSlugs.js.map | 1 - dist/modules/i18n/helpers.d.ts | 17 -- dist/modules/i18n/helpers.js | 22 --- dist/modules/i18n/helpers.js.map | 1 - dist/modules/i18n/index.d.ts | 10 - dist/modules/i18n/index.js | 9 - dist/modules/i18n/index.js.map | 1 - dist/modules/i18n/localeMiddleware.d.ts | 73 ------- dist/modules/i18n/localeMiddleware.js | 67 ------- dist/modules/i18n/localeMiddleware.js.map | 1 - dist/modules/i18n/localizationConfig.d.ts | 9 - dist/modules/i18n/localizationConfig.js | 19 -- dist/modules/i18n/localizationConfig.js.map | 1 - dist/modules/i18n/localizedPath.d.ts | 70 ------- dist/modules/i18n/localizedPath.js | 71 ------- dist/modules/i18n/localizedPath.js.map | 1 - dist/modules/i18n/negotiateLocale.d.ts | 29 --- dist/modules/i18n/negotiateLocale.js | 68 ------- dist/modules/i18n/negotiateLocale.js.map | 1 - dist/modules/i18n/types.d.ts | 20 -- dist/modules/i18n/types.js | 7 - dist/modules/i18n/types.js.map | 1 - dist/modules/i18n/validation.d.ts | 6 - dist/modules/i18n/validation.js | 30 --- dist/modules/i18n/validation.js.map | 1 - dist/modules/pages/getSystemPagePath.d.ts | 49 ----- dist/modules/pages/getSystemPagePath.js | 36 ---- dist/modules/pages/getSystemPagePath.js.map | 1 - dist/modules/pages/index.d.ts | 4 - dist/modules/pages/index.js | 5 - dist/modules/pages/index.js.map | 1 - dist/modules/pages/systemPagesFields.d.ts | 12 -- dist/modules/pages/systemPagesFields.js | 36 ---- dist/modules/pages/systemPagesFields.js.map | 1 - dist/modules/pages/types.d.ts | 26 --- dist/modules/pages/types.js | 12 -- dist/modules/pages/types.js.map | 1 - dist/modules/payload/getGlobal.d.ts | 15 -- dist/modules/payload/getGlobal.js | 19 -- dist/modules/payload/getGlobal.js.map | 1 - dist/modules/payload/getSiteIntegrations.d.ts | 21 -- dist/modules/payload/getSiteIntegrations.js | 22 --- .../payload/getSiteIntegrations.js.map | 1 - dist/modules/payload/getSiteSettings.d.ts | 15 -- dist/modules/payload/getSiteSettings.js | 16 -- dist/modules/payload/getSiteSettings.js.map | 1 - dist/modules/payload/index.d.ts | 4 - dist/modules/payload/index.js | 5 - dist/modules/payload/index.js.map | 1 - dist/modules/seo/autoFillMeta.d.ts | 21 -- dist/modules/seo/autoFillMeta.js | 47 ----- dist/modules/seo/autoFillMeta.js.map | 1 - dist/modules/seo/buildMetadata.d.ts | 69 ------- dist/modules/seo/buildMetadata.js | 76 -------- dist/modules/seo/buildMetadata.js.map | 1 - dist/modules/seo/buildRobots.d.ts | 36 ---- dist/modules/seo/buildRobots.js | 32 ---- dist/modules/seo/buildRobots.js.map | 1 - dist/modules/seo/buildSitemapEntries.d.ts | 61 ------ dist/modules/seo/buildSitemapEntries.js | 158 ---------------- dist/modules/seo/buildSitemapEntries.js.map | 1 - dist/modules/seo/composeTitle.d.ts | 25 --- dist/modules/seo/composeTitle.js | 20 -- dist/modules/seo/composeTitle.js.map | 1 - dist/modules/seo/createMetadataGenerator.d.ts | 74 -------- dist/modules/seo/createMetadataGenerator.js | 68 ------- .../seo/createMetadataGenerator.js.map | 1 - dist/modules/seo/createPageMetadata.d.ts | 59 ------ dist/modules/seo/createPageMetadata.js | 100 ---------- dist/modules/seo/createPageMetadata.js.map | 1 - dist/modules/seo/hreflang.d.ts | 34 ---- dist/modules/seo/hreflang.js | 35 ---- dist/modules/seo/hreflang.js.map | 1 - dist/modules/seo/index.d.ts | 20 -- dist/modules/seo/index.js | 15 -- dist/modules/seo/index.js.map | 1 - dist/modules/seo/injectAutoFillMeta.d.ts | 11 -- dist/modules/seo/injectAutoFillMeta.js | 35 ---- dist/modules/seo/injectAutoFillMeta.js.map | 1 - dist/modules/seo/injectSeoTabs.d.ts | 17 -- dist/modules/seo/injectSeoTabs.js | 79 -------- dist/modules/seo/injectSeoTabs.js.map | 1 - dist/modules/seo/readSiteMetaConfig.d.ts | 24 --- dist/modules/seo/readSiteMetaConfig.js | 26 --- dist/modules/seo/readSiteMetaConfig.js.map | 1 - dist/modules/seo/seoPluginConfig.d.ts | 18 -- dist/modules/seo/seoPluginConfig.js | 40 ---- dist/modules/seo/seoPluginConfig.js.map | 1 - dist/modules/seo/slugsAcrossLocales.d.ts | 18 -- dist/modules/seo/slugsAcrossLocales.js | 24 --- dist/modules/seo/slugsAcrossLocales.js.map | 1 - dist/modules/seo/types.d.ts | 40 ---- dist/modules/seo/types.js | 7 - dist/modules/seo/types.js.map | 1 - dist/modules/slug/buildSlugField.d.ts | 30 --- dist/modules/slug/buildSlugField.js | 55 ------ dist/modules/slug/buildSlugField.js.map | 1 - dist/modules/slug/index.d.ts | 1 - dist/modules/slug/index.js | 3 - dist/modules/slug/index.js.map | 1 - dist/modules/turnstile/Turnstile.d.ts | 30 --- dist/modules/turnstile/Turnstile.js | 64 ------- dist/modules/turnstile/Turnstile.js.map | 1 - dist/modules/turnstile/client.d.ts | 2 - dist/modules/turnstile/client.js | 6 - dist/modules/turnstile/client.js.map | 1 - dist/modules/turnstile/index.d.ts | 1 - dist/modules/turnstile/index.js | 5 - dist/modules/turnstile/index.js.map | 1 - dist/modules/turnstile/verify.d.ts | 23 --- dist/modules/turnstile/verify.js | 41 ---- dist/modules/turnstile/verify.js.map | 1 - dist/plugin.d.ts | 26 --- dist/plugin.js | 100 ---------- dist/plugin.js.map | 1 - dist/types.d.ts | 48 ----- dist/types.js | 6 - dist/types.js.map | 1 - 295 files changed, 9 insertions(+), 5880 deletions(-) delete mode 100644 dist/exports/client.d.ts delete mode 100644 dist/exports/client.js delete mode 100644 dist/exports/client.js.map delete mode 100644 dist/exports/next-middleware.d.ts delete mode 100644 dist/exports/next-middleware.js delete mode 100644 dist/exports/next-middleware.js.map delete mode 100644 dist/exports/rsc.d.ts delete mode 100644 dist/exports/rsc.js delete mode 100644 dist/exports/rsc.js.map delete mode 100644 dist/exports/server.d.ts delete mode 100644 dist/exports/server.js delete mode 100644 dist/exports/server.js.map delete mode 100644 dist/globals/CookieSettings/fields.d.ts delete mode 100644 dist/globals/CookieSettings/fields.js delete mode 100644 dist/globals/CookieSettings/fields.js.map delete mode 100644 dist/globals/CookieSettings/index.d.ts delete mode 100644 dist/globals/CookieSettings/index.js delete mode 100644 dist/globals/CookieSettings/index.js.map delete mode 100644 dist/globals/SiteIntegrations/fields/analytics.d.ts delete mode 100644 dist/globals/SiteIntegrations/fields/analytics.js delete mode 100644 dist/globals/SiteIntegrations/fields/analytics.js.map delete mode 100644 dist/globals/SiteIntegrations/fields/smtp.d.ts delete mode 100644 dist/globals/SiteIntegrations/fields/smtp.js delete mode 100644 dist/globals/SiteIntegrations/fields/smtp.js.map delete mode 100644 dist/globals/SiteIntegrations/fields/storage.d.ts delete mode 100644 dist/globals/SiteIntegrations/fields/storage.js delete mode 100644 dist/globals/SiteIntegrations/fields/storage.js.map delete mode 100644 dist/globals/SiteIntegrations/fields/turnstile.d.ts delete mode 100644 dist/globals/SiteIntegrations/fields/turnstile.js delete mode 100644 dist/globals/SiteIntegrations/fields/turnstile.js.map delete mode 100644 dist/globals/SiteIntegrations/index.d.ts delete mode 100644 dist/globals/SiteIntegrations/index.js delete mode 100644 dist/globals/SiteIntegrations/index.js.map delete mode 100644 dist/globals/SiteSettings/fields/general.d.ts delete mode 100644 dist/globals/SiteSettings/fields/general.js delete mode 100644 dist/globals/SiteSettings/fields/general.js.map delete mode 100644 dist/globals/SiteSettings/fields/navigation.d.ts delete mode 100644 dist/globals/SiteSettings/fields/navigation.js delete mode 100644 dist/globals/SiteSettings/fields/navigation.js.map delete mode 100644 dist/globals/SiteSettings/fields/theme.d.ts delete mode 100644 dist/globals/SiteSettings/fields/theme.js delete mode 100644 dist/globals/SiteSettings/fields/theme.js.map delete mode 100644 dist/globals/SiteSettings/index.d.ts delete mode 100644 dist/globals/SiteSettings/index.js delete mode 100644 dist/globals/SiteSettings/index.js.map delete mode 100644 dist/index.d.ts delete mode 100644 dist/index.js delete mode 100644 dist/index.js.map delete mode 100644 dist/modules/access/access.d.ts delete mode 100644 dist/modules/access/access.js delete mode 100644 dist/modules/access/access.js.map delete mode 100644 dist/modules/access/index.d.ts delete mode 100644 dist/modules/access/index.js delete mode 100644 dist/modules/access/index.js.map delete mode 100644 dist/modules/access/injectRoles.d.ts delete mode 100644 dist/modules/access/injectRoles.js delete mode 100644 dist/modules/access/injectRoles.js.map delete mode 100644 dist/modules/access/predicates.d.ts delete mode 100644 dist/modules/access/predicates.js delete mode 100644 dist/modules/access/predicates.js.map delete mode 100644 dist/modules/access/rolesField.d.ts delete mode 100644 dist/modules/access/rolesField.js delete mode 100644 dist/modules/access/rolesField.js.map delete mode 100644 dist/modules/access/types.d.ts delete mode 100644 dist/modules/access/types.js delete mode 100644 dist/modules/access/types.js.map delete mode 100644 dist/modules/analytics/Analytics.d.ts delete mode 100644 dist/modules/analytics/Analytics.js delete mode 100644 dist/modules/analytics/Analytics.js.map delete mode 100644 dist/modules/analytics/client.d.ts delete mode 100644 dist/modules/analytics/client.js delete mode 100644 dist/modules/analytics/client.js.map delete mode 100644 dist/modules/analytics/getAnalyticsConfig.d.ts delete mode 100644 dist/modules/analytics/getAnalyticsConfig.js delete mode 100644 dist/modules/analytics/getAnalyticsConfig.js.map delete mode 100644 dist/modules/analytics/index.d.ts delete mode 100644 dist/modules/analytics/index.js delete mode 100644 dist/modules/analytics/index.js.map delete mode 100644 dist/modules/analytics/types.d.ts delete mode 100644 dist/modules/analytics/types.js delete mode 100644 dist/modules/analytics/types.js.map delete mode 100644 dist/modules/blocks/RenderBlocks.d.ts delete mode 100644 dist/modules/blocks/RenderBlocks.js delete mode 100644 dist/modules/blocks/RenderBlocks.js.map delete mode 100644 dist/modules/blocks/index.d.ts delete mode 100644 dist/modules/blocks/index.js delete mode 100644 dist/modules/blocks/index.js.map delete mode 100644 dist/modules/blocks/types.d.ts delete mode 100644 dist/modules/blocks/types.js delete mode 100644 dist/modules/blocks/types.js.map delete mode 100644 dist/modules/consent/ConsentContext.d.ts delete mode 100644 dist/modules/consent/ConsentContext.js delete mode 100644 dist/modules/consent/ConsentContext.js.map delete mode 100644 dist/modules/consent/CookieBanner.d.ts delete mode 100644 dist/modules/consent/CookieBanner.js delete mode 100644 dist/modules/consent/CookieBanner.js.map delete mode 100644 dist/modules/consent/CookieButton.d.ts delete mode 100644 dist/modules/consent/CookieButton.js delete mode 100644 dist/modules/consent/CookieButton.js.map delete mode 100644 dist/modules/consent/categories.d.ts delete mode 100644 dist/modules/consent/categories.js delete mode 100644 dist/modules/consent/categories.js.map delete mode 100644 dist/modules/consent/client.d.ts delete mode 100644 dist/modules/consent/client.js delete mode 100644 dist/modules/consent/client.js.map delete mode 100644 dist/modules/consent/getConsentTexts.d.ts delete mode 100644 dist/modules/consent/getConsentTexts.js delete mode 100644 dist/modules/consent/getConsentTexts.js.map delete mode 100644 dist/modules/consent/googleConsent.d.ts delete mode 100644 dist/modules/consent/googleConsent.js delete mode 100644 dist/modules/consent/googleConsent.js.map delete mode 100644 dist/modules/consent/index.d.ts delete mode 100644 dist/modules/consent/index.js delete mode 100644 dist/modules/consent/index.js.map delete mode 100644 dist/modules/consent/storage.d.ts delete mode 100644 dist/modules/consent/storage.js delete mode 100644 dist/modules/consent/storage.js.map delete mode 100644 dist/modules/consent/texts.d.ts delete mode 100644 dist/modules/consent/texts.js delete mode 100644 dist/modules/consent/texts.js.map delete mode 100644 dist/modules/consent/useConsent.d.ts delete mode 100644 dist/modules/consent/useConsent.js delete mode 100644 dist/modules/consent/useConsent.js.map delete mode 100644 dist/modules/content/archiveFields.d.ts delete mode 100644 dist/modules/content/archiveFields.js delete mode 100644 dist/modules/content/archiveFields.js.map delete mode 100644 dist/modules/content/getArchiveEntries.d.ts delete mode 100644 dist/modules/content/getArchiveEntries.js delete mode 100644 dist/modules/content/getArchiveEntries.js.map delete mode 100644 dist/modules/content/index.d.ts delete mode 100644 dist/modules/content/index.js delete mode 100644 dist/modules/content/index.js.map delete mode 100644 dist/modules/content/paths.d.ts delete mode 100644 dist/modules/content/paths.js delete mode 100644 dist/modules/content/paths.js.map delete mode 100644 dist/modules/content/resolveRoute.d.ts delete mode 100644 dist/modules/content/resolveRoute.js delete mode 100644 dist/modules/content/resolveRoute.js.map delete mode 100644 dist/modules/content/types.d.ts delete mode 100644 dist/modules/content/types.js delete mode 100644 dist/modules/content/types.js.map delete mode 100644 dist/modules/email/index.d.ts delete mode 100644 dist/modules/email/index.js delete mode 100644 dist/modules/email/index.js.map delete mode 100644 dist/modules/email/panelSmtpAdapter.d.ts delete mode 100644 dist/modules/email/panelSmtpAdapter.js delete mode 100644 dist/modules/email/panelSmtpAdapter.js.map delete mode 100644 dist/modules/email/sendEmail.d.ts delete mode 100644 dist/modules/email/sendEmail.js delete mode 100644 dist/modules/email/sendEmail.js.map delete mode 100644 dist/modules/forms/formsPluginConfig.d.ts delete mode 100644 dist/modules/forms/formsPluginConfig.js delete mode 100644 dist/modules/forms/formsPluginConfig.js.map delete mode 100644 dist/modules/forms/index.d.ts delete mode 100644 dist/modules/forms/index.js delete mode 100644 dist/modules/forms/index.js.map delete mode 100644 dist/modules/forms/rateLimit.d.ts delete mode 100644 dist/modules/forms/rateLimit.js delete mode 100644 dist/modules/forms/rateLimit.js.map delete mode 100644 dist/modules/forms/submitForm.d.ts delete mode 100644 dist/modules/forms/submitForm.js delete mode 100644 dist/modules/forms/submitForm.js.map delete mode 100644 dist/modules/forms/types.d.ts delete mode 100644 dist/modules/forms/types.js delete mode 100644 dist/modules/forms/types.js.map delete mode 100644 dist/modules/forms/validateSubmission.d.ts delete mode 100644 dist/modules/forms/validateSubmission.js delete mode 100644 dist/modules/forms/validateSubmission.js.map delete mode 100644 dist/modules/frontend/createContentHelpers.d.ts delete mode 100644 dist/modules/frontend/createContentHelpers.js delete mode 100644 dist/modules/frontend/createContentHelpers.js.map delete mode 100644 dist/modules/frontend/index.d.ts delete mode 100644 dist/modules/frontend/index.js delete mode 100644 dist/modules/frontend/index.js.map delete mode 100644 dist/modules/i18n/getLocalizedSlugs.d.ts delete mode 100644 dist/modules/i18n/getLocalizedSlugs.js delete mode 100644 dist/modules/i18n/getLocalizedSlugs.js.map delete mode 100644 dist/modules/i18n/helpers.d.ts delete mode 100644 dist/modules/i18n/helpers.js delete mode 100644 dist/modules/i18n/helpers.js.map delete mode 100644 dist/modules/i18n/index.d.ts delete mode 100644 dist/modules/i18n/index.js delete mode 100644 dist/modules/i18n/index.js.map delete mode 100644 dist/modules/i18n/localeMiddleware.d.ts delete mode 100644 dist/modules/i18n/localeMiddleware.js delete mode 100644 dist/modules/i18n/localeMiddleware.js.map delete mode 100644 dist/modules/i18n/localizationConfig.d.ts delete mode 100644 dist/modules/i18n/localizationConfig.js delete mode 100644 dist/modules/i18n/localizationConfig.js.map delete mode 100644 dist/modules/i18n/localizedPath.d.ts delete mode 100644 dist/modules/i18n/localizedPath.js delete mode 100644 dist/modules/i18n/localizedPath.js.map delete mode 100644 dist/modules/i18n/negotiateLocale.d.ts delete mode 100644 dist/modules/i18n/negotiateLocale.js delete mode 100644 dist/modules/i18n/negotiateLocale.js.map delete mode 100644 dist/modules/i18n/types.d.ts delete mode 100644 dist/modules/i18n/types.js delete mode 100644 dist/modules/i18n/types.js.map delete mode 100644 dist/modules/i18n/validation.d.ts delete mode 100644 dist/modules/i18n/validation.js delete mode 100644 dist/modules/i18n/validation.js.map delete mode 100644 dist/modules/pages/getSystemPagePath.d.ts delete mode 100644 dist/modules/pages/getSystemPagePath.js delete mode 100644 dist/modules/pages/getSystemPagePath.js.map delete mode 100644 dist/modules/pages/index.d.ts delete mode 100644 dist/modules/pages/index.js delete mode 100644 dist/modules/pages/index.js.map delete mode 100644 dist/modules/pages/systemPagesFields.d.ts delete mode 100644 dist/modules/pages/systemPagesFields.js delete mode 100644 dist/modules/pages/systemPagesFields.js.map delete mode 100644 dist/modules/pages/types.d.ts delete mode 100644 dist/modules/pages/types.js delete mode 100644 dist/modules/pages/types.js.map delete mode 100644 dist/modules/payload/getGlobal.d.ts delete mode 100644 dist/modules/payload/getGlobal.js delete mode 100644 dist/modules/payload/getGlobal.js.map delete mode 100644 dist/modules/payload/getSiteIntegrations.d.ts delete mode 100644 dist/modules/payload/getSiteIntegrations.js delete mode 100644 dist/modules/payload/getSiteIntegrations.js.map delete mode 100644 dist/modules/payload/getSiteSettings.d.ts delete mode 100644 dist/modules/payload/getSiteSettings.js delete mode 100644 dist/modules/payload/getSiteSettings.js.map delete mode 100644 dist/modules/payload/index.d.ts delete mode 100644 dist/modules/payload/index.js delete mode 100644 dist/modules/payload/index.js.map delete mode 100644 dist/modules/seo/autoFillMeta.d.ts delete mode 100644 dist/modules/seo/autoFillMeta.js delete mode 100644 dist/modules/seo/autoFillMeta.js.map delete mode 100644 dist/modules/seo/buildMetadata.d.ts delete mode 100644 dist/modules/seo/buildMetadata.js delete mode 100644 dist/modules/seo/buildMetadata.js.map delete mode 100644 dist/modules/seo/buildRobots.d.ts delete mode 100644 dist/modules/seo/buildRobots.js delete mode 100644 dist/modules/seo/buildRobots.js.map delete mode 100644 dist/modules/seo/buildSitemapEntries.d.ts delete mode 100644 dist/modules/seo/buildSitemapEntries.js delete mode 100644 dist/modules/seo/buildSitemapEntries.js.map delete mode 100644 dist/modules/seo/composeTitle.d.ts delete mode 100644 dist/modules/seo/composeTitle.js delete mode 100644 dist/modules/seo/composeTitle.js.map delete mode 100644 dist/modules/seo/createMetadataGenerator.d.ts delete mode 100644 dist/modules/seo/createMetadataGenerator.js delete mode 100644 dist/modules/seo/createMetadataGenerator.js.map delete mode 100644 dist/modules/seo/createPageMetadata.d.ts delete mode 100644 dist/modules/seo/createPageMetadata.js delete mode 100644 dist/modules/seo/createPageMetadata.js.map delete mode 100644 dist/modules/seo/hreflang.d.ts delete mode 100644 dist/modules/seo/hreflang.js delete mode 100644 dist/modules/seo/hreflang.js.map delete mode 100644 dist/modules/seo/index.d.ts delete mode 100644 dist/modules/seo/index.js delete mode 100644 dist/modules/seo/index.js.map delete mode 100644 dist/modules/seo/injectAutoFillMeta.d.ts delete mode 100644 dist/modules/seo/injectAutoFillMeta.js delete mode 100644 dist/modules/seo/injectAutoFillMeta.js.map delete mode 100644 dist/modules/seo/injectSeoTabs.d.ts delete mode 100644 dist/modules/seo/injectSeoTabs.js delete mode 100644 dist/modules/seo/injectSeoTabs.js.map delete mode 100644 dist/modules/seo/readSiteMetaConfig.d.ts delete mode 100644 dist/modules/seo/readSiteMetaConfig.js delete mode 100644 dist/modules/seo/readSiteMetaConfig.js.map delete mode 100644 dist/modules/seo/seoPluginConfig.d.ts delete mode 100644 dist/modules/seo/seoPluginConfig.js delete mode 100644 dist/modules/seo/seoPluginConfig.js.map delete mode 100644 dist/modules/seo/slugsAcrossLocales.d.ts delete mode 100644 dist/modules/seo/slugsAcrossLocales.js delete mode 100644 dist/modules/seo/slugsAcrossLocales.js.map delete mode 100644 dist/modules/seo/types.d.ts delete mode 100644 dist/modules/seo/types.js delete mode 100644 dist/modules/seo/types.js.map delete mode 100644 dist/modules/slug/buildSlugField.d.ts delete mode 100644 dist/modules/slug/buildSlugField.js delete mode 100644 dist/modules/slug/buildSlugField.js.map delete mode 100644 dist/modules/slug/index.d.ts delete mode 100644 dist/modules/slug/index.js delete mode 100644 dist/modules/slug/index.js.map delete mode 100644 dist/modules/turnstile/Turnstile.d.ts delete mode 100644 dist/modules/turnstile/Turnstile.js delete mode 100644 dist/modules/turnstile/Turnstile.js.map delete mode 100644 dist/modules/turnstile/client.d.ts delete mode 100644 dist/modules/turnstile/client.js delete mode 100644 dist/modules/turnstile/client.js.map delete mode 100644 dist/modules/turnstile/index.d.ts delete mode 100644 dist/modules/turnstile/index.js delete mode 100644 dist/modules/turnstile/index.js.map delete mode 100644 dist/modules/turnstile/verify.d.ts delete mode 100644 dist/modules/turnstile/verify.js delete mode 100644 dist/modules/turnstile/verify.js.map delete mode 100644 dist/plugin.d.ts delete mode 100644 dist/plugin.js delete mode 100644 dist/plugin.js.map delete mode 100644 dist/types.d.ts delete mode 100644 dist/types.js delete mode 100644 dist/types.js.map diff --git a/.gitignore b/.gitignore index db8e35d..f98eca6 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,12 @@ next-env.d.ts /playwright-report/ /blob-report/ /playwright/.cache/ +# local db & build artifacts +*.db +*.tgz +0 + +# local db & build artifacts +*.db +*.tgz +0 diff --git a/dist/exports/client.d.ts b/dist/exports/client.d.ts deleted file mode 100644 index c33e6eb..0000000 --- a/dist/exports/client.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -export { Analytics } from '../modules/analytics/client.js'; -/** - * Entry point: ipal-kit/client - * - * Client-side ('use client') exports — React hooks, providers, and UI - * components. Kept separate from the main entry so server bundles don't pull in - * client-only code. - */ -export { ConsentProvider, CookieBanner, CookieButton, useConsent, useConsentContext, } from '../modules/consent/client.js'; -export type { CookieBannerClassNames } from '../modules/consent/client.js'; -export { Turnstile } from '../modules/turnstile/client.js'; -export type { TurnstileProps } from '../modules/turnstile/client.js'; diff --git a/dist/exports/client.js b/dist/exports/client.js deleted file mode 100644 index df1c2ee..0000000 --- a/dist/exports/client.js +++ /dev/null @@ -1,12 +0,0 @@ -'use client'; -export { Analytics } from '../modules/analytics/client.js'; -/** - * Entry point: ipal-kit/client - * - * Client-side ('use client') exports — React hooks, providers, and UI - * components. Kept separate from the main entry so server bundles don't pull in - * client-only code. - */ export { ConsentProvider, CookieBanner, CookieButton, useConsent, useConsentContext } from '../modules/consent/client.js'; -export { Turnstile } from '../modules/turnstile/client.js'; - -//# sourceMappingURL=client.js.map \ No newline at end of file diff --git a/dist/exports/client.js.map b/dist/exports/client.js.map deleted file mode 100644 index 66254f1..0000000 --- a/dist/exports/client.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["'use client'\nexport { Analytics } from '../modules/analytics/client.js'\n/**\n * Entry point: ipal-kit/client\n *\n * Client-side ('use client') exports — React hooks, providers, and UI\n * components. Kept separate from the main entry so server bundles don't pull in\n * client-only code.\n */\nexport {\n ConsentProvider,\n CookieBanner,\n CookieButton,\n useConsent,\n useConsentContext,\n} from '../modules/consent/client.js'\nexport type { CookieBannerClassNames } from '../modules/consent/client.js'\nexport { Turnstile } from '../modules/turnstile/client.js'\nexport type { TurnstileProps } from '../modules/turnstile/client.js'\n"],"names":["Analytics","ConsentProvider","CookieBanner","CookieButton","useConsent","useConsentContext","Turnstile"],"mappings":"AAAA;AACA,SAASA,SAAS,QAAQ,iCAAgC;AAC1D;;;;;;CAMC,GACD,SACEC,eAAe,EACfC,YAAY,EACZC,YAAY,EACZC,UAAU,EACVC,iBAAiB,QACZ,+BAA8B;AAErC,SAASC,SAAS,QAAQ,iCAAgC"} \ No newline at end of file diff --git a/dist/exports/next-middleware.d.ts b/dist/exports/next-middleware.d.ts deleted file mode 100644 index 4e5702f..0000000 --- a/dist/exports/next-middleware.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Entry point: ipal-kit/next/middleware - * - * Re-exports the locale middleware factory and default matcher for use in a - * client project's middleware.ts. Kept as a thin re-export so the Next-facing - * surface is separate from the main package export. - */ -export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from '../modules/i18n/index.js'; -export type { LocaleMiddlewareResult } from '../modules/i18n/index.js'; diff --git a/dist/exports/next-middleware.js b/dist/exports/next-middleware.js deleted file mode 100644 index 3ebd34f..0000000 --- a/dist/exports/next-middleware.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Entry point: ipal-kit/next/middleware - * - * Re-exports the locale middleware factory and default matcher for use in a - * client project's middleware.ts. Kept as a thin re-export so the Next-facing - * surface is separate from the main package export. - */ export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from '../modules/i18n/index.js'; - -//# sourceMappingURL=next-middleware.js.map \ No newline at end of file diff --git a/dist/exports/next-middleware.js.map b/dist/exports/next-middleware.js.map deleted file mode 100644 index ad94411..0000000 --- a/dist/exports/next-middleware.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../src/exports/next-middleware.ts"],"sourcesContent":["/**\n * Entry point: ipal-kit/next/middleware\n *\n * Re-exports the locale middleware factory and default matcher for use in a\n * client project's middleware.ts. Kept as a thin re-export so the Next-facing\n * surface is separate from the main package export.\n */\nexport { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from '../modules/i18n/index.js'\nexport type { LocaleMiddlewareResult } from '../modules/i18n/index.js'\n"],"names":["createLocaleMiddleware","DEFAULT_MIDDLEWARE_MATCHER"],"mappings":"AAAA;;;;;;CAMC,GACD,SAASA,sBAAsB,EAAEC,0BAA0B,QAAQ,2BAA0B"} \ No newline at end of file diff --git a/dist/exports/rsc.d.ts b/dist/exports/rsc.d.ts deleted file mode 100644 index 6aa81d0..0000000 --- a/dist/exports/rsc.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Entry point: ipal-kit/rsc - * - * Server-component exports. RenderBlocks is a React Server Component, so it - * lives here rather than in the main package entry to keep React out of the - * server-config bundle. - */ -export { RenderBlocks } from '../modules/blocks/index.js'; -export type { BlockComponentMap, BlockData, EnhanceProps, RenderBlocksProps, } from '../modules/blocks/index.js'; diff --git a/dist/exports/rsc.js b/dist/exports/rsc.js deleted file mode 100644 index fbe1661..0000000 --- a/dist/exports/rsc.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Entry point: ipal-kit/rsc - * - * Server-component exports. RenderBlocks is a React Server Component, so it - * lives here rather than in the main package entry to keep React out of the - * server-config bundle. - */ export { RenderBlocks } from '../modules/blocks/index.js'; - -//# sourceMappingURL=rsc.js.map \ No newline at end of file diff --git a/dist/exports/rsc.js.map b/dist/exports/rsc.js.map deleted file mode 100644 index 6f4f24d..0000000 --- a/dist/exports/rsc.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["/**\n * Entry point: ipal-kit/rsc\n *\n * Server-component exports. RenderBlocks is a React Server Component, so it\n * lives here rather than in the main package entry to keep React out of the\n * server-config bundle.\n */\nexport { RenderBlocks } from '../modules/blocks/index.js'\nexport type {\n BlockComponentMap,\n BlockData,\n EnhanceProps,\n RenderBlocksProps,\n} from '../modules/blocks/index.js'\n"],"names":["RenderBlocks"],"mappings":"AAAA;;;;;;CAMC,GACD,SAASA,YAAY,QAAQ,6BAA4B"} \ No newline at end of file diff --git a/dist/exports/server.d.ts b/dist/exports/server.d.ts deleted file mode 100644 index 2d85acf..0000000 --- a/dist/exports/server.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Entry point: ipal-kit/server - * - * Server-only runtime functions — these import 'server-only' (Turnstile secret, - * SMTP password, nodemailer). Kept OUT of the main entry so that loading the - * Payload config or running `payload generate:importmap` (both Node scripts, - * no bundler) never pulls in 'server-only', which throws outside a bundled - * server context. - * - * Import these only from Server Actions, route handlers, or server components — - * never from the Payload config or client code. - */ -export { verifyTurnstile } from '../modules/turnstile/index.js'; -export { sendEmail } from '../modules/email/index.js'; -export type { SendEmailArgs, SendEmailResult } from '../modules/email/index.js'; -export { submitForm } from '../modules/forms/index.js'; -export type { SubmitFormArgs, SubmitFormResult } from '../modules/forms/index.js'; diff --git a/dist/exports/server.js b/dist/exports/server.js deleted file mode 100644 index 8b9d79d..0000000 --- a/dist/exports/server.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Entry point: ipal-kit/server - * - * Server-only runtime functions — these import 'server-only' (Turnstile secret, - * SMTP password, nodemailer). Kept OUT of the main entry so that loading the - * Payload config or running `payload generate:importmap` (both Node scripts, - * no bundler) never pulls in 'server-only', which throws outside a bundled - * server context. - * - * Import these only from Server Actions, route handlers, or server components — - * never from the Payload config or client code. - */ export { verifyTurnstile } from '../modules/turnstile/index.js'; -export { sendEmail } from '../modules/email/index.js'; -export { submitForm } from '../modules/forms/index.js'; - -//# sourceMappingURL=server.js.map \ No newline at end of file diff --git a/dist/exports/server.js.map b/dist/exports/server.js.map deleted file mode 100644 index 8e406f9..0000000 --- a/dist/exports/server.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../src/exports/server.ts"],"sourcesContent":["/**\n * Entry point: ipal-kit/server\n *\n * Server-only runtime functions — these import 'server-only' (Turnstile secret,\n * SMTP password, nodemailer). Kept OUT of the main entry so that loading the\n * Payload config or running `payload generate:importmap` (both Node scripts,\n * no bundler) never pulls in 'server-only', which throws outside a bundled\n * server context.\n *\n * Import these only from Server Actions, route handlers, or server components —\n * never from the Payload config or client code.\n */\nexport { verifyTurnstile } from '../modules/turnstile/index.js'\nexport { sendEmail } from '../modules/email/index.js'\nexport type { SendEmailArgs, SendEmailResult } from '../modules/email/index.js'\nexport { submitForm } from '../modules/forms/index.js'\nexport type { SubmitFormArgs, SubmitFormResult } from '../modules/forms/index.js'\n"],"names":["verifyTurnstile","sendEmail","submitForm"],"mappings":"AAAA;;;;;;;;;;;CAWC,GACD,SAASA,eAAe,QAAQ,gCAA+B;AAC/D,SAASC,SAAS,QAAQ,4BAA2B;AAErD,SAASC,UAAU,QAAQ,4BAA2B"} \ No newline at end of file diff --git a/dist/globals/CookieSettings/fields.d.ts b/dist/globals/CookieSettings/fields.d.ts deleted file mode 100644 index 98bfbd5..0000000 --- a/dist/globals/CookieSettings/fields.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Field } from 'payload'; -/** - * Fields for the CookieSettings global — GDPR consent banner copy, editable - * per locale. The privacy-policy link is NOT stored here: it comes from the - * pages module (system page role 'privacyPolicy'), keeping one source of truth. - */ -export declare const cookieSettingsFields: Field[]; diff --git a/dist/globals/CookieSettings/fields.js b/dist/globals/CookieSettings/fields.js deleted file mode 100644 index 8f08ab6..0000000 --- a/dist/globals/CookieSettings/fields.js +++ /dev/null @@ -1,87 +0,0 @@ -import { CONSENT_CATEGORIES } from '../../modules/consent/index.js'; -/** - * Fields for the CookieSettings global — GDPR consent banner copy, editable - * per locale. The privacy-policy link is NOT stored here: it comes from the - * pages module (system page role 'privacyPolicy'), keeping one source of truth. - */ export const cookieSettingsFields = [ - { - name: 'message', - type: 'textarea', - admin: { - description: 'Main consent message shown in the banner.' - }, - localized: true - }, - { - name: 'settingsTitle', - type: 'text', - admin: { - description: 'Heading for the detailed settings panel.' - }, - localized: true - }, - { - name: 'buttons', - type: 'group', - admin: { - description: 'Button labels.' - }, - fields: [ - { - name: 'acceptAll', - type: 'text', - localized: true - }, - { - name: 'reject', - type: 'text', - localized: true - }, - { - name: 'settings', - type: 'text', - localized: true - }, - { - name: 'save', - type: 'text', - localized: true - }, - { - name: 'back', - type: 'text', - localized: true - } - ] - }, - { - name: 'categories', - type: 'array', - admin: { - description: 'Per-category titles and descriptions.' - }, - fields: [ - { - name: 'key', - type: 'select', - options: CONSENT_CATEGORIES.map((c)=>({ - label: c, - value: c - })), - required: true - }, - { - name: 'title', - type: 'text', - localized: true - }, - { - name: 'description', - type: 'textarea', - localized: true - } - ] - } -]; - -//# sourceMappingURL=fields.js.map \ No newline at end of file diff --git a/dist/globals/CookieSettings/fields.js.map b/dist/globals/CookieSettings/fields.js.map deleted file mode 100644 index d8bbdcb..0000000 --- a/dist/globals/CookieSettings/fields.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/globals/CookieSettings/fields.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nimport { CONSENT_CATEGORIES } from '../../modules/consent/index.js'\n\n/**\n * Fields for the CookieSettings global — GDPR consent banner copy, editable\n * per locale. The privacy-policy link is NOT stored here: it comes from the\n * pages module (system page role 'privacyPolicy'), keeping one source of truth.\n */\nexport const cookieSettingsFields: Field[] = [\n {\n name: 'message',\n type: 'textarea',\n admin: {\n description: 'Main consent message shown in the banner.',\n },\n localized: true,\n },\n {\n name: 'settingsTitle',\n type: 'text',\n admin: {\n description: 'Heading for the detailed settings panel.',\n },\n localized: true,\n },\n {\n name: 'buttons',\n type: 'group',\n admin: {\n description: 'Button labels.',\n },\n fields: [\n { name: 'acceptAll', type: 'text', localized: true },\n { name: 'reject', type: 'text', localized: true },\n { name: 'settings', type: 'text', localized: true },\n { name: 'save', type: 'text', localized: true },\n { name: 'back', type: 'text', localized: true },\n ],\n },\n {\n name: 'categories',\n type: 'array',\n admin: {\n description: 'Per-category titles and descriptions.',\n },\n fields: [\n {\n name: 'key',\n type: 'select',\n options: CONSENT_CATEGORIES.map((c) => ({ label: c, value: c })),\n required: true,\n },\n { name: 'title', type: 'text', localized: true },\n { name: 'description', type: 'textarea', localized: true },\n ],\n },\n]\n"],"names":["CONSENT_CATEGORIES","cookieSettingsFields","name","type","admin","description","localized","fields","options","map","c","label","value","required"],"mappings":"AAEA,SAASA,kBAAkB,QAAQ,iCAAgC;AAEnE;;;;CAIC,GACD,OAAO,MAAMC,uBAAgC;IAC3C;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,WAAW;IACb;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,WAAW;IACb;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAE,QAAQ;YACN;gBAAEL,MAAM;gBAAaC,MAAM;gBAAQG,WAAW;YAAK;YACnD;gBAAEJ,MAAM;gBAAUC,MAAM;gBAAQG,WAAW;YAAK;YAChD;gBAAEJ,MAAM;gBAAYC,MAAM;gBAAQG,WAAW;YAAK;YAClD;gBAAEJ,MAAM;gBAAQC,MAAM;gBAAQG,WAAW;YAAK;YAC9C;gBAAEJ,MAAM;gBAAQC,MAAM;gBAAQG,WAAW;YAAK;SAC/C;IACH;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAE,QAAQ;YACN;gBACEL,MAAM;gBACNC,MAAM;gBACNK,SAASR,mBAAmBS,GAAG,CAAC,CAACC,IAAO,CAAA;wBAAEC,OAAOD;wBAAGE,OAAOF;oBAAE,CAAA;gBAC7DG,UAAU;YACZ;YACA;gBAAEX,MAAM;gBAASC,MAAM;gBAAQG,WAAW;YAAK;YAC/C;gBAAEJ,MAAM;gBAAeC,MAAM;gBAAYG,WAAW;YAAK;SAC1D;IACH;CACD,CAAA"} \ No newline at end of file diff --git a/dist/globals/CookieSettings/index.d.ts b/dist/globals/CookieSettings/index.d.ts deleted file mode 100644 index 3e21127..0000000 --- a/dist/globals/CookieSettings/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { GlobalConfig } from 'payload'; -/** - * Builds the CookieSettings global — consent banner copy managed by editors. - * Public read (the banner needs it on the frontend for anonymous visitors). - */ -export declare function buildCookieSettings(): GlobalConfig; diff --git a/dist/globals/CookieSettings/index.js b/dist/globals/CookieSettings/index.js deleted file mode 100644 index eef7b2f..0000000 --- a/dist/globals/CookieSettings/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import { cookieSettingsFields } from './fields.js'; -/** - * Builds the CookieSettings global — consent banner copy managed by editors. - * Public read (the banner needs it on the frontend for anonymous visitors). - */ export function buildCookieSettings() { - return { - slug: 'cookie-settings', - access: { - read: ()=>true - }, - admin: { - group: 'Settings' - }, - fields: cookieSettingsFields, - label: 'Cookie Settings' - }; -} - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/globals/CookieSettings/index.js.map b/dist/globals/CookieSettings/index.js.map deleted file mode 100644 index f2c58d5..0000000 --- a/dist/globals/CookieSettings/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/globals/CookieSettings/index.ts"],"sourcesContent":["import type { GlobalConfig } from 'payload'\n\nimport { cookieSettingsFields } from './fields.js'\n\n/**\n * Builds the CookieSettings global — consent banner copy managed by editors.\n * Public read (the banner needs it on the frontend for anonymous visitors).\n */\nexport function buildCookieSettings(): GlobalConfig {\n return {\n slug: 'cookie-settings',\n access: {\n read: () => true,\n },\n admin: {\n group: 'Settings',\n },\n fields: cookieSettingsFields,\n label: 'Cookie Settings',\n }\n}\n"],"names":["cookieSettingsFields","buildCookieSettings","slug","access","read","admin","group","fields","label"],"mappings":"AAEA,SAASA,oBAAoB,QAAQ,cAAa;AAElD;;;CAGC,GACD,OAAO,SAASC;IACd,OAAO;QACLC,MAAM;QACNC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,OAAO;YACLC,OAAO;QACT;QACAC,QAAQP;QACRQ,OAAO;IACT;AACF"} \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/fields/analytics.d.ts b/dist/globals/SiteIntegrations/fields/analytics.d.ts deleted file mode 100644 index 518dfbd..0000000 --- a/dist/globals/SiteIntegrations/fields/analytics.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Field } from 'payload'; -/** - * Analytics configuration — GA4 measurement ID and GTM container ID. - * IDs are public (exposed client-side), so no read restriction needed. - */ -export declare const analyticsFields: Field[]; diff --git a/dist/globals/SiteIntegrations/fields/analytics.js b/dist/globals/SiteIntegrations/fields/analytics.js deleted file mode 100644 index 31e3543..0000000 --- a/dist/globals/SiteIntegrations/fields/analytics.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Analytics configuration — GA4 measurement ID and GTM container ID. - * IDs are public (exposed client-side), so no read restriction needed. - */ export const analyticsFields = [ - { - name: 'ga4MeasurementId', - type: 'text', - admin: { - description: 'Google Analytics 4 Measurement ID.', - placeholder: 'G-XXXXXXXXXX' - } - }, - { - name: 'gtmContainerId', - type: 'text', - admin: { - description: 'Google Tag Manager container ID.', - placeholder: 'GTM-XXXXXXX' - } - } -]; - -//# sourceMappingURL=analytics.js.map \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/fields/analytics.js.map b/dist/globals/SiteIntegrations/fields/analytics.js.map deleted file mode 100644 index 34eed37..0000000 --- a/dist/globals/SiteIntegrations/fields/analytics.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../../src/globals/SiteIntegrations/fields/analytics.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Analytics configuration — GA4 measurement ID and GTM container ID.\n * IDs are public (exposed client-side), so no read restriction needed.\n */\nexport const analyticsFields: Field[] = [\n {\n name: 'ga4MeasurementId',\n type: 'text',\n admin: {\n description: 'Google Analytics 4 Measurement ID.',\n placeholder: 'G-XXXXXXXXXX',\n },\n },\n {\n name: 'gtmContainerId',\n type: 'text',\n admin: {\n description: 'Google Tag Manager container ID.',\n placeholder: 'GTM-XXXXXXX',\n },\n },\n]\n"],"names":["analyticsFields","name","type","admin","description","placeholder"],"mappings":"AAEA;;;CAGC,GACD,OAAO,MAAMA,kBAA2B;IACtC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;YACbC,aAAa;QACf;IACF;IACA;QACEJ,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;YACbC,aAAa;QACf;IACF;CACD,CAAA"} \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/fields/smtp.d.ts b/dist/globals/SiteIntegrations/fields/smtp.d.ts deleted file mode 100644 index ed0419f..0000000 --- a/dist/globals/SiteIntegrations/fields/smtp.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Field } from 'payload'; -/** - * SMTP transport settings for outbound email. - * - * Protected at the global level (SiteIntegrations requires an authenticated - * user), so all fields — including the password — stay editable in the admin - * panel while remaining inaccessible to anonymous API requests. - */ -export declare const smtpFields: Field[]; diff --git a/dist/globals/SiteIntegrations/fields/smtp.js b/dist/globals/SiteIntegrations/fields/smtp.js deleted file mode 100644 index 748dc0e..0000000 --- a/dist/globals/SiteIntegrations/fields/smtp.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * SMTP transport settings for outbound email. - * - * Protected at the global level (SiteIntegrations requires an authenticated - * user), so all fields — including the password — stay editable in the admin - * panel while remaining inaccessible to anonymous API requests. - */ export const smtpFields = [ - { - type: 'row', - fields: [ - { - name: 'smtpHost', - type: 'text', - admin: { - placeholder: 'smtp.example.com', - width: '70%' - } - }, - { - name: 'smtpPort', - type: 'number', - admin: { - width: '30%' - }, - defaultValue: 587 - } - ] - }, - { - name: 'smtpUser', - type: 'text', - admin: { - description: 'SMTP account username.' - } - }, - { - name: 'smtpPassword', - type: 'text', - admin: { - description: 'SMTP account password.' - } - }, - { - name: 'smtpFromAddress', - type: 'email', - admin: { - description: 'Default "from" address for outgoing mail.' - } - }, - { - name: 'smtpFromName', - type: 'text', - admin: { - description: 'Default "from" display name.' - } - } -]; - -//# sourceMappingURL=smtp.js.map \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/fields/smtp.js.map b/dist/globals/SiteIntegrations/fields/smtp.js.map deleted file mode 100644 index 6ca3457..0000000 --- a/dist/globals/SiteIntegrations/fields/smtp.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../../src/globals/SiteIntegrations/fields/smtp.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * SMTP transport settings for outbound email.\n *\n * Protected at the global level (SiteIntegrations requires an authenticated\n * user), so all fields — including the password — stay editable in the admin\n * panel while remaining inaccessible to anonymous API requests.\n */\nexport const smtpFields: Field[] = [\n {\n type: 'row',\n fields: [\n {\n name: 'smtpHost',\n type: 'text',\n admin: { placeholder: 'smtp.example.com', width: '70%' },\n },\n {\n name: 'smtpPort',\n type: 'number',\n admin: { width: '30%' },\n defaultValue: 587,\n },\n ],\n },\n {\n name: 'smtpUser',\n type: 'text',\n admin: {\n description: 'SMTP account username.',\n },\n },\n {\n name: 'smtpPassword',\n type: 'text',\n admin: {\n description: 'SMTP account password.',\n },\n },\n {\n name: 'smtpFromAddress',\n type: 'email',\n admin: {\n description: 'Default \"from\" address for outgoing mail.',\n },\n },\n {\n name: 'smtpFromName',\n type: 'text',\n admin: {\n description: 'Default \"from\" display name.',\n },\n },\n]\n"],"names":["smtpFields","type","fields","name","admin","placeholder","width","defaultValue","description"],"mappings":"AAEA;;;;;;CAMC,GACD,OAAO,MAAMA,aAAsB;IACjC;QACEC,MAAM;QACNC,QAAQ;YACN;gBACEC,MAAM;gBACNF,MAAM;gBACNG,OAAO;oBAAEC,aAAa;oBAAoBC,OAAO;gBAAM;YACzD;YACA;gBACEH,MAAM;gBACNF,MAAM;gBACNG,OAAO;oBAAEE,OAAO;gBAAM;gBACtBC,cAAc;YAChB;SACD;IACH;IACA;QACEJ,MAAM;QACNF,MAAM;QACNG,OAAO;YACLI,aAAa;QACf;IACF;IACA;QACEL,MAAM;QACNF,MAAM;QACNG,OAAO;YACLI,aAAa;QACf;IACF;IACA;QACEL,MAAM;QACNF,MAAM;QACNG,OAAO;YACLI,aAAa;QACf;IACF;IACA;QACEL,MAAM;QACNF,MAAM;QACNG,OAAO;YACLI,aAAa;QACf;IACF;CACD,CAAA"} \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/fields/storage.d.ts b/dist/globals/SiteIntegrations/fields/storage.d.ts deleted file mode 100644 index 1b47103..0000000 --- a/dist/globals/SiteIntegrations/fields/storage.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Field } from 'payload'; -/** - * Cloudflare R2 storage credentials. - * Reserved for future use — media offloading to R2. - * - * Protected at the global level (SiteIntegrations requires an authenticated - * user), so the access keys stay editable in the admin panel while remaining - * inaccessible to anonymous API requests. - */ -export declare const storageFields: Field[]; diff --git a/dist/globals/SiteIntegrations/fields/storage.js b/dist/globals/SiteIntegrations/fields/storage.js deleted file mode 100644 index f700fe0..0000000 --- a/dist/globals/SiteIntegrations/fields/storage.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Cloudflare R2 storage credentials. - * Reserved for future use — media offloading to R2. - * - * Protected at the global level (SiteIntegrations requires an authenticated - * user), so the access keys stay editable in the admin panel while remaining - * inaccessible to anonymous API requests. - */ export const storageFields = [ - { - name: 'r2Bucket', - type: 'text', - admin: { - description: 'R2 bucket name.' - } - }, - { - name: 'r2Endpoint', - type: 'text', - admin: { - description: 'R2 S3-compatible endpoint URL.' - } - }, - { - name: 'r2AccessKeyId', - type: 'text', - admin: { - description: 'R2 access key ID.' - } - }, - { - name: 'r2SecretAccessKey', - type: 'text', - admin: { - description: 'R2 secret access key.' - } - } -]; - -//# sourceMappingURL=storage.js.map \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/fields/storage.js.map b/dist/globals/SiteIntegrations/fields/storage.js.map deleted file mode 100644 index f84dc37..0000000 --- a/dist/globals/SiteIntegrations/fields/storage.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../../src/globals/SiteIntegrations/fields/storage.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Cloudflare R2 storage credentials.\n * Reserved for future use — media offloading to R2.\n *\n * Protected at the global level (SiteIntegrations requires an authenticated\n * user), so the access keys stay editable in the admin panel while remaining\n * inaccessible to anonymous API requests.\n */\nexport const storageFields: Field[] = [\n {\n name: 'r2Bucket',\n type: 'text',\n admin: {\n description: 'R2 bucket name.',\n },\n },\n {\n name: 'r2Endpoint',\n type: 'text',\n admin: {\n description: 'R2 S3-compatible endpoint URL.',\n },\n },\n {\n name: 'r2AccessKeyId',\n type: 'text',\n admin: {\n description: 'R2 access key ID.',\n },\n },\n {\n name: 'r2SecretAccessKey',\n type: 'text',\n admin: {\n description: 'R2 secret access key.',\n },\n },\n]\n"],"names":["storageFields","name","type","admin","description"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,MAAMA,gBAAyB;IACpC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;CACD,CAAA"} \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/fields/turnstile.d.ts b/dist/globals/SiteIntegrations/fields/turnstile.d.ts deleted file mode 100644 index 5963ee3..0000000 --- a/dist/globals/SiteIntegrations/fields/turnstile.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Field } from 'payload'; -/** - * Cloudflare Turnstile credentials. - * - * siteKey is public (rendered in the widget); secretKey is used for - * server-side verification. Both are protected at the global level - * (SiteIntegrations requires an authenticated user) rather than per-field, - * so they remain editable in the admin panel. - */ -export declare const turnstileFields: Field[]; diff --git a/dist/globals/SiteIntegrations/fields/turnstile.js b/dist/globals/SiteIntegrations/fields/turnstile.js deleted file mode 100644 index be87901..0000000 --- a/dist/globals/SiteIntegrations/fields/turnstile.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Cloudflare Turnstile credentials. - * - * siteKey is public (rendered in the widget); secretKey is used for - * server-side verification. Both are protected at the global level - * (SiteIntegrations requires an authenticated user) rather than per-field, - * so they remain editable in the admin panel. - */ export const turnstileFields = [ - { - name: 'turnstileSiteKey', - type: 'text', - admin: { - description: 'Public site key rendered in the Turnstile widget.' - } - }, - { - name: 'turnstileSecretKey', - type: 'text', - admin: { - description: 'Secret key used for server-side verification.' - } - } -]; - -//# sourceMappingURL=turnstile.js.map \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/fields/turnstile.js.map b/dist/globals/SiteIntegrations/fields/turnstile.js.map deleted file mode 100644 index 634b86a..0000000 --- a/dist/globals/SiteIntegrations/fields/turnstile.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../../src/globals/SiteIntegrations/fields/turnstile.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Cloudflare Turnstile credentials.\n *\n * siteKey is public (rendered in the widget); secretKey is used for\n * server-side verification. Both are protected at the global level\n * (SiteIntegrations requires an authenticated user) rather than per-field,\n * so they remain editable in the admin panel.\n */\nexport const turnstileFields: Field[] = [\n {\n name: 'turnstileSiteKey',\n type: 'text',\n admin: {\n description: 'Public site key rendered in the Turnstile widget.',\n },\n },\n {\n name: 'turnstileSecretKey',\n type: 'text',\n admin: {\n description: 'Secret key used for server-side verification.',\n },\n },\n]\n"],"names":["turnstileFields","name","type","admin","description"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,MAAMA,kBAA2B;IACtC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;IACA;QACEH,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;IACF;CACD,CAAA"} \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/index.d.ts b/dist/globals/SiteIntegrations/index.d.ts deleted file mode 100644 index 52e35d7..0000000 --- a/dist/globals/SiteIntegrations/index.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Field, GlobalConfig } from 'payload'; -type BuildSiteIntegrationsArgs = { - /** Extra fields injected by the client project */ - additionalFields?: Field[]; -}; -/** - * Builds the SiteIntegrations global. - * - * Holds third-party service credentials. Access is enforced at the global - * level — the whole global requires an authenticated user — so secrets stay - * out of anonymous API responses while remaining editable in the admin panel - * and readable via the server-side Local API. (Field-level read:false was - * avoided because it also hides fields from the admin UI, making them - * impossible to enter.) - * - * Unnamed tabs keep data flat (siteIntegrations.ga4MeasurementId). - */ -export declare function buildSiteIntegrations({ additionalFields, }?: BuildSiteIntegrationsArgs): GlobalConfig; -export {}; diff --git a/dist/globals/SiteIntegrations/index.js b/dist/globals/SiteIntegrations/index.js deleted file mode 100644 index caabdad..0000000 --- a/dist/globals/SiteIntegrations/index.js +++ /dev/null @@ -1,62 +0,0 @@ -import { isAdmin } from '../../modules/access/index.js'; -import { analyticsFields } from './fields/analytics.js'; -import { smtpFields } from './fields/smtp.js'; -import { storageFields } from './fields/storage.js'; -import { turnstileFields } from './fields/turnstile.js'; -/** - * Builds the SiteIntegrations global. - * - * Holds third-party service credentials. Access is enforced at the global - * level — the whole global requires an authenticated user — so secrets stay - * out of anonymous API responses while remaining editable in the admin panel - * and readable via the server-side Local API. (Field-level read:false was - * avoided because it also hides fields from the admin UI, making them - * impossible to enter.) - * - * Unnamed tabs keep data flat (siteIntegrations.ga4MeasurementId). - */ export function buildSiteIntegrations({ additionalFields } = {}) { - return { - slug: 'site-integrations', - access: { - // Admin-only — secrets live here. Anonymous and non-admin users get - // nothing through the API; admins read/edit in the panel and via Local API. - read: ({ req: { user } })=>isAdmin(user), - update: ({ req: { user } })=>isAdmin(user) - }, - admin: { - group: 'Settings' - }, - fields: [ - { - type: 'tabs', - tabs: [ - { - fields: analyticsFields, - label: 'Analytics' - }, - { - fields: turnstileFields, - label: 'Turnstile' - }, - { - fields: smtpFields, - label: 'SMTP' - }, - { - fields: storageFields, - label: 'Storage' - }, - ...additionalFields?.length ? [ - { - fields: additionalFields, - label: 'Custom' - } - ] : [] - ] - } - ], - label: 'Site Integrations' - }; -} - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/globals/SiteIntegrations/index.js.map b/dist/globals/SiteIntegrations/index.js.map deleted file mode 100644 index 7aa28fa..0000000 --- a/dist/globals/SiteIntegrations/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/globals/SiteIntegrations/index.ts"],"sourcesContent":["import type { Field, GlobalConfig } from 'payload'\n\nimport { isAdmin } from '../../modules/access/index.js'\nimport { analyticsFields } from './fields/analytics.js'\nimport { smtpFields } from './fields/smtp.js'\nimport { storageFields } from './fields/storage.js'\nimport { turnstileFields } from './fields/turnstile.js'\n\ntype BuildSiteIntegrationsArgs = {\n /** Extra fields injected by the client project */\n additionalFields?: Field[]\n}\n\n/**\n * Builds the SiteIntegrations global.\n *\n * Holds third-party service credentials. Access is enforced at the global\n * level — the whole global requires an authenticated user — so secrets stay\n * out of anonymous API responses while remaining editable in the admin panel\n * and readable via the server-side Local API. (Field-level read:false was\n * avoided because it also hides fields from the admin UI, making them\n * impossible to enter.)\n *\n * Unnamed tabs keep data flat (siteIntegrations.ga4MeasurementId).\n */\nexport function buildSiteIntegrations({\n additionalFields,\n}: BuildSiteIntegrationsArgs = {}): GlobalConfig {\n return {\n slug: 'site-integrations',\n access: {\n // Admin-only — secrets live here. Anonymous and non-admin users get\n // nothing through the API; admins read/edit in the panel and via Local API.\n read: ({ req: { user } }) => isAdmin(user),\n update: ({ req: { user } }) => isAdmin(user),\n },\n admin: {\n group: 'Settings',\n },\n fields: [\n {\n type: 'tabs',\n tabs: [\n { fields: analyticsFields, label: 'Analytics' },\n { fields: turnstileFields, label: 'Turnstile' },\n { fields: smtpFields, label: 'SMTP' },\n { fields: storageFields, label: 'Storage' },\n ...(additionalFields?.length ? [{ fields: additionalFields, label: 'Custom' }] : []),\n ],\n },\n ],\n label: 'Site Integrations',\n }\n}\n"],"names":["isAdmin","analyticsFields","smtpFields","storageFields","turnstileFields","buildSiteIntegrations","additionalFields","slug","access","read","req","user","update","admin","group","fields","type","tabs","label","length"],"mappings":"AAEA,SAASA,OAAO,QAAQ,gCAA+B;AACvD,SAASC,eAAe,QAAQ,wBAAuB;AACvD,SAASC,UAAU,QAAQ,mBAAkB;AAC7C,SAASC,aAAa,QAAQ,sBAAqB;AACnD,SAASC,eAAe,QAAQ,wBAAuB;AAOvD;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,sBAAsB,EACpCC,gBAAgB,EACU,GAAG,CAAC,CAAC;IAC/B,OAAO;QACLC,MAAM;QACNC,QAAQ;YACN,oEAAoE;YACpE,4EAA4E;YAC5EC,MAAM,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKX,QAAQW;YACrCC,QAAQ,CAAC,EAAEF,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKX,QAAQW;QACzC;QACAE,OAAO;YACLC,OAAO;QACT;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;oBACJ;wBAAEF,QAAQd;wBAAiBiB,OAAO;oBAAY;oBAC9C;wBAAEH,QAAQX;wBAAiBc,OAAO;oBAAY;oBAC9C;wBAAEH,QAAQb;wBAAYgB,OAAO;oBAAO;oBACpC;wBAAEH,QAAQZ;wBAAee,OAAO;oBAAU;uBACtCZ,kBAAkBa,SAAS;wBAAC;4BAAEJ,QAAQT;4BAAkBY,OAAO;wBAAS;qBAAE,GAAG,EAAE;iBACpF;YACH;SACD;QACDA,OAAO;IACT;AACF"} \ No newline at end of file diff --git a/dist/globals/SiteSettings/fields/general.d.ts b/dist/globals/SiteSettings/fields/general.d.ts deleted file mode 100644 index 0a7c294..0000000 --- a/dist/globals/SiteSettings/fields/general.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Field } from 'payload'; -/** - * General site identity fields. - * Consumed by SEO/OG (siteName, defaultShareImage) and frontend (logo). - */ -export declare const generalFields: Field[]; diff --git a/dist/globals/SiteSettings/fields/general.js b/dist/globals/SiteSettings/fields/general.js deleted file mode 100644 index 50f4797..0000000 --- a/dist/globals/SiteSettings/fields/general.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * General site identity fields. - * Consumed by SEO/OG (siteName, defaultShareImage) and frontend (logo). - */ export const generalFields = [ - { - name: 'siteName', - type: 'text', - admin: { - description: 'Used in page titles and Open Graph metadata.' - }, - localized: true, - required: true - }, - { - name: 'titleOrder', - type: 'select', - admin: { - description: 'Which comes first in browser tabs.' - }, - defaultValue: 'page-first', - options: [ - { - label: 'Page first — About Us | Acme', - value: 'page-first' - }, - { - label: 'Site first — Acme | About Us', - value: 'site-first' - } - ] - }, - { - name: 'titleSeparator', - type: 'select', - admin: { - description: 'Separates the page title from the site name in browser tabs.' - }, - defaultValue: '|', - options: [ - { - label: 'Pipe — Page | Site', - value: '|' - }, - { - label: 'Dash — Page – Site', - value: '–' - }, - { - label: 'Hyphen — Page - Site', - value: '-' - }, - { - label: 'Bullet — Page · Site', - value: '·' - }, - { - label: 'Slash — Page / Site', - value: '/' - } - ] - }, - { - name: 'logo', - type: 'upload', - admin: { - description: 'Primary site logo.' - }, - relationTo: 'media' - }, - { - name: 'defaultShareImage', - type: 'upload', - admin: { - description: 'Fallback Open Graph image when a page has none.' - }, - relationTo: 'media' - }, - { - name: 'favicon', - type: 'upload', - admin: { - description: 'Square source icon (PNG or SVG) for the browser tab. Rendered by the frontend.' - }, - relationTo: 'media' - } -]; - -//# sourceMappingURL=general.js.map \ No newline at end of file diff --git a/dist/globals/SiteSettings/fields/general.js.map b/dist/globals/SiteSettings/fields/general.js.map deleted file mode 100644 index 003be51..0000000 --- a/dist/globals/SiteSettings/fields/general.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../../src/globals/SiteSettings/fields/general.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * General site identity fields.\n * Consumed by SEO/OG (siteName, defaultShareImage) and frontend (logo).\n */\nexport const generalFields: Field[] = [\n {\n name: 'siteName',\n type: 'text',\n admin: {\n description: 'Used in page titles and Open Graph metadata.',\n },\n localized: true,\n required: true,\n },\n {\n name: 'titleOrder',\n type: 'select',\n admin: {\n description: 'Which comes first in browser tabs.',\n },\n defaultValue: 'page-first',\n options: [\n { label: 'Page first — About Us | Acme', value: 'page-first' },\n { label: 'Site first — Acme | About Us', value: 'site-first' },\n ],\n },\n {\n name: 'titleSeparator',\n type: 'select',\n admin: {\n description: 'Separates the page title from the site name in browser tabs.',\n },\n defaultValue: '|',\n options: [\n { label: 'Pipe — Page | Site', value: '|' },\n { label: 'Dash — Page – Site', value: '–' },\n { label: 'Hyphen — Page - Site', value: '-' },\n { label: 'Bullet — Page · Site', value: '·' },\n { label: 'Slash — Page / Site', value: '/' },\n ],\n },\n {\n name: 'logo',\n type: 'upload',\n admin: {\n description: 'Primary site logo.',\n },\n relationTo: 'media',\n },\n {\n name: 'defaultShareImage',\n type: 'upload',\n admin: {\n description: 'Fallback Open Graph image when a page has none.',\n },\n relationTo: 'media',\n },\n {\n name: 'favicon',\n type: 'upload',\n admin: {\n description: 'Square source icon (PNG or SVG) for the browser tab. Rendered by the frontend.',\n },\n relationTo: 'media',\n },\n]\n"],"names":["generalFields","name","type","admin","description","localized","required","defaultValue","options","label","value","relationTo"],"mappings":"AAEA;;;CAGC,GACD,OAAO,MAAMA,gBAAyB;IACpC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,WAAW;QACXC,UAAU;IACZ;IACA;QACEL,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAG,cAAc;QACdC,SAAS;YACP;gBAAEC,OAAO;gBAAkCC,OAAO;YAAa;YAC/D;gBAAED,OAAO;gBAAkCC,OAAO;YAAa;SAChE;IACH;IACA;QACET,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAG,cAAc;QACdC,SAAS;YACP;gBAAEC,OAAO;gBAAwBC,OAAO;YAAI;YAC5C;gBAAED,OAAO;gBAAwBC,OAAO;YAAI;YAC5C;gBAAED,OAAO;gBAA0BC,OAAO;YAAI;YAC9C;gBAAED,OAAO;gBAA0BC,OAAO;YAAI;YAC9C;gBAAED,OAAO;gBAAyBC,OAAO;YAAI;SAC9C;IACH;IACA;QACET,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAO,YAAY;IACd;IACA;QACEV,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAO,YAAY;IACd;IACA;QACEV,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAO,YAAY;IACd;CACD,CAAA"} \ No newline at end of file diff --git a/dist/globals/SiteSettings/fields/navigation.d.ts b/dist/globals/SiteSettings/fields/navigation.d.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/dist/globals/SiteSettings/fields/navigation.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/dist/globals/SiteSettings/fields/navigation.js b/dist/globals/SiteSettings/fields/navigation.js deleted file mode 100644 index 0dd88ba..0000000 --- a/dist/globals/SiteSettings/fields/navigation.js +++ /dev/null @@ -1,2 +0,0 @@ - -//# sourceMappingURL=navigation.js.map \ No newline at end of file diff --git a/dist/globals/SiteSettings/fields/navigation.js.map b/dist/globals/SiteSettings/fields/navigation.js.map deleted file mode 100644 index 93c4e97..0000000 --- a/dist/globals/SiteSettings/fields/navigation.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../../src/globals/SiteSettings/fields/navigation.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/globals/SiteSettings/fields/theme.d.ts b/dist/globals/SiteSettings/fields/theme.d.ts deleted file mode 100644 index 8f527ed..0000000 --- a/dist/globals/SiteSettings/fields/theme.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Field } from 'payload'; -/** - * Theme behavior configuration. - * - * These are decisions, not visuals — the plugin stores them, the client - * template reads them and decides whether to render a toggle and which - * mode to start in. Appearance itself stays in the template. - */ -export declare const themeFields: Field[]; diff --git a/dist/globals/SiteSettings/fields/theme.js b/dist/globals/SiteSettings/fields/theme.js deleted file mode 100644 index f434b1e..0000000 --- a/dist/globals/SiteSettings/fields/theme.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Theme behavior configuration. - * - * These are decisions, not visuals — the plugin stores them, the client - * template reads them and decides whether to render a toggle and which - * mode to start in. Appearance itself stays in the template. - */ export const themeFields = [ - { - name: 'defaultTheme', - type: 'select', - admin: { - description: 'Theme applied on a visitor’s first visit.' - }, - defaultValue: 'light', - options: [ - { - label: 'Light', - value: 'light' - }, - { - label: 'Dark', - value: 'dark' - } - ], - required: true - }, - { - name: 'allowThemeToggle', - type: 'checkbox', - admin: { - description: 'Show a light/dark switch on the site.' - }, - defaultValue: true - } -]; - -//# sourceMappingURL=theme.js.map \ No newline at end of file diff --git a/dist/globals/SiteSettings/fields/theme.js.map b/dist/globals/SiteSettings/fields/theme.js.map deleted file mode 100644 index 506a34b..0000000 --- a/dist/globals/SiteSettings/fields/theme.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../../src/globals/SiteSettings/fields/theme.ts"],"sourcesContent":["import type { Field } from 'payload'\n\n/**\n * Theme behavior configuration.\n *\n * These are decisions, not visuals — the plugin stores them, the client\n * template reads them and decides whether to render a toggle and which\n * mode to start in. Appearance itself stays in the template.\n */\nexport const themeFields: Field[] = [\n {\n name: 'defaultTheme',\n type: 'select',\n admin: {\n description: 'Theme applied on a visitor’s first visit.',\n },\n defaultValue: 'light',\n options: [\n { label: 'Light', value: 'light' },\n { label: 'Dark', value: 'dark' },\n ],\n required: true,\n },\n {\n name: 'allowThemeToggle',\n type: 'checkbox',\n admin: {\n description: 'Show a light/dark switch on the site.',\n },\n defaultValue: true,\n },\n]\n"],"names":["themeFields","name","type","admin","description","defaultValue","options","label","value","required"],"mappings":"AAEA;;;;;;CAMC,GACD,OAAO,MAAMA,cAAuB;IAClC;QACEC,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,cAAc;QACdC,SAAS;YACP;gBAAEC,OAAO;gBAASC,OAAO;YAAQ;YACjC;gBAAED,OAAO;gBAAQC,OAAO;YAAO;SAChC;QACDC,UAAU;IACZ;IACA;QACER,MAAM;QACNC,MAAM;QACNC,OAAO;YACLC,aAAa;QACf;QACAC,cAAc;IAChB;CACD,CAAA"} \ No newline at end of file diff --git a/dist/globals/SiteSettings/index.d.ts b/dist/globals/SiteSettings/index.d.ts deleted file mode 100644 index debf327..0000000 --- a/dist/globals/SiteSettings/index.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Field, GlobalConfig } from 'payload'; -type BuildSiteSettingsArgs = { - /** Extra fields injected by the client project */ - additionalFields?: Field[]; - /** - * Fields for the "System Pages" tab — page assignments by role (homepage, - * privacy policy, collection archives). Assembled by the caller, which is the - * only place that knows which modules are enabled. The tab is omitted when - * this is empty. - */ - systemPageFields?: Field[]; -}; -/** - * Builds the SiteSettings global. - * - * Composes tabs out of field groups; it deliberately doesn't know where those - * groups come from. Earlier it imported the pages and content modules directly, - * which meant a global — a presentation-layer concern — had an opinion about - * blogs. Now the plugin's composition root passes fields in, and adding a new - * kind of page assignment needs no change here. - * - * Uses unnamed tabs — data stays flat (siteSettings.siteName, not - * siteSettings.general.siteName). Client-provided fields land in their own - * "Custom" tab so core data paths never shift. - */ -export declare function buildSiteSettings({ additionalFields, systemPageFields, }?: BuildSiteSettingsArgs): GlobalConfig; -export {}; diff --git a/dist/globals/SiteSettings/index.js b/dist/globals/SiteSettings/index.js deleted file mode 100644 index 613c0f2..0000000 --- a/dist/globals/SiteSettings/index.js +++ /dev/null @@ -1,55 +0,0 @@ -import { generalFields } from './fields/general.js'; -import { themeFields } from './fields/theme.js'; -/** - * Builds the SiteSettings global. - * - * Composes tabs out of field groups; it deliberately doesn't know where those - * groups come from. Earlier it imported the pages and content modules directly, - * which meant a global — a presentation-layer concern — had an opinion about - * blogs. Now the plugin's composition root passes fields in, and adding a new - * kind of page assignment needs no change here. - * - * Uses unnamed tabs — data stays flat (siteSettings.siteName, not - * siteSettings.general.siteName). Client-provided fields land in their own - * "Custom" tab so core data paths never shift. - */ export function buildSiteSettings({ additionalFields, systemPageFields } = {}) { - return { - slug: 'site-settings', - access: { - read: ()=>true - }, - admin: { - group: 'Settings' - }, - fields: [ - { - type: 'tabs', - tabs: [ - { - fields: generalFields, - label: 'General' - }, - { - fields: themeFields, - label: 'Theme' - }, - ...systemPageFields?.length ? [ - { - fields: systemPageFields, - label: 'System Pages' - } - ] : [], - ...additionalFields?.length ? [ - { - fields: additionalFields, - label: 'Custom' - } - ] : [] - ] - } - ], - label: 'Site Settings' - }; -} - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/globals/SiteSettings/index.js.map b/dist/globals/SiteSettings/index.js.map deleted file mode 100644 index 9e4d89f..0000000 --- a/dist/globals/SiteSettings/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/globals/SiteSettings/index.ts"],"sourcesContent":["import type { Field, GlobalConfig } from 'payload'\n\nimport { generalFields } from './fields/general.js'\nimport { themeFields } from './fields/theme.js'\n\ntype BuildSiteSettingsArgs = {\n /** Extra fields injected by the client project */\n additionalFields?: Field[]\n /**\n * Fields for the \"System Pages\" tab — page assignments by role (homepage,\n * privacy policy, collection archives). Assembled by the caller, which is the\n * only place that knows which modules are enabled. The tab is omitted when\n * this is empty.\n */\n systemPageFields?: Field[]\n}\n\n/**\n * Builds the SiteSettings global.\n *\n * Composes tabs out of field groups; it deliberately doesn't know where those\n * groups come from. Earlier it imported the pages and content modules directly,\n * which meant a global — a presentation-layer concern — had an opinion about\n * blogs. Now the plugin's composition root passes fields in, and adding a new\n * kind of page assignment needs no change here.\n *\n * Uses unnamed tabs — data stays flat (siteSettings.siteName, not\n * siteSettings.general.siteName). Client-provided fields land in their own\n * \"Custom\" tab so core data paths never shift.\n */\nexport function buildSiteSettings({\n additionalFields,\n systemPageFields,\n}: BuildSiteSettingsArgs = {}): GlobalConfig {\n return {\n slug: 'site-settings',\n access: {\n read: () => true,\n },\n admin: {\n group: 'Settings',\n },\n fields: [\n {\n type: 'tabs',\n tabs: [\n {\n fields: generalFields,\n label: 'General',\n },\n {\n fields: themeFields,\n label: 'Theme',\n },\n ...(systemPageFields?.length\n ? [{ fields: systemPageFields, label: 'System Pages' }]\n : []),\n ...(additionalFields?.length ? [{ fields: additionalFields, label: 'Custom' }] : []),\n ],\n },\n ],\n label: 'Site Settings',\n }\n}\n"],"names":["generalFields","themeFields","buildSiteSettings","additionalFields","systemPageFields","slug","access","read","admin","group","fields","type","tabs","label","length"],"mappings":"AAEA,SAASA,aAAa,QAAQ,sBAAqB;AACnD,SAASC,WAAW,QAAQ,oBAAmB;AAc/C;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,kBAAkB,EAChCC,gBAAgB,EAChBC,gBAAgB,EACM,GAAG,CAAC,CAAC;IAC3B,OAAO;QACLC,MAAM;QACNC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,OAAO;YACLC,OAAO;QACT;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;oBACJ;wBACEF,QAAQV;wBACRa,OAAO;oBACT;oBACA;wBACEH,QAAQT;wBACRY,OAAO;oBACT;uBACIT,kBAAkBU,SAClB;wBAAC;4BAAEJ,QAAQN;4BAAkBS,OAAO;wBAAe;qBAAE,GACrD,EAAE;uBACFV,kBAAkBW,SAAS;wBAAC;4BAAEJ,QAAQP;4BAAkBU,OAAO;wBAAS;qBAAE,GAAG,EAAE;iBACpF;YACH;SACD;QACDA,OAAO;IACT;AACF"} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts deleted file mode 100644 index 26b729e..0000000 --- a/dist/index.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -export type { AccessOption, Role } from './modules/access/index.js'; -export { adminOnly, adminOnlyField, adminOrEditor, adminOrEditorField, adminOrSelf, authenticated, hasMinimumRole, isAdmin, isEditor, requireRole, requireRoleField, ROLE_HIERARCHY, } from './modules/access/index.js'; -export type { AnalyticsConfig } from './modules/analytics/index.js'; -export { getAnalyticsConfig } from './modules/analytics/index.js'; -export { ACCEPT_ALL_CONSENT, CONSENT_CATEGORIES, CONSENT_COOKIE, CONSENT_MAX_AGE, CONSENT_VERSION, DEFAULT_CONSENT, getConsentTexts, parseConsent, REJECT_ALL_CONSENT, serializeConsent, setDefaultConsent, updateConsent, } from './modules/consent/index.js'; -export type { ConsentCategory, ConsentState, ConsentTexts } from './modules/consent/index.js'; -export type { ContentCollectionOption, ContentOption, ResolvedRoute, } from './modules/content/index.js'; -export { archiveFieldName, buildArchivePath, buildEntryPath, getArchiveEntries, parsePageParam, resolveRoute, } from './modules/content/index.js'; -export type { ArchiveEntries } from './modules/content/index.js'; -export { panelSmtpAdapter } from './modules/email/panelSmtpAdapter.js'; -export type { PanelSmtpAdapterArgs } from './modules/email/panelSmtpAdapter.js'; -export { buildFormsPlugin } from './modules/forms/formsPluginConfig.js'; -export type { FormsCollectionOverrides, FormsFieldsOverride, FormsOption, } from './modules/forms/types.js'; -export { createContentHelpers } from './modules/frontend/index.js'; -export type { I18nConfig, LocaleDefinition, LocalizedSlugs } from './modules/i18n/index.js'; -export { buildLocalizedPath, getDefaultLocale, getLocaleCodes, getLocaleDefinition, getLocalizedSlugs, isValidLocale, LOCALE_COOKIE_NAME, matchAcceptLanguage, negotiateLocale, switchLocalePath, } from './modules/i18n/index.js'; -export type { LocaleMiddlewareResult } from './modules/i18n/index.js'; -export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from './modules/i18n/index.js'; -export type { PagesOption, SystemPageRole } from './modules/pages/index.js'; -export { ALL_SYSTEM_PAGE_ROLES, getSystemPagePath } from './modules/pages/index.js'; -export type { GlobalQueryOptions } from './modules/payload/index.js'; -export { getGlobal, getSiteIntegrations, getSiteSettings, SITE_INTEGRATIONS_SLUG, SITE_SETTINGS_SLUG, } from './modules/payload/index.js'; -export type { PageMetadata, SeoMeta, SeoOption } from './modules/seo/index.js'; -export { buildHreflangAlternates, buildMetadata, composeTitle } from './modules/seo/index.js'; -export type { AutoFillMapping, RobotsRules, SitemapEntry } from './modules/seo/index.js'; -export { buildAutoFillMetaHook, buildRobots, buildSitemapEntries, createMetadataGenerator, createPageMetadata, injectAutoFillMeta, } from './modules/seo/index.js'; -export { buildSlugField, toSlug } from './modules/slug/index.js'; -export { ipalKit } from './plugin.js'; -export type { IpalOptions } from './types.js'; diff --git a/dist/index.js b/dist/index.js deleted file mode 100644 index 8ac3b44..0000000 --- a/dist/index.js +++ /dev/null @@ -1,20 +0,0 @@ -export { adminOnly, adminOnlyField, adminOrEditor, adminOrEditorField, adminOrSelf, authenticated, hasMinimumRole, isAdmin, isEditor, requireRole, requireRoleField, ROLE_HIERARCHY } from './modules/access/index.js'; -export { getAnalyticsConfig } from './modules/analytics/index.js'; -export { ACCEPT_ALL_CONSENT, CONSENT_CATEGORIES, CONSENT_COOKIE, CONSENT_MAX_AGE, CONSENT_VERSION, DEFAULT_CONSENT, getConsentTexts, parseConsent, REJECT_ALL_CONSENT, serializeConsent, setDefaultConsent, updateConsent } from './modules/consent/index.js'; -export { archiveFieldName, buildArchivePath, buildEntryPath, getArchiveEntries, parsePageParam, resolveRoute } from './modules/content/index.js'; -// Imported straight from the file, NOT from ./modules/email/index.js — that -// barrel re-exports sendEmail, which imports 'server-only' and would crash when -// Payload loads the config (or runs generate:importmap) as a plain Node script. -export { panelSmtpAdapter } from './modules/email/panelSmtpAdapter.js'; -export { buildFormsPlugin } from './modules/forms/formsPluginConfig.js'; -export { createContentHelpers } from './modules/frontend/index.js'; -export { buildLocalizedPath, getDefaultLocale, getLocaleCodes, getLocaleDefinition, getLocalizedSlugs, isValidLocale, LOCALE_COOKIE_NAME, matchAcceptLanguage, negotiateLocale, switchLocalePath } from './modules/i18n/index.js'; -export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from './modules/i18n/index.js'; -export { ALL_SYSTEM_PAGE_ROLES, getSystemPagePath } from './modules/pages/index.js'; -export { getGlobal, getSiteIntegrations, getSiteSettings, SITE_INTEGRATIONS_SLUG, SITE_SETTINGS_SLUG } from './modules/payload/index.js'; -export { buildHreflangAlternates, buildMetadata, composeTitle } from './modules/seo/index.js'; -export { buildAutoFillMetaHook, buildRobots, buildSitemapEntries, createMetadataGenerator, createPageMetadata, injectAutoFillMeta } from './modules/seo/index.js'; -export { buildSlugField, toSlug } from './modules/slug/index.js'; -export { ipalKit } from './plugin.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map deleted file mode 100644 index b8b0389..0000000 --- a/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type { AccessOption, Role } from './modules/access/index.js'\nexport {\n adminOnly,\n adminOnlyField,\n adminOrEditor,\n adminOrEditorField,\n adminOrSelf,\n authenticated,\n hasMinimumRole,\n isAdmin,\n isEditor,\n requireRole,\n requireRoleField,\n ROLE_HIERARCHY,\n} from './modules/access/index.js'\nexport type { AnalyticsConfig } from './modules/analytics/index.js'\nexport { getAnalyticsConfig } from './modules/analytics/index.js'\nexport {\n ACCEPT_ALL_CONSENT,\n CONSENT_CATEGORIES,\n CONSENT_COOKIE,\n CONSENT_MAX_AGE,\n CONSENT_VERSION,\n DEFAULT_CONSENT,\n getConsentTexts,\n parseConsent,\n REJECT_ALL_CONSENT,\n serializeConsent,\n setDefaultConsent,\n updateConsent,\n} from './modules/consent/index.js'\nexport type { ConsentCategory, ConsentState, ConsentTexts } from './modules/consent/index.js'\nexport type {\n ContentCollectionOption,\n ContentOption,\n ResolvedRoute,\n} from './modules/content/index.js'\nexport {\n archiveFieldName,\n buildArchivePath,\n buildEntryPath,\n getArchiveEntries,\n parsePageParam,\n resolveRoute,\n} from './modules/content/index.js'\nexport type { ArchiveEntries } from './modules/content/index.js'\n// Imported straight from the file, NOT from ./modules/email/index.js — that\n// barrel re-exports sendEmail, which imports 'server-only' and would crash when\n// Payload loads the config (or runs generate:importmap) as a plain Node script.\nexport { panelSmtpAdapter } from './modules/email/panelSmtpAdapter.js'\nexport type { PanelSmtpAdapterArgs } from './modules/email/panelSmtpAdapter.js'\nexport { buildFormsPlugin } from './modules/forms/formsPluginConfig.js'\nexport type {\n FormsCollectionOverrides,\n FormsFieldsOverride,\n FormsOption,\n} from './modules/forms/types.js'\nexport { createContentHelpers } from './modules/frontend/index.js'\nexport type { I18nConfig, LocaleDefinition, LocalizedSlugs } from './modules/i18n/index.js'\nexport {\n buildLocalizedPath,\n getDefaultLocale,\n getLocaleCodes,\n getLocaleDefinition,\n getLocalizedSlugs,\n isValidLocale,\n LOCALE_COOKIE_NAME,\n matchAcceptLanguage,\n negotiateLocale,\n switchLocalePath,\n} from './modules/i18n/index.js'\nexport type { LocaleMiddlewareResult } from './modules/i18n/index.js'\nexport { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from './modules/i18n/index.js'\nexport type { PagesOption, SystemPageRole } from './modules/pages/index.js'\nexport { ALL_SYSTEM_PAGE_ROLES, getSystemPagePath } from './modules/pages/index.js'\nexport type { GlobalQueryOptions } from './modules/payload/index.js'\nexport {\n getGlobal,\n getSiteIntegrations,\n getSiteSettings,\n SITE_INTEGRATIONS_SLUG,\n SITE_SETTINGS_SLUG,\n} from './modules/payload/index.js'\nexport type { PageMetadata, SeoMeta, SeoOption } from './modules/seo/index.js'\nexport { buildHreflangAlternates, buildMetadata, composeTitle } from './modules/seo/index.js'\nexport type { AutoFillMapping, RobotsRules, SitemapEntry } from './modules/seo/index.js'\nexport {\n buildAutoFillMetaHook,\n buildRobots,\n buildSitemapEntries,\n createMetadataGenerator,\n createPageMetadata,\n injectAutoFillMeta,\n} from './modules/seo/index.js'\nexport { buildSlugField, toSlug } from './modules/slug/index.js'\nexport { ipalKit } from './plugin.js'\nexport type { IpalOptions } from './types.js'\n"],"names":["adminOnly","adminOnlyField","adminOrEditor","adminOrEditorField","adminOrSelf","authenticated","hasMinimumRole","isAdmin","isEditor","requireRole","requireRoleField","ROLE_HIERARCHY","getAnalyticsConfig","ACCEPT_ALL_CONSENT","CONSENT_CATEGORIES","CONSENT_COOKIE","CONSENT_MAX_AGE","CONSENT_VERSION","DEFAULT_CONSENT","getConsentTexts","parseConsent","REJECT_ALL_CONSENT","serializeConsent","setDefaultConsent","updateConsent","archiveFieldName","buildArchivePath","buildEntryPath","getArchiveEntries","parsePageParam","resolveRoute","panelSmtpAdapter","buildFormsPlugin","createContentHelpers","buildLocalizedPath","getDefaultLocale","getLocaleCodes","getLocaleDefinition","getLocalizedSlugs","isValidLocale","LOCALE_COOKIE_NAME","matchAcceptLanguage","negotiateLocale","switchLocalePath","createLocaleMiddleware","DEFAULT_MIDDLEWARE_MATCHER","ALL_SYSTEM_PAGE_ROLES","getSystemPagePath","getGlobal","getSiteIntegrations","getSiteSettings","SITE_INTEGRATIONS_SLUG","SITE_SETTINGS_SLUG","buildHreflangAlternates","buildMetadata","composeTitle","buildAutoFillMetaHook","buildRobots","buildSitemapEntries","createMetadataGenerator","createPageMetadata","injectAutoFillMeta","buildSlugField","toSlug","ipalKit"],"mappings":"AACA,SACEA,SAAS,EACTC,cAAc,EACdC,aAAa,EACbC,kBAAkB,EAClBC,WAAW,EACXC,aAAa,EACbC,cAAc,EACdC,OAAO,EACPC,QAAQ,EACRC,WAAW,EACXC,gBAAgB,EAChBC,cAAc,QACT,4BAA2B;AAElC,SAASC,kBAAkB,QAAQ,+BAA8B;AACjE,SACEC,kBAAkB,EAClBC,kBAAkB,EAClBC,cAAc,EACdC,eAAe,EACfC,eAAe,EACfC,eAAe,EACfC,eAAe,EACfC,YAAY,EACZC,kBAAkB,EAClBC,gBAAgB,EAChBC,iBAAiB,EACjBC,aAAa,QACR,6BAA4B;AAOnC,SACEC,gBAAgB,EAChBC,gBAAgB,EAChBC,cAAc,EACdC,iBAAiB,EACjBC,cAAc,EACdC,YAAY,QACP,6BAA4B;AAEnC,4EAA4E;AAC5E,gFAAgF;AAChF,gFAAgF;AAChF,SAASC,gBAAgB,QAAQ,sCAAqC;AAEtE,SAASC,gBAAgB,QAAQ,uCAAsC;AAMvE,SAASC,oBAAoB,QAAQ,8BAA6B;AAElE,SACEC,kBAAkB,EAClBC,gBAAgB,EAChBC,cAAc,EACdC,mBAAmB,EACnBC,iBAAiB,EACjBC,aAAa,EACbC,kBAAkB,EAClBC,mBAAmB,EACnBC,eAAe,EACfC,gBAAgB,QACX,0BAAyB;AAEhC,SAASC,sBAAsB,EAAEC,0BAA0B,QAAQ,0BAAyB;AAE5F,SAASC,qBAAqB,EAAEC,iBAAiB,QAAQ,2BAA0B;AAEnF,SACEC,SAAS,EACTC,mBAAmB,EACnBC,eAAe,EACfC,sBAAsB,EACtBC,kBAAkB,QACb,6BAA4B;AAEnC,SAASC,uBAAuB,EAAEC,aAAa,EAAEC,YAAY,QAAQ,yBAAwB;AAE7F,SACEC,qBAAqB,EACrBC,WAAW,EACXC,mBAAmB,EACnBC,uBAAuB,EACvBC,kBAAkB,EAClBC,kBAAkB,QACb,yBAAwB;AAC/B,SAASC,cAAc,EAAEC,MAAM,QAAQ,0BAAyB;AAChE,SAASC,OAAO,QAAQ,cAAa"} \ No newline at end of file diff --git a/dist/modules/access/access.d.ts b/dist/modules/access/access.d.ts deleted file mode 100644 index ec76b6a..0000000 --- a/dist/modules/access/access.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Access, FieldAccess } from 'payload'; -import type { Role } from './types.js'; -/** - * Collection-level access (returns boolean | Where). - * Use in collection `access.read/create/update/delete`. - */ -export declare const adminOnly: Access; -export declare const adminOrEditor: Access; -export declare const authenticated: Access; -/** Requires at least the given role. */ -export declare const requireRole: (minimum: Role) => Access; -/** Admins see all; others are constrained to their own document. */ -export declare const adminOrSelf: Access; -/** - * Field-level access (returns boolean only — no Where support). - * Use in field `access.read/update`. - */ -export declare const adminOnlyField: FieldAccess; -export declare const adminOrEditorField: FieldAccess; -/** Requires at least the given role, for field-level access. */ -export declare const requireRoleField: (minimum: Role) => FieldAccess; diff --git a/dist/modules/access/access.js b/dist/modules/access/access.js deleted file mode 100644 index 16e56d0..0000000 --- a/dist/modules/access/access.js +++ /dev/null @@ -1,29 +0,0 @@ -import { hasMinimumRole, isAdmin } from './predicates.js'; -/** - * Collection-level access (returns boolean | Where). - * Use in collection `access.read/create/update/delete`. - */ export const adminOnly = ({ req: { user } })=>isAdmin(user); -export const adminOrEditor = ({ req: { user } })=>hasMinimumRole(user, 'editor'); -export const authenticated = ({ req: { user } })=>Boolean(user); -/** Requires at least the given role. */ export const requireRole = (minimum)=>({ req: { user } })=>hasMinimumRole(user, minimum); -/** Admins see all; others are constrained to their own document. */ export const adminOrSelf = ({ req: { user } })=>{ - if (isAdmin(user)) { - return true; - } - if (!user) { - return false; - } - return { - id: { - equals: user.id - } - }; -}; -/** - * Field-level access (returns boolean only — no Where support). - * Use in field `access.read/update`. - */ export const adminOnlyField = ({ req: { user } })=>isAdmin(user); -export const adminOrEditorField = ({ req: { user } })=>hasMinimumRole(user, 'editor'); -/** Requires at least the given role, for field-level access. */ export const requireRoleField = (minimum)=>({ req: { user } })=>hasMinimumRole(user, minimum); - -//# sourceMappingURL=access.js.map \ No newline at end of file diff --git a/dist/modules/access/access.js.map b/dist/modules/access/access.js.map deleted file mode 100644 index 4fd68e0..0000000 --- a/dist/modules/access/access.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/access/access.ts"],"sourcesContent":["import type { Access, FieldAccess } from 'payload'\n\nimport type { Role } from './types.js'\n\nimport { hasMinimumRole, isAdmin } from './predicates.js'\n\n/**\n * Collection-level access (returns boolean | Where).\n * Use in collection `access.read/create/update/delete`.\n */\nexport const adminOnly: Access = ({ req: { user } }) => isAdmin(user)\n\nexport const adminOrEditor: Access = ({ req: { user } }) => hasMinimumRole(user, 'editor')\n\nexport const authenticated: Access = ({ req: { user } }) => Boolean(user)\n\n/** Requires at least the given role. */\nexport const requireRole =\n (minimum: Role): Access =>\n ({ req: { user } }) =>\n hasMinimumRole(user, minimum)\n\n/** Admins see all; others are constrained to their own document. */\nexport const adminOrSelf: Access = ({ req: { user } }) => {\n if (isAdmin(user)) {return true}\n if (!user) {return false}\n return { id: { equals: user.id } }\n}\n\n/**\n * Field-level access (returns boolean only — no Where support).\n * Use in field `access.read/update`.\n */\nexport const adminOnlyField: FieldAccess = ({ req: { user } }) => isAdmin(user)\n\nexport const adminOrEditorField: FieldAccess = ({ req: { user } }) => hasMinimumRole(user, 'editor')\n\n/** Requires at least the given role, for field-level access. */\nexport const requireRoleField =\n (minimum: Role): FieldAccess =>\n ({ req: { user } }) =>\n hasMinimumRole(user, minimum)\n"],"names":["hasMinimumRole","isAdmin","adminOnly","req","user","adminOrEditor","authenticated","Boolean","requireRole","minimum","adminOrSelf","id","equals","adminOnlyField","adminOrEditorField","requireRoleField"],"mappings":"AAIA,SAASA,cAAc,EAAEC,OAAO,QAAQ,kBAAiB;AAEzD;;;CAGC,GACD,OAAO,MAAMC,YAAoB,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKH,QAAQG,MAAK;AAErE,OAAO,MAAMC,gBAAwB,CAAC,EAAEF,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKJ,eAAeI,MAAM,UAAS;AAE1F,OAAO,MAAME,gBAAwB,CAAC,EAAEH,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKG,QAAQH,MAAK;AAEzE,sCAAsC,GACtC,OAAO,MAAMI,cACX,CAACC,UACD,CAAC,EAAEN,KAAK,EAAEC,IAAI,EAAE,EAAE,GAChBJ,eAAeI,MAAMK,SAAQ;AAEjC,kEAAkE,GAClE,OAAO,MAAMC,cAAsB,CAAC,EAAEP,KAAK,EAAEC,IAAI,EAAE,EAAE;IACnD,IAAIH,QAAQG,OAAO;QAAC,OAAO;IAAI;IAC/B,IAAI,CAACA,MAAM;QAAC,OAAO;IAAK;IACxB,OAAO;QAAEO,IAAI;YAAEC,QAAQR,KAAKO,EAAE;QAAC;IAAE;AACnC,EAAC;AAED;;;CAGC,GACD,OAAO,MAAME,iBAA8B,CAAC,EAAEV,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKH,QAAQG,MAAK;AAE/E,OAAO,MAAMU,qBAAkC,CAAC,EAAEX,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKJ,eAAeI,MAAM,UAAS;AAEpG,8DAA8D,GAC9D,OAAO,MAAMW,mBACX,CAACN,UACD,CAAC,EAAEN,KAAK,EAAEC,IAAI,EAAE,EAAE,GAChBJ,eAAeI,MAAMK,SAAQ"} \ No newline at end of file diff --git a/dist/modules/access/index.d.ts b/dist/modules/access/index.d.ts deleted file mode 100644 index e6a8d16..0000000 --- a/dist/modules/access/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { adminOnly, adminOnlyField, adminOrEditor, adminOrEditorField, adminOrSelf, authenticated, requireRole, requireRoleField, } from './access.js'; -export { injectRoles } from './injectRoles.js'; -export { hasMinimumRole, isAdmin, isEditor } from './predicates.js'; -export { buildRolesField } from './rolesField.js'; -export type { AccessOption, Role } from './types.js'; -export { ROLE_HIERARCHY } from './types.js'; diff --git a/dist/modules/access/index.js b/dist/modules/access/index.js deleted file mode 100644 index f466bc5..0000000 --- a/dist/modules/access/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export { adminOnly, adminOnlyField, adminOrEditor, adminOrEditorField, adminOrSelf, authenticated, requireRole, requireRoleField } from './access.js'; -export { injectRoles } from './injectRoles.js'; -export { hasMinimumRole, isAdmin, isEditor } from './predicates.js'; -export { buildRolesField } from './rolesField.js'; -export { ROLE_HIERARCHY } from './types.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/access/index.js.map b/dist/modules/access/index.js.map deleted file mode 100644 index 6a3b064..0000000 --- a/dist/modules/access/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/access/index.ts"],"sourcesContent":["export {\n adminOnly,\n adminOnlyField,\n adminOrEditor,\n adminOrEditorField,\n adminOrSelf,\n authenticated,\n requireRole,\n requireRoleField,\n} from './access.js'\nexport { injectRoles } from './injectRoles.js'\nexport { hasMinimumRole, isAdmin, isEditor } from './predicates.js'\nexport { buildRolesField } from './rolesField.js'\nexport type { AccessOption, Role } from './types.js'\nexport { ROLE_HIERARCHY } from './types.js'\n"],"names":["adminOnly","adminOnlyField","adminOrEditor","adminOrEditorField","adminOrSelf","authenticated","requireRole","requireRoleField","injectRoles","hasMinimumRole","isAdmin","isEditor","buildRolesField","ROLE_HIERARCHY"],"mappings":"AAAA,SACEA,SAAS,EACTC,cAAc,EACdC,aAAa,EACbC,kBAAkB,EAClBC,WAAW,EACXC,aAAa,EACbC,WAAW,EACXC,gBAAgB,QACX,cAAa;AACpB,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,cAAc,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,kBAAiB;AACnE,SAASC,eAAe,QAAQ,kBAAiB;AAEjD,SAASC,cAAc,QAAQ,aAAY"} \ No newline at end of file diff --git a/dist/modules/access/injectRoles.d.ts b/dist/modules/access/injectRoles.d.ts deleted file mode 100644 index 41d8a0d..0000000 --- a/dist/modules/access/injectRoles.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Config } from 'payload'; -import type { AccessOption } from './types.js'; -/** - * Injects the fixed `roles` field into the client's auth collection. - * - * The plugin owns the role definition; the client owns the collection. This - * finds the collection by slug and appends the field. We control the whole - * stack, so no conflict handling is needed — the field is simply added. - */ -export declare function injectRoles(config: Config, access: AccessOption): Config; diff --git a/dist/modules/access/injectRoles.js b/dist/modules/access/injectRoles.js deleted file mode 100644 index e43897b..0000000 --- a/dist/modules/access/injectRoles.js +++ /dev/null @@ -1,27 +0,0 @@ -import { buildRolesField } from './rolesField.js'; -/** - * Injects the fixed `roles` field into the client's auth collection. - * - * The plugin owns the role definition; the client owns the collection. This - * finds the collection by slug and appends the field. We control the whole - * stack, so no conflict handling is needed — the field is simply added. - */ export function injectRoles(config, access) { - const rolesField = buildRolesField(access.defaultRole); - return { - ...config, - collections: (config.collections ?? []).map((collection)=>{ - if (collection.slug !== access.authCollection) { - return collection; - } - return { - ...collection, - fields: [ - ...collection.fields, - rolesField - ] - }; - }) - }; -} - -//# sourceMappingURL=injectRoles.js.map \ No newline at end of file diff --git a/dist/modules/access/injectRoles.js.map b/dist/modules/access/injectRoles.js.map deleted file mode 100644 index 8e74bdf..0000000 --- a/dist/modules/access/injectRoles.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/access/injectRoles.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport type { AccessOption } from './types.js'\n\nimport { buildRolesField } from './rolesField.js'\n\n/**\n * Injects the fixed `roles` field into the client's auth collection.\n *\n * The plugin owns the role definition; the client owns the collection. This\n * finds the collection by slug and appends the field. We control the whole\n * stack, so no conflict handling is needed — the field is simply added.\n */\nexport function injectRoles(config: Config, access: AccessOption): Config {\n const rolesField = buildRolesField(access.defaultRole)\n\n return {\n ...config,\n collections: (config.collections ?? []).map((collection) => {\n if (collection.slug !== access.authCollection) {\n return collection\n }\n return {\n ...collection,\n fields: [...collection.fields, rolesField],\n }\n }),\n }\n}\n"],"names":["buildRolesField","injectRoles","config","access","rolesField","defaultRole","collections","map","collection","slug","authCollection","fields"],"mappings":"AAIA,SAASA,eAAe,QAAQ,kBAAiB;AAEjD;;;;;;CAMC,GACD,OAAO,SAASC,YAAYC,MAAc,EAAEC,MAAoB;IAC9D,MAAMC,aAAaJ,gBAAgBG,OAAOE,WAAW;IAErD,OAAO;QACL,GAAGH,MAAM;QACTI,aAAa,AAACJ,CAAAA,OAAOI,WAAW,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC;YAC3C,IAAIA,WAAWC,IAAI,KAAKN,OAAOO,cAAc,EAAE;gBAC7C,OAAOF;YACT;YACA,OAAO;gBACL,GAAGA,UAAU;gBACbG,QAAQ;uBAAIH,WAAWG,MAAM;oBAAEP;iBAAW;YAC5C;QACF;IACF;AACF"} \ No newline at end of file diff --git a/dist/modules/access/predicates.d.ts b/dist/modules/access/predicates.d.ts deleted file mode 100644 index 50d0f3b..0000000 --- a/dist/modules/access/predicates.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Role } from './types.js'; -/** - * The plugin can't know the client's generated User type, and Payload types - * `req.user` loosely (UntypedUser | null). Predicates therefore accept an - * unknown-ish user and read `roles` defensively — no assumptions about shape - * beyond an optional roles array. - */ -type MaybeUser = { - roles?: null | Role[]; -} | null | Record | undefined; -/** - * True if the user holds at least the given role in the hierarchy. - * admin satisfies 'editor' and 'user'; editor satisfies 'user'. - */ -export declare function hasMinimumRole(user: MaybeUser, minimum: Role): boolean; -/** True if the user is an admin. */ -export declare function isAdmin(user: MaybeUser): boolean; -/** True if the user is an editor or higher (editor, admin). */ -export declare function isEditor(user: MaybeUser): boolean; -export {}; diff --git a/dist/modules/access/predicates.js b/dist/modules/access/predicates.js deleted file mode 100644 index b68a956..0000000 --- a/dist/modules/access/predicates.js +++ /dev/null @@ -1,32 +0,0 @@ -import { ROLE_HIERARCHY } from './types.js'; -/** Safely extracts the roles array from a loosely-typed user. */ function getRoles(user) { - if (!user || typeof user !== 'object') { - return []; - } - const roles = user.roles; - if (!Array.isArray(roles)) { - return []; - } - return roles.filter((role)=>ROLE_HIERARCHY.includes(role)); -} -/** Highest-privilege role index the user holds, or -1 if none. */ function highestRoleIndex(user) { - const roles = getRoles(user); - if (!roles.length) { - return -1; - } - return Math.max(...roles.map((role)=>ROLE_HIERARCHY.indexOf(role))); -} -/** - * True if the user holds at least the given role in the hierarchy. - * admin satisfies 'editor' and 'user'; editor satisfies 'user'. - */ export function hasMinimumRole(user, minimum) { - return highestRoleIndex(user) >= ROLE_HIERARCHY.indexOf(minimum); -} -/** True if the user is an admin. */ export function isAdmin(user) { - return hasMinimumRole(user, 'admin'); -} -/** True if the user is an editor or higher (editor, admin). */ export function isEditor(user) { - return hasMinimumRole(user, 'editor'); -} - -//# sourceMappingURL=predicates.js.map \ No newline at end of file diff --git a/dist/modules/access/predicates.js.map b/dist/modules/access/predicates.js.map deleted file mode 100644 index cd6db80..0000000 --- a/dist/modules/access/predicates.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/access/predicates.ts"],"sourcesContent":["import type { Role } from './types.js'\n\nimport { ROLE_HIERARCHY } from './types.js'\n\n/**\n * The plugin can't know the client's generated User type, and Payload types\n * `req.user` loosely (UntypedUser | null). Predicates therefore accept an\n * unknown-ish user and read `roles` defensively — no assumptions about shape\n * beyond an optional roles array.\n */\ntype MaybeUser = { roles?: null | Role[] } | null | Record | undefined\n\n/** Safely extracts the roles array from a loosely-typed user. */\nfunction getRoles(user: MaybeUser): Role[] {\n if (!user || typeof user !== 'object') {return []}\n const roles = (user as { roles?: unknown }).roles\n if (!Array.isArray(roles)) {return []}\n return roles.filter((role): role is Role => ROLE_HIERARCHY.includes(role as Role))\n}\n\n/** Highest-privilege role index the user holds, or -1 if none. */\nfunction highestRoleIndex(user: MaybeUser): number {\n const roles = getRoles(user)\n if (!roles.length) {return -1}\n return Math.max(...roles.map((role) => ROLE_HIERARCHY.indexOf(role)))\n}\n\n/**\n * True if the user holds at least the given role in the hierarchy.\n * admin satisfies 'editor' and 'user'; editor satisfies 'user'.\n */\nexport function hasMinimumRole(user: MaybeUser, minimum: Role): boolean {\n return highestRoleIndex(user) >= ROLE_HIERARCHY.indexOf(minimum)\n}\n\n/** True if the user is an admin. */\nexport function isAdmin(user: MaybeUser): boolean {\n return hasMinimumRole(user, 'admin')\n}\n\n/** True if the user is an editor or higher (editor, admin). */\nexport function isEditor(user: MaybeUser): boolean {\n return hasMinimumRole(user, 'editor')\n}\n"],"names":["ROLE_HIERARCHY","getRoles","user","roles","Array","isArray","filter","role","includes","highestRoleIndex","length","Math","max","map","indexOf","hasMinimumRole","minimum","isAdmin","isEditor"],"mappings":"AAEA,SAASA,cAAc,QAAQ,aAAY;AAU3C,+DAA+D,GAC/D,SAASC,SAASC,IAAe;IAC/B,IAAI,CAACA,QAAQ,OAAOA,SAAS,UAAU;QAAC,OAAO,EAAE;IAAA;IACjD,MAAMC,QAAQ,AAACD,KAA6BC,KAAK;IACjD,IAAI,CAACC,MAAMC,OAAO,CAACF,QAAQ;QAAC,OAAO,EAAE;IAAA;IACrC,OAAOA,MAAMG,MAAM,CAAC,CAACC,OAAuBP,eAAeQ,QAAQ,CAACD;AACtE;AAEA,gEAAgE,GAChE,SAASE,iBAAiBP,IAAe;IACvC,MAAMC,QAAQF,SAASC;IACvB,IAAI,CAACC,MAAMO,MAAM,EAAE;QAAC,OAAO,CAAC;IAAC;IAC7B,OAAOC,KAAKC,GAAG,IAAIT,MAAMU,GAAG,CAAC,CAACN,OAASP,eAAec,OAAO,CAACP;AAChE;AAEA;;;CAGC,GACD,OAAO,SAASQ,eAAeb,IAAe,EAAEc,OAAa;IAC3D,OAAOP,iBAAiBP,SAASF,eAAec,OAAO,CAACE;AAC1D;AAEA,kCAAkC,GAClC,OAAO,SAASC,QAAQf,IAAe;IACrC,OAAOa,eAAeb,MAAM;AAC9B;AAEA,6DAA6D,GAC7D,OAAO,SAASgB,SAAShB,IAAe;IACtC,OAAOa,eAAeb,MAAM;AAC9B"} \ No newline at end of file diff --git a/dist/modules/access/rolesField.d.ts b/dist/modules/access/rolesField.d.ts deleted file mode 100644 index 7724f6d..0000000 --- a/dist/modules/access/rolesField.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Field } from 'payload'; -import type { Role } from './types.js'; -/** - * Builds the fixed `roles` field the plugin injects into the auth collection. - * - * Saved to the JWT so role checks avoid a database lookup. Only admins can - * change roles, preventing privilege escalation by lower-privilege users. - */ -export declare function buildRolesField(defaultRole?: Role): Field; diff --git a/dist/modules/access/rolesField.js b/dist/modules/access/rolesField.js deleted file mode 100644 index 73f474b..0000000 --- a/dist/modules/access/rolesField.js +++ /dev/null @@ -1,32 +0,0 @@ -import { isAdmin } from './predicates.js'; -import { ROLE_HIERARCHY } from './types.js'; -/** - * Builds the fixed `roles` field the plugin injects into the auth collection. - * - * Saved to the JWT so role checks avoid a database lookup. Only admins can - * change roles, preventing privilege escalation by lower-privilege users. - */ export function buildRolesField(defaultRole = 'user') { - return { - name: 'roles', - type: 'select', - access: { - // Only admins may assign or change roles - update: ({ req: { user } })=>isAdmin(user) - }, - admin: { - description: 'Role hierarchy: admin > editor > user.' - }, - defaultValue: [ - defaultRole - ], - hasMany: true, - options: ROLE_HIERARCHY.map((role)=>({ - label: role.charAt(0).toUpperCase() + role.slice(1), - value: role - })), - required: true, - saveToJWT: true - }; -} - -//# sourceMappingURL=rolesField.js.map \ No newline at end of file diff --git a/dist/modules/access/rolesField.js.map b/dist/modules/access/rolesField.js.map deleted file mode 100644 index ac8872c..0000000 --- a/dist/modules/access/rolesField.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/access/rolesField.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nimport type { Role } from './types.js'\n\nimport { isAdmin } from './predicates.js'\nimport { ROLE_HIERARCHY } from './types.js'\n\n/**\n * Builds the fixed `roles` field the plugin injects into the auth collection.\n *\n * Saved to the JWT so role checks avoid a database lookup. Only admins can\n * change roles, preventing privilege escalation by lower-privilege users.\n */\nexport function buildRolesField(defaultRole: Role = 'user'): Field {\n return {\n name: 'roles',\n type: 'select',\n access: {\n // Only admins may assign or change roles\n update: ({ req: { user } }) => isAdmin(user),\n },\n admin: {\n description: 'Role hierarchy: admin > editor > user.',\n },\n defaultValue: [defaultRole],\n hasMany: true,\n options: ROLE_HIERARCHY.map((role) => ({\n label: role.charAt(0).toUpperCase() + role.slice(1),\n value: role,\n })),\n required: true,\n saveToJWT: true,\n }\n}\n"],"names":["isAdmin","ROLE_HIERARCHY","buildRolesField","defaultRole","name","type","access","update","req","user","admin","description","defaultValue","hasMany","options","map","role","label","charAt","toUpperCase","slice","value","required","saveToJWT"],"mappings":"AAIA,SAASA,OAAO,QAAQ,kBAAiB;AACzC,SAASC,cAAc,QAAQ,aAAY;AAE3C;;;;;CAKC,GACD,OAAO,SAASC,gBAAgBC,cAAoB,MAAM;IACxD,OAAO;QACLC,MAAM;QACNC,MAAM;QACNC,QAAQ;YACN,yCAAyC;YACzCC,QAAQ,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAE,EAAE,GAAKT,QAAQS;QACzC;QACAC,OAAO;YACLC,aAAa;QACf;QACAC,cAAc;YAACT;SAAY;QAC3BU,SAAS;QACTC,SAASb,eAAec,GAAG,CAAC,CAACC,OAAU,CAAA;gBACrCC,OAAOD,KAAKE,MAAM,CAAC,GAAGC,WAAW,KAAKH,KAAKI,KAAK,CAAC;gBACjDC,OAAOL;YACT,CAAA;QACAM,UAAU;QACVC,WAAW;IACb;AACF"} \ No newline at end of file diff --git a/dist/modules/access/types.d.ts b/dist/modules/access/types.d.ts deleted file mode 100644 index 851d4cd..0000000 --- a/dist/modules/access/types.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Role hierarchy, lowest to highest privilege. - * A higher role satisfies any requirement met by a lower one. - */ -export declare const ROLE_HIERARCHY: readonly ["user", "editor", "admin"]; -export type Role = (typeof ROLE_HIERARCHY)[number]; -/** - * Access-control options. - * - * The plugin injects a fixed `roles` field into the client's auth collection - * — the collection itself belongs to the client (create-payload-app), the - * role definition belongs to the plugin. - */ -export type AccessOption = { - /** Slug of the client's auth collection, e.g. 'users'. */ - authCollection: string; - /** Role assigned to new users. Defaults to 'user'. */ - defaultRole?: Role; -}; diff --git a/dist/modules/access/types.js b/dist/modules/access/types.js deleted file mode 100644 index 3afbaa8..0000000 --- a/dist/modules/access/types.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Role hierarchy, lowest to highest privilege. - * A higher role satisfies any requirement met by a lower one. - */ export const ROLE_HIERARCHY = [ - 'user', - 'editor', - 'admin' -]; - -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/modules/access/types.js.map b/dist/modules/access/types.js.map deleted file mode 100644 index 795ba05..0000000 --- a/dist/modules/access/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/access/types.ts"],"sourcesContent":["/**\n * Role hierarchy, lowest to highest privilege.\n * A higher role satisfies any requirement met by a lower one.\n */\nexport const ROLE_HIERARCHY = ['user', 'editor', 'admin'] as const\n\nexport type Role = (typeof ROLE_HIERARCHY)[number]\n\n/**\n * Access-control options.\n *\n * The plugin injects a fixed `roles` field into the client's auth collection\n * — the collection itself belongs to the client (create-payload-app), the\n * role definition belongs to the plugin.\n */\nexport type AccessOption = {\n /** Slug of the client's auth collection, e.g. 'users'. */\n authCollection: string\n /** Role assigned to new users. Defaults to 'user'. */\n defaultRole?: Role\n}\n"],"names":["ROLE_HIERARCHY"],"mappings":"AAAA;;;CAGC,GACD,OAAO,MAAMA,iBAAiB;IAAC;IAAQ;IAAU;CAAQ,CAAS"} \ No newline at end of file diff --git a/dist/modules/analytics/Analytics.d.ts b/dist/modules/analytics/Analytics.d.ts deleted file mode 100644 index 4a2fd87..0000000 --- a/dist/modules/analytics/Analytics.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { AnalyticsConfig } from './types.js'; -declare global { - interface Window { - dataLayer?: unknown[]; - } -} -/** - * Loads Google Analytics — GTM if a container ID is set, otherwise GA4 gtag. - * - * Consent Mode is initialised to the visitor's stored consent BEFORE the tag - * loads (via setDefaultConsent from the consent module), so tags respect the - * choice from the first hit; useConsent sends an update the moment the visitor - * decides. IDs are public and come from SiteIntegrations, passed in as props - * (the client project reads them server-side). - * - * Renders nothing — it only injects the scripts. Mount once, high in the tree - * (e.g. root layout), inside ConsentProvider. - */ -export declare function Analytics({ ga4MeasurementId, gtmContainerId }: AnalyticsConfig): null; diff --git a/dist/modules/analytics/Analytics.js b/dist/modules/analytics/Analytics.js deleted file mode 100644 index f3eef21..0000000 --- a/dist/modules/analytics/Analytics.js +++ /dev/null @@ -1,63 +0,0 @@ -'use client'; -import { useEffect } from 'react'; -import { gtag } from '../consent/googleConsent.js'; -import { CONSENT_COOKIE, DEFAULT_CONSENT, parseConsent, setDefaultConsent } from '../consent/index.js'; -const GTM_SCRIPT = (id)=>`https://www.googletagmanager.com/gtm.js?id=${id}`; -const GA4_SCRIPT = (id)=>`https://www.googletagmanager.com/gtag/js?id=${id}`; -/** Reads the current consent state from cookie, or defaults if none. */ function readConsent() { - if (typeof document === 'undefined') { - return DEFAULT_CONSENT; - } - const raw = document.cookie.split('; ').find((c)=>c.startsWith(`${CONSENT_COOKIE}=`))?.split('=')[1]; - return parseConsent(raw) ?? DEFAULT_CONSENT; -} -function injectScript(src) { - if (document.querySelector(`script[src="${src}"]`)) { - return; - } - const s = document.createElement('script'); - s.src = src; - s.async = true; - document.head.appendChild(s); -} -/** - * Loads Google Analytics — GTM if a container ID is set, otherwise GA4 gtag. - * - * Consent Mode is initialised to the visitor's stored consent BEFORE the tag - * loads (via setDefaultConsent from the consent module), so tags respect the - * choice from the first hit; useConsent sends an update the moment the visitor - * decides. IDs are public and come from SiteIntegrations, passed in as props - * (the client project reads them server-side). - * - * Renders nothing — it only injects the scripts. Mount once, high in the tree - * (e.g. root layout), inside ConsentProvider. - */ export function Analytics({ ga4MeasurementId, gtmContainerId }) { - useEffect(()=>{ - if (!ga4MeasurementId && !gtmContainerId) { - return; - } - // 1. Consent Mode default = the visitor's stored choice, before tags load. - setDefaultConsent(readConsent()); - // 2. Load the tag. - if (gtmContainerId) { - window.dataLayer = window.dataLayer || []; - window.dataLayer.push({ - event: 'gtm.js', - 'gtm.start': Date.now() - }); - injectScript(GTM_SCRIPT(gtmContainerId)); - } else if (ga4MeasurementId) { - injectScript(GA4_SCRIPT(ga4MeasurementId)); - // Uses the shared gtag (pushes `arguments`, as Google's tags expect); - // real gtag.js wires itself up once the script loads. - gtag('js', new Date()); - gtag('config', ga4MeasurementId); - } - }, [ - ga4MeasurementId, - gtmContainerId - ]); - return null; -} - -//# sourceMappingURL=Analytics.js.map \ No newline at end of file diff --git a/dist/modules/analytics/Analytics.js.map b/dist/modules/analytics/Analytics.js.map deleted file mode 100644 index 7fa6bc1..0000000 --- a/dist/modules/analytics/Analytics.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/analytics/Analytics.tsx"],"sourcesContent":["'use client'\nimport { useEffect } from 'react'\n\nimport type { AnalyticsConfig } from './types.js'\n\nimport { gtag } from '../consent/googleConsent.js'\nimport { CONSENT_COOKIE, DEFAULT_CONSENT, parseConsent, setDefaultConsent } from '../consent/index.js'\n\ndeclare global {\n interface Window {\n dataLayer?: unknown[]\n }\n}\n\nconst GTM_SCRIPT = (id: string) => `https://www.googletagmanager.com/gtm.js?id=${id}`\nconst GA4_SCRIPT = (id: string) => `https://www.googletagmanager.com/gtag/js?id=${id}`\n\n/** Reads the current consent state from cookie, or defaults if none. */\nfunction readConsent() {\n if (typeof document === 'undefined') {return DEFAULT_CONSENT}\n const raw = document.cookie\n .split('; ')\n .find((c) => c.startsWith(`${CONSENT_COOKIE}=`))\n ?.split('=')[1]\n return parseConsent(raw) ?? DEFAULT_CONSENT\n}\n\nfunction injectScript(src: string) {\n if (document.querySelector(`script[src=\"${src}\"]`)) {return}\n const s = document.createElement('script')\n s.src = src\n s.async = true\n document.head.appendChild(s)\n}\n\n/**\n * Loads Google Analytics — GTM if a container ID is set, otherwise GA4 gtag.\n *\n * Consent Mode is initialised to the visitor's stored consent BEFORE the tag\n * loads (via setDefaultConsent from the consent module), so tags respect the\n * choice from the first hit; useConsent sends an update the moment the visitor\n * decides. IDs are public and come from SiteIntegrations, passed in as props\n * (the client project reads them server-side).\n *\n * Renders nothing — it only injects the scripts. Mount once, high in the tree\n * (e.g. root layout), inside ConsentProvider.\n */\nexport function Analytics({ ga4MeasurementId, gtmContainerId }: AnalyticsConfig) {\n useEffect(() => {\n if (!ga4MeasurementId && !gtmContainerId) {return}\n\n // 1. Consent Mode default = the visitor's stored choice, before tags load.\n setDefaultConsent(readConsent())\n\n // 2. Load the tag.\n if (gtmContainerId) {\n window.dataLayer = window.dataLayer || []\n window.dataLayer.push({ event: 'gtm.js', 'gtm.start': Date.now() })\n injectScript(GTM_SCRIPT(gtmContainerId))\n } else if (ga4MeasurementId) {\n injectScript(GA4_SCRIPT(ga4MeasurementId))\n // Uses the shared gtag (pushes `arguments`, as Google's tags expect);\n // real gtag.js wires itself up once the script loads.\n gtag('js', new Date())\n gtag('config', ga4MeasurementId)\n }\n }, [ga4MeasurementId, gtmContainerId])\n\n return null\n}\n"],"names":["useEffect","gtag","CONSENT_COOKIE","DEFAULT_CONSENT","parseConsent","setDefaultConsent","GTM_SCRIPT","id","GA4_SCRIPT","readConsent","document","raw","cookie","split","find","c","startsWith","injectScript","src","querySelector","s","createElement","async","head","appendChild","Analytics","ga4MeasurementId","gtmContainerId","window","dataLayer","push","event","Date","now"],"mappings":"AAAA;AACA,SAASA,SAAS,QAAQ,QAAO;AAIjC,SAASC,IAAI,QAAQ,8BAA6B;AAClD,SAASC,cAAc,EAAEC,eAAe,EAAEC,YAAY,EAAEC,iBAAiB,QAAS,sBAAqB;AAQvG,MAAMC,aAAa,CAACC,KAAe,CAAC,2CAA2C,EAAEA,IAAI;AACrF,MAAMC,aAAa,CAACD,KAAe,CAAC,4CAA4C,EAAEA,IAAI;AAEtF,sEAAsE,GACtE,SAASE;IACP,IAAI,OAAOC,aAAa,aAAa;QAAC,OAAOP;IAAe;IAC5D,MAAMQ,MAAMD,SAASE,MAAM,CACxBC,KAAK,CAAC,MACNC,IAAI,CAAC,CAACC,IAAMA,EAAEC,UAAU,CAAC,GAAGd,eAAe,CAAC,CAAC,IAC5CW,MAAM,IAAI,CAAC,EAAE;IACjB,OAAOT,aAAaO,QAAQR;AAC9B;AAEA,SAASc,aAAaC,GAAW;IAC/B,IAAIR,SAASS,aAAa,CAAC,CAAC,YAAY,EAAED,IAAI,EAAE,CAAC,GAAG;QAAC;IAAM;IAC3D,MAAME,IAAIV,SAASW,aAAa,CAAC;IACjCD,EAAEF,GAAG,GAAGA;IACRE,EAAEE,KAAK,GAAG;IACVZ,SAASa,IAAI,CAACC,WAAW,CAACJ;AAC5B;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASK,UAAU,EAAEC,gBAAgB,EAAEC,cAAc,EAAmB;IAC7E3B,UAAU;QACR,IAAI,CAAC0B,oBAAoB,CAACC,gBAAgB;YAAC;QAAM;QAEjD,2EAA2E;QAC3EtB,kBAAkBI;QAElB,mBAAmB;QACnB,IAAIkB,gBAAgB;YAClBC,OAAOC,SAAS,GAAGD,OAAOC,SAAS,IAAI,EAAE;YACzCD,OAAOC,SAAS,CAACC,IAAI,CAAC;gBAAEC,OAAO;gBAAU,aAAaC,KAAKC,GAAG;YAAG;YACjEhB,aAAaX,WAAWqB;QAC1B,OAAO,IAAID,kBAAkB;YAC3BT,aAAaT,WAAWkB;YACxB,sEAAsE;YACtE,sDAAsD;YACtDzB,KAAK,MAAM,IAAI+B;YACf/B,KAAK,UAAUyB;QACjB;IACF,GAAG;QAACA;QAAkBC;KAAe;IAErC,OAAO;AACT"} \ No newline at end of file diff --git a/dist/modules/analytics/client.d.ts b/dist/modules/analytics/client.d.ts deleted file mode 100644 index 39e5126..0000000 --- a/dist/modules/analytics/client.d.ts +++ /dev/null @@ -1 +0,0 @@ -export { Analytics } from './Analytics.js'; diff --git a/dist/modules/analytics/client.js b/dist/modules/analytics/client.js deleted file mode 100644 index 8b711fa..0000000 --- a/dist/modules/analytics/client.js +++ /dev/null @@ -1,4 +0,0 @@ -'use client'; -export { Analytics } from './Analytics.js'; - -//# sourceMappingURL=client.js.map \ No newline at end of file diff --git a/dist/modules/analytics/client.js.map b/dist/modules/analytics/client.js.map deleted file mode 100644 index 734ed82..0000000 --- a/dist/modules/analytics/client.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/analytics/client.ts"],"sourcesContent":["'use client'\nexport { Analytics } from './Analytics.js'\n"],"names":["Analytics"],"mappings":"AAAA;AACA,SAASA,SAAS,QAAQ,iBAAgB"} \ No newline at end of file diff --git a/dist/modules/analytics/getAnalyticsConfig.d.ts b/dist/modules/analytics/getAnalyticsConfig.d.ts deleted file mode 100644 index 16252a2..0000000 --- a/dist/modules/analytics/getAnalyticsConfig.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { AnalyticsConfig } from './types.js'; -/** - * Reads the public analytics IDs from SiteIntegrations, server-side. - * - * Returns only the two public IDs (GA4 / GTM) — safe to pass to the client - * component. Does NOT expose any secret from SiteIntegrations. - */ -export declare function getAnalyticsConfig(payload: BasePayload): Promise; diff --git a/dist/modules/analytics/getAnalyticsConfig.js b/dist/modules/analytics/getAnalyticsConfig.js deleted file mode 100644 index 2b6cfd0..0000000 --- a/dist/modules/analytics/getAnalyticsConfig.js +++ /dev/null @@ -1,15 +0,0 @@ -import { getSiteIntegrations } from '../payload/index.js'; -/** - * Reads the public analytics IDs from SiteIntegrations, server-side. - * - * Returns only the two public IDs (GA4 / GTM) — safe to pass to the client - * component. Does NOT expose any secret from SiteIntegrations. - */ export async function getAnalyticsConfig(payload) { - const integrations = await getSiteIntegrations(payload); - return { - ga4MeasurementId: integrations.ga4MeasurementId ?? null, - gtmContainerId: integrations.gtmContainerId ?? null - }; -} - -//# sourceMappingURL=getAnalyticsConfig.js.map \ No newline at end of file diff --git a/dist/modules/analytics/getAnalyticsConfig.js.map b/dist/modules/analytics/getAnalyticsConfig.js.map deleted file mode 100644 index 493a328..0000000 --- a/dist/modules/analytics/getAnalyticsConfig.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/analytics/getAnalyticsConfig.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { AnalyticsConfig } from './types.js'\n\nimport { getSiteIntegrations } from '../payload/index.js'\n\n/**\n * Reads the public analytics IDs from SiteIntegrations, server-side.\n *\n * Returns only the two public IDs (GA4 / GTM) — safe to pass to the client\n * component. Does NOT expose any secret from SiteIntegrations.\n */\nexport async function getAnalyticsConfig(payload: BasePayload): Promise {\n const integrations = await getSiteIntegrations(payload)\n return {\n ga4MeasurementId: integrations.ga4MeasurementId ?? null,\n gtmContainerId: integrations.gtmContainerId ?? null,\n }\n}\n"],"names":["getSiteIntegrations","getAnalyticsConfig","payload","integrations","ga4MeasurementId","gtmContainerId"],"mappings":"AAIA,SAASA,mBAAmB,QAAQ,sBAAqB;AAEzD;;;;;CAKC,GACD,OAAO,eAAeC,mBAAmBC,OAAoB;IAC3D,MAAMC,eAAe,MAAMH,oBAAqCE;IAChE,OAAO;QACLE,kBAAkBD,aAAaC,gBAAgB,IAAI;QACnDC,gBAAgBF,aAAaE,cAAc,IAAI;IACjD;AACF"} \ No newline at end of file diff --git a/dist/modules/analytics/index.d.ts b/dist/modules/analytics/index.d.ts deleted file mode 100644 index 776468d..0000000 --- a/dist/modules/analytics/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { AnalyticsConfig } from './types.js'; -export { getAnalyticsConfig } from './getAnalyticsConfig.js'; diff --git a/dist/modules/analytics/index.js b/dist/modules/analytics/index.js deleted file mode 100644 index 11b6e49..0000000 --- a/dist/modules/analytics/index.js +++ /dev/null @@ -1,5 +0,0 @@ -// Server-safe: config type + the helper that reads IDs from SiteIntegrations. -// The component is client-side — exported via ./client. -export { getAnalyticsConfig } from './getAnalyticsConfig.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/analytics/index.js.map b/dist/modules/analytics/index.js.map deleted file mode 100644 index 8a3ea22..0000000 --- a/dist/modules/analytics/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/analytics/index.ts"],"sourcesContent":["// Server-safe: config type + the helper that reads IDs from SiteIntegrations.\n// The component is client-side — exported via ./client.\nexport type { AnalyticsConfig } from './types.js'\nexport { getAnalyticsConfig } from './getAnalyticsConfig.js'\n"],"names":["getAnalyticsConfig"],"mappings":"AAAA,8EAA8E;AAC9E,oEAAoE;AAEpE,SAASA,kBAAkB,QAAQ,0BAAyB"} \ No newline at end of file diff --git a/dist/modules/analytics/types.d.ts b/dist/modules/analytics/types.d.ts deleted file mode 100644 index ac35fff..0000000 --- a/dist/modules/analytics/types.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Analytics IDs, read from SiteIntegrations (public — safe on the client). - */ -export type AnalyticsConfig = { - /** GA4 Measurement ID, e.g. 'G-XXXXXXXXXX'. */ - ga4MeasurementId?: null | string; - /** GTM Container ID, e.g. 'GTM-XXXXXXX'. */ - gtmContainerId?: null | string; -}; diff --git a/dist/modules/analytics/types.js b/dist/modules/analytics/types.js deleted file mode 100644 index 93dfc2e..0000000 --- a/dist/modules/analytics/types.js +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Analytics IDs, read from SiteIntegrations (public — safe on the client). - */ export { }; - -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/modules/analytics/types.js.map b/dist/modules/analytics/types.js.map deleted file mode 100644 index 95b37d4..0000000 --- a/dist/modules/analytics/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/analytics/types.ts"],"sourcesContent":["/**\n * Analytics IDs, read from SiteIntegrations (public — safe on the client).\n */\nexport type AnalyticsConfig = {\n /** GA4 Measurement ID, e.g. 'G-XXXXXXXXXX'. */\n ga4MeasurementId?: null | string\n /** GTM Container ID, e.g. 'GTM-XXXXXXX'. */\n gtmContainerId?: null | string\n}\n"],"names":[],"mappings":"AAAA;;CAEC,GACD,WAKC"} \ No newline at end of file diff --git a/dist/modules/blocks/RenderBlocks.d.ts b/dist/modules/blocks/RenderBlocks.d.ts deleted file mode 100644 index 11eb2f8..0000000 --- a/dist/modules/blocks/RenderBlocks.d.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { BlockComponentMap, BlockData, EnhanceProps } from './types.js'; -export type RenderBlocksProps = { - /** Block data array from a Payload document (e.g. page.layout). */ - blocks: BlockData[] | null | undefined; - /** Client-provided map of blockType → component. */ - components: BlockComponentMap; - /** - * Optional client hook to inject block-specific props (nav anchors, etc.). - * Keeps the engine generic — the plugin knows no concrete block types. - */ - enhanceProps?: EnhanceProps; -}; -/** - * Generic, server-side block renderer. - * - * Iterates a document's blocks and renders each via the client's component - * map. Unknown blocks are skipped (null), never thrown. Block-specific logic is - * delegated to the client's optional `enhanceProps` — the plugin itself is - * block-agnostic. - * - * The client owns components and block schemas (they pass `components` and - * define blocks in their config); the plugin owns only the iteration and - * wiring. No wrapper markup is added — spacing/layout belong to the client's - * components. - * - * Nested blocks: a block that needs to render child blocks should render its - * own and import the registry itself, rather than receiving the - * component map as a prop. Passing the map as a prop breaks React Server - * Components — functions (client components) can't cross the server→client - * boundary via props. - */ -export declare function RenderBlocks({ blocks, components, enhanceProps }: RenderBlocksProps): import("react/jsx-runtime").JSX.Element | null; diff --git a/dist/modules/blocks/RenderBlocks.js b/dist/modules/blocks/RenderBlocks.js deleted file mode 100644 index 2cfb1ea..0000000 --- a/dist/modules/blocks/RenderBlocks.js +++ /dev/null @@ -1,44 +0,0 @@ -import { jsx as _jsx } from "react/jsx-runtime"; -import { Fragment } from 'react'; -/** - * Generic, server-side block renderer. - * - * Iterates a document's blocks and renders each via the client's component - * map. Unknown blocks are skipped (null), never thrown. Block-specific logic is - * delegated to the client's optional `enhanceProps` — the plugin itself is - * block-agnostic. - * - * The client owns components and block schemas (they pass `components` and - * define blocks in their config); the plugin owns only the iteration and - * wiring. No wrapper markup is added — spacing/layout belong to the client's - * components. - * - * Nested blocks: a block that needs to render child blocks should render its - * own and import the registry itself, rather than receiving the - * component map as a prop. Passing the map as a prop breaks React Server - * Components — functions (client components) can't cross the server→client - * boundary via props. - */ export function RenderBlocks({ blocks, components, enhanceProps }) { - if (!blocks || !Array.isArray(blocks) || blocks.length === 0) { - return null; - } - return /*#__PURE__*/ _jsx(Fragment, { - children: blocks.map((block, index)=>{ - const Component = components[block.blockType]; - if (!Component) { - return null; - } - const extra = enhanceProps ? enhanceProps({ - allBlocks: blocks, - block, - index - }) : {}; - return /*#__PURE__*/ _jsx(Component, { - ...block, - ...extra - }, index); - }) - }); -} - -//# sourceMappingURL=RenderBlocks.js.map \ No newline at end of file diff --git a/dist/modules/blocks/RenderBlocks.js.map b/dist/modules/blocks/RenderBlocks.js.map deleted file mode 100644 index a292dfe..0000000 --- a/dist/modules/blocks/RenderBlocks.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/blocks/RenderBlocks.tsx"],"sourcesContent":["import { Fragment } from 'react'\n\nimport type { BlockComponentMap, BlockData, EnhanceProps } from './types.js'\n\nexport type RenderBlocksProps = {\n /** Block data array from a Payload document (e.g. page.layout). */\n blocks: BlockData[] | null | undefined\n /** Client-provided map of blockType → component. */\n components: BlockComponentMap\n /**\n * Optional client hook to inject block-specific props (nav anchors, etc.).\n * Keeps the engine generic — the plugin knows no concrete block types.\n */\n enhanceProps?: EnhanceProps\n}\n\n/**\n * Generic, server-side block renderer.\n *\n * Iterates a document's blocks and renders each via the client's component\n * map. Unknown blocks are skipped (null), never thrown. Block-specific logic is\n * delegated to the client's optional `enhanceProps` — the plugin itself is\n * block-agnostic.\n *\n * The client owns components and block schemas (they pass `components` and\n * define blocks in their config); the plugin owns only the iteration and\n * wiring. No wrapper markup is added — spacing/layout belong to the client's\n * components.\n *\n * Nested blocks: a block that needs to render child blocks should render its\n * own and import the registry itself, rather than receiving the\n * component map as a prop. Passing the map as a prop breaks React Server\n * Components — functions (client components) can't cross the server→client\n * boundary via props.\n */\nexport function RenderBlocks({ blocks, components, enhanceProps }: RenderBlocksProps) {\n if (!blocks || !Array.isArray(blocks) || blocks.length === 0) {\n return null\n }\n\n return (\n \n {blocks.map((block, index) => {\n const Component = components[block.blockType]\n if (!Component) {return null}\n\n const extra = enhanceProps ? enhanceProps({ allBlocks: blocks, block, index }) : {}\n\n return \n })}\n \n )\n}\n"],"names":["Fragment","RenderBlocks","blocks","components","enhanceProps","Array","isArray","length","map","block","index","Component","blockType","extra","allBlocks"],"mappings":";AAAA,SAASA,QAAQ,QAAQ,QAAO;AAgBhC;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,aAAa,EAAEC,MAAM,EAAEC,UAAU,EAAEC,YAAY,EAAqB;IAClF,IAAI,CAACF,UAAU,CAACG,MAAMC,OAAO,CAACJ,WAAWA,OAAOK,MAAM,KAAK,GAAG;QAC5D,OAAO;IACT;IAEA,qBACE,KAACP;kBACEE,OAAOM,GAAG,CAAC,CAACC,OAAOC;YAClB,MAAMC,YAAYR,UAAU,CAACM,MAAMG,SAAS,CAAC;YAC7C,IAAI,CAACD,WAAW;gBAAC,OAAO;YAAI;YAE5B,MAAME,QAAQT,eAAeA,aAAa;gBAAEU,WAAWZ;gBAAQO;gBAAOC;YAAM,KAAK,CAAC;YAElF,qBAAO,KAACC;gBAAuB,GAAGF,KAAK;gBAAG,GAAGI,KAAK;eAA3BH;QACzB;;AAGN"} \ No newline at end of file diff --git a/dist/modules/blocks/index.d.ts b/dist/modules/blocks/index.d.ts deleted file mode 100644 index a1d1fa5..0000000 --- a/dist/modules/blocks/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { RenderBlocks } from './RenderBlocks.js'; -export type { RenderBlocksProps } from './RenderBlocks.js'; -export type { BlockComponentMap, BlockData, EnhanceProps } from './types.js'; diff --git a/dist/modules/blocks/index.js b/dist/modules/blocks/index.js deleted file mode 100644 index 4563f88..0000000 --- a/dist/modules/blocks/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { RenderBlocks } from './RenderBlocks.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/blocks/index.js.map b/dist/modules/blocks/index.js.map deleted file mode 100644 index b502360..0000000 --- a/dist/modules/blocks/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/blocks/index.ts"],"sourcesContent":["export { RenderBlocks } from './RenderBlocks.js'\nexport type { RenderBlocksProps } from './RenderBlocks.js'\nexport type { BlockComponentMap, BlockData, EnhanceProps } from './types.js'\n"],"names":["RenderBlocks"],"mappings":"AAAA,SAASA,YAAY,QAAQ,oBAAmB"} \ No newline at end of file diff --git a/dist/modules/blocks/types.d.ts b/dist/modules/blocks/types.d.ts deleted file mode 100644 index 22e8c8b..0000000 --- a/dist/modules/blocks/types.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { ComponentType } from 'react'; -/** - * A single block's data as stored by Payload — always has a blockType, plus - * arbitrary block-specific fields. The plugin stays generic over the shape. - */ -export type BlockData = { - [key: string]: unknown; - blockType: string; -}; -/** - * Maps a blockType to the client's component for it. - * The client owns the components; the plugin only receives this map. - */ -export type BlockComponentMap = Record>; -/** - * Optional per-block prop enhancer supplied by the client. - * - * Lets the client inject block-specific logic (e.g. collect anchors for a nav - * block) without the plugin knowing any concrete block types. Returns extra - * props merged into the rendered block. - */ -export type EnhanceProps = (args: { - allBlocks: BlockData[]; - block: BlockData; - index: number; -}) => Record; diff --git a/dist/modules/blocks/types.js b/dist/modules/blocks/types.js deleted file mode 100644 index f7a3e54..0000000 --- a/dist/modules/blocks/types.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Optional per-block prop enhancer supplied by the client. - * - * Lets the client inject block-specific logic (e.g. collect anchors for a nav - * block) without the plugin knowing any concrete block types. Returns extra - * props merged into the rendered block. - */ export { }; - -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/modules/blocks/types.js.map b/dist/modules/blocks/types.js.map deleted file mode 100644 index 9650f08..0000000 --- a/dist/modules/blocks/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/blocks/types.ts"],"sourcesContent":["import type { ComponentType } from 'react'\n\n/**\n * A single block's data as stored by Payload — always has a blockType, plus\n * arbitrary block-specific fields. The plugin stays generic over the shape.\n */\nexport type BlockData = {\n [key: string]: unknown\n blockType: string\n}\n\n/**\n * Maps a blockType to the client's component for it.\n * The client owns the components; the plugin only receives this map.\n */\nexport type BlockComponentMap = Record>\n\n/**\n * Optional per-block prop enhancer supplied by the client.\n *\n * Lets the client inject block-specific logic (e.g. collect anchors for a nav\n * block) without the plugin knowing any concrete block types. Returns extra\n * props merged into the rendered block.\n */\nexport type EnhanceProps = (args: {\n allBlocks: BlockData[]\n block: BlockData\n index: number\n}) => Record\n"],"names":[],"mappings":"AAiBA;;;;;;CAMC,GACD,WAI6B"} \ No newline at end of file diff --git a/dist/modules/consent/ConsentContext.d.ts b/dist/modules/consent/ConsentContext.d.ts deleted file mode 100644 index 27b3fb6..0000000 --- a/dist/modules/consent/ConsentContext.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { type ReactNode } from 'react'; -import type { ConsentTexts } from './texts.js'; -import { useConsent } from './useConsent.js'; -type ConsentContextValue = { - texts: ConsentTexts; -} & ReturnType; -export declare function ConsentProvider({ children, texts }: { - children: ReactNode; - texts: ConsentTexts; -}): import("react/jsx-runtime").JSX.Element; -export declare function useConsentContext(): ConsentContextValue; -export {}; diff --git a/dist/modules/consent/ConsentContext.js b/dist/modules/consent/ConsentContext.js deleted file mode 100644 index 77a88bc..0000000 --- a/dist/modules/consent/ConsentContext.js +++ /dev/null @@ -1,24 +0,0 @@ -'use client'; -import { jsx as _jsx } from "react/jsx-runtime"; -import { createContext, use } from 'react'; -import { useConsent } from './useConsent.js'; -const ConsentContext = /*#__PURE__*/ createContext(null); -export function ConsentProvider({ children, texts }) { - const consent = useConsent(); - return /*#__PURE__*/ _jsx(ConsentContext, { - value: { - ...consent, - texts - }, - children: children - }); -} -export function useConsentContext() { - const ctx = use(ConsentContext); - if (!ctx) { - throw new Error('useConsentContext must be used within ConsentProvider'); - } - return ctx; -} - -//# sourceMappingURL=ConsentContext.js.map \ No newline at end of file diff --git a/dist/modules/consent/ConsentContext.js.map b/dist/modules/consent/ConsentContext.js.map deleted file mode 100644 index a5c7f2e..0000000 --- a/dist/modules/consent/ConsentContext.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/ConsentContext.tsx"],"sourcesContent":["'use client'\nimport { createContext, type ReactNode, use } from 'react'\n\nimport type { ConsentTexts } from './texts.js'\n\nimport { useConsent } from './useConsent.js'\n\ntype ConsentContextValue = { texts: ConsentTexts } & ReturnType\n\nconst ConsentContext = createContext(null)\n\nexport function ConsentProvider({ children, texts }: { children: ReactNode; texts: ConsentTexts }) {\n const consent = useConsent()\n return {children}\n}\n\nexport function useConsentContext(): ConsentContextValue {\n const ctx = use(ConsentContext)\n if (!ctx) {throw new Error('useConsentContext must be used within ConsentProvider')}\n return ctx\n}\n"],"names":["createContext","use","useConsent","ConsentContext","ConsentProvider","children","texts","consent","value","useConsentContext","ctx","Error"],"mappings":"AAAA;;AACA,SAASA,aAAa,EAAkBC,GAAG,QAAQ,QAAO;AAI1D,SAASC,UAAU,QAAQ,kBAAiB;AAI5C,MAAMC,+BAAiBH,cAA0C;AAEjE,OAAO,SAASI,gBAAgB,EAAEC,QAAQ,EAAEC,KAAK,EAAgD;IAC/F,MAAMC,UAAUL;IAChB,qBAAO,KAACC;QAAeK,OAAO;YAAE,GAAGD,OAAO;YAAED;QAAM;kBAAID;;AACxD;AAEA,OAAO,SAASI;IACd,MAAMC,MAAMT,IAAIE;IAChB,IAAI,CAACO,KAAK;QAAC,MAAM,IAAIC,MAAM;IAAwD;IACnF,OAAOD;AACT"} \ No newline at end of file diff --git a/dist/modules/consent/CookieBanner.d.ts b/dist/modules/consent/CookieBanner.d.ts deleted file mode 100644 index f9fb179..0000000 --- a/dist/modules/consent/CookieBanner.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Optional class overrides — for when tokens aren't enough, e.g. turning the - * bottom bar into a centred modal. Replaces the slot's classes outright. - */ -export type CookieBannerClassNames = { - primaryButton?: string; - root?: string; - secondaryButton?: string; -}; -export declare function CookieBanner({ classNames }?: { - classNames?: CookieBannerClassNames; -}): import("react/jsx-runtime").JSX.Element | null; diff --git a/dist/modules/consent/CookieBanner.js b/dist/modules/consent/CookieBanner.js deleted file mode 100644 index aae002e..0000000 --- a/dist/modules/consent/CookieBanner.js +++ /dev/null @@ -1,179 +0,0 @@ -'use client'; -import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; -import { Cookie } from 'lucide-react'; -import { useEffect, useState } from 'react'; -import { CONSENT_CATEGORIES } from './categories.js'; -import { useConsentContext } from './ConsentContext.js'; -/** - * Colours and radius come from CSS custom properties with built-in fallbacks, - * so the banner looks right with no setup, and re-skinning per client means - * declaring a few variables — no imports, no props, no specificity fights: - * - * ```css - * :root { - * --ipal-primary: #16a34a; - * --ipal-radius: 1rem; - * } - * ``` - * - * Available: --ipal-surface, --ipal-border, --ipal-text, --ipal-text-strong, - * --ipal-text-muted, --ipal-primary, --ipal-primary-hover, --ipal-primary-text, - * --ipal-hover, --ipal-radius. Each has a `-dark` counterpart used under - * Tailwind's `dark:` variant (e.g. --ipal-surface-dark). - * - * Layout stays in Tailwind: spacing and flow aren't things a brand changes, and - * exposing them as tokens would mean re-inventing CSS one variable at a time. - */ const surface = 'bg-[var(--ipal-surface,#fff)] dark:bg-[var(--ipal-surface-dark,#171717)]'; -const border = 'border-[var(--ipal-border,#e5e5e5)] dark:border-[var(--ipal-border-dark,#262626)]'; -const radius = 'rounded-[var(--ipal-radius,0.375rem)]'; -const accent = 'text-[var(--ipal-primary,#2563eb)]'; -const defaults = { - primaryButton: `${radius} px-3 py-1.5 text-sm font-medium bg-[var(--ipal-primary,#2563eb)] text-[var(--ipal-primary-text,#fff)] hover:bg-[var(--ipal-primary-hover,#1d4ed8)]`, - root: `fixed inset-x-0 bottom-0 z-50 border-t p-4 shadow-lg ${surface} ${border}`, - secondaryButton: `${radius} px-3 py-1.5 text-sm font-medium text-[var(--ipal-text,#404040)] hover:bg-[var(--ipal-hover,#f5f5f5)] dark:text-[var(--ipal-text-dark,#e5e5e5)] dark:hover:bg-[var(--ipal-hover-dark,#262626)]` -}; -const bodyText = 'text-[var(--ipal-text,#404040)] dark:text-[var(--ipal-text-dark,#d4d4d4)]'; -const strongText = 'text-[var(--ipal-text-strong,#171717)] dark:text-[var(--ipal-text-strong-dark,#f5f5f5)]'; -const mutedText = 'text-[var(--ipal-text-muted,#737373)] dark:text-[var(--ipal-text-muted-dark,#a3a3a3)]'; -export function CookieBanner({ classNames } = {}) { - const { acceptAll, decided, rejectAll, savePreferences, state, texts } = useConsentContext(); - const [showSettings, setShowSettings] = useState(false); - const [choices, setChoices] = useState(state); - useEffect(()=>{ - setChoices(state); - }, [ - state - ]); - if (decided) { - return null; - } - const toggle = (cat)=>{ - if (cat === 'necessary') { - return; - } - setChoices((prev)=>({ - ...prev, - [cat]: !prev[cat] - })); - }; - const rootClass = classNames?.root ?? defaults.root; - const primaryClass = classNames?.primaryButton ?? defaults.primaryButton; - const secondaryClass = classNames?.secondaryButton ?? defaults.secondaryButton; - return /*#__PURE__*/ _jsx("div", { - "aria-label": "Cookie consent", - className: rootClass, - "data-ipal": "banner", - role: "dialog", - children: /*#__PURE__*/ _jsx("div", { - className: "mx-auto max-w-4xl", - children: !showSettings ? /*#__PURE__*/ _jsxs("div", { - className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", - children: [ - /*#__PURE__*/ _jsxs("div", { - className: "flex items-start gap-3", - children: [ - /*#__PURE__*/ _jsx(Cookie, { - "aria-hidden": "true", - className: `h-6 w-6 shrink-0 ${accent}` - }), - /*#__PURE__*/ _jsxs("p", { - className: `text-justify text-sm ${bodyText}`, - children: [ - texts.message, - texts.privacyLink && /*#__PURE__*/ _jsxs(_Fragment, { - children: [ - ' ', - /*#__PURE__*/ _jsx("a", { - className: `${accent} underline hover:no-underline`, - href: texts.privacyLink.href, - children: texts.privacyLink.label - }) - ] - }) - ] - }) - ] - }), - /*#__PURE__*/ _jsxs("div", { - className: "flex shrink-0 gap-2", - children: [ - /*#__PURE__*/ _jsx("button", { - className: secondaryClass, - onClick: ()=>setShowSettings(true), - children: texts.buttons.settings - }), - /*#__PURE__*/ _jsx("button", { - className: secondaryClass, - onClick: rejectAll, - children: texts.buttons.reject - }), - /*#__PURE__*/ _jsx("button", { - className: primaryClass, - onClick: acceptAll, - children: texts.buttons.acceptAll - }) - ] - }) - ] - }) : /*#__PURE__*/ _jsxs("div", { - className: "flex flex-col gap-4", - children: [ - /*#__PURE__*/ _jsxs("h2", { - className: `flex items-center gap-2 text-base font-semibold ${strongText}`, - children: [ - /*#__PURE__*/ _jsx(Cookie, { - "aria-hidden": "true", - className: `h-5 w-5 ${accent}` - }), - texts.settingsTitle - ] - }), - /*#__PURE__*/ _jsx("ul", { - className: "flex flex-col gap-3", - children: CONSENT_CATEGORIES.map((cat)=>/*#__PURE__*/ _jsxs("li", { - className: "flex items-start justify-between gap-4", - children: [ - /*#__PURE__*/ _jsxs("div", { - children: [ - /*#__PURE__*/ _jsx("p", { - className: `text-sm font-medium ${strongText}`, - children: texts.categories[cat].title - }), - /*#__PURE__*/ _jsx("p", { - className: `text-xs ${mutedText}`, - children: texts.categories[cat].description - }) - ] - }), - /*#__PURE__*/ _jsx("input", { - checked: cat === 'necessary' ? true : choices[cat], - className: "mt-1 h-4 w-4 shrink-0 accent-[var(--ipal-primary,#2563eb)]", - disabled: cat === 'necessary', - onChange: ()=>toggle(cat), - type: "checkbox" - }) - ] - }, cat)) - }), - /*#__PURE__*/ _jsxs("div", { - className: "flex justify-end gap-2", - children: [ - /*#__PURE__*/ _jsx("button", { - className: secondaryClass, - onClick: ()=>setShowSettings(false), - children: texts.buttons.back - }), - /*#__PURE__*/ _jsx("button", { - className: primaryClass, - onClick: ()=>savePreferences(choices), - children: texts.buttons.save - }) - ] - }) - ] - }) - }) - }); -} - -//# sourceMappingURL=CookieBanner.js.map \ No newline at end of file diff --git a/dist/modules/consent/CookieBanner.js.map b/dist/modules/consent/CookieBanner.js.map deleted file mode 100644 index f268c1a..0000000 --- a/dist/modules/consent/CookieBanner.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/CookieBanner.tsx"],"sourcesContent":["'use client'\nimport { Cookie } from 'lucide-react'\nimport { useEffect, useState } from 'react'\n\nimport { CONSENT_CATEGORIES, type ConsentCategory } from './categories.js'\nimport { useConsentContext } from './ConsentContext.js'\n\n/**\n * Optional class overrides — for when tokens aren't enough, e.g. turning the\n * bottom bar into a centred modal. Replaces the slot's classes outright.\n */\nexport type CookieBannerClassNames = {\n primaryButton?: string\n root?: string\n secondaryButton?: string\n}\n\n/**\n * Colours and radius come from CSS custom properties with built-in fallbacks,\n * so the banner looks right with no setup, and re-skinning per client means\n * declaring a few variables — no imports, no props, no specificity fights:\n *\n * ```css\n * :root {\n * --ipal-primary: #16a34a;\n * --ipal-radius: 1rem;\n * }\n * ```\n *\n * Available: --ipal-surface, --ipal-border, --ipal-text, --ipal-text-strong,\n * --ipal-text-muted, --ipal-primary, --ipal-primary-hover, --ipal-primary-text,\n * --ipal-hover, --ipal-radius. Each has a `-dark` counterpart used under\n * Tailwind's `dark:` variant (e.g. --ipal-surface-dark).\n *\n * Layout stays in Tailwind: spacing and flow aren't things a brand changes, and\n * exposing them as tokens would mean re-inventing CSS one variable at a time.\n */\nconst surface = 'bg-[var(--ipal-surface,#fff)] dark:bg-[var(--ipal-surface-dark,#171717)]'\nconst border = 'border-[var(--ipal-border,#e5e5e5)] dark:border-[var(--ipal-border-dark,#262626)]'\nconst radius = 'rounded-[var(--ipal-radius,0.375rem)]'\nconst accent = 'text-[var(--ipal-primary,#2563eb)]'\n\nconst defaults = {\n primaryButton: `${radius} px-3 py-1.5 text-sm font-medium bg-[var(--ipal-primary,#2563eb)] text-[var(--ipal-primary-text,#fff)] hover:bg-[var(--ipal-primary-hover,#1d4ed8)]`,\n root: `fixed inset-x-0 bottom-0 z-50 border-t p-4 shadow-lg ${surface} ${border}`,\n secondaryButton: `${radius} px-3 py-1.5 text-sm font-medium text-[var(--ipal-text,#404040)] hover:bg-[var(--ipal-hover,#f5f5f5)] dark:text-[var(--ipal-text-dark,#e5e5e5)] dark:hover:bg-[var(--ipal-hover-dark,#262626)]`,\n}\n\nconst bodyText = 'text-[var(--ipal-text,#404040)] dark:text-[var(--ipal-text-dark,#d4d4d4)]'\nconst strongText =\n 'text-[var(--ipal-text-strong,#171717)] dark:text-[var(--ipal-text-strong-dark,#f5f5f5)]'\nconst mutedText =\n 'text-[var(--ipal-text-muted,#737373)] dark:text-[var(--ipal-text-muted-dark,#a3a3a3)]'\n\nexport function CookieBanner({ classNames }: { classNames?: CookieBannerClassNames } = {}) {\n const { acceptAll, decided, rejectAll, savePreferences, state, texts } = useConsentContext()\n const [showSettings, setShowSettings] = useState(false)\n const [choices, setChoices] = useState>(state)\n\n useEffect(() => {\n setChoices(state)\n }, [state])\n\n if (decided) {return null}\n\n const toggle = (cat: ConsentCategory) => {\n if (cat === 'necessary') {return}\n setChoices((prev) => ({ ...prev, [cat]: !prev[cat] }))\n }\n\n const rootClass = classNames?.root ?? defaults.root\n const primaryClass = classNames?.primaryButton ?? defaults.primaryButton\n const secondaryClass = classNames?.secondaryButton ?? defaults.secondaryButton\n\n return (\n
\n
\n {!showSettings ? (\n
\n
\n \n

\n {texts.message}\n {texts.privacyLink && (\n <>\n {' '}\n \n {texts.privacyLink.label}\n \n \n )}\n

\n
\n
\n \n \n \n
\n
\n ) : (\n
\n

\n \n {texts.settingsTitle}\n

\n
    \n {CONSENT_CATEGORIES.map((cat) => (\n
  • \n
    \n

    \n {texts.categories[cat].title}\n

    \n

    {texts.categories[cat].description}

    \n
    \n toggle(cat)}\n type=\"checkbox\"\n />\n
  • \n ))}\n
\n
\n \n \n
\n
\n )}\n
\n
\n )\n}\n"],"names":["Cookie","useEffect","useState","CONSENT_CATEGORIES","useConsentContext","surface","border","radius","accent","defaults","primaryButton","root","secondaryButton","bodyText","strongText","mutedText","CookieBanner","classNames","acceptAll","decided","rejectAll","savePreferences","state","texts","showSettings","setShowSettings","choices","setChoices","toggle","cat","prev","rootClass","primaryClass","secondaryClass","div","aria-label","className","data-ipal","role","aria-hidden","p","message","privacyLink","a","href","label","button","onClick","buttons","settings","reject","h2","settingsTitle","ul","map","li","categories","title","description","input","checked","disabled","onChange","type","back","save"],"mappings":"AAAA;;AACA,SAASA,MAAM,QAAQ,eAAc;AACrC,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAE3C,SAASC,kBAAkB,QAA8B,kBAAiB;AAC1E,SAASC,iBAAiB,QAAQ,sBAAqB;AAYvD;;;;;;;;;;;;;;;;;;;CAmBC,GACD,MAAMC,UAAU;AAChB,MAAMC,SAAS;AACf,MAAMC,SAAS;AACf,MAAMC,SAAS;AAEf,MAAMC,WAAW;IACfC,eAAe,GAAGH,OAAO,mJAAmJ,CAAC;IAC7KI,MAAM,CAAC,qDAAqD,EAAEN,QAAQ,CAAC,EAAEC,QAAQ;IACjFM,iBAAiB,GAAGL,OAAO,8LAA8L,CAAC;AAC5N;AAEA,MAAMM,WAAW;AACjB,MAAMC,aACJ;AACF,MAAMC,YACJ;AAEF,OAAO,SAASC,aAAa,EAAEC,UAAU,EAA2C,GAAG,CAAC,CAAC;IACvF,MAAM,EAAEC,SAAS,EAAEC,OAAO,EAAEC,SAAS,EAAEC,eAAe,EAAEC,KAAK,EAAEC,KAAK,EAAE,GAAGnB;IACzE,MAAM,CAACoB,cAAcC,gBAAgB,GAAGvB,SAAS;IACjD,MAAM,CAACwB,SAASC,WAAW,GAAGzB,SAA2CoB;IAEzErB,UAAU;QACR0B,WAAWL;IACb,GAAG;QAACA;KAAM;IAEV,IAAIH,SAAS;QAAC,OAAO;IAAI;IAEzB,MAAMS,SAAS,CAACC;QACd,IAAIA,QAAQ,aAAa;YAAC;QAAM;QAChCF,WAAW,CAACG,OAAU,CAAA;gBAAE,GAAGA,IAAI;gBAAE,CAACD,IAAI,EAAE,CAACC,IAAI,CAACD,IAAI;YAAC,CAAA;IACrD;IAEA,MAAME,YAAYd,YAAYN,QAAQF,SAASE,IAAI;IACnD,MAAMqB,eAAef,YAAYP,iBAAiBD,SAASC,aAAa;IACxE,MAAMuB,iBAAiBhB,YAAYL,mBAAmBH,SAASG,eAAe;IAE9E,qBACE,KAACsB;QAAIC,cAAW;QAAiBC,WAAWL;QAAWM,aAAU;QAASC,MAAK;kBAC7E,cAAA,KAACJ;YAAIE,WAAU;sBACZ,CAACZ,6BACA,MAACU;gBAAIE,WAAU;;kCACb,MAACF;wBAAIE,WAAU;;0CACb,KAACpC;gCAAOuC,eAAY;gCAAOH,WAAW,CAAC,iBAAiB,EAAE5B,QAAQ;;0CAClE,MAACgC;gCAAEJ,WAAW,CAAC,qBAAqB,EAAEvB,UAAU;;oCAC7CU,MAAMkB,OAAO;oCACblB,MAAMmB,WAAW,kBAChB;;4CACG;0DACD,KAACC;gDACCP,WAAW,GAAG5B,OAAO,6BAA6B,CAAC;gDACnDoC,MAAMrB,MAAMmB,WAAW,CAACE,IAAI;0DAE3BrB,MAAMmB,WAAW,CAACG,KAAK;;;;;;;;kCAMlC,MAACX;wBAAIE,WAAU;;0CACb,KAACU;gCAAOV,WAAWH;gCAAgBc,SAAS,IAAMtB,gBAAgB;0CAC/DF,MAAMyB,OAAO,CAACC,QAAQ;;0CAEzB,KAACH;gCAAOV,WAAWH;gCAAgBc,SAAS3B;0CACzCG,MAAMyB,OAAO,CAACE,MAAM;;0CAEvB,KAACJ;gCAAOV,WAAWJ;gCAAce,SAAS7B;0CACvCK,MAAMyB,OAAO,CAAC9B,SAAS;;;;;+BAK9B,MAACgB;gBAAIE,WAAU;;kCACb,MAACe;wBAAGf,WAAW,CAAC,gDAAgD,EAAEtB,YAAY;;0CAC5E,KAACd;gCAAOuC,eAAY;gCAAOH,WAAW,CAAC,QAAQ,EAAE5B,QAAQ;;4BACxDe,MAAM6B,aAAa;;;kCAEtB,KAACC;wBAAGjB,WAAU;kCACXjC,mBAAmBmD,GAAG,CAAC,CAACzB,oBACvB,MAAC0B;gCAAGnB,WAAU;;kDACZ,MAACF;;0DACC,KAACM;gDAAEJ,WAAW,CAAC,oBAAoB,EAAEtB,YAAY;0DAC9CS,MAAMiC,UAAU,CAAC3B,IAAI,CAAC4B,KAAK;;0DAE9B,KAACjB;gDAAEJ,WAAW,CAAC,QAAQ,EAAErB,WAAW;0DAAGQ,MAAMiC,UAAU,CAAC3B,IAAI,CAAC6B,WAAW;;;;kDAE1E,KAACC;wCACCC,SAAS/B,QAAQ,cAAc,OAAOH,OAAO,CAACG,IAAI;wCAClDO,WAAU;wCACVyB,UAAUhC,QAAQ;wCAClBiC,UAAU,IAAMlC,OAAOC;wCACvBkC,MAAK;;;+BAZmDlC;;kCAiBhE,MAACK;wBAAIE,WAAU;;0CACb,KAACU;gCAAOV,WAAWH;gCAAgBc,SAAS,IAAMtB,gBAAgB;0CAC/DF,MAAMyB,OAAO,CAACgB,IAAI;;0CAErB,KAAClB;gCAAOV,WAAWJ;gCAAce,SAAS,IAAM1B,gBAAgBK;0CAC7DH,MAAMyB,OAAO,CAACiB,IAAI;;;;;;;;AAQnC"} \ No newline at end of file diff --git a/dist/modules/consent/CookieButton.d.ts b/dist/modules/consent/CookieButton.d.ts deleted file mode 100644 index c099fab..0000000 --- a/dist/modules/consent/CookieButton.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Floating "cookie settings" button — lets visitors reopen the consent panel - * after deciding (GDPR: withdrawing consent must be as easy as giving it). - * Hidden while the banner is showing. - * - * Colours follow the same CSS custom properties as CookieBanner - * (--ipal-surface, --ipal-border, --ipal-hover, --ipal-text), so a client's - * palette applies to both without touching either component. Pass className to - * replace the styling outright. - */ -export declare function CookieButton({ className }?: { - className?: string; -}): import("react/jsx-runtime").JSX.Element | null; diff --git a/dist/modules/consent/CookieButton.js b/dist/modules/consent/CookieButton.js deleted file mode 100644 index 9b8ec8e..0000000 --- a/dist/modules/consent/CookieButton.js +++ /dev/null @@ -1,32 +0,0 @@ -'use client'; -import { jsx as _jsx } from "react/jsx-runtime"; -import { Cookie } from 'lucide-react'; -import { useConsentContext } from './ConsentContext.js'; -/** - * Floating "cookie settings" button — lets visitors reopen the consent panel - * after deciding (GDPR: withdrawing consent must be as easy as giving it). - * Hidden while the banner is showing. - * - * Colours follow the same CSS custom properties as CookieBanner - * (--ipal-surface, --ipal-border, --ipal-hover, --ipal-text), so a client's - * palette applies to both without touching either component. Pass className to - * replace the styling outright. - */ export function CookieButton({ className } = {}) { - const { decided, reopen } = useConsentContext(); - if (!decided) { - return null; - } - const cls = className ?? 'fixed bottom-4 left-4 z-40 flex h-11 w-11 items-center justify-center rounded-full border shadow-md transition-colors ' + 'bg-[var(--ipal-surface,#fff)] border-[var(--ipal-border,#e5e5e5)] hover:bg-[var(--ipal-hover,#f5f5f5)] ' + 'dark:bg-[var(--ipal-surface-dark,#171717)] dark:border-[var(--ipal-border-dark,#262626)] dark:hover:bg-[var(--ipal-hover-dark,#262626)]'; - return /*#__PURE__*/ _jsx("button", { - "aria-label": "Cookie settings", - className: cls, - "data-ipal": "cookie-button", - onClick: reopen, - children: /*#__PURE__*/ _jsx(Cookie, { - "aria-hidden": "true", - className: "h-5 w-5 text-[var(--ipal-text,#525252)] dark:text-[var(--ipal-text-dark,#d4d4d4)]" - }) - }); -} - -//# sourceMappingURL=CookieButton.js.map \ No newline at end of file diff --git a/dist/modules/consent/CookieButton.js.map b/dist/modules/consent/CookieButton.js.map deleted file mode 100644 index 42d2f2e..0000000 --- a/dist/modules/consent/CookieButton.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/CookieButton.tsx"],"sourcesContent":["'use client'\nimport { Cookie } from 'lucide-react'\n\nimport { useConsentContext } from './ConsentContext.js'\n\n/**\n * Floating \"cookie settings\" button — lets visitors reopen the consent panel\n * after deciding (GDPR: withdrawing consent must be as easy as giving it).\n * Hidden while the banner is showing.\n *\n * Colours follow the same CSS custom properties as CookieBanner\n * (--ipal-surface, --ipal-border, --ipal-hover, --ipal-text), so a client's\n * palette applies to both without touching either component. Pass className to\n * replace the styling outright.\n */\nexport function CookieButton({ className }: { className?: string } = {}) {\n const { decided, reopen } = useConsentContext()\n\n if (!decided) {return null}\n\n const cls =\n className ??\n 'fixed bottom-4 left-4 z-40 flex h-11 w-11 items-center justify-center rounded-full border shadow-md transition-colors ' +\n 'bg-[var(--ipal-surface,#fff)] border-[var(--ipal-border,#e5e5e5)] hover:bg-[var(--ipal-hover,#f5f5f5)] ' +\n 'dark:bg-[var(--ipal-surface-dark,#171717)] dark:border-[var(--ipal-border-dark,#262626)] dark:hover:bg-[var(--ipal-hover-dark,#262626)]'\n\n return (\n \n )\n}\n"],"names":["Cookie","useConsentContext","CookieButton","className","decided","reopen","cls","button","aria-label","data-ipal","onClick","aria-hidden"],"mappings":"AAAA;;AACA,SAASA,MAAM,QAAQ,eAAc;AAErC,SAASC,iBAAiB,QAAQ,sBAAqB;AAEvD;;;;;;;;;CASC,GACD,OAAO,SAASC,aAAa,EAAEC,SAAS,EAA0B,GAAG,CAAC,CAAC;IACrE,MAAM,EAAEC,OAAO,EAAEC,MAAM,EAAE,GAAGJ;IAE5B,IAAI,CAACG,SAAS;QAAC,OAAO;IAAI;IAE1B,MAAME,MACJH,aACA,2HACE,4GACA;IAEJ,qBACE,KAACI;QAAOC,cAAW;QAAkBL,WAAWG;QAAKG,aAAU;QAAgBC,SAASL;kBACtF,cAAA,KAACL;YACCW,eAAY;YACZR,WAAU;;;AAIlB"} \ No newline at end of file diff --git a/dist/modules/consent/categories.d.ts b/dist/modules/consent/categories.d.ts deleted file mode 100644 index aeefb63..0000000 --- a/dist/modules/consent/categories.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Cookie consent categories (GDPR). 'necessary' is always granted and cannot be - * disabled. The rest default to false until the user opts in. - */ -export declare const CONSENT_CATEGORIES: readonly ["necessary", "functional", "analytics", "marketing"]; -export type ConsentCategory = (typeof CONSENT_CATEGORIES)[number]; -export type ConsentState = Record; -export declare const DEFAULT_CONSENT: ConsentState; -export declare const ACCEPT_ALL_CONSENT: ConsentState; -export declare const REJECT_ALL_CONSENT: ConsentState; diff --git a/dist/modules/consent/categories.js b/dist/modules/consent/categories.js deleted file mode 100644 index 5a7fc4d..0000000 --- a/dist/modules/consent/categories.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Cookie consent categories (GDPR). 'necessary' is always granted and cannot be - * disabled. The rest default to false until the user opts in. - */ export const CONSENT_CATEGORIES = [ - 'necessary', - 'functional', - 'analytics', - 'marketing' -]; -export const DEFAULT_CONSENT = { - necessary: true, - functional: false, - analytics: false, - marketing: false -}; -export const ACCEPT_ALL_CONSENT = { - necessary: true, - functional: true, - analytics: true, - marketing: true -}; -export const REJECT_ALL_CONSENT = { - ...DEFAULT_CONSENT -}; - -//# sourceMappingURL=categories.js.map \ No newline at end of file diff --git a/dist/modules/consent/categories.js.map b/dist/modules/consent/categories.js.map deleted file mode 100644 index 20e7768..0000000 --- a/dist/modules/consent/categories.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/categories.ts"],"sourcesContent":["/**\n * Cookie consent categories (GDPR). 'necessary' is always granted and cannot be\n * disabled. The rest default to false until the user opts in.\n */\nexport const CONSENT_CATEGORIES = ['necessary', 'functional', 'analytics', 'marketing'] as const\n\nexport type ConsentCategory = (typeof CONSENT_CATEGORIES)[number]\n\nexport type ConsentState = Record\n\nexport const DEFAULT_CONSENT: ConsentState = {\n necessary: true,\n functional: false,\n analytics: false,\n marketing: false,\n}\n\nexport const ACCEPT_ALL_CONSENT: ConsentState = {\n necessary: true,\n functional: true,\n analytics: true,\n marketing: true,\n}\n\nexport const REJECT_ALL_CONSENT: ConsentState = { ...DEFAULT_CONSENT }\n"],"names":["CONSENT_CATEGORIES","DEFAULT_CONSENT","necessary","functional","analytics","marketing","ACCEPT_ALL_CONSENT","REJECT_ALL_CONSENT"],"mappings":"AAAA;;;CAGC,GACD,OAAO,MAAMA,qBAAqB;IAAC;IAAa;IAAc;IAAa;CAAY,CAAS;AAMhG,OAAO,MAAMC,kBAAgC;IAC3CC,WAAW;IACXC,YAAY;IACZC,WAAW;IACXC,WAAW;AACb,EAAC;AAED,OAAO,MAAMC,qBAAmC;IAC9CJ,WAAW;IACXC,YAAY;IACZC,WAAW;IACXC,WAAW;AACb,EAAC;AAED,OAAO,MAAME,qBAAmC;IAAE,GAAGN,eAAe;AAAC,EAAC"} \ No newline at end of file diff --git a/dist/modules/consent/client.d.ts b/dist/modules/consent/client.d.ts deleted file mode 100644 index a6f0d8b..0000000 --- a/dist/modules/consent/client.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { ConsentProvider, useConsentContext } from './ConsentContext.js'; -export { CookieBanner } from './CookieBanner.js'; -export type { CookieBannerClassNames } from './CookieBanner.js'; -export { CookieButton } from './CookieButton.js'; -export { useConsent } from './useConsent.js'; diff --git a/dist/modules/consent/client.js b/dist/modules/consent/client.js deleted file mode 100644 index 12946c3..0000000 --- a/dist/modules/consent/client.js +++ /dev/null @@ -1,8 +0,0 @@ -'use client'; -export { ConsentProvider, useConsentContext } from './ConsentContext.js'; -export { CookieBanner } from './CookieBanner.js'; -export { CookieButton } from './CookieButton.js'; -// Client-only consent exports — hook, provider, and UI components. -export { useConsent } from './useConsent.js'; - -//# sourceMappingURL=client.js.map \ No newline at end of file diff --git a/dist/modules/consent/client.js.map b/dist/modules/consent/client.js.map deleted file mode 100644 index e28050d..0000000 --- a/dist/modules/consent/client.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/client.ts"],"sourcesContent":["'use client'\nexport { ConsentProvider, useConsentContext } from './ConsentContext.js'\nexport { CookieBanner } from './CookieBanner.js'\nexport type { CookieBannerClassNames } from './CookieBanner.js'\nexport { CookieButton } from './CookieButton.js'\n// Client-only consent exports — hook, provider, and UI components.\nexport { useConsent } from './useConsent.js'\n"],"names":["ConsentProvider","useConsentContext","CookieBanner","CookieButton","useConsent"],"mappings":"AAAA;AACA,SAASA,eAAe,EAAEC,iBAAiB,QAAQ,sBAAqB;AACxE,SAASC,YAAY,QAAQ,oBAAmB;AAEhD,SAASC,YAAY,QAAQ,oBAAmB;AAChD,mEAAmE;AACnE,SAASC,UAAU,QAAQ,kBAAiB"} \ No newline at end of file diff --git a/dist/modules/consent/getConsentTexts.d.ts b/dist/modules/consent/getConsentTexts.d.ts deleted file mode 100644 index 109b769..0000000 --- a/dist/modules/consent/getConsentTexts.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { I18nConfig } from '../i18n/index.js'; -import type { ConsentTexts } from './texts.js'; -type GetConsentTextsArgs = { - config: I18nConfig; - locale?: string; - payload: BasePayload; - /** - * Privacy-policy label + the system page (from SiteSettings.privacyPolicy, - * read with locale:'all') to link to. Optional — omit for no link. - */ - privacyPolicy?: { - label: string; - page: { - slug?: null | Record; - } | null; - }; -}; -/** - * Resolves consent banner texts from the CookieSettings global, falling back to - * English defaults per field. The privacy-policy link is built from the pages - * module (system page role), not stored in CookieSettings — one source of truth. - */ -export declare function getConsentTexts({ config, locale, payload, privacyPolicy, }: GetConsentTextsArgs): Promise; -export {}; diff --git a/dist/modules/consent/getConsentTexts.js b/dist/modules/consent/getConsentTexts.js deleted file mode 100644 index 6b2a0be..0000000 --- a/dist/modules/consent/getConsentTexts.js +++ /dev/null @@ -1,79 +0,0 @@ -import { getSystemPagePath } from '../pages/index.js'; -import { getGlobal } from '../payload/index.js'; -import { CONSENT_CATEGORIES } from './categories.js'; -/** English fallback used when the global has no value for a field. */ const FALLBACK = { - buttons: { - acceptAll: 'Accept all', - back: 'Back', - reject: 'Reject', - save: 'Save preferences', - settings: 'Settings' - }, - categories: { - analytics: { - description: 'Help us understand usage.', - title: 'Analytics' - }, - functional: { - description: 'Remember preferences.', - title: 'Functional' - }, - marketing: { - description: 'Used to show relevant ads.', - title: 'Marketing' - }, - necessary: { - description: 'Required for the site to work, including your language and theme preferences. Always on.', - title: 'Necessary' - } - }, - message: 'We use cookies to run the site, analyze traffic, and improve your experience. You can accept all, reject non-essential, or choose which to allow.', - privacyLink: null, - settingsTitle: 'Cookie settings' -}; -/** - * Resolves consent banner texts from the CookieSettings global, falling back to - * English defaults per field. The privacy-policy link is built from the pages - * module (system page role), not stored in CookieSettings — one source of truth. - */ export async function getConsentTexts({ config, locale, payload, privacyPolicy }) { - const g = await getGlobal(payload, 'cookie-settings', { - locale - }); - const categories = {}; - for (const cat of CONSENT_CATEGORIES){ - const entry = g.categories?.find((c)=>c.key === cat); - categories[cat] = { - description: entry?.description || FALLBACK.categories[cat].description, - title: entry?.title || FALLBACK.categories[cat].title - }; - } - let privacyLink = null; - if (privacyPolicy?.page && locale) { - const href = getSystemPagePath({ - config, - locale, - page: privacyPolicy.page - }); - if (href) { - privacyLink = { - href, - label: privacyPolicy.label - }; - } - } - return { - buttons: { - acceptAll: g.buttons?.acceptAll || FALLBACK.buttons.acceptAll, - back: g.buttons?.back || FALLBACK.buttons.back, - reject: g.buttons?.reject || FALLBACK.buttons.reject, - save: g.buttons?.save || FALLBACK.buttons.save, - settings: g.buttons?.settings || FALLBACK.buttons.settings - }, - categories, - message: g.message || FALLBACK.message, - privacyLink, - settingsTitle: g.settingsTitle || FALLBACK.settingsTitle - }; -} - -//# sourceMappingURL=getConsentTexts.js.map \ No newline at end of file diff --git a/dist/modules/consent/getConsentTexts.js.map b/dist/modules/consent/getConsentTexts.js.map deleted file mode 100644 index af0898f..0000000 --- a/dist/modules/consent/getConsentTexts.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/getConsentTexts.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { I18nConfig } from '../i18n/index.js'\nimport type { ConsentTexts } from './texts.js'\n\nimport { getSystemPagePath } from '../pages/index.js'\nimport { getGlobal } from '../payload/index.js'\nimport { CONSENT_CATEGORIES } from './categories.js'\n\n/** English fallback used when the global has no value for a field. */\nconst FALLBACK: ConsentTexts = {\n buttons: {\n acceptAll: 'Accept all',\n back: 'Back',\n reject: 'Reject',\n save: 'Save preferences',\n settings: 'Settings',\n },\n categories: {\n analytics: { description: 'Help us understand usage.', title: 'Analytics' },\n functional: { description: 'Remember preferences.', title: 'Functional' },\n marketing: { description: 'Used to show relevant ads.', title: 'Marketing' },\n necessary: {\n description:\n 'Required for the site to work, including your language and theme preferences. Always on.',\n title: 'Necessary',\n },\n },\n message:\n 'We use cookies to run the site, analyze traffic, and improve your experience. You can accept all, reject non-essential, or choose which to allow.',\n privacyLink: null,\n settingsTitle: 'Cookie settings',\n}\n\ntype CookieSettingsData = {\n buttons?: null | Partial\n categories?: Array<{ description?: null | string; key: string; title?: null | string }> | null\n message?: null | string\n settingsTitle?: null | string\n}\n\ntype GetConsentTextsArgs = {\n config: I18nConfig\n locale?: string\n payload: BasePayload\n /**\n * Privacy-policy label + the system page (from SiteSettings.privacyPolicy,\n * read with locale:'all') to link to. Optional — omit for no link.\n */\n privacyPolicy?: {\n label: string\n page: { slug?: null | Record } | null\n }\n}\n\n/**\n * Resolves consent banner texts from the CookieSettings global, falling back to\n * English defaults per field. The privacy-policy link is built from the pages\n * module (system page role), not stored in CookieSettings — one source of truth.\n */\nexport async function getConsentTexts({\n config,\n locale,\n payload,\n privacyPolicy,\n}: GetConsentTextsArgs): Promise {\n const g = await getGlobal(payload, 'cookie-settings', { locale })\n\n const categories = {} as ConsentTexts['categories']\n for (const cat of CONSENT_CATEGORIES) {\n const entry = g.categories?.find((c) => c.key === cat)\n categories[cat] = {\n description: entry?.description || FALLBACK.categories[cat].description,\n title: entry?.title || FALLBACK.categories[cat].title,\n }\n }\n\n let privacyLink: ConsentTexts['privacyLink'] = null\n if (privacyPolicy?.page && locale) {\n const href = getSystemPagePath({ config, locale, page: privacyPolicy.page })\n if (href) {\n privacyLink = { href, label: privacyPolicy.label }\n }\n }\n\n return {\n buttons: {\n acceptAll: g.buttons?.acceptAll || FALLBACK.buttons.acceptAll,\n back: g.buttons?.back || FALLBACK.buttons.back,\n reject: g.buttons?.reject || FALLBACK.buttons.reject,\n save: g.buttons?.save || FALLBACK.buttons.save,\n settings: g.buttons?.settings || FALLBACK.buttons.settings,\n },\n categories,\n message: g.message || FALLBACK.message,\n privacyLink,\n settingsTitle: g.settingsTitle || FALLBACK.settingsTitle,\n }\n}\n"],"names":["getSystemPagePath","getGlobal","CONSENT_CATEGORIES","FALLBACK","buttons","acceptAll","back","reject","save","settings","categories","analytics","description","title","functional","marketing","necessary","message","privacyLink","settingsTitle","getConsentTexts","config","locale","payload","privacyPolicy","g","cat","entry","find","c","key","page","href","label"],"mappings":"AAKA,SAASA,iBAAiB,QAAQ,oBAAmB;AACrD,SAASC,SAAS,QAAQ,sBAAqB;AAC/C,SAASC,kBAAkB,QAAQ,kBAAiB;AAEpD,oEAAoE,GACpE,MAAMC,WAAyB;IAC7BC,SAAS;QACPC,WAAW;QACXC,MAAM;QACNC,QAAQ;QACRC,MAAM;QACNC,UAAU;IACZ;IACAC,YAAY;QACVC,WAAW;YAAEC,aAAa;YAA6BC,OAAO;QAAY;QAC1EC,YAAY;YAAEF,aAAa;YAAyBC,OAAO;QAAa;QACxEE,WAAW;YAAEH,aAAa;YAA8BC,OAAO;QAAY;QAC3EG,WAAW;YACTJ,aACE;YACFC,OAAO;QACT;IACF;IACAI,SACE;IACFC,aAAa;IACbC,eAAe;AACjB;AAuBA;;;;CAIC,GACD,OAAO,eAAeC,gBAAgB,EACpCC,MAAM,EACNC,MAAM,EACNC,OAAO,EACPC,aAAa,EACO;IACpB,MAAMC,IAAI,MAAMxB,UAA8BsB,SAAS,mBAAmB;QAAED;IAAO;IAEnF,MAAMZ,aAAa,CAAC;IACpB,KAAK,MAAMgB,OAAOxB,mBAAoB;QACpC,MAAMyB,QAAQF,EAAEf,UAAU,EAAEkB,KAAK,CAACC,IAAMA,EAAEC,GAAG,KAAKJ;QAClDhB,UAAU,CAACgB,IAAI,GAAG;YAChBd,aAAae,OAAOf,eAAeT,SAASO,UAAU,CAACgB,IAAI,CAACd,WAAW;YACvEC,OAAOc,OAAOd,SAASV,SAASO,UAAU,CAACgB,IAAI,CAACb,KAAK;QACvD;IACF;IAEA,IAAIK,cAA2C;IAC/C,IAAIM,eAAeO,QAAQT,QAAQ;QACjC,MAAMU,OAAOhC,kBAAkB;YAAEqB;YAAQC;YAAQS,MAAMP,cAAcO,IAAI;QAAC;QAC1E,IAAIC,MAAM;YACRd,cAAc;gBAAEc;gBAAMC,OAAOT,cAAcS,KAAK;YAAC;QACnD;IACF;IAEA,OAAO;QACL7B,SAAS;YACPC,WAAWoB,EAAErB,OAAO,EAAEC,aAAaF,SAASC,OAAO,CAACC,SAAS;YAC7DC,MAAMmB,EAAErB,OAAO,EAAEE,QAAQH,SAASC,OAAO,CAACE,IAAI;YAC9CC,QAAQkB,EAAErB,OAAO,EAAEG,UAAUJ,SAASC,OAAO,CAACG,MAAM;YACpDC,MAAMiB,EAAErB,OAAO,EAAEI,QAAQL,SAASC,OAAO,CAACI,IAAI;YAC9CC,UAAUgB,EAAErB,OAAO,EAAEK,YAAYN,SAASC,OAAO,CAACK,QAAQ;QAC5D;QACAC;QACAO,SAASQ,EAAER,OAAO,IAAId,SAASc,OAAO;QACtCC;QACAC,eAAeM,EAAEN,aAAa,IAAIhB,SAASgB,aAAa;IAC1D;AACF"} \ No newline at end of file diff --git a/dist/modules/consent/googleConsent.d.ts b/dist/modules/consent/googleConsent.d.ts deleted file mode 100644 index ccd3ea9..0000000 --- a/dist/modules/consent/googleConsent.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { ConsentState } from './categories.js'; -declare global { - interface Window { - dataLayer?: unknown[]; - } -} -/** - * Mirrors Google's canonical snippet: `function gtag(){dataLayer.push(arguments)}`. - * - * Pushing `arguments` rather than a plain array is not a stylistic detail. - * Google's tags recognise a consent command by the Arguments object it arrives - * in; a real array lands in the dataLayer as ordinary data and the command is - * silently ignored — the tag loads, but consent never updates and analytics - * stays denied. The rest parameter exists only to type the call sites. - * - * Exported for the analytics module (which needs `js`/`config` commands) — - * intentionally not re-exported from the package's public API. - */ -export declare function gtag(..._args: unknown[]): void; -/** Sets the default consent state (call before Google tags load). */ -export declare function setDefaultConsent(state: ConsentState): void; -/** Updates consent after the user decides. */ -export declare function updateConsent(state: ConsentState): void; diff --git a/dist/modules/consent/googleConsent.js b/dist/modules/consent/googleConsent.js deleted file mode 100644 index f196814..0000000 --- a/dist/modules/consent/googleConsent.js +++ /dev/null @@ -1,42 +0,0 @@ -function toSignals(state) { - const g = (b)=>b ? 'granted' : 'denied'; - return { - ad_personalization: g(state.marketing), - ad_storage: g(state.marketing), - ad_user_data: g(state.marketing), - analytics_storage: g(state.analytics), - functionality_storage: g(state.functional), - personalization_storage: g(state.functional), - security_storage: 'granted' - }; -} -/** - * Mirrors Google's canonical snippet: `function gtag(){dataLayer.push(arguments)}`. - * - * Pushing `arguments` rather than a plain array is not a stylistic detail. - * Google's tags recognise a consent command by the Arguments object it arrives - * in; a real array lands in the dataLayer as ordinary data and the command is - * silently ignored — the tag loads, but consent never updates and analytics - * stays denied. The rest parameter exists only to type the call sites. - * - * Exported for the analytics module (which needs `js`/`config` commands) — - * intentionally not re-exported from the package's public API. - */ export function gtag(..._args) { - if (typeof window === 'undefined') { - return; - } - window.dataLayer = window.dataLayer || []; - // eslint-disable-next-line prefer-rest-params - window.dataLayer.push(arguments); -} -/** Sets the default consent state (call before Google tags load). */ export function setDefaultConsent(state) { - gtag('consent', 'default', { - ...toSignals(state), - wait_for_update: 500 - }); -} -/** Updates consent after the user decides. */ export function updateConsent(state) { - gtag('consent', 'update', toSignals(state)); -} - -//# sourceMappingURL=googleConsent.js.map \ No newline at end of file diff --git a/dist/modules/consent/googleConsent.js.map b/dist/modules/consent/googleConsent.js.map deleted file mode 100644 index cccd52f..0000000 --- a/dist/modules/consent/googleConsent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/googleConsent.ts"],"sourcesContent":["import type { ConsentState } from './categories.js'\n\ntype ConsentValue = 'denied' | 'granted'\ntype GoogleConsentSignals = Record\n\nfunction toSignals(state: ConsentState): GoogleConsentSignals {\n const g = (b: boolean): ConsentValue => (b ? 'granted' : 'denied')\n return {\n ad_personalization: g(state.marketing),\n ad_storage: g(state.marketing),\n ad_user_data: g(state.marketing),\n analytics_storage: g(state.analytics),\n functionality_storage: g(state.functional),\n personalization_storage: g(state.functional),\n security_storage: 'granted', // necessary — always on\n }\n}\n\ndeclare global {\n interface Window {\n dataLayer?: unknown[]\n }\n}\n\n/**\n * Mirrors Google's canonical snippet: `function gtag(){dataLayer.push(arguments)}`.\n *\n * Pushing `arguments` rather than a plain array is not a stylistic detail.\n * Google's tags recognise a consent command by the Arguments object it arrives\n * in; a real array lands in the dataLayer as ordinary data and the command is\n * silently ignored — the tag loads, but consent never updates and analytics\n * stays denied. The rest parameter exists only to type the call sites.\n *\n * Exported for the analytics module (which needs `js`/`config` commands) —\n * intentionally not re-exported from the package's public API.\n */\nexport function gtag(..._args: unknown[]) {\n if (typeof window === 'undefined') {return}\n window.dataLayer = window.dataLayer || []\n // eslint-disable-next-line prefer-rest-params\n window.dataLayer.push(arguments)\n}\n\n/** Sets the default consent state (call before Google tags load). */\nexport function setDefaultConsent(state: ConsentState) {\n gtag('consent', 'default', {\n ...toSignals(state),\n wait_for_update: 500, // ms to wait for an update before firing\n })\n}\n\n/** Updates consent after the user decides. */\nexport function updateConsent(state: ConsentState) {\n gtag('consent', 'update', toSignals(state))\n}\n"],"names":["toSignals","state","g","b","ad_personalization","marketing","ad_storage","ad_user_data","analytics_storage","analytics","functionality_storage","functional","personalization_storage","security_storage","gtag","_args","window","dataLayer","push","arguments","setDefaultConsent","wait_for_update","updateConsent"],"mappings":"AAKA,SAASA,UAAUC,KAAmB;IACpC,MAAMC,IAAI,CAACC,IAA8BA,IAAI,YAAY;IACzD,OAAO;QACLC,oBAAoBF,EAAED,MAAMI,SAAS;QACrCC,YAAYJ,EAAED,MAAMI,SAAS;QAC7BE,cAAcL,EAAED,MAAMI,SAAS;QAC/BG,mBAAmBN,EAAED,MAAMQ,SAAS;QACpCC,uBAAuBR,EAAED,MAAMU,UAAU;QACzCC,yBAAyBV,EAAED,MAAMU,UAAU;QAC3CE,kBAAkB;IACpB;AACF;AAQA;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,KAAK,GAAGC,KAAgB;IACtC,IAAI,OAAOC,WAAW,aAAa;QAAC;IAAM;IAC1CA,OAAOC,SAAS,GAAGD,OAAOC,SAAS,IAAI,EAAE;IACzC,8CAA8C;IAC9CD,OAAOC,SAAS,CAACC,IAAI,CAACC;AACxB;AAEA,mEAAmE,GACnE,OAAO,SAASC,kBAAkBnB,KAAmB;IACnDa,KAAK,WAAW,WAAW;QACzB,GAAGd,UAAUC,MAAM;QACnBoB,iBAAiB;IACnB;AACF;AAEA,4CAA4C,GAC5C,OAAO,SAASC,cAAcrB,KAAmB;IAC/Ca,KAAK,WAAW,UAAUd,UAAUC;AACtC"} \ No newline at end of file diff --git a/dist/modules/consent/index.d.ts b/dist/modules/consent/index.d.ts deleted file mode 100644 index 46883b4..0000000 --- a/dist/modules/consent/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { ACCEPT_ALL_CONSENT, CONSENT_CATEGORIES, DEFAULT_CONSENT, REJECT_ALL_CONSENT, } from './categories.js'; -export type { ConsentCategory, ConsentState } from './categories.js'; -export { getConsentTexts } from './getConsentTexts.js'; -export { setDefaultConsent, updateConsent } from './googleConsent.js'; -export { CONSENT_COOKIE, CONSENT_MAX_AGE, CONSENT_VERSION, parseConsent, serializeConsent, } from './storage.js'; -export type { ConsentTexts } from './texts.js'; diff --git a/dist/modules/consent/index.js b/dist/modules/consent/index.js deleted file mode 100644 index 4ec72b8..0000000 --- a/dist/modules/consent/index.js +++ /dev/null @@ -1,9 +0,0 @@ -// Server-safe exports (logic, types, helper). Client components (banner, -// provider, hook) are exported separately via ./client to keep the RSC/client -// boundary clean. -export { ACCEPT_ALL_CONSENT, CONSENT_CATEGORIES, DEFAULT_CONSENT, REJECT_ALL_CONSENT } from './categories.js'; -export { getConsentTexts } from './getConsentTexts.js'; -export { setDefaultConsent, updateConsent } from './googleConsent.js'; -export { CONSENT_COOKIE, CONSENT_MAX_AGE, CONSENT_VERSION, parseConsent, serializeConsent } from './storage.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/consent/index.js.map b/dist/modules/consent/index.js.map deleted file mode 100644 index c041a2c..0000000 --- a/dist/modules/consent/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/index.ts"],"sourcesContent":["// Server-safe exports (logic, types, helper). Client components (banner,\n// provider, hook) are exported separately via ./client to keep the RSC/client\n// boundary clean.\nexport {\n ACCEPT_ALL_CONSENT,\n CONSENT_CATEGORIES,\n DEFAULT_CONSENT,\n REJECT_ALL_CONSENT,\n} from './categories.js'\nexport type { ConsentCategory, ConsentState } from './categories.js'\nexport { getConsentTexts } from './getConsentTexts.js'\nexport { setDefaultConsent, updateConsent } from './googleConsent.js'\nexport {\n CONSENT_COOKIE,\n CONSENT_MAX_AGE,\n CONSENT_VERSION,\n parseConsent,\n serializeConsent,\n} from './storage.js'\nexport type { ConsentTexts } from './texts.js'\n"],"names":["ACCEPT_ALL_CONSENT","CONSENT_CATEGORIES","DEFAULT_CONSENT","REJECT_ALL_CONSENT","getConsentTexts","setDefaultConsent","updateConsent","CONSENT_COOKIE","CONSENT_MAX_AGE","CONSENT_VERSION","parseConsent","serializeConsent"],"mappings":"AAAA,yEAAyE;AACzE,8EAA8E;AAC9E,kBAAkB;AAClB,SACEA,kBAAkB,EAClBC,kBAAkB,EAClBC,eAAe,EACfC,kBAAkB,QACb,kBAAiB;AAExB,SAASC,eAAe,QAAQ,uBAAsB;AACtD,SAASC,iBAAiB,EAAEC,aAAa,QAAQ,qBAAoB;AACrE,SACEC,cAAc,EACdC,eAAe,EACfC,eAAe,EACfC,YAAY,EACZC,gBAAgB,QACX,eAAc"} \ No newline at end of file diff --git a/dist/modules/consent/storage.d.ts b/dist/modules/consent/storage.d.ts deleted file mode 100644 index 647066a..0000000 --- a/dist/modules/consent/storage.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { ConsentState } from './categories.js'; -/** - * Consent persistence in a cookie. Stored with a version so we can invalidate - * old consents when the policy changes, and a timestamp (GDPR: consent must be - * dated). Readable both client-side (banner) and server-side (script gating). - */ -export declare const CONSENT_COOKIE = "cookie-consent"; -export declare const CONSENT_VERSION = 1; -export declare const CONSENT_MAX_AGE: number; -export declare function serializeConsent(state: ConsentState): string; -export declare function parseConsent(raw: string | undefined): ConsentState | null; diff --git a/dist/modules/consent/storage.js b/dist/modules/consent/storage.js deleted file mode 100644 index 882c8ec..0000000 --- a/dist/modules/consent/storage.js +++ /dev/null @@ -1,42 +0,0 @@ -import { CONSENT_CATEGORIES, DEFAULT_CONSENT } from './categories.js'; -/** - * Consent persistence in a cookie. Stored with a version so we can invalidate - * old consents when the policy changes, and a timestamp (GDPR: consent must be - * dated). Readable both client-side (banner) and server-side (script gating). - */ export const CONSENT_COOKIE = 'cookie-consent'; -export const CONSENT_VERSION = 1; -export const CONSENT_MAX_AGE = 60 * 60 * 24 * 180 // 180 days -; -export function serializeConsent(state) { - const payload = { - state, - timestamp: new Date().toISOString(), - version: CONSENT_VERSION - }; - return encodeURIComponent(JSON.stringify(payload)); -} -export function parseConsent(raw) { - if (!raw) { - return null; - } - try { - const parsed = JSON.parse(decodeURIComponent(raw)); - if (parsed.version !== CONSENT_VERSION) { - return null; - } - const state = { - ...DEFAULT_CONSENT - }; - for (const cat of CONSENT_CATEGORIES){ - if (typeof parsed.state?.[cat] === 'boolean') { - state[cat] = parsed.state[cat]; - } - } - state.necessary = true; - return state; - } catch { - return null; - } -} - -//# sourceMappingURL=storage.js.map \ No newline at end of file diff --git a/dist/modules/consent/storage.js.map b/dist/modules/consent/storage.js.map deleted file mode 100644 index 039588f..0000000 --- a/dist/modules/consent/storage.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/storage.ts"],"sourcesContent":["import type { ConsentState } from './categories.js'\n\nimport { CONSENT_CATEGORIES, DEFAULT_CONSENT } from './categories.js'\n\n/**\n * Consent persistence in a cookie. Stored with a version so we can invalidate\n * old consents when the policy changes, and a timestamp (GDPR: consent must be\n * dated). Readable both client-side (banner) and server-side (script gating).\n */\nexport const CONSENT_COOKIE = 'cookie-consent'\nexport const CONSENT_VERSION = 1\nexport const CONSENT_MAX_AGE = 60 * 60 * 24 * 180 // 180 days\n\ntype StoredConsent = {\n state: ConsentState\n timestamp: string\n version: number\n}\n\nexport function serializeConsent(state: ConsentState): string {\n const payload: StoredConsent = {\n state,\n timestamp: new Date().toISOString(),\n version: CONSENT_VERSION,\n }\n return encodeURIComponent(JSON.stringify(payload))\n}\n\nexport function parseConsent(raw: string | undefined): ConsentState | null {\n if (!raw) {return null}\n try {\n const parsed = JSON.parse(decodeURIComponent(raw)) as StoredConsent\n if (parsed.version !== CONSENT_VERSION) {return null}\n const state = { ...DEFAULT_CONSENT }\n for (const cat of CONSENT_CATEGORIES) {\n if (typeof parsed.state?.[cat] === 'boolean') {state[cat] = parsed.state[cat]}\n }\n state.necessary = true\n return state\n } catch {\n return null\n }\n}\n"],"names":["CONSENT_CATEGORIES","DEFAULT_CONSENT","CONSENT_COOKIE","CONSENT_VERSION","CONSENT_MAX_AGE","serializeConsent","state","payload","timestamp","Date","toISOString","version","encodeURIComponent","JSON","stringify","parseConsent","raw","parsed","parse","decodeURIComponent","cat","necessary"],"mappings":"AAEA,SAASA,kBAAkB,EAAEC,eAAe,QAAQ,kBAAiB;AAErE;;;;CAIC,GACD,OAAO,MAAMC,iBAAiB,iBAAgB;AAC9C,OAAO,MAAMC,kBAAkB,EAAC;AAChC,OAAO,MAAMC,kBAAkB,KAAK,KAAK,KAAK,IAAI,WAAW;CAAZ;AAQjD,OAAO,SAASC,iBAAiBC,KAAmB;IAClD,MAAMC,UAAyB;QAC7BD;QACAE,WAAW,IAAIC,OAAOC,WAAW;QACjCC,SAASR;IACX;IACA,OAAOS,mBAAmBC,KAAKC,SAAS,CAACP;AAC3C;AAEA,OAAO,SAASQ,aAAaC,GAAuB;IAClD,IAAI,CAACA,KAAK;QAAC,OAAO;IAAI;IACtB,IAAI;QACF,MAAMC,SAASJ,KAAKK,KAAK,CAACC,mBAAmBH;QAC7C,IAAIC,OAAON,OAAO,KAAKR,iBAAiB;YAAC,OAAO;QAAI;QACpD,MAAMG,QAAQ;YAAE,GAAGL,eAAe;QAAC;QACnC,KAAK,MAAMmB,OAAOpB,mBAAoB;YACpC,IAAI,OAAOiB,OAAOX,KAAK,EAAE,CAACc,IAAI,KAAK,WAAW;gBAACd,KAAK,CAACc,IAAI,GAAGH,OAAOX,KAAK,CAACc,IAAI;YAAA;QAC/E;QACAd,MAAMe,SAAS,GAAG;QAClB,OAAOf;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF"} \ No newline at end of file diff --git a/dist/modules/consent/texts.d.ts b/dist/modules/consent/texts.d.ts deleted file mode 100644 index c3b040e..0000000 --- a/dist/modules/consent/texts.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { ConsentCategory } from './categories.js'; -export type ConsentTexts = { - buttons: { - acceptAll: string; - back: string; - reject: string; - save: string; - settings: string; - }; - categories: Record; - message: string; - /** null = no privacy-policy link shown */ - privacyLink: { - href: string; - label: string; - } | null; - settingsTitle: string; -}; diff --git a/dist/modules/consent/texts.js b/dist/modules/consent/texts.js deleted file mode 100644 index 11b227e..0000000 --- a/dist/modules/consent/texts.js +++ /dev/null @@ -1,3 +0,0 @@ -export { }; - -//# sourceMappingURL=texts.js.map \ No newline at end of file diff --git a/dist/modules/consent/texts.js.map b/dist/modules/consent/texts.js.map deleted file mode 100644 index 275e5a3..0000000 --- a/dist/modules/consent/texts.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/texts.ts"],"sourcesContent":["import type { ConsentCategory } from './categories.js'\n\nexport type ConsentTexts = {\n buttons: {\n acceptAll: string\n back: string\n reject: string\n save: string\n settings: string\n }\n categories: Record\n message: string\n /** null = no privacy-policy link shown */\n privacyLink: { href: string; label: string } | null\n settingsTitle: string\n}\n"],"names":[],"mappings":"AAEA,WAaC"} \ No newline at end of file diff --git a/dist/modules/consent/useConsent.d.ts b/dist/modules/consent/useConsent.d.ts deleted file mode 100644 index 0622592..0000000 --- a/dist/modules/consent/useConsent.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { ConsentCategory, ConsentState } from './categories.js'; -export declare function useConsent(): { - acceptAll: () => void; - decided: boolean; - rejectAll: () => void; - reopen: () => void; - savePreferences: (choices: Partial>) => void; - state: ConsentState; -}; diff --git a/dist/modules/consent/useConsent.js b/dist/modules/consent/useConsent.js deleted file mode 100644 index f66c2ed..0000000 --- a/dist/modules/consent/useConsent.js +++ /dev/null @@ -1,51 +0,0 @@ -'use client'; -import { useCallback, useEffect, useState } from 'react'; -import { ACCEPT_ALL_CONSENT, DEFAULT_CONSENT, REJECT_ALL_CONSENT } from './categories.js'; -import { updateConsent } from './googleConsent.js'; -import { CONSENT_COOKIE, CONSENT_MAX_AGE, parseConsent, serializeConsent } from './storage.js'; -export function useConsent() { - const [state, setState] = useState(DEFAULT_CONSENT); - const [decided, setDecided] = useState(false); - useEffect(()=>{ - const raw = document.cookie.split('; ').find((c)=>c.startsWith(`${CONSENT_COOKIE}=`))?.split('=')[1]; - const parsed = parseConsent(raw); - if (parsed) { - setState(parsed); - setDecided(true); - } - }, []); - const persist = useCallback((next)=>{ - document.cookie = `${CONSENT_COOKIE}=${serializeConsent(next)};path=/;max-age=${CONSENT_MAX_AGE};samesite=lax`; - setState(next); - setDecided(true); - // Tell Google right away. Tags are already on the page holding whatever - // defaults Analytics set at load; without this update they keep them for - // the rest of the session and never write their cookies — the banner would - // look like it worked while nothing changed. - updateConsent(next); - }, []); - const acceptAll = useCallback(()=>persist(ACCEPT_ALL_CONSENT), [ - persist - ]); - const rejectAll = useCallback(()=>persist(REJECT_ALL_CONSENT), [ - persist - ]); - const savePreferences = useCallback((choices)=>persist({ - ...DEFAULT_CONSENT, - ...choices, - necessary: true - }), [ - persist - ]); - const reopen = useCallback(()=>setDecided(false), []); - return { - acceptAll, - decided, - rejectAll, - reopen, - savePreferences, - state - }; -} - -//# sourceMappingURL=useConsent.js.map \ No newline at end of file diff --git a/dist/modules/consent/useConsent.js.map b/dist/modules/consent/useConsent.js.map deleted file mode 100644 index 3a761c1..0000000 --- a/dist/modules/consent/useConsent.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/consent/useConsent.ts"],"sourcesContent":["'use client'\nimport { useCallback, useEffect, useState } from 'react'\n\nimport type { ConsentCategory, ConsentState } from './categories.js'\n\nimport { ACCEPT_ALL_CONSENT, DEFAULT_CONSENT, REJECT_ALL_CONSENT } from './categories.js'\nimport { updateConsent } from './googleConsent.js'\nimport { CONSENT_COOKIE, CONSENT_MAX_AGE, parseConsent, serializeConsent } from './storage.js'\n\nexport function useConsent() {\n const [state, setState] = useState(DEFAULT_CONSENT)\n const [decided, setDecided] = useState(false)\n\n useEffect(() => {\n const raw = document.cookie\n .split('; ')\n .find((c) => c.startsWith(`${CONSENT_COOKIE}=`))\n ?.split('=')[1]\n const parsed = parseConsent(raw)\n if (parsed) {\n setState(parsed)\n setDecided(true)\n }\n }, [])\n\n const persist = useCallback((next: ConsentState) => {\n document.cookie = `${CONSENT_COOKIE}=${serializeConsent(next)};path=/;max-age=${CONSENT_MAX_AGE};samesite=lax`\n setState(next)\n setDecided(true)\n // Tell Google right away. Tags are already on the page holding whatever\n // defaults Analytics set at load; without this update they keep them for\n // the rest of the session and never write their cookies — the banner would\n // look like it worked while nothing changed.\n updateConsent(next)\n }, [])\n\n const acceptAll = useCallback(() => persist(ACCEPT_ALL_CONSENT), [persist])\n const rejectAll = useCallback(() => persist(REJECT_ALL_CONSENT), [persist])\n const savePreferences = useCallback(\n (choices: Partial>) =>\n persist({ ...DEFAULT_CONSENT, ...choices, necessary: true }),\n [persist],\n )\n const reopen = useCallback(() => setDecided(false), [])\n\n return { acceptAll, decided, rejectAll, reopen, savePreferences, state }\n}\n"],"names":["useCallback","useEffect","useState","ACCEPT_ALL_CONSENT","DEFAULT_CONSENT","REJECT_ALL_CONSENT","updateConsent","CONSENT_COOKIE","CONSENT_MAX_AGE","parseConsent","serializeConsent","useConsent","state","setState","decided","setDecided","raw","document","cookie","split","find","c","startsWith","parsed","persist","next","acceptAll","rejectAll","savePreferences","choices","necessary","reopen"],"mappings":"AAAA;AACA,SAASA,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAIxD,SAASC,kBAAkB,EAAEC,eAAe,EAAEC,kBAAkB,QAAQ,kBAAiB;AACzF,SAASC,aAAa,QAAQ,qBAAoB;AAClD,SAASC,cAAc,EAAEC,eAAe,EAAEC,YAAY,EAAEC,gBAAgB,QAAQ,eAAc;AAE9F,OAAO,SAASC;IACd,MAAM,CAACC,OAAOC,SAAS,GAAGX,SAAuBE;IACjD,MAAM,CAACU,SAASC,WAAW,GAAGb,SAAS;IAEvCD,UAAU;QACR,MAAMe,MAAMC,SAASC,MAAM,CACxBC,KAAK,CAAC,MACNC,IAAI,CAAC,CAACC,IAAMA,EAAEC,UAAU,CAAC,GAAGf,eAAe,CAAC,CAAC,IAC5CY,MAAM,IAAI,CAAC,EAAE;QACjB,MAAMI,SAASd,aAAaO;QAC5B,IAAIO,QAAQ;YACVV,SAASU;YACTR,WAAW;QACb;IACF,GAAG,EAAE;IAEL,MAAMS,UAAUxB,YAAY,CAACyB;QAC3BR,SAASC,MAAM,GAAG,GAAGX,eAAe,CAAC,EAAEG,iBAAiBe,MAAM,gBAAgB,EAAEjB,gBAAgB,aAAa,CAAC;QAC9GK,SAASY;QACTV,WAAW;QACX,wEAAwE;QACxE,yEAAyE;QACzE,2EAA2E;QAC3E,6CAA6C;QAC7CT,cAAcmB;IAChB,GAAG,EAAE;IAEL,MAAMC,YAAY1B,YAAY,IAAMwB,QAAQrB,qBAAqB;QAACqB;KAAQ;IAC1E,MAAMG,YAAY3B,YAAY,IAAMwB,QAAQnB,qBAAqB;QAACmB;KAAQ;IAC1E,MAAMI,kBAAkB5B,YACtB,CAAC6B,UACCL,QAAQ;YAAE,GAAGpB,eAAe;YAAE,GAAGyB,OAAO;YAAEC,WAAW;QAAK,IAC5D;QAACN;KAAQ;IAEX,MAAMO,SAAS/B,YAAY,IAAMe,WAAW,QAAQ,EAAE;IAEtD,OAAO;QAAEW;QAAWZ;QAASa;QAAWI;QAAQH;QAAiBhB;IAAM;AACzE"} \ No newline at end of file diff --git a/dist/modules/content/archiveFields.d.ts b/dist/modules/content/archiveFields.d.ts deleted file mode 100644 index b41da45..0000000 --- a/dist/modules/content/archiveFields.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Field } from 'payload'; -import type { ContentOption } from './types.js'; -/** - * Builds one relationship field per content collection, pointing at the - * client's Pages collection. - * - * Sits alongside the system-page roles (homepage, privacy policy) because it's - * the same idea: a page referenced by function rather than by slug. The - * assignment is locale-agnostic — one page document — while the resulting path - * is locale-aware, since the page's slug is localized. Assign the "Artykuły" - * page once and you get /pl/artykuly and /en/articles from its own slugs. - */ -export declare function buildArchiveFields(content: ContentOption, pagesSlug: string): Field[]; diff --git a/dist/modules/content/archiveFields.js b/dist/modules/content/archiveFields.js deleted file mode 100644 index f563b16..0000000 --- a/dist/modules/content/archiveFields.js +++ /dev/null @@ -1,27 +0,0 @@ -import { archiveFieldName } from './types.js'; -/** - * Builds one relationship field per content collection, pointing at the - * client's Pages collection. - * - * Sits alongside the system-page roles (homepage, privacy policy) because it's - * the same idea: a page referenced by function rather than by slug. The - * assignment is locale-agnostic — one page document — while the resulting path - * is locale-aware, since the page's slug is localized. Assign the "Artykuły" - * page once and you get /pl/artykuly and /en/articles from its own slugs. - */ export function buildArchiveFields(content, pagesSlug) { - return content.collections.map((collection)=>{ - const label = collection.label ?? collection.slug; - return { - name: archiveFieldName(collection.slug), - type: 'relationship', - admin: { - description: `Page listing ${label}. Its slug becomes the URL segment for entries (e.g. /pl/artykuly/moj-wpis).` - }, - label: `${label} — archive page`, - maxDepth: 1, - relationTo: pagesSlug - }; - }); -} - -//# sourceMappingURL=archiveFields.js.map \ No newline at end of file diff --git a/dist/modules/content/archiveFields.js.map b/dist/modules/content/archiveFields.js.map deleted file mode 100644 index 2f4b94e..0000000 --- a/dist/modules/content/archiveFields.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/content/archiveFields.ts"],"sourcesContent":["import type { Field, RelationshipField } from 'payload'\n\nimport type { ContentOption } from './types.js'\n\nimport { archiveFieldName } from './types.js'\n\n/**\n * Builds one relationship field per content collection, pointing at the\n * client's Pages collection.\n *\n * Sits alongside the system-page roles (homepage, privacy policy) because it's\n * the same idea: a page referenced by function rather than by slug. The\n * assignment is locale-agnostic — one page document — while the resulting path\n * is locale-aware, since the page's slug is localized. Assign the \"Artykuły\"\n * page once and you get /pl/artykuly and /en/articles from its own slugs.\n */\nexport function buildArchiveFields(content: ContentOption, pagesSlug: string): Field[] {\n return content.collections.map((collection): RelationshipField => {\n const label = collection.label ?? collection.slug\n\n return {\n name: archiveFieldName(collection.slug),\n type: 'relationship',\n admin: {\n description: `Page listing ${label}. Its slug becomes the URL segment for entries (e.g. /pl/artykuly/moj-wpis).`,\n },\n label: `${label} — archive page`,\n maxDepth: 1,\n relationTo: pagesSlug,\n }\n })\n}\n"],"names":["archiveFieldName","buildArchiveFields","content","pagesSlug","collections","map","collection","label","slug","name","type","admin","description","maxDepth","relationTo"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,aAAY;AAE7C;;;;;;;;;CASC,GACD,OAAO,SAASC,mBAAmBC,OAAsB,EAAEC,SAAiB;IAC1E,OAAOD,QAAQE,WAAW,CAACC,GAAG,CAAC,CAACC;QAC9B,MAAMC,QAAQD,WAAWC,KAAK,IAAID,WAAWE,IAAI;QAEjD,OAAO;YACLC,MAAMT,iBAAiBM,WAAWE,IAAI;YACtCE,MAAM;YACNC,OAAO;gBACLC,aAAa,CAAC,aAAa,EAAEL,MAAM,4EAA4E,CAAC;YAClH;YACAA,OAAO,GAAGA,MAAM,eAAe,CAAC;YAChCM,UAAU;YACVC,YAAYX;QACd;IACF;AACF"} \ No newline at end of file diff --git a/dist/modules/content/getArchiveEntries.d.ts b/dist/modules/content/getArchiveEntries.d.ts deleted file mode 100644 index 43a5b23..0000000 --- a/dist/modules/content/getArchiveEntries.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { BasePayload } from 'payload'; -export type ArchiveEntries> = { - docs: T[]; - hasNextPage: boolean; - hasPrevPage: boolean; - /** 1-based. */ - page: number; - totalDocs: number; - totalPages: number; -}; -type GetArchiveEntriesArgs = { - /** Collection to list, e.g. 'posts'. */ - collection: string; - /** Relationship depth. Defaults to 1 — enough for a cover image. */ - depth?: number; - locale: string; - /** 1-based. Values below 1 are clamped. */ - page?: number; - payload: BasePayload; - /** Defaults to 10. */ - perPage?: number; - /** Payload sort string. Defaults to newest first. */ - sort?: string; - /** Extra constraints merged into the query, e.g. { category: { equals: id } }. */ - where?: Record; -}; -/** - * Fetches one page of a collection's entries for an archive listing. - * - * Only published documents are returned when the collection has drafts enabled; - * Payload's `where` on `_status` is left to the caller, since a collection - * without drafts has no such field. - * - * Returns pagination facts rather than markup — the listing itself belongs to - * the client (as a block on the archive page, most likely), because how a list - * of posts should look isn't something a plugin can decide. - */ -export declare function getArchiveEntries>({ collection, depth, locale, page, payload, perPage, sort, where, }: GetArchiveEntriesArgs): Promise>; -export {}; diff --git a/dist/modules/content/getArchiveEntries.js b/dist/modules/content/getArchiveEntries.js deleted file mode 100644 index d9493d9..0000000 --- a/dist/modules/content/getArchiveEntries.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Fetches one page of a collection's entries for an archive listing. - * - * Only published documents are returned when the collection has drafts enabled; - * Payload's `where` on `_status` is left to the caller, since a collection - * without drafts has no such field. - * - * Returns pagination facts rather than markup — the listing itself belongs to - * the client (as a block on the archive page, most likely), because how a list - * of posts should look isn't something a plugin can decide. - */ export async function getArchiveEntries({ collection, depth = 1, locale, page = 1, payload, perPage = 10, sort = '-createdAt', where }) { - const result = await payload.find({ - collection: collection, - depth, - limit: perPage, - locale: locale, - page: Math.max(1, page), - sort, - ...where ? { - where: where - } : {} - }); - return { - docs: result.docs, - hasNextPage: result.hasNextPage, - hasPrevPage: result.hasPrevPage, - page: result.page ?? 1, - totalDocs: result.totalDocs, - totalPages: result.totalPages - }; -} - -//# sourceMappingURL=getArchiveEntries.js.map \ No newline at end of file diff --git a/dist/modules/content/getArchiveEntries.js.map b/dist/modules/content/getArchiveEntries.js.map deleted file mode 100644 index 879cde1..0000000 --- a/dist/modules/content/getArchiveEntries.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/content/getArchiveEntries.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nexport type ArchiveEntries> = {\n docs: T[]\n hasNextPage: boolean\n hasPrevPage: boolean\n /** 1-based. */\n page: number\n totalDocs: number\n totalPages: number\n}\n\ntype GetArchiveEntriesArgs = {\n /** Collection to list, e.g. 'posts'. */\n collection: string\n /** Relationship depth. Defaults to 1 — enough for a cover image. */\n depth?: number\n locale: string\n /** 1-based. Values below 1 are clamped. */\n page?: number\n payload: BasePayload\n /** Defaults to 10. */\n perPage?: number\n /** Payload sort string. Defaults to newest first. */\n sort?: string\n /** Extra constraints merged into the query, e.g. { category: { equals: id } }. */\n where?: Record\n}\n\n/**\n * Fetches one page of a collection's entries for an archive listing.\n *\n * Only published documents are returned when the collection has drafts enabled;\n * Payload's `where` on `_status` is left to the caller, since a collection\n * without drafts has no such field.\n *\n * Returns pagination facts rather than markup — the listing itself belongs to\n * the client (as a block on the archive page, most likely), because how a list\n * of posts should look isn't something a plugin can decide.\n */\nexport async function getArchiveEntries>({\n collection,\n depth = 1,\n locale,\n page = 1,\n payload,\n perPage = 10,\n sort = '-createdAt',\n where,\n}: GetArchiveEntriesArgs): Promise> {\n const result = await payload.find({\n collection: collection as never,\n depth,\n limit: perPage,\n locale: locale as never,\n page: Math.max(1, page),\n sort,\n ...(where ? { where: where as never } : {}),\n })\n\n return {\n docs: result.docs as T[],\n hasNextPage: result.hasNextPage,\n hasPrevPage: result.hasPrevPage,\n page: result.page ?? 1,\n totalDocs: result.totalDocs,\n totalPages: result.totalPages,\n }\n}\n"],"names":["getArchiveEntries","collection","depth","locale","page","payload","perPage","sort","where","result","find","limit","Math","max","docs","hasNextPage","hasPrevPage","totalDocs","totalPages"],"mappings":"AA6BA;;;;;;;;;;CAUC,GACD,OAAO,eAAeA,kBAA+C,EACnEC,UAAU,EACVC,QAAQ,CAAC,EACTC,MAAM,EACNC,OAAO,CAAC,EACRC,OAAO,EACPC,UAAU,EAAE,EACZC,OAAO,YAAY,EACnBC,KAAK,EACiB;IACtB,MAAMC,SAAS,MAAMJ,QAAQK,IAAI,CAAC;QAChCT,YAAYA;QACZC;QACAS,OAAOL;QACPH,QAAQA;QACRC,MAAMQ,KAAKC,GAAG,CAAC,GAAGT;QAClBG;QACA,GAAIC,QAAQ;YAAEA,OAAOA;QAAe,IAAI,CAAC,CAAC;IAC5C;IAEA,OAAO;QACLM,MAAML,OAAOK,IAAI;QACjBC,aAAaN,OAAOM,WAAW;QAC/BC,aAAaP,OAAOO,WAAW;QAC/BZ,MAAMK,OAAOL,IAAI,IAAI;QACrBa,WAAWR,OAAOQ,SAAS;QAC3BC,YAAYT,OAAOS,UAAU;IAC/B;AACF"} \ No newline at end of file diff --git a/dist/modules/content/index.d.ts b/dist/modules/content/index.d.ts deleted file mode 100644 index d275a30..0000000 --- a/dist/modules/content/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { buildArchiveFields } from './archiveFields.js'; -export { getArchiveEntries } from './getArchiveEntries.js'; -export type { ArchiveEntries } from './getArchiveEntries.js'; -export { buildArchivePath, buildEntryPath, parsePageParam } from './paths.js'; -export { resolveRoute } from './resolveRoute.js'; -export type { ResolvedRoute } from './resolveRoute.js'; -export type { ContentCollectionOption, ContentOption } from './types.js'; -export { archiveFieldName } from './types.js'; diff --git a/dist/modules/content/index.js b/dist/modules/content/index.js deleted file mode 100644 index 385dea1..0000000 --- a/dist/modules/content/index.js +++ /dev/null @@ -1,7 +0,0 @@ -export { buildArchiveFields } from './archiveFields.js'; -export { getArchiveEntries } from './getArchiveEntries.js'; -export { buildArchivePath, buildEntryPath, parsePageParam } from './paths.js'; -export { resolveRoute } from './resolveRoute.js'; -export { archiveFieldName } from './types.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/content/index.js.map b/dist/modules/content/index.js.map deleted file mode 100644 index 165ea1f..0000000 --- a/dist/modules/content/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/content/index.ts"],"sourcesContent":["export { buildArchiveFields } from './archiveFields.js'\nexport { getArchiveEntries } from './getArchiveEntries.js'\nexport type { ArchiveEntries } from './getArchiveEntries.js'\nexport { buildArchivePath, buildEntryPath, parsePageParam } from './paths.js'\nexport { resolveRoute } from './resolveRoute.js'\nexport type { ResolvedRoute } from './resolveRoute.js'\nexport type { ContentCollectionOption, ContentOption } from './types.js'\nexport { archiveFieldName } from './types.js'\n"],"names":["buildArchiveFields","getArchiveEntries","buildArchivePath","buildEntryPath","parsePageParam","resolveRoute","archiveFieldName"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,qBAAoB;AACvD,SAASC,iBAAiB,QAAQ,yBAAwB;AAE1D,SAASC,gBAAgB,EAAEC,cAAc,EAAEC,cAAc,QAAQ,aAAY;AAC7E,SAASC,YAAY,QAAQ,oBAAmB;AAGhD,SAASC,gBAAgB,QAAQ,aAAY"} \ No newline at end of file diff --git a/dist/modules/content/paths.d.ts b/dist/modules/content/paths.d.ts deleted file mode 100644 index b234613..0000000 --- a/dist/modules/content/paths.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -type ArchivePathArgs = { - /** Archive page's slug in this locale, e.g. 'artykuly'. */ - archiveSlug: string; - locale: string; - /** 1-based. Page 1 is omitted from the URL. */ - page?: number; -}; -/** - * Path to an archive listing: /pl/artykuly, /pl/artykuly?page=2. - * - * Pagination goes in the query string rather than the path. A path segment - * (/pl/artykuly/2) would be ambiguous with an entry slugged "2", and the - * alternative (/pl/artykuly/strona/2) drags in yet another localized segment to - * configure and translate. Google has understood ?page= for years, and - * rel=next/prev — the reason people used to prefer path segments — was retired. - */ -export declare function buildArchivePath({ archiveSlug, locale, page }: ArchivePathArgs): string; -type EntryPathArgs = { - /** Archive page's slug in this locale, e.g. 'artykuly'. */ - archiveSlug: string; - /** Entry's slug in this locale, e.g. 'moj-post'. */ - entrySlug: string; - locale: string; -}; -/** Path to a single entry: /pl/artykuly/moj-post. */ -export declare function buildEntryPath({ archiveSlug, entrySlug, locale }: EntryPathArgs): string; -/** - * Reads a page number out of a query parameter. - * - * Anything that isn't a positive integer is page 1 — `?page=abc`, `?page=-5`, - * `?page=1.5` and a repeated `?page=1&page=2` all have to resolve to something, - * and silently showing the first page beats a 500 on a URL a crawler invented. - */ -export declare function parsePageParam(value: string | string[] | undefined): number; -export {}; diff --git a/dist/modules/content/paths.js b/dist/modules/content/paths.js deleted file mode 100644 index 44caf07..0000000 --- a/dist/modules/content/paths.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Path to an archive listing: /pl/artykuly, /pl/artykuly?page=2. - * - * Pagination goes in the query string rather than the path. A path segment - * (/pl/artykuly/2) would be ambiguous with an entry slugged "2", and the - * alternative (/pl/artykuly/strona/2) drags in yet another localized segment to - * configure and translate. Google has understood ?page= for years, and - * rel=next/prev — the reason people used to prefer path segments — was retired. - */ export function buildArchivePath({ archiveSlug, locale, page = 1 }) { - const base = `/${locale}/${archiveSlug}`; - return page > 1 ? `${base}?page=${page}` : base; -} -/** Path to a single entry: /pl/artykuly/moj-post. */ export function buildEntryPath({ archiveSlug, entrySlug, locale }) { - return `/${locale}/${archiveSlug}/${entrySlug}`; -} -/** - * Reads a page number out of a query parameter. - * - * Anything that isn't a positive integer is page 1 — `?page=abc`, `?page=-5`, - * `?page=1.5` and a repeated `?page=1&page=2` all have to resolve to something, - * and silently showing the first page beats a 500 on a URL a crawler invented. - */ export function parsePageParam(value) { - const raw = Array.isArray(value) ? value[0] : value; - const n = Number(raw); - return Number.isInteger(n) && n > 0 ? n : 1; -} - -//# sourceMappingURL=paths.js.map \ No newline at end of file diff --git a/dist/modules/content/paths.js.map b/dist/modules/content/paths.js.map deleted file mode 100644 index 92c556b..0000000 --- a/dist/modules/content/paths.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/content/paths.ts"],"sourcesContent":["type ArchivePathArgs = {\n /** Archive page's slug in this locale, e.g. 'artykuly'. */\n archiveSlug: string\n locale: string\n /** 1-based. Page 1 is omitted from the URL. */\n page?: number\n}\n\n/**\n * Path to an archive listing: /pl/artykuly, /pl/artykuly?page=2.\n *\n * Pagination goes in the query string rather than the path. A path segment\n * (/pl/artykuly/2) would be ambiguous with an entry slugged \"2\", and the\n * alternative (/pl/artykuly/strona/2) drags in yet another localized segment to\n * configure and translate. Google has understood ?page= for years, and\n * rel=next/prev — the reason people used to prefer path segments — was retired.\n */\nexport function buildArchivePath({ archiveSlug, locale, page = 1 }: ArchivePathArgs): string {\n const base = `/${locale}/${archiveSlug}`\n return page > 1 ? `${base}?page=${page}` : base\n}\n\ntype EntryPathArgs = {\n /** Archive page's slug in this locale, e.g. 'artykuly'. */\n archiveSlug: string\n /** Entry's slug in this locale, e.g. 'moj-post'. */\n entrySlug: string\n locale: string\n}\n\n/** Path to a single entry: /pl/artykuly/moj-post. */\nexport function buildEntryPath({ archiveSlug, entrySlug, locale }: EntryPathArgs): string {\n return `/${locale}/${archiveSlug}/${entrySlug}`\n}\n\n/**\n * Reads a page number out of a query parameter.\n *\n * Anything that isn't a positive integer is page 1 — `?page=abc`, `?page=-5`,\n * `?page=1.5` and a repeated `?page=1&page=2` all have to resolve to something,\n * and silently showing the first page beats a 500 on a URL a crawler invented.\n */\nexport function parsePageParam(value: string | string[] | undefined): number {\n const raw = Array.isArray(value) ? value[0] : value\n const n = Number(raw)\n return Number.isInteger(n) && n > 0 ? n : 1\n}\n"],"names":["buildArchivePath","archiveSlug","locale","page","base","buildEntryPath","entrySlug","parsePageParam","value","raw","Array","isArray","n","Number","isInteger"],"mappings":"AAQA;;;;;;;;CAQC,GACD,OAAO,SAASA,iBAAiB,EAAEC,WAAW,EAAEC,MAAM,EAAEC,OAAO,CAAC,EAAmB;IACjF,MAAMC,OAAO,CAAC,CAAC,EAAEF,OAAO,CAAC,EAAED,aAAa;IACxC,OAAOE,OAAO,IAAI,GAAGC,KAAK,MAAM,EAAED,MAAM,GAAGC;AAC7C;AAUA,mDAAmD,GACnD,OAAO,SAASC,eAAe,EAAEJ,WAAW,EAAEK,SAAS,EAAEJ,MAAM,EAAiB;IAC9E,OAAO,CAAC,CAAC,EAAEA,OAAO,CAAC,EAAED,YAAY,CAAC,EAAEK,WAAW;AACjD;AAEA;;;;;;CAMC,GACD,OAAO,SAASC,eAAeC,KAAoC;IACjE,MAAMC,MAAMC,MAAMC,OAAO,CAACH,SAASA,KAAK,CAAC,EAAE,GAAGA;IAC9C,MAAMI,IAAIC,OAAOJ;IACjB,OAAOI,OAAOC,SAAS,CAACF,MAAMA,IAAI,IAAIA,IAAI;AAC5C"} \ No newline at end of file diff --git a/dist/modules/content/resolveRoute.d.ts b/dist/modules/content/resolveRoute.d.ts deleted file mode 100644 index 9570715..0000000 --- a/dist/modules/content/resolveRoute.d.ts +++ /dev/null @@ -1,68 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { ContentOption } from './types.js'; -export type ResolvedRoute = -/** Locale root — the page assigned as Homepage in System Pages. */ -{ - /** The archive page this entry lives under — its slug is the URL prefix. */ - archive: Record; - collection: string; - doc: Record; - type: 'entry'; -} -/** An ordinary page. */ - | { - collection: string; - doc: Record; - /** 1-based, from ?page=. */ - page: number; - perPage: number; - type: 'archive'; -} -/** A collection's archive page, e.g. /pl/artykuly. */ - | { - doc: Record; - type: 'home'; -} -/** A single entry, e.g. /pl/artykuly/moj-post. */ - | { - doc: Record; - type: 'page'; -}; -type ResolveRouteArgs = { - content?: ContentOption; - locale: string; - /** Page number from the query string (?page=2). Defaults to 1. */ - page?: number; - /** Client's Pages collection slug. Defaults to 'pages'. */ - pagesSlug?: string; - payload: BasePayload; - /** Route segments after the locale, e.g. ['artykuly', 'moj-post']. */ - segments?: string[]; - /** SiteSettings global slug. Defaults to 'site-settings'. */ - settingsSlug?: string; -}; -/** - * Works out what a URL points at: the home page, an ordinary page, a - * collection's archive, or a single entry. - * - * Routing only. An archive result says which collection to list and on which - * page, but doesn't fetch the entries — that's `getArchiveEntries`, called by - * whoever actually needs them. Keeping the two apart means listing changes - * (sorting, filtering, pagination) never touch routing rules, and metadata - * generation doesn't pay for a query it would discard. - * - * The trick is that archive prefixes aren't configured anywhere — they're the - * slug of whichever page an editor assigned as that collection's archive. So - * /pl/artykuly and /en/articles come from one assignment, and renaming the page - * moves the whole section. - * - * Resolution order matters: a first segment matching an archive's slug wins - * over an ordinary page of the same name, because an archive *is* a page and - * would otherwise shadow its own entries. - * - * Depth beyond {archive}/{entry} isn't supported — categories in the path would - * make canonical and hreflang ambiguous (the same entry reachable under several - * URLs). - */ -export declare function resolveRoute({ content, locale, page, pagesSlug, payload, segments, settingsSlug, }: ResolveRouteArgs): Promise; -export {}; diff --git a/dist/modules/content/resolveRoute.js b/dist/modules/content/resolveRoute.js deleted file mode 100644 index d2341fb..0000000 --- a/dist/modules/content/resolveRoute.js +++ /dev/null @@ -1,111 +0,0 @@ -import { archiveFieldName } from './types.js'; -/** - * Works out what a URL points at: the home page, an ordinary page, a - * collection's archive, or a single entry. - * - * Routing only. An archive result says which collection to list and on which - * page, but doesn't fetch the entries — that's `getArchiveEntries`, called by - * whoever actually needs them. Keeping the two apart means listing changes - * (sorting, filtering, pagination) never touch routing rules, and metadata - * generation doesn't pay for a query it would discard. - * - * The trick is that archive prefixes aren't configured anywhere — they're the - * slug of whichever page an editor assigned as that collection's archive. So - * /pl/artykuly and /en/articles come from one assignment, and renaming the page - * moves the whole section. - * - * Resolution order matters: a first segment matching an archive's slug wins - * over an ordinary page of the same name, because an archive *is* a page and - * would otherwise shadow its own entries. - * - * Depth beyond {archive}/{entry} isn't supported — categories in the path would - * make canonical and hreflang ambiguous (the same entry reachable under several - * URLs). - */ export async function resolveRoute({ content, locale, page = 1, pagesSlug = 'pages', payload, segments, settingsSlug = 'site-settings' }) { - const settings = await payload.findGlobal({ - slug: settingsSlug, - depth: 1, - locale: locale - }); - // Locale root → Homepage from System Pages. - if (!segments?.length) { - const homepage = settings.homepage; - if (homepage && typeof homepage === 'object') { - return { - type: 'home', - doc: homepage - }; - } - return null; - } - const [first, ...rest] = segments; - // Does the first segment name an archive page? - for (const collection of content?.collections ?? []){ - const archive = settings[archiveFieldName(collection.slug)]; - if (!archive || typeof archive !== 'object') { - continue; - } - const archiveDoc = archive; - if (archiveDoc.slug !== first) { - continue; - } - // /pl/artykuly → the archive page itself. - if (rest.length === 0) { - return { - type: 'archive', - collection: collection.slug, - doc: archiveDoc, - page, - perPage: collection.perPage ?? 10 - }; - } - // /pl/artykuly/moj-post → an entry. - if (rest.length === 1) { - const found = await payload.find({ - collection: collection.slug, - depth: 2, - limit: 1, - locale: locale, - where: { - slug: { - equals: rest[0] - } - } - }); - const entry = found.docs[0]; - if (!entry) { - return null; - } - return { - type: 'entry', - archive: archiveDoc, - collection: collection.slug, - doc: entry - }; - } - // Deeper than {archive}/{entry}. - return null; - } - // An ordinary page. Joined, so nested slugs ('a/b') keep working. - const found = await payload.find({ - collection: pagesSlug, - depth: 2, - limit: 1, - locale: locale, - where: { - slug: { - equals: segments.join('/') - } - } - }); - const doc = found.docs[0]; - if (!doc) { - return null; - } - return { - type: 'page', - doc: doc - }; -} - -//# sourceMappingURL=resolveRoute.js.map \ No newline at end of file diff --git a/dist/modules/content/resolveRoute.js.map b/dist/modules/content/resolveRoute.js.map deleted file mode 100644 index bb6b4dc..0000000 --- a/dist/modules/content/resolveRoute.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/content/resolveRoute.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { ContentOption } from './types.js'\n\nimport { archiveFieldName } from './types.js'\n\ntype ArchivePage = { id: number | string; slug?: unknown }\n\nexport type ResolvedRoute =\n /** Locale root — the page assigned as Homepage in System Pages. */\n | {\n /** The archive page this entry lives under — its slug is the URL prefix. */\n archive: Record\n collection: string\n doc: Record\n type: 'entry'\n }\n /** An ordinary page. */\n | {\n collection: string\n doc: Record\n /** 1-based, from ?page=. */\n page: number\n perPage: number\n type: 'archive'\n }\n /** A collection's archive page, e.g. /pl/artykuly. */\n | { doc: Record; type: 'home' }\n /** A single entry, e.g. /pl/artykuly/moj-post. */\n | { doc: Record; type: 'page' }\n\ntype ResolveRouteArgs = {\n content?: ContentOption\n locale: string\n /** Page number from the query string (?page=2). Defaults to 1. */\n page?: number\n /** Client's Pages collection slug. Defaults to 'pages'. */\n pagesSlug?: string\n payload: BasePayload\n /** Route segments after the locale, e.g. ['artykuly', 'moj-post']. */\n segments?: string[]\n /** SiteSettings global slug. Defaults to 'site-settings'. */\n settingsSlug?: string\n}\n\n/**\n * Works out what a URL points at: the home page, an ordinary page, a\n * collection's archive, or a single entry.\n *\n * Routing only. An archive result says which collection to list and on which\n * page, but doesn't fetch the entries — that's `getArchiveEntries`, called by\n * whoever actually needs them. Keeping the two apart means listing changes\n * (sorting, filtering, pagination) never touch routing rules, and metadata\n * generation doesn't pay for a query it would discard.\n *\n * The trick is that archive prefixes aren't configured anywhere — they're the\n * slug of whichever page an editor assigned as that collection's archive. So\n * /pl/artykuly and /en/articles come from one assignment, and renaming the page\n * moves the whole section.\n *\n * Resolution order matters: a first segment matching an archive's slug wins\n * over an ordinary page of the same name, because an archive *is* a page and\n * would otherwise shadow its own entries.\n *\n * Depth beyond {archive}/{entry} isn't supported — categories in the path would\n * make canonical and hreflang ambiguous (the same entry reachable under several\n * URLs).\n */\nexport async function resolveRoute({\n content,\n locale,\n page = 1,\n pagesSlug = 'pages',\n payload,\n segments,\n settingsSlug = 'site-settings',\n}: ResolveRouteArgs): Promise {\n const settings = (await payload.findGlobal({\n slug: settingsSlug,\n depth: 1,\n locale: locale as never,\n })) as Record\n\n // Locale root → Homepage from System Pages.\n if (!segments?.length) {\n const homepage = settings.homepage\n if (homepage && typeof homepage === 'object') {\n return { type: 'home', doc: homepage as Record }\n }\n return null\n }\n\n const [first, ...rest] = segments\n\n // Does the first segment name an archive page?\n for (const collection of content?.collections ?? []) {\n const archive = settings[archiveFieldName(collection.slug)]\n if (!archive || typeof archive !== 'object') {continue}\n\n const archiveDoc = archive as ArchivePage\n if (archiveDoc.slug !== first) {continue}\n\n // /pl/artykuly → the archive page itself.\n if (rest.length === 0) {\n return {\n type: 'archive',\n collection: collection.slug,\n doc: archiveDoc as Record,\n page,\n perPage: collection.perPage ?? 10,\n }\n }\n\n // /pl/artykuly/moj-post → an entry.\n if (rest.length === 1) {\n const found = await payload.find({\n collection: collection.slug as never,\n depth: 2,\n limit: 1,\n locale: locale as never,\n where: { slug: { equals: rest[0] } },\n })\n const entry = found.docs[0]\n if (!entry) {return null}\n\n return {\n type: 'entry',\n archive: archiveDoc as Record,\n collection: collection.slug,\n doc: entry as Record,\n }\n }\n\n // Deeper than {archive}/{entry}.\n return null\n }\n\n // An ordinary page. Joined, so nested slugs ('a/b') keep working.\n const found = await payload.find({\n collection: pagesSlug as never,\n depth: 2,\n limit: 1,\n locale: locale as never,\n where: { slug: { equals: segments.join('/') } },\n })\n const doc = found.docs[0]\n if (!doc) {return null}\n\n return { type: 'page', doc: doc as Record }\n}\n"],"names":["archiveFieldName","resolveRoute","content","locale","page","pagesSlug","payload","segments","settingsSlug","settings","findGlobal","slug","depth","length","homepage","type","doc","first","rest","collection","collections","archive","archiveDoc","perPage","found","find","limit","where","equals","entry","docs","join"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,aAAY;AAyC7C;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACD,OAAO,eAAeC,aAAa,EACjCC,OAAO,EACPC,MAAM,EACNC,OAAO,CAAC,EACRC,YAAY,OAAO,EACnBC,OAAO,EACPC,QAAQ,EACRC,eAAe,eAAe,EACb;IACjB,MAAMC,WAAY,MAAMH,QAAQI,UAAU,CAAC;QACzCC,MAAMH;QACNI,OAAO;QACPT,QAAQA;IACV;IAEA,4CAA4C;IAC5C,IAAI,CAACI,UAAUM,QAAQ;QACrB,MAAMC,WAAWL,SAASK,QAAQ;QAClC,IAAIA,YAAY,OAAOA,aAAa,UAAU;YAC5C,OAAO;gBAAEC,MAAM;gBAAQC,KAAKF;YAAoC;QAClE;QACA,OAAO;IACT;IAEA,MAAM,CAACG,OAAO,GAAGC,KAAK,GAAGX;IAEzB,+CAA+C;IAC/C,KAAK,MAAMY,cAAcjB,SAASkB,eAAe,EAAE,CAAE;QACnD,MAAMC,UAAUZ,QAAQ,CAACT,iBAAiBmB,WAAWR,IAAI,EAAE;QAC3D,IAAI,CAACU,WAAW,OAAOA,YAAY,UAAU;YAAC;QAAQ;QAEtD,MAAMC,aAAaD;QACnB,IAAIC,WAAWX,IAAI,KAAKM,OAAO;YAAC;QAAQ;QAExC,0CAA0C;QAC1C,IAAIC,KAAKL,MAAM,KAAK,GAAG;YACrB,OAAO;gBACLE,MAAM;gBACNI,YAAYA,WAAWR,IAAI;gBAC3BK,KAAKM;gBACLlB;gBACAmB,SAASJ,WAAWI,OAAO,IAAI;YACjC;QACF;QAEA,oCAAoC;QACpC,IAAIL,KAAKL,MAAM,KAAK,GAAG;YACrB,MAAMW,QAAQ,MAAMlB,QAAQmB,IAAI,CAAC;gBAC/BN,YAAYA,WAAWR,IAAI;gBAC3BC,OAAO;gBACPc,OAAO;gBACPvB,QAAQA;gBACRwB,OAAO;oBAAEhB,MAAM;wBAAEiB,QAAQV,IAAI,CAAC,EAAE;oBAAC;gBAAE;YACrC;YACA,MAAMW,QAAQL,MAAMM,IAAI,CAAC,EAAE;YAC3B,IAAI,CAACD,OAAO;gBAAC,OAAO;YAAI;YAExB,OAAO;gBACLd,MAAM;gBACNM,SAASC;gBACTH,YAAYA,WAAWR,IAAI;gBAC3BK,KAAKa;YACP;QACF;QAEA,iCAAiC;QACjC,OAAO;IACT;IAEA,kEAAkE;IAClE,MAAML,QAAQ,MAAMlB,QAAQmB,IAAI,CAAC;QAC/BN,YAAYd;QACZO,OAAO;QACPc,OAAO;QACPvB,QAAQA;QACRwB,OAAO;YAAEhB,MAAM;gBAAEiB,QAAQrB,SAASwB,IAAI,CAAC;YAAK;QAAE;IAChD;IACA,MAAMf,MAAMQ,MAAMM,IAAI,CAAC,EAAE;IACzB,IAAI,CAACd,KAAK;QAAC,OAAO;IAAI;IAEtB,OAAO;QAAED,MAAM;QAAQC,KAAKA;IAA+B;AAC7D"} \ No newline at end of file diff --git a/dist/modules/content/types.d.ts b/dist/modules/content/types.d.ts deleted file mode 100644 index 9adf4be..0000000 --- a/dist/modules/content/types.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * A collection whose entries live under an archive page, e.g. blog posts at - * /pl/artykuly/moj-post. - * - * The collection itself belongs to the client — Payload keeps its schema in - * code, so adding one is always a code change. What this option adds is the - * routing: the plugin exposes an "archive page" assignment in SiteSettings, and - * whichever page an editor picks becomes the URL segment. Rename that page from - * "Artykuły" to "Wpisy" and the path follows, per locale, with no deploy. - */ -export type ContentCollectionOption = { - /** Slug of the client's collection, e.g. 'posts'. */ - slug: string; - /** Admin label for the archive assignment. Defaults to the slug. */ - label?: string; - /** Entries per page in listings. Defaults to 10. */ - perPage?: number; -}; -/** - * Plugin option wiring archive-backed collections into routing. - */ -export type ContentOption = { - collections: ContentCollectionOption[]; -}; -/** - * Field name holding a collection's archive page in SiteSettings. - * `posts` → `postsArchive`. - */ -export declare function archiveFieldName(collectionSlug: string): string; diff --git a/dist/modules/content/types.js b/dist/modules/content/types.js deleted file mode 100644 index 976c958..0000000 --- a/dist/modules/content/types.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * A collection whose entries live under an archive page, e.g. blog posts at - * /pl/artykuly/moj-post. - * - * The collection itself belongs to the client — Payload keeps its schema in - * code, so adding one is always a code change. What this option adds is the - * routing: the plugin exposes an "archive page" assignment in SiteSettings, and - * whichever page an editor picks becomes the URL segment. Rename that page from - * "Artykuły" to "Wpisy" and the path follows, per locale, with no deploy. - */ /** - * Field name holding a collection's archive page in SiteSettings. - * `posts` → `postsArchive`. - */ export function archiveFieldName(collectionSlug) { - return `${collectionSlug}Archive`; -} - -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/modules/content/types.js.map b/dist/modules/content/types.js.map deleted file mode 100644 index c7df3bf..0000000 --- a/dist/modules/content/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/content/types.ts"],"sourcesContent":["/**\n * A collection whose entries live under an archive page, e.g. blog posts at\n * /pl/artykuly/moj-post.\n *\n * The collection itself belongs to the client — Payload keeps its schema in\n * code, so adding one is always a code change. What this option adds is the\n * routing: the plugin exposes an \"archive page\" assignment in SiteSettings, and\n * whichever page an editor picks becomes the URL segment. Rename that page from\n * \"Artykuły\" to \"Wpisy\" and the path follows, per locale, with no deploy.\n */\nexport type ContentCollectionOption = {\n /** Slug of the client's collection, e.g. 'posts'. */\n slug: string\n /** Admin label for the archive assignment. Defaults to the slug. */\n label?: string\n /** Entries per page in listings. Defaults to 10. */\n perPage?: number\n}\n\n/**\n * Plugin option wiring archive-backed collections into routing.\n */\nexport type ContentOption = {\n collections: ContentCollectionOption[]\n}\n\n/**\n * Field name holding a collection's archive page in SiteSettings.\n * `posts` → `postsArchive`.\n */\nexport function archiveFieldName(collectionSlug: string): string {\n return `${collectionSlug}Archive`\n}\n"],"names":["archiveFieldName","collectionSlug"],"mappings":"AAAA;;;;;;;;;CASC,GAiBD;;;CAGC,GACD,OAAO,SAASA,iBAAiBC,cAAsB;IACrD,OAAO,GAAGA,eAAe,OAAO,CAAC;AACnC"} \ No newline at end of file diff --git a/dist/modules/email/index.d.ts b/dist/modules/email/index.d.ts deleted file mode 100644 index 225c347..0000000 --- a/dist/modules/email/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { sendEmail } from './sendEmail.js'; -export type { SendEmailArgs, SendEmailResult } from './sendEmail.js'; diff --git a/dist/modules/email/index.js b/dist/modules/email/index.js deleted file mode 100644 index 385c3b2..0000000 --- a/dist/modules/email/index.js +++ /dev/null @@ -1,5 +0,0 @@ -// Server-only exports. sendEmail imports 'server-only' (SMTP password, nodemailer) -// so this must never be imported from a client component. -export { sendEmail } from './sendEmail.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/email/index.js.map b/dist/modules/email/index.js.map deleted file mode 100644 index d62dbdf..0000000 --- a/dist/modules/email/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/email/index.ts"],"sourcesContent":["// Server-only exports. sendEmail imports 'server-only' (SMTP password, nodemailer)\n// so this must never be imported from a client component.\nexport { sendEmail } from './sendEmail.js'\nexport type { SendEmailArgs, SendEmailResult } from './sendEmail.js'\n"],"names":["sendEmail"],"mappings":"AAAA,mFAAmF;AACnF,0DAA0D;AAC1D,SAASA,SAAS,QAAQ,iBAAgB"} \ No newline at end of file diff --git a/dist/modules/email/panelSmtpAdapter.d.ts b/dist/modules/email/panelSmtpAdapter.d.ts deleted file mode 100644 index 2d34ed2..0000000 --- a/dist/modules/email/panelSmtpAdapter.d.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { PayloadEmailAdapter } from 'payload'; -export type PanelSmtpAdapterArgs = { - /** - * Used only until the panel is filled in — Payload requires a from-address - * synchronously at boot, before any global can be read. Once SiteIntegrations - * has an address, it wins. - */ - fallbackFromAddress?: string; - fallbackFromName?: string; -}; -/** - * Payload email adapter backed by the SMTP settings in the SiteIntegrations - * global. - * - * Why this exists: Payload builds its email adapter once, at boot, from the - * config — which would normally mean SMTP credentials living in env vars and a - * redeploy to change them. This adapter instead resolves SMTP on every send, so - * an editor can change the mailbox in the admin panel and the next email uses - * it. - * - * Wiring it into the config means `payload.sendEmail` works everywhere — which - * includes the form-builder's own submission emails (the ones an editor - * configures per form under "Emails"). Those go out over the panel's SMTP with - * no extra code. - * - * Boot-time constraints shape two details: - * - `defaultFromAddress` / `defaultFromName` must be returned synchronously, so - * they're placeholders; the real from-address is applied per message below. - * - nodemailer is imported dynamically inside sendEmail, so merely loading the - * Payload config (or running `generate:importmap`) doesn't pull it in. - */ -export declare const panelSmtpAdapter: (args?: PanelSmtpAdapterArgs) => PayloadEmailAdapter; diff --git a/dist/modules/email/panelSmtpAdapter.js b/dist/modules/email/panelSmtpAdapter.js deleted file mode 100644 index 19133e5..0000000 --- a/dist/modules/email/panelSmtpAdapter.js +++ /dev/null @@ -1,83 +0,0 @@ -import { getSiteIntegrations } from '../payload/index.js'; -/** - * Payload email adapter backed by the SMTP settings in the SiteIntegrations - * global. - * - * Why this exists: Payload builds its email adapter once, at boot, from the - * config — which would normally mean SMTP credentials living in env vars and a - * redeploy to change them. This adapter instead resolves SMTP on every send, so - * an editor can change the mailbox in the admin panel and the next email uses - * it. - * - * Wiring it into the config means `payload.sendEmail` works everywhere — which - * includes the form-builder's own submission emails (the ones an editor - * configures per form under "Emails"). Those go out over the panel's SMTP with - * no extra code. - * - * Boot-time constraints shape two details: - * - `defaultFromAddress` / `defaultFromName` must be returned synchronously, so - * they're placeholders; the real from-address is applied per message below. - * - nodemailer is imported dynamically inside sendEmail, so merely loading the - * Payload config (or running `generate:importmap`) doesn't pull it in. - */ export const panelSmtpAdapter = (args = {})=>({ payload })=>({ - name: 'ipal-panel-smtp', - defaultFromAddress: args.fallbackFromAddress ?? 'noreply@localhost', - defaultFromName: args.fallbackFromName ?? 'Website', - sendEmail: async (message)=>{ - const smtp = await getSiteIntegrations(payload); - const host = smtp.smtpHost; - const port = smtp.smtpPort ?? 587; - const user = smtp.smtpUser; - const pass = smtp.smtpPassword; - if (!host || !user || !pass) { - payload.logger.error('[ipal] Email not sent: SMTP is not configured in Site Integrations.'); - return { - error: 'SMTP is not configured in Site Integrations.', - sent: false - }; - } - // The panel is the source of truth for the sender; fall back to whatever - // the caller set (Payload fills in defaultFromAddress when unset). - const fromAddress = smtp.smtpFromAddress; - const fromName = smtp.smtpFromName; - const from = fromAddress ? fromName ? `${fromName} <${fromAddress}>` : fromAddress : message.from; - // Defence in depth against header injection. Modern nodemailer already - // normalises CRLF in standard headers, but the form-builder interpolates - // user data into the subject via {{field}} placeholders, so strip any - // newlines from header-bound values before they reach the transport. - const stripCRLF = (v)=>String(v ?? '').replace(/[\r\n]+/g, ' ').trim(); - const { default: nodemailer } = await import('nodemailer'); - const transporter = nodemailer.createTransport({ - auth: { - pass, - user - }, - host, - port, - secure: port === 465 - }); - try { - const info = await transporter.sendMail({ - ...message, - from, - ...message.subject ? { - subject: stripCRLF(message.subject) - } : {} - }); - return { - messageId: info.messageId, - sent: true - }; - } catch (err) { - // Never surface SMTP internals to the caller — a form submission - // shouldn't fail loudly because the mailbox is misconfigured. - payload.logger.error(`[ipal] Email send failed: ${err.message}`); - return { - error: 'Failed to send email.', - sent: false - }; - } - } - }); - -//# sourceMappingURL=panelSmtpAdapter.js.map \ No newline at end of file diff --git a/dist/modules/email/panelSmtpAdapter.js.map b/dist/modules/email/panelSmtpAdapter.js.map deleted file mode 100644 index 06b1854..0000000 --- a/dist/modules/email/panelSmtpAdapter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/email/panelSmtpAdapter.ts"],"sourcesContent":["import type { PayloadEmailAdapter, SendEmailOptions } from 'payload'\n\nimport { getSiteIntegrations } from '../payload/index.js'\n\n/** SMTP fields the adapter reads from SiteIntegrations. */\ntype SmtpIntegrations = {\n smtpFromAddress?: null | string\n smtpFromName?: null | string\n smtpHost?: null | string\n smtpPassword?: null | string\n smtpPort?: null | number\n smtpUser?: null | string\n}\n\nexport type PanelSmtpAdapterArgs = {\n /**\n * Used only until the panel is filled in — Payload requires a from-address\n * synchronously at boot, before any global can be read. Once SiteIntegrations\n * has an address, it wins.\n */\n fallbackFromAddress?: string\n fallbackFromName?: string\n}\n\n/**\n * Payload email adapter backed by the SMTP settings in the SiteIntegrations\n * global.\n *\n * Why this exists: Payload builds its email adapter once, at boot, from the\n * config — which would normally mean SMTP credentials living in env vars and a\n * redeploy to change them. This adapter instead resolves SMTP on every send, so\n * an editor can change the mailbox in the admin panel and the next email uses\n * it.\n *\n * Wiring it into the config means `payload.sendEmail` works everywhere — which\n * includes the form-builder's own submission emails (the ones an editor\n * configures per form under \"Emails\"). Those go out over the panel's SMTP with\n * no extra code.\n *\n * Boot-time constraints shape two details:\n * - `defaultFromAddress` / `defaultFromName` must be returned synchronously, so\n * they're placeholders; the real from-address is applied per message below.\n * - nodemailer is imported dynamically inside sendEmail, so merely loading the\n * Payload config (or running `generate:importmap`) doesn't pull it in.\n */\nexport const panelSmtpAdapter =\n (args: PanelSmtpAdapterArgs = {}): PayloadEmailAdapter =>\n ({ payload }) => ({\n name: 'ipal-panel-smtp',\n defaultFromAddress: args.fallbackFromAddress ?? 'noreply@localhost',\n defaultFromName: args.fallbackFromName ?? 'Website',\n\n sendEmail: async (message: SendEmailOptions) => {\n const smtp = await getSiteIntegrations(payload)\n\n const host = smtp.smtpHost\n const port = smtp.smtpPort ?? 587\n const user = smtp.smtpUser\n const pass = smtp.smtpPassword\n\n if (!host || !user || !pass) {\n payload.logger.error('[ipal] Email not sent: SMTP is not configured in Site Integrations.')\n return { error: 'SMTP is not configured in Site Integrations.', sent: false }\n }\n\n // The panel is the source of truth for the sender; fall back to whatever\n // the caller set (Payload fills in defaultFromAddress when unset).\n const fromAddress = smtp.smtpFromAddress\n const fromName = smtp.smtpFromName\n const from = fromAddress\n ? fromName\n ? `${fromName} <${fromAddress}>`\n : fromAddress\n : message.from\n\n // Defence in depth against header injection. Modern nodemailer already\n // normalises CRLF in standard headers, but the form-builder interpolates\n // user data into the subject via {{field}} placeholders, so strip any\n // newlines from header-bound values before they reach the transport.\n const stripCRLF = (v: unknown): string =>\n String(v ?? '')\n .replace(/[\\r\\n]+/g, ' ')\n .trim()\n\n const { default: nodemailer } = await import('nodemailer')\n\n const transporter = nodemailer.createTransport({\n auth: { pass, user },\n host,\n port,\n secure: port === 465, // implicit TLS on 465, STARTTLS otherwise\n })\n\n try {\n const info = await transporter.sendMail({\n ...message,\n from,\n ...(message.subject ? { subject: stripCRLF(message.subject) } : {}),\n })\n return { messageId: info.messageId, sent: true }\n } catch (err) {\n // Never surface SMTP internals to the caller — a form submission\n // shouldn't fail loudly because the mailbox is misconfigured.\n payload.logger.error(`[ipal] Email send failed: ${(err as Error).message}`)\n return { error: 'Failed to send email.', sent: false }\n }\n },\n })\n"],"names":["getSiteIntegrations","panelSmtpAdapter","args","payload","name","defaultFromAddress","fallbackFromAddress","defaultFromName","fallbackFromName","sendEmail","message","smtp","host","smtpHost","port","smtpPort","user","smtpUser","pass","smtpPassword","logger","error","sent","fromAddress","smtpFromAddress","fromName","smtpFromName","from","stripCRLF","v","String","replace","trim","default","nodemailer","transporter","createTransport","auth","secure","info","sendMail","subject","messageId","err"],"mappings":"AAEA,SAASA,mBAAmB,QAAQ,sBAAqB;AAsBzD;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,MAAMC,mBACX,CAACC,OAA6B,CAAC,CAAC,GAChC,CAAC,EAAEC,OAAO,EAAE,GAAM,CAAA;YAChBC,MAAM;YACNC,oBAAoBH,KAAKI,mBAAmB,IAAI;YAChDC,iBAAiBL,KAAKM,gBAAgB,IAAI;YAE1CC,WAAW,OAAOC;gBAChB,MAAMC,OAAO,MAAMX,oBAAsCG;gBAEzD,MAAMS,OAAOD,KAAKE,QAAQ;gBAC1B,MAAMC,OAAOH,KAAKI,QAAQ,IAAI;gBAC9B,MAAMC,OAAOL,KAAKM,QAAQ;gBAC1B,MAAMC,OAAOP,KAAKQ,YAAY;gBAE9B,IAAI,CAACP,QAAQ,CAACI,QAAQ,CAACE,MAAM;oBAC3Bf,QAAQiB,MAAM,CAACC,KAAK,CAAC;oBACrB,OAAO;wBAAEA,OAAO;wBAAgDC,MAAM;oBAAM;gBAC9E;gBAEA,yEAAyE;gBACzE,mEAAmE;gBACnE,MAAMC,cAAcZ,KAAKa,eAAe;gBACxC,MAAMC,WAAWd,KAAKe,YAAY;gBAClC,MAAMC,OAAOJ,cACTE,WACE,GAAGA,SAAS,EAAE,EAAEF,YAAY,CAAC,CAAC,GAC9BA,cACFb,QAAQiB,IAAI;gBAEhB,uEAAuE;gBACvE,yEAAyE;gBACzE,sEAAsE;gBACtE,qEAAqE;gBACrE,MAAMC,YAAY,CAACC,IACjBC,OAAOD,KAAK,IACTE,OAAO,CAAC,YAAY,KACpBC,IAAI;gBAET,MAAM,EAAEC,SAASC,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;gBAE7C,MAAMC,cAAcD,WAAWE,eAAe,CAAC;oBAC7CC,MAAM;wBAAEnB;wBAAMF;oBAAK;oBACnBJ;oBACAE;oBACAwB,QAAQxB,SAAS;gBACnB;gBAEA,IAAI;oBACF,MAAMyB,OAAO,MAAMJ,YAAYK,QAAQ,CAAC;wBACtC,GAAG9B,OAAO;wBACViB;wBACA,GAAIjB,QAAQ+B,OAAO,GAAG;4BAAEA,SAASb,UAAUlB,QAAQ+B,OAAO;wBAAE,IAAI,CAAC,CAAC;oBACpE;oBACA,OAAO;wBAAEC,WAAWH,KAAKG,SAAS;wBAAEpB,MAAM;oBAAK;gBACjD,EAAE,OAAOqB,KAAK;oBACZ,iEAAiE;oBACjE,8DAA8D;oBAC9DxC,QAAQiB,MAAM,CAACC,KAAK,CAAC,CAAC,0BAA0B,EAAE,AAACsB,IAAcjC,OAAO,EAAE;oBAC1E,OAAO;wBAAEW,OAAO;wBAAyBC,MAAM;oBAAM;gBACvD;YACF;QACF,CAAA,EAAE"} \ No newline at end of file diff --git a/dist/modules/email/sendEmail.d.ts b/dist/modules/email/sendEmail.d.ts deleted file mode 100644 index 57e5eaa..0000000 --- a/dist/modules/email/sendEmail.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -import 'server-only'; -import type { BasePayload } from 'payload'; -export type SendEmailArgs = { - /** Override the configured from-address for this message. */ - from?: string; - /** HTML body. */ - html: string; - /** Payload instance — used to read SMTP config from SiteIntegrations. */ - payload: BasePayload; - replyTo?: string; - subject: string; - /** Optional plain-text fallback. */ - text?: string; - to: string | string[]; -}; -export type SendEmailResult = { - error: string; - sent: false; -} | { - messageId: string; - sent: true; -}; -/** - * Sends an email using SMTP settings from the SiteIntegrations global. - * - * Reads config at call time (not at boot) so editors can change SMTP in the - * admin panel without a restart — consistent with the plugin's panel-managed - * model. `server-only` keeps the SMTP password out of any client bundle. - * - * Returns a result object rather than throwing, so callers (e.g. form - * submission) can handle failure without a try/catch and never leak SMTP - * details to the client. - */ -export declare function sendEmail({ from, html, payload, replyTo, subject, text, to, }: SendEmailArgs): Promise; diff --git a/dist/modules/email/sendEmail.js b/dist/modules/email/sendEmail.js deleted file mode 100644 index a208f78..0000000 --- a/dist/modules/email/sendEmail.js +++ /dev/null @@ -1,70 +0,0 @@ -import 'server-only'; -import nodemailer from 'nodemailer'; -import { getSiteIntegrations } from '../payload/index.js'; -/** - * Sends an email using SMTP settings from the SiteIntegrations global. - * - * Reads config at call time (not at boot) so editors can change SMTP in the - * admin panel without a restart — consistent with the plugin's panel-managed - * model. `server-only` keeps the SMTP password out of any client bundle. - * - * Returns a result object rather than throwing, so callers (e.g. form - * submission) can handle failure without a try/catch and never leak SMTP - * details to the client. - */ export async function sendEmail({ from, html, payload, replyTo, subject, text, to }) { - const smtp = await getSiteIntegrations(payload); - const host = smtp.smtpHost; - const port = smtp.smtpPort ?? 587; - const user = smtp.smtpUser; - const pass = smtp.smtpPassword; - if (!host || !user || !pass) { - return { - error: 'SMTP is not configured in Site Integrations.', - sent: false - }; - } - const fromAddress = from ?? smtp.smtpFromAddress; - if (!fromAddress) { - return { - error: 'No from-address configured.', - sent: false - }; - } - const fromName = smtp.smtpFromName; - const fromHeader = fromName ? `${fromName} <${fromAddress}>` : fromAddress; - const transporter = nodemailer.createTransport({ - auth: { - pass, - user - }, - host, - port, - secure: port === 465 - }); - try { - const info = await transporter.sendMail({ - from: fromHeader, - html, - subject, - to, - ...text ? { - text - } : {}, - ...replyTo ? { - replyTo - } : {} - }); - return { - messageId: info.messageId, - sent: true - }; - } catch (err) { - payload.logger.error(`[ipal] Email send failed: ${err.message}`); - return { - error: 'Failed to send email.', - sent: false - }; - } -} - -//# sourceMappingURL=sendEmail.js.map \ No newline at end of file diff --git a/dist/modules/email/sendEmail.js.map b/dist/modules/email/sendEmail.js.map deleted file mode 100644 index 75456cf..0000000 --- a/dist/modules/email/sendEmail.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/email/sendEmail.ts"],"sourcesContent":["import 'server-only'\n\nimport type { BasePayload } from 'payload'\n\nimport nodemailer from 'nodemailer'\n\nimport { getSiteIntegrations } from '../payload/index.js'\n\n/** SMTP fields the sender reads from SiteIntegrations. */\ntype SmtpIntegrations = {\n smtpFromAddress?: null | string\n smtpFromName?: null | string\n smtpHost?: null | string\n smtpPassword?: null | string\n smtpPort?: null | number\n smtpUser?: null | string\n}\n\nexport type SendEmailArgs = {\n /** Override the configured from-address for this message. */\n from?: string\n /** HTML body. */\n html: string\n /** Payload instance — used to read SMTP config from SiteIntegrations. */\n payload: BasePayload\n replyTo?: string\n subject: string\n /** Optional plain-text fallback. */\n text?: string\n to: string | string[]\n}\n\nexport type SendEmailResult = { error: string; sent: false } | { messageId: string; sent: true }\n\n/**\n * Sends an email using SMTP settings from the SiteIntegrations global.\n *\n * Reads config at call time (not at boot) so editors can change SMTP in the\n * admin panel without a restart — consistent with the plugin's panel-managed\n * model. `server-only` keeps the SMTP password out of any client bundle.\n *\n * Returns a result object rather than throwing, so callers (e.g. form\n * submission) can handle failure without a try/catch and never leak SMTP\n * details to the client.\n */\nexport async function sendEmail({\n from,\n html,\n payload,\n replyTo,\n subject,\n text,\n to,\n}: SendEmailArgs): Promise {\n const smtp = await getSiteIntegrations(payload)\n\n const host = smtp.smtpHost\n const port = smtp.smtpPort ?? 587\n const user = smtp.smtpUser\n const pass = smtp.smtpPassword\n\n if (!host || !user || !pass) {\n return { error: 'SMTP is not configured in Site Integrations.', sent: false }\n }\n\n const fromAddress = from ?? smtp.smtpFromAddress\n if (!fromAddress) {\n return { error: 'No from-address configured.', sent: false }\n }\n\n const fromName = smtp.smtpFromName\n const fromHeader = fromName ? `${fromName} <${fromAddress}>` : fromAddress\n\n const transporter = nodemailer.createTransport({\n auth: { pass, user },\n host,\n port,\n secure: port === 465, // implicit TLS on 465, STARTTLS otherwise\n })\n\n try {\n const info = await transporter.sendMail({\n from: fromHeader,\n html,\n subject,\n to,\n ...(text ? { text } : {}),\n ...(replyTo ? { replyTo } : {}),\n })\n return { messageId: info.messageId, sent: true }\n } catch (err) {\n payload.logger.error(`[ipal] Email send failed: ${(err as Error).message}`)\n return { error: 'Failed to send email.', sent: false }\n }\n}\n"],"names":["nodemailer","getSiteIntegrations","sendEmail","from","html","payload","replyTo","subject","text","to","smtp","host","smtpHost","port","smtpPort","user","smtpUser","pass","smtpPassword","error","sent","fromAddress","smtpFromAddress","fromName","smtpFromName","fromHeader","transporter","createTransport","auth","secure","info","sendMail","messageId","err","logger","message"],"mappings":"AAAA,OAAO,cAAa;AAIpB,OAAOA,gBAAgB,aAAY;AAEnC,SAASC,mBAAmB,QAAQ,sBAAqB;AA4BzD;;;;;;;;;;CAUC,GACD,OAAO,eAAeC,UAAU,EAC9BC,IAAI,EACJC,IAAI,EACJC,OAAO,EACPC,OAAO,EACPC,OAAO,EACPC,IAAI,EACJC,EAAE,EACY;IACd,MAAMC,OAAO,MAAMT,oBAAsCI;IAEzD,MAAMM,OAAOD,KAAKE,QAAQ;IAC1B,MAAMC,OAAOH,KAAKI,QAAQ,IAAI;IAC9B,MAAMC,OAAOL,KAAKM,QAAQ;IAC1B,MAAMC,OAAOP,KAAKQ,YAAY;IAE9B,IAAI,CAACP,QAAQ,CAACI,QAAQ,CAACE,MAAM;QAC3B,OAAO;YAAEE,OAAO;YAAgDC,MAAM;QAAM;IAC9E;IAEA,MAAMC,cAAclB,QAAQO,KAAKY,eAAe;IAChD,IAAI,CAACD,aAAa;QAChB,OAAO;YAAEF,OAAO;YAA+BC,MAAM;QAAM;IAC7D;IAEA,MAAMG,WAAWb,KAAKc,YAAY;IAClC,MAAMC,aAAaF,WAAW,GAAGA,SAAS,EAAE,EAAEF,YAAY,CAAC,CAAC,GAAGA;IAE/D,MAAMK,cAAc1B,WAAW2B,eAAe,CAAC;QAC7CC,MAAM;YAAEX;YAAMF;QAAK;QACnBJ;QACAE;QACAgB,QAAQhB,SAAS;IACnB;IAEA,IAAI;QACF,MAAMiB,OAAO,MAAMJ,YAAYK,QAAQ,CAAC;YACtC5B,MAAMsB;YACNrB;YACAG;YACAE;YACA,GAAID,OAAO;gBAAEA;YAAK,IAAI,CAAC,CAAC;YACxB,GAAIF,UAAU;gBAAEA;YAAQ,IAAI,CAAC,CAAC;QAChC;QACA,OAAO;YAAE0B,WAAWF,KAAKE,SAAS;YAAEZ,MAAM;QAAK;IACjD,EAAE,OAAOa,KAAK;QACZ5B,QAAQ6B,MAAM,CAACf,KAAK,CAAC,CAAC,0BAA0B,EAAE,AAACc,IAAcE,OAAO,EAAE;QAC1E,OAAO;YAAEhB,OAAO;YAAyBC,MAAM;QAAM;IACvD;AACF"} \ No newline at end of file diff --git a/dist/modules/forms/formsPluginConfig.d.ts b/dist/modules/forms/formsPluginConfig.d.ts deleted file mode 100644 index 87dcea8..0000000 --- a/dist/modules/forms/formsPluginConfig.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Plugin } from 'payload'; -import type { FormsOption } from './types.js'; -/** - * Configures @payloadcms/plugin-form-builder from IPAL's FormsOption. - * - * Provides the form/form-submissions collections and field types. Email - * delivery is intentionally NOT handled here — the plugin's own SMTP-from-panel - * sender (submitForm) does that, so the form-builder's built-in email (which - * needs a Payload email adapter) is left unused. - */ -export declare function buildFormsPlugin(forms: FormsOption): Plugin; diff --git a/dist/modules/forms/formsPluginConfig.js b/dist/modules/forms/formsPluginConfig.js deleted file mode 100644 index aaec36a..0000000 --- a/dist/modules/forms/formsPluginConfig.js +++ /dev/null @@ -1,37 +0,0 @@ -import { formBuilderPlugin } from '@payloadcms/plugin-form-builder'; -/** - * Configures @payloadcms/plugin-form-builder from IPAL's FormsOption. - * - * Provides the form/form-submissions collections and field types. Email - * delivery is intentionally NOT handled here — the plugin's own SMTP-from-panel - * sender (submitForm) does that, so the form-builder's built-in email (which - * needs a Payload email adapter) is left unused. - */ export function buildFormsPlugin(forms) { - return formBuilderPlugin({ - fields: { - checkbox: true, - email: true, - message: true, - number: true, - payment: false, - select: true, - text: true, - textarea: true, - ...forms.fields - }, - ...forms.redirectRelationships ? { - redirectRelationships: forms.redirectRelationships - } : {}, - // Client-supplied collection overrides (e.g. a per-form notification - // address). The plugin provides the hook, not the opinion about which - // extra fields a form should carry. - ...forms.formOverrides ? { - formOverrides: forms.formOverrides - } : {}, - ...forms.formSubmissionOverrides ? { - formSubmissionOverrides: forms.formSubmissionOverrides - } : {} - }); -} - -//# sourceMappingURL=formsPluginConfig.js.map \ No newline at end of file diff --git a/dist/modules/forms/formsPluginConfig.js.map b/dist/modules/forms/formsPluginConfig.js.map deleted file mode 100644 index 9fb3ee5..0000000 --- a/dist/modules/forms/formsPluginConfig.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/forms/formsPluginConfig.ts"],"sourcesContent":["import type { Plugin } from 'payload'\n\nimport { formBuilderPlugin } from '@payloadcms/plugin-form-builder'\n\nimport type { FormsOption } from './types.js'\n\n/**\n * Configures @payloadcms/plugin-form-builder from IPAL's FormsOption.\n *\n * Provides the form/form-submissions collections and field types. Email\n * delivery is intentionally NOT handled here — the plugin's own SMTP-from-panel\n * sender (submitForm) does that, so the form-builder's built-in email (which\n * needs a Payload email adapter) is left unused.\n */\nexport function buildFormsPlugin(forms: FormsOption): Plugin {\n return formBuilderPlugin({\n fields: {\n checkbox: true,\n email: true,\n message: true,\n number: true,\n payment: false,\n select: true,\n text: true,\n textarea: true,\n ...forms.fields,\n },\n ...(forms.redirectRelationships ? { redirectRelationships: forms.redirectRelationships } : {}),\n // Client-supplied collection overrides (e.g. a per-form notification\n // address). The plugin provides the hook, not the opinion about which\n // extra fields a form should carry.\n ...(forms.formOverrides ? { formOverrides: forms.formOverrides } : {}),\n ...(forms.formSubmissionOverrides\n ? { formSubmissionOverrides: forms.formSubmissionOverrides }\n : {}),\n })\n}\n"],"names":["formBuilderPlugin","buildFormsPlugin","forms","fields","checkbox","email","message","number","payment","select","text","textarea","redirectRelationships","formOverrides","formSubmissionOverrides"],"mappings":"AAEA,SAASA,iBAAiB,QAAQ,kCAAiC;AAInE;;;;;;;CAOC,GACD,OAAO,SAASC,iBAAiBC,KAAkB;IACjD,OAAOF,kBAAkB;QACvBG,QAAQ;YACNC,UAAU;YACVC,OAAO;YACPC,SAAS;YACTC,QAAQ;YACRC,SAAS;YACTC,QAAQ;YACRC,MAAM;YACNC,UAAU;YACV,GAAGT,MAAMC,MAAM;QACjB;QACA,GAAID,MAAMU,qBAAqB,GAAG;YAAEA,uBAAuBV,MAAMU,qBAAqB;QAAC,IAAI,CAAC,CAAC;QAC7F,qEAAqE;QACrE,sEAAsE;QACtE,oCAAoC;QACpC,GAAIV,MAAMW,aAAa,GAAG;YAAEA,eAAeX,MAAMW,aAAa;QAAC,IAAI,CAAC,CAAC;QACrE,GAAIX,MAAMY,uBAAuB,GAC7B;YAAEA,yBAAyBZ,MAAMY,uBAAuB;QAAC,IACzD,CAAC,CAAC;IACR;AACF"} \ No newline at end of file diff --git a/dist/modules/forms/index.d.ts b/dist/modules/forms/index.d.ts deleted file mode 100644 index ace80ae..0000000 --- a/dist/modules/forms/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { buildFormsPlugin } from './formsPluginConfig.js'; -export { checkRateLimit } from './rateLimit.js'; -export type { RateLimitArgs } from './rateLimit.js'; -export { submitForm } from './submitForm.js'; -export type { SubmitFormArgs, SubmitFormResult } from './submitForm.js'; -export type { FormsCollectionOverrides, FormsFieldsOverride, FormsOption } from './types.js'; -export { validateSubmission } from './validateSubmission.js'; -export type { FormValidationResult } from './validateSubmission.js'; diff --git a/dist/modules/forms/index.js b/dist/modules/forms/index.js deleted file mode 100644 index 384833d..0000000 --- a/dist/modules/forms/index.js +++ /dev/null @@ -1,8 +0,0 @@ -// buildFormsPlugin is config-time (safe anywhere). submitForm is server-only -// (Turnstile secret) — never import it from a client component. -export { buildFormsPlugin } from './formsPluginConfig.js'; -export { checkRateLimit } from './rateLimit.js'; -export { submitForm } from './submitForm.js'; -export { validateSubmission } from './validateSubmission.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/forms/index.js.map b/dist/modules/forms/index.js.map deleted file mode 100644 index 00c6df4..0000000 --- a/dist/modules/forms/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/forms/index.ts"],"sourcesContent":["// buildFormsPlugin is config-time (safe anywhere). submitForm is server-only\n// (Turnstile secret) — never import it from a client component.\nexport { buildFormsPlugin } from './formsPluginConfig.js'\nexport { checkRateLimit } from './rateLimit.js'\nexport type { RateLimitArgs } from './rateLimit.js'\nexport { submitForm } from './submitForm.js'\nexport type { SubmitFormArgs, SubmitFormResult } from './submitForm.js'\nexport type { FormsCollectionOverrides, FormsFieldsOverride, FormsOption } from './types.js'\nexport { validateSubmission } from './validateSubmission.js'\nexport type { FormValidationResult } from './validateSubmission.js'\n"],"names":["buildFormsPlugin","checkRateLimit","submitForm","validateSubmission"],"mappings":"AAAA,6EAA6E;AAC7E,gEAAgE;AAChE,SAASA,gBAAgB,QAAQ,yBAAwB;AACzD,SAASC,cAAc,QAAQ,iBAAgB;AAE/C,SAASC,UAAU,QAAQ,kBAAiB;AAG5C,SAASC,kBAAkB,QAAQ,0BAAyB"} \ No newline at end of file diff --git a/dist/modules/forms/rateLimit.d.ts b/dist/modules/forms/rateLimit.d.ts deleted file mode 100644 index 61eeac0..0000000 --- a/dist/modules/forms/rateLimit.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -export type RateLimitArgs = { - /** Identifier to limit on — typically the client IP. */ - key: string; - /** Max submissions allowed per window. Defaults to 5. */ - max?: number; - /** Window length in ms. Defaults to 60_000 (one minute). */ - windowMs?: number; -}; -/** - * Returns true when the request is within the limit, false when it should be - * rejected. A missing key (no IP) is allowed through — better to accept a - * submission than to block everyone behind a proxy that strips the header. - */ -export declare function checkRateLimit({ key, max, windowMs }: RateLimitArgs): boolean; diff --git a/dist/modules/forms/rateLimit.js b/dist/modules/forms/rateLimit.js deleted file mode 100644 index 1b70b31..0000000 --- a/dist/modules/forms/rateLimit.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Per-IP sliding window, in memory. - * - * Deliberately simple: no Redis, no dependency. The trade-off is that the - * counter lives in one process — with several instances behind a load balancer - * each keeps its own, so the effective limit is per-instance, not global. For a - * contact form that's fine (it raises the cost of flooding without pretending - * to be airtight); a high-security form should put a real limiter in front. - * - * State is module-level, so it survives between requests but resets on redeploy - * — acceptable for abuse throttling. - */ const buckets = new Map(); -/** Sweep expired buckets occasionally so the map doesn't grow unbounded. */ let lastSweep = Date.now(); -const SWEEP_INTERVAL = 60_000; -function sweep(now) { - if (now - lastSweep < SWEEP_INTERVAL) { - return; - } - lastSweep = now; - for (const [key, bucket] of buckets){ - if (bucket.resetAt <= now) { - buckets.delete(key); - } - } -} -/** - * Returns true when the request is within the limit, false when it should be - * rejected. A missing key (no IP) is allowed through — better to accept a - * submission than to block everyone behind a proxy that strips the header. - */ export function checkRateLimit({ key, max = 5, windowMs = 60_000 }) { - if (!key) { - return true; - } - const now = Date.now(); - sweep(now); - const bucket = buckets.get(key); - if (!bucket || bucket.resetAt <= now) { - buckets.set(key, { - count: 1, - resetAt: now + windowMs - }); - return true; - } - if (bucket.count >= max) { - return false; - } - bucket.count += 1; - return true; -} - -//# sourceMappingURL=rateLimit.js.map \ No newline at end of file diff --git a/dist/modules/forms/rateLimit.js.map b/dist/modules/forms/rateLimit.js.map deleted file mode 100644 index 8ba4636..0000000 --- a/dist/modules/forms/rateLimit.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/forms/rateLimit.ts"],"sourcesContent":["type Bucket = { count: number; resetAt: number }\n\n/**\n * Per-IP sliding window, in memory.\n *\n * Deliberately simple: no Redis, no dependency. The trade-off is that the\n * counter lives in one process — with several instances behind a load balancer\n * each keeps its own, so the effective limit is per-instance, not global. For a\n * contact form that's fine (it raises the cost of flooding without pretending\n * to be airtight); a high-security form should put a real limiter in front.\n *\n * State is module-level, so it survives between requests but resets on redeploy\n * — acceptable for abuse throttling.\n */\nconst buckets = new Map()\n\n/** Sweep expired buckets occasionally so the map doesn't grow unbounded. */\nlet lastSweep = Date.now()\nconst SWEEP_INTERVAL = 60_000\n\nfunction sweep(now: number) {\n if (now - lastSweep < SWEEP_INTERVAL) {return}\n lastSweep = now\n for (const [key, bucket] of buckets) {\n if (bucket.resetAt <= now) {buckets.delete(key)}\n }\n}\n\nexport type RateLimitArgs = {\n /** Identifier to limit on — typically the client IP. */\n key: string\n /** Max submissions allowed per window. Defaults to 5. */\n max?: number\n /** Window length in ms. Defaults to 60_000 (one minute). */\n windowMs?: number\n}\n\n/**\n * Returns true when the request is within the limit, false when it should be\n * rejected. A missing key (no IP) is allowed through — better to accept a\n * submission than to block everyone behind a proxy that strips the header.\n */\nexport function checkRateLimit({ key, max = 5, windowMs = 60_000 }: RateLimitArgs): boolean {\n if (!key) {return true}\n\n const now = Date.now()\n sweep(now)\n\n const bucket = buckets.get(key)\n\n if (!bucket || bucket.resetAt <= now) {\n buckets.set(key, { count: 1, resetAt: now + windowMs })\n return true\n }\n\n if (bucket.count >= max) {\n return false\n }\n\n bucket.count += 1\n return true\n}\n"],"names":["buckets","Map","lastSweep","Date","now","SWEEP_INTERVAL","sweep","key","bucket","resetAt","delete","checkRateLimit","max","windowMs","get","set","count"],"mappings":"AAEA;;;;;;;;;;;CAWC,GACD,MAAMA,UAAU,IAAIC;AAEpB,0EAA0E,GAC1E,IAAIC,YAAYC,KAAKC,GAAG;AACxB,MAAMC,iBAAiB;AAEvB,SAASC,MAAMF,GAAW;IACxB,IAAIA,MAAMF,YAAYG,gBAAgB;QAAC;IAAM;IAC7CH,YAAYE;IACZ,KAAK,MAAM,CAACG,KAAKC,OAAO,IAAIR,QAAS;QACnC,IAAIQ,OAAOC,OAAO,IAAIL,KAAK;YAACJ,QAAQU,MAAM,CAACH;QAAI;IACjD;AACF;AAWA;;;;CAIC,GACD,OAAO,SAASI,eAAe,EAAEJ,GAAG,EAAEK,MAAM,CAAC,EAAEC,WAAW,MAAM,EAAiB;IAC/E,IAAI,CAACN,KAAK;QAAC,OAAO;IAAI;IAEtB,MAAMH,MAAMD,KAAKC,GAAG;IACpBE,MAAMF;IAEN,MAAMI,SAASR,QAAQc,GAAG,CAACP;IAE3B,IAAI,CAACC,UAAUA,OAAOC,OAAO,IAAIL,KAAK;QACpCJ,QAAQe,GAAG,CAACR,KAAK;YAAES,OAAO;YAAGP,SAASL,MAAMS;QAAS;QACrD,OAAO;IACT;IAEA,IAAIL,OAAOQ,KAAK,IAAIJ,KAAK;QACvB,OAAO;IACT;IAEAJ,OAAOQ,KAAK,IAAI;IAChB,OAAO;AACT"} \ No newline at end of file diff --git a/dist/modules/forms/submitForm.d.ts b/dist/modules/forms/submitForm.d.ts deleted file mode 100644 index b2b2814..0000000 --- a/dist/modules/forms/submitForm.d.ts +++ /dev/null @@ -1,72 +0,0 @@ -import 'server-only'; -import type { BasePayload } from 'payload'; -export type SubmitFormArgs = { - /** Submitted field data — shape matches the form's fields. */ - data: Record; - /** Form-builder form ID this submission belongs to. */ - formId: string; - /** Client IP — used for Turnstile and rate limiting. */ - ip?: string; - /** - * Rate limit: max submissions per IP per minute. Defaults to 5. - * Set to 0 to disable (e.g. when a real limiter sits in front). - */ - maxPerMinute?: number; - payload: BasePayload; - /** Turnstile token; when present it is verified, when absent it is skipped. */ - turnstileToken?: string; -}; -/** - * Why a submission failed — a code, never a user-facing string. - * - * The plugin knows what went wrong; it deliberately doesn't decide how to say - * it. The frontend maps these to its own copy, in its own language, and renders - * whatever component fits — a field error, a toast, a full message. The plugin - * has no business choosing the wording or the locale. - * - * - `rate_limited` — too many submissions from this IP - * - `turnstile` — bot check failed - * - `validation` — a field is missing/too long, or unknown keys were sent; - * `field` and `kind` narrow it down when a specific field is - * at fault (absent for whole-payload problems like unknown keys) - * - `not_found` — no form with this id - * - `error` — persistence failed unexpectedly - */ -export type SubmitFailure = { - /** The offending field's name, when one field is at fault. */ - field?: string; - /** What was wrong with it. */ - kind?: 'required' | 'too_long' | 'unknown_fields'; - reason: 'validation'; - success: false; -} | { - reason: 'error'; - success: false; -} | { - reason: 'not_found'; - success: false; -} | { - reason: 'rate_limited'; - success: false; -} | { - reason: 'turnstile'; - success: false; -}; -export type SubmitFormResult = { - submissionId: number | string; - success: true; -} | SubmitFailure; -/** - * Handles a form submission end to end: rate limit, verify Turnstile, validate - * against the form's own schema, then store. - * - * The order is cost-ascending on purpose — the cheapest checks reject first, so - * a flood never reaches Turnstile's network call or the database. - * - * Emails aren't sent here. The form-builder sends whatever an editor configured - * under the form's "Emails" tab (form-submissions hook → payload.sendEmail), - * which goes out over panelSmtpAdapter. Storing the submission is enough. - * - * server-only: touches the Turnstile secret. - */ -export declare function submitForm({ data, formId, ip, maxPerMinute, payload, turnstileToken, }: SubmitFormArgs): Promise; diff --git a/dist/modules/forms/submitForm.js b/dist/modules/forms/submitForm.js deleted file mode 100644 index cf5217d..0000000 --- a/dist/modules/forms/submitForm.js +++ /dev/null @@ -1,91 +0,0 @@ -import 'server-only'; -import { verifyTurnstile } from '../turnstile/index.js'; -import { checkRateLimit } from './rateLimit.js'; -import { validateSubmission } from './validateSubmission.js'; -/** - * Handles a form submission end to end: rate limit, verify Turnstile, validate - * against the form's own schema, then store. - * - * The order is cost-ascending on purpose — the cheapest checks reject first, so - * a flood never reaches Turnstile's network call or the database. - * - * Emails aren't sent here. The form-builder sends whatever an editor configured - * under the form's "Emails" tab (form-submissions hook → payload.sendEmail), - * which goes out over panelSmtpAdapter. Storing the submission is enough. - * - * server-only: touches the Turnstile secret. - */ export async function submitForm({ data, formId, ip, maxPerMinute = 5, payload, turnstileToken }) { - // 1. Rate limit — cheapest gate, drops a flood before any real work. - if (maxPerMinute > 0 && ip) { - if (!checkRateLimit({ - key: ip, - max: maxPerMinute - })) { - return { - reason: 'rate_limited', - success: false - }; - } - } - // 2. Turnstile — verify when a token is supplied; reject on failure. - if (turnstileToken !== undefined) { - const ok = await verifyTurnstile({ - ip, - payload, - token: turnstileToken - }); - if (!ok) { - return { - reason: 'turnstile', - success: false - }; - } - } - // 3. Validate against the form's schema. A public endpoint can't trust the - // shape of `data` — drop unknown keys, enforce required, cap length. - const validation = await validateSubmission(payload, formId, data); - if (!validation.ok) { - if (validation.reason === 'not_found') { - return { - reason: 'not_found', - success: false - }; - } - return { - reason: 'validation', - success: false, - ...validation.field ? { - field: validation.field - } : {}, - ...validation.kind ? { - kind: validation.kind - } : {} - }; - } - // 4. Persist (form-builder shape: submissionData array). Triggers the email - // hook. Only validated, known fields are stored. - try { - const submission = await payload.create({ - collection: 'form-submissions', - data: { - form: formId, - submissionData: Object.entries(validation.cleaned).map(([field, value])=>({ - field, - value: value == null ? '' : String(value) - })) - } - }); - return { - submissionId: submission.id, - success: true - }; - } catch (err) { - payload.logger.error(`[ipal] Form submission failed: ${err.message}`); - return { - reason: 'error', - success: false - }; - } -} - -//# sourceMappingURL=submitForm.js.map \ No newline at end of file diff --git a/dist/modules/forms/submitForm.js.map b/dist/modules/forms/submitForm.js.map deleted file mode 100644 index b289675..0000000 --- a/dist/modules/forms/submitForm.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/forms/submitForm.ts"],"sourcesContent":["import 'server-only'\n\nimport type { BasePayload } from 'payload'\n\nimport { verifyTurnstile } from '../turnstile/index.js'\nimport { checkRateLimit } from './rateLimit.js'\nimport { validateSubmission } from './validateSubmission.js'\n\nexport type SubmitFormArgs = {\n /** Submitted field data — shape matches the form's fields. */\n data: Record\n /** Form-builder form ID this submission belongs to. */\n formId: string\n /** Client IP — used for Turnstile and rate limiting. */\n ip?: string\n /**\n * Rate limit: max submissions per IP per minute. Defaults to 5.\n * Set to 0 to disable (e.g. when a real limiter sits in front).\n */\n maxPerMinute?: number\n payload: BasePayload\n /** Turnstile token; when present it is verified, when absent it is skipped. */\n turnstileToken?: string\n}\n\n/**\n * Why a submission failed — a code, never a user-facing string.\n *\n * The plugin knows what went wrong; it deliberately doesn't decide how to say\n * it. The frontend maps these to its own copy, in its own language, and renders\n * whatever component fits — a field error, a toast, a full message. The plugin\n * has no business choosing the wording or the locale.\n *\n * - `rate_limited` — too many submissions from this IP\n * - `turnstile` — bot check failed\n * - `validation` — a field is missing/too long, or unknown keys were sent;\n * `field` and `kind` narrow it down when a specific field is\n * at fault (absent for whole-payload problems like unknown keys)\n * - `not_found` — no form with this id\n * - `error` — persistence failed unexpectedly\n */\nexport type SubmitFailure =\n | {\n /** The offending field's name, when one field is at fault. */\n field?: string\n /** What was wrong with it. */\n kind?: 'required' | 'too_long' | 'unknown_fields'\n reason: 'validation'\n success: false\n }\n | { reason: 'error'; success: false }\n | { reason: 'not_found'; success: false }\n | { reason: 'rate_limited'; success: false }\n | { reason: 'turnstile'; success: false }\n\nexport type SubmitFormResult = { submissionId: number | string; success: true } | SubmitFailure\n\n/**\n * Handles a form submission end to end: rate limit, verify Turnstile, validate\n * against the form's own schema, then store.\n *\n * The order is cost-ascending on purpose — the cheapest checks reject first, so\n * a flood never reaches Turnstile's network call or the database.\n *\n * Emails aren't sent here. The form-builder sends whatever an editor configured\n * under the form's \"Emails\" tab (form-submissions hook → payload.sendEmail),\n * which goes out over panelSmtpAdapter. Storing the submission is enough.\n *\n * server-only: touches the Turnstile secret.\n */\nexport async function submitForm({\n data,\n formId,\n ip,\n maxPerMinute = 5,\n payload,\n turnstileToken,\n}: SubmitFormArgs): Promise {\n // 1. Rate limit — cheapest gate, drops a flood before any real work.\n if (maxPerMinute > 0 && ip) {\n if (!checkRateLimit({ key: ip, max: maxPerMinute })) {\n return { reason: 'rate_limited', success: false }\n }\n }\n\n // 2. Turnstile — verify when a token is supplied; reject on failure.\n if (turnstileToken !== undefined) {\n const ok = await verifyTurnstile({ ip, payload, token: turnstileToken })\n if (!ok) {\n return { reason: 'turnstile', success: false }\n }\n }\n\n // 3. Validate against the form's schema. A public endpoint can't trust the\n // shape of `data` — drop unknown keys, enforce required, cap length.\n const validation = await validateSubmission(payload, formId, data)\n if (!validation.ok) {\n if (validation.reason === 'not_found') {\n return { reason: 'not_found', success: false }\n }\n return {\n reason: 'validation',\n success: false,\n ...(validation.field ? { field: validation.field } : {}),\n ...(validation.kind ? { kind: validation.kind } : {}),\n }\n }\n\n // 4. Persist (form-builder shape: submissionData array). Triggers the email\n // hook. Only validated, known fields are stored.\n try {\n const submission = await payload.create({\n collection: 'form-submissions',\n data: {\n form: formId,\n submissionData: Object.entries(validation.cleaned).map(([field, value]) => ({\n field,\n value: value == null ? '' : String(value),\n })),\n } as never,\n })\n return { submissionId: submission.id, success: true }\n } catch (err) {\n payload.logger.error(`[ipal] Form submission failed: ${(err as Error).message}`)\n return { reason: 'error', success: false }\n }\n}\n"],"names":["verifyTurnstile","checkRateLimit","validateSubmission","submitForm","data","formId","ip","maxPerMinute","payload","turnstileToken","key","max","reason","success","undefined","ok","token","validation","field","kind","submission","create","collection","form","submissionData","Object","entries","cleaned","map","value","String","submissionId","id","err","logger","error","message"],"mappings":"AAAA,OAAO,cAAa;AAIpB,SAASA,eAAe,QAAQ,wBAAuB;AACvD,SAASC,cAAc,QAAQ,iBAAgB;AAC/C,SAASC,kBAAkB,QAAQ,0BAAyB;AAmD5D;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeC,WAAW,EAC/BC,IAAI,EACJC,MAAM,EACNC,EAAE,EACFC,eAAe,CAAC,EAChBC,OAAO,EACPC,cAAc,EACC;IACf,qEAAqE;IACrE,IAAIF,eAAe,KAAKD,IAAI;QAC1B,IAAI,CAACL,eAAe;YAAES,KAAKJ;YAAIK,KAAKJ;QAAa,IAAI;YACnD,OAAO;gBAAEK,QAAQ;gBAAgBC,SAAS;YAAM;QAClD;IACF;IAEA,qEAAqE;IACrE,IAAIJ,mBAAmBK,WAAW;QAChC,MAAMC,KAAK,MAAMf,gBAAgB;YAAEM;YAAIE;YAASQ,OAAOP;QAAe;QACtE,IAAI,CAACM,IAAI;YACP,OAAO;gBAAEH,QAAQ;gBAAaC,SAAS;YAAM;QAC/C;IACF;IAEA,2EAA2E;IAC3E,wEAAwE;IACxE,MAAMI,aAAa,MAAMf,mBAAmBM,SAASH,QAAQD;IAC7D,IAAI,CAACa,WAAWF,EAAE,EAAE;QAClB,IAAIE,WAAWL,MAAM,KAAK,aAAa;YACrC,OAAO;gBAAEA,QAAQ;gBAAaC,SAAS;YAAM;QAC/C;QACA,OAAO;YACLD,QAAQ;YACRC,SAAS;YACT,GAAII,WAAWC,KAAK,GAAG;gBAAEA,OAAOD,WAAWC,KAAK;YAAC,IAAI,CAAC,CAAC;YACvD,GAAID,WAAWE,IAAI,GAAG;gBAAEA,MAAMF,WAAWE,IAAI;YAAC,IAAI,CAAC,CAAC;QACtD;IACF;IAEA,4EAA4E;IAC5E,oDAAoD;IACpD,IAAI;QACF,MAAMC,aAAa,MAAMZ,QAAQa,MAAM,CAAC;YACtCC,YAAY;YACZlB,MAAM;gBACJmB,MAAMlB;gBACNmB,gBAAgBC,OAAOC,OAAO,CAACT,WAAWU,OAAO,EAAEC,GAAG,CAAC,CAAC,CAACV,OAAOW,MAAM,GAAM,CAAA;wBAC1EX;wBACAW,OAAOA,SAAS,OAAO,KAAKC,OAAOD;oBACrC,CAAA;YACF;QACF;QACA,OAAO;YAAEE,cAAcX,WAAWY,EAAE;YAAEnB,SAAS;QAAK;IACtD,EAAE,OAAOoB,KAAK;QACZzB,QAAQ0B,MAAM,CAACC,KAAK,CAAC,CAAC,+BAA+B,EAAE,AAACF,IAAcG,OAAO,EAAE;QAC/E,OAAO;YAAExB,QAAQ;YAASC,SAAS;QAAM;IAC3C;AACF"} \ No newline at end of file diff --git a/dist/modules/forms/types.d.ts b/dist/modules/forms/types.d.ts deleted file mode 100644 index 70192b2..0000000 --- a/dist/modules/forms/types.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { CollectionConfig, Field } from 'payload'; -/** - * Receives the collection's default fields and returns the final list — add, - * remove, or reorder. Same shape the form-builder uses. - */ -export type FormsFieldsOverride = (args: { - defaultFields: Field[]; -}) => Field[]; -/** - * Overrides for a forms-related collection: replace the fields and/or any - * other collection setting (admin, access, hooks…). - */ -export type FormsCollectionOverrides = { - fields?: FormsFieldsOverride; -} & Partial>; -/** - * Forms configuration — mirrors the fields a client enables in the - * form-builder plugin. Kept minimal; the plugin passes these through. - */ -export type FormsOption = { - /** Field types available in the form builder. Sensible defaults applied. */ - fields?: { - checkbox?: boolean; - email?: boolean; - message?: boolean; - number?: boolean; - payment?: boolean; - select?: boolean; - text?: boolean; - textarea?: boolean; - }; - /** - * Override the forms collection. The plugin stays opinion-free about what a - * form needs beyond its fields — a client that wants, say, a per-form - * notification address adds it here: - * - * formOverrides: { - * fields: ({ defaultFields }) => [ - * ...defaultFields, - * { name: 'notificationEmail', type: 'email' }, - * ], - * } - */ - formOverrides?: FormsCollectionOverrides; - /** Override the form-submissions collection (same shape). */ - formSubmissionOverrides?: FormsCollectionOverrides; - /** Collections a form can redirect to (e.g. ['pages']). */ - redirectRelationships?: string[]; -}; diff --git a/dist/modules/forms/types.js b/dist/modules/forms/types.js deleted file mode 100644 index 6f3a4f2..0000000 --- a/dist/modules/forms/types.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Forms configuration — mirrors the fields a client enables in the - * form-builder plugin. Kept minimal; the plugin passes these through. - */ export { }; - -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/modules/forms/types.js.map b/dist/modules/forms/types.js.map deleted file mode 100644 index c58be3d..0000000 --- a/dist/modules/forms/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/forms/types.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload'\n\n/**\n * Receives the collection's default fields and returns the final list — add,\n * remove, or reorder. Same shape the form-builder uses.\n */\nexport type FormsFieldsOverride = (args: { defaultFields: Field[] }) => Field[]\n\n/**\n * Overrides for a forms-related collection: replace the fields and/or any\n * other collection setting (admin, access, hooks…).\n */\nexport type FormsCollectionOverrides = {\n fields?: FormsFieldsOverride\n} & Partial>\n\n/**\n * Forms configuration — mirrors the fields a client enables in the\n * form-builder plugin. Kept minimal; the plugin passes these through.\n */\nexport type FormsOption = {\n /** Field types available in the form builder. Sensible defaults applied. */\n fields?: {\n checkbox?: boolean\n email?: boolean\n message?: boolean\n number?: boolean\n payment?: boolean\n select?: boolean\n text?: boolean\n textarea?: boolean\n }\n /**\n * Override the forms collection. The plugin stays opinion-free about what a\n * form needs beyond its fields — a client that wants, say, a per-form\n * notification address adds it here:\n *\n * formOverrides: {\n * fields: ({ defaultFields }) => [\n * ...defaultFields,\n * { name: 'notificationEmail', type: 'email' },\n * ],\n * }\n */\n formOverrides?: FormsCollectionOverrides\n /** Override the form-submissions collection (same shape). */\n formSubmissionOverrides?: FormsCollectionOverrides\n /** Collections a form can redirect to (e.g. ['pages']). */\n redirectRelationships?: string[]\n}\n"],"names":[],"mappings":"AAgBA;;;CAGC,GACD,WA6BC"} \ No newline at end of file diff --git a/dist/modules/forms/validateSubmission.d.ts b/dist/modules/forms/validateSubmission.d.ts deleted file mode 100644 index 536d3a0..0000000 --- a/dist/modules/forms/validateSubmission.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { BasePayload } from 'payload'; -/** A form-builder field, trimmed to what validation needs. */ -type FormField = { - blockType?: string; - label?: string; - name?: string; - required?: boolean | null; -}; -type FormDoc = { - fields?: FormField[]; - id: number | string; - /** Per-form notification address, when the client added the field. */ - notificationEmail?: string; - title?: string; -}; -/** - * Validation outcome — codes, not user-facing strings. The frontend turns these - * into its own copy (see SubmitFailure in submitForm). - */ -export type FormValidationResult = { - /** Offending field, when a single field is at fault. */ - field?: string; - kind: 'required' | 'too_long' | 'unknown_fields'; - ok: false; - reason: 'invalid'; -} | { - cleaned: Record; - form: FormDoc; - ok: true; -} | { - ok: false; - reason: 'not_found'; -}; -/** - * Checks submitted data against the form's own definition, rather than trusting - * whatever arrived. - * - * The server action is a public endpoint: a caller can skip the rendered form - * and post arbitrary keys. Without this, unknown fields would be stored, - * required fields could be missing, and an oversized value could sail through. - * So we load the form, keep only keys that are real fields, reject when a - * required one is blank, and cap length. - * - * Returns the loaded form on success so the caller doesn't fetch it twice, and - * a code + offending field on failure so the frontend can point at it. - */ -export declare function validateSubmission(payload: BasePayload, formId: string, data: Record): Promise; -export {}; diff --git a/dist/modules/forms/validateSubmission.js b/dist/modules/forms/validateSubmission.js deleted file mode 100644 index 97aae90..0000000 --- a/dist/modules/forms/validateSubmission.js +++ /dev/null @@ -1,79 +0,0 @@ -/** Field block types that don't carry a submittable value. */ const NON_DATA_BLOCKS = new Set([ - 'message' -]); -/** Hard ceiling on a single field's length, independent of the form config. */ const MAX_FIELD_LENGTH = 5000; -/** - * Checks submitted data against the form's own definition, rather than trusting - * whatever arrived. - * - * The server action is a public endpoint: a caller can skip the rendered form - * and post arbitrary keys. Without this, unknown fields would be stored, - * required fields could be missing, and an oversized value could sail through. - * So we load the form, keep only keys that are real fields, reject when a - * required one is blank, and cap length. - * - * Returns the loaded form on success so the caller doesn't fetch it twice, and - * a code + offending field on failure so the frontend can point at it. - */ export async function validateSubmission(payload, formId, data) { - let form; - try { - form = await payload.findByID({ - id: formId, - collection: 'forms', - depth: 0 - }); - } catch { - return { - ok: false, - reason: 'not_found' - }; - } - const fields = (form.fields ?? []).filter((f)=>typeof f.name === 'string' && !NON_DATA_BLOCKS.has(f.blockType ?? '')); - const known = new Map(fields.map((f)=>[ - f.name, - f - ])); - const cleaned = {}; - for (const field of fields){ - const value = data[field.name]; - const isBlank = value == null || typeof value === 'string' && value.trim() === '' || value === false; - if (field.required && isBlank) { - return { - field: field.name, - kind: 'required', - ok: false, - reason: 'invalid' - }; - } - if (typeof value === 'string' && value.length > MAX_FIELD_LENGTH) { - return { - field: field.name, - kind: 'too_long', - ok: false, - reason: 'invalid' - }; - } - // Only carry through keys that belong to the form — unknown keys from a - // hand-crafted request are dropped, not stored. - if (value !== undefined) { - cleaned[field.name] = value; - } - } - // Reject outright if the payload carried keys the form doesn't define — a - // sign the request wasn't produced by the rendered form. - const unknownKeys = Object.keys(data).filter((k)=>!known.has(k)); - if (unknownKeys.length > 0) { - return { - kind: 'unknown_fields', - ok: false, - reason: 'invalid' - }; - } - return { - cleaned, - form, - ok: true - }; -} - -//# sourceMappingURL=validateSubmission.js.map \ No newline at end of file diff --git a/dist/modules/forms/validateSubmission.js.map b/dist/modules/forms/validateSubmission.js.map deleted file mode 100644 index 7717af9..0000000 --- a/dist/modules/forms/validateSubmission.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/forms/validateSubmission.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\n/** A form-builder field, trimmed to what validation needs. */\ntype FormField = {\n blockType?: string\n label?: string\n name?: string\n required?: boolean | null\n}\n\ntype FormDoc = {\n fields?: FormField[]\n id: number | string\n /** Per-form notification address, when the client added the field. */\n notificationEmail?: string\n title?: string\n}\n\n/**\n * Validation outcome — codes, not user-facing strings. The frontend turns these\n * into its own copy (see SubmitFailure in submitForm).\n */\nexport type FormValidationResult =\n | {\n /** Offending field, when a single field is at fault. */\n field?: string\n kind: 'required' | 'too_long' | 'unknown_fields'\n ok: false\n reason: 'invalid'\n }\n | { cleaned: Record; form: FormDoc; ok: true }\n | { ok: false; reason: 'not_found' }\n\n/** Field block types that don't carry a submittable value. */\nconst NON_DATA_BLOCKS = new Set(['message'])\n\n/** Hard ceiling on a single field's length, independent of the form config. */\nconst MAX_FIELD_LENGTH = 5000\n\n/**\n * Checks submitted data against the form's own definition, rather than trusting\n * whatever arrived.\n *\n * The server action is a public endpoint: a caller can skip the rendered form\n * and post arbitrary keys. Without this, unknown fields would be stored,\n * required fields could be missing, and an oversized value could sail through.\n * So we load the form, keep only keys that are real fields, reject when a\n * required one is blank, and cap length.\n *\n * Returns the loaded form on success so the caller doesn't fetch it twice, and\n * a code + offending field on failure so the frontend can point at it.\n */\nexport async function validateSubmission(\n payload: BasePayload,\n formId: string,\n data: Record,\n): Promise {\n let form: FormDoc\n try {\n form = (await payload.findByID({\n id: formId,\n collection: 'forms',\n depth: 0,\n })) as FormDoc\n } catch {\n return { ok: false, reason: 'not_found' }\n }\n\n const fields = (form.fields ?? []).filter(\n (f): f is { name: string } & FormField =>\n typeof f.name === 'string' && !NON_DATA_BLOCKS.has(f.blockType ?? ''),\n )\n const known = new Map(fields.map((f) => [f.name, f]))\n\n const cleaned: Record = {}\n\n for (const field of fields) {\n const value = data[field.name]\n const isBlank =\n value == null || (typeof value === 'string' && value.trim() === '') || value === false\n\n if (field.required && isBlank) {\n return { field: field.name, kind: 'required', ok: false, reason: 'invalid' }\n }\n\n if (typeof value === 'string' && value.length > MAX_FIELD_LENGTH) {\n return { field: field.name, kind: 'too_long', ok: false, reason: 'invalid' }\n }\n\n // Only carry through keys that belong to the form — unknown keys from a\n // hand-crafted request are dropped, not stored.\n if (value !== undefined) {\n cleaned[field.name] = value\n }\n }\n\n // Reject outright if the payload carried keys the form doesn't define — a\n // sign the request wasn't produced by the rendered form.\n const unknownKeys = Object.keys(data).filter((k) => !known.has(k))\n if (unknownKeys.length > 0) {\n return { kind: 'unknown_fields', ok: false, reason: 'invalid' }\n }\n\n return { cleaned, form, ok: true }\n}\n"],"names":["NON_DATA_BLOCKS","Set","MAX_FIELD_LENGTH","validateSubmission","payload","formId","data","form","findByID","id","collection","depth","ok","reason","fields","filter","f","name","has","blockType","known","Map","map","cleaned","field","value","isBlank","trim","required","kind","length","undefined","unknownKeys","Object","keys","k"],"mappings":"AAiCA,4DAA4D,GAC5D,MAAMA,kBAAkB,IAAIC,IAAI;IAAC;CAAU;AAE3C,6EAA6E,GAC7E,MAAMC,mBAAmB;AAEzB;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeC,mBACpBC,OAAoB,EACpBC,MAAc,EACdC,IAA6B;IAE7B,IAAIC;IACJ,IAAI;QACFA,OAAQ,MAAMH,QAAQI,QAAQ,CAAC;YAC7BC,IAAIJ;YACJK,YAAY;YACZC,OAAO;QACT;IACF,EAAE,OAAM;QACN,OAAO;YAAEC,IAAI;YAAOC,QAAQ;QAAY;IAC1C;IAEA,MAAMC,SAAS,AAACP,CAAAA,KAAKO,MAAM,IAAI,EAAE,AAAD,EAAGC,MAAM,CACvC,CAACC,IACC,OAAOA,EAAEC,IAAI,KAAK,YAAY,CAACjB,gBAAgBkB,GAAG,CAACF,EAAEG,SAAS,IAAI;IAEtE,MAAMC,QAAQ,IAAIC,IAAIP,OAAOQ,GAAG,CAAC,CAACN,IAAM;YAACA,EAAEC,IAAI;YAAED;SAAE;IAEnD,MAAMO,UAAmC,CAAC;IAE1C,KAAK,MAAMC,SAASV,OAAQ;QAC1B,MAAMW,QAAQnB,IAAI,CAACkB,MAAMP,IAAI,CAAC;QAC9B,MAAMS,UACJD,SAAS,QAAS,OAAOA,UAAU,YAAYA,MAAME,IAAI,OAAO,MAAOF,UAAU;QAEnF,IAAID,MAAMI,QAAQ,IAAIF,SAAS;YAC7B,OAAO;gBAAEF,OAAOA,MAAMP,IAAI;gBAAEY,MAAM;gBAAYjB,IAAI;gBAAOC,QAAQ;YAAU;QAC7E;QAEA,IAAI,OAAOY,UAAU,YAAYA,MAAMK,MAAM,GAAG5B,kBAAkB;YAChE,OAAO;gBAAEsB,OAAOA,MAAMP,IAAI;gBAAEY,MAAM;gBAAYjB,IAAI;gBAAOC,QAAQ;YAAU;QAC7E;QAEA,wEAAwE;QACxE,gDAAgD;QAChD,IAAIY,UAAUM,WAAW;YACvBR,OAAO,CAACC,MAAMP,IAAI,CAAC,GAAGQ;QACxB;IACF;IAEA,0EAA0E;IAC1E,yDAAyD;IACzD,MAAMO,cAAcC,OAAOC,IAAI,CAAC5B,MAAMS,MAAM,CAAC,CAACoB,IAAM,CAACf,MAAMF,GAAG,CAACiB;IAC/D,IAAIH,YAAYF,MAAM,GAAG,GAAG;QAC1B,OAAO;YAAED,MAAM;YAAkBjB,IAAI;YAAOC,QAAQ;QAAU;IAChE;IAEA,OAAO;QAAEU;QAAShB;QAAMK,IAAI;IAAK;AACnC"} \ No newline at end of file diff --git a/dist/modules/frontend/createContentHelpers.d.ts b/dist/modules/frontend/createContentHelpers.d.ts deleted file mode 100644 index 47b2150..0000000 --- a/dist/modules/frontend/createContentHelpers.d.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { BasePayload, SanitizedConfig } from 'payload'; -import type { ArchiveEntries, ContentOption, ResolvedRoute } from '../content/index.js'; -import type { I18nConfig } from '../i18n/index.js'; -import type { RobotsRules, SitemapEntry } from '../seo/index.js'; -type CreateContentHelpersArgs = { - /** - * Absolute site origin for sitemap/robots URLs. Falls back to - * NEXT_PUBLIC_SERVER_URL, then to a relative origin (which most crawlers - * reject, so set one in production). - */ - baseUrl?: string; - /** - * The client's payload config promise (the default export of payload.config). - * Passed in because the plugin never imports the client's config directly. - */ - config: Promise | SanitizedConfig; - /** Archive-backed collections, same value as the plugin option. */ - content?: ContentOption; - /** - * i18n config. Required only if you want the ready-made `sitemap` / `robots` - * handlers — they need the locale list to emit hreflang. - */ - i18n?: I18nConfig; - /** Pages collection slug. Defaults to 'pages'. */ - pagesSlug?: string; - /** SiteSettings global slug. Defaults to 'site-settings'. */ - settingsSlug?: string; -}; -/** - * Bundles the per-request data helpers a frontend needs — the same cached - * wrappers every project was writing by hand (getPayload, settings, locale - * list, route resolution, archive entries). - * - * Everything is wrapped in React `cache()`, so within one request a value is - * fetched once no matter how many times it's asked for — which matters because - * Next runs generateMetadata and the page component separately, and both hit - * these. Crucially the Payload instance is cached *here*, once, so every helper - * shares it; that's why this is a factory and not loose functions importing a - * shared module. - * - * ```ts - * // src/lib/content.ts - * import { createContentHelpers } from 'ipal-kit' - * import config from '@/payload.config' - * import { contentConfig } from '@/content.config' - * - * export const { getCachedPayload, getSettings, getConfiguredLocales, resolveRoute, getEntries } = - * createContentHelpers({ config, content: contentConfig }) - * ``` - * - * `resolveRoute` and `getEntries` are separate on purpose: routing decides what - * a URL is, fetching gets the listing. Metadata generation needs the first and - * not the second, and a page component composes them in two obvious lines. - */ -export declare function createContentHelpers({ baseUrl, config, content, i18n, pagesSlug, settingsSlug, }: CreateContentHelpersArgs): { - getCachedPayload: () => Promise; - getConfiguredLocales: () => Promise; - getEntries: (collection: string, locale: string, page: number, perPage: number) => Promise; - getSettings: (locale: string) => Promise; - resolveRoute: (locale: string, segments: string[] | undefined, page: number) => Promise; - robots: () => RobotsRules; - sitemap: () => Promise; -}; -export {}; diff --git a/dist/modules/frontend/createContentHelpers.js b/dist/modules/frontend/createContentHelpers.js deleted file mode 100644 index 1342ee6..0000000 --- a/dist/modules/frontend/createContentHelpers.js +++ /dev/null @@ -1,111 +0,0 @@ -import { getPayload } from 'payload'; -import { cache } from 'react'; -import { getArchiveEntries, resolveRoute as resolveRouteRaw } from '../content/index.js'; -import { buildRobots, buildSitemapEntries } from '../seo/index.js'; -/** - * Bundles the per-request data helpers a frontend needs — the same cached - * wrappers every project was writing by hand (getPayload, settings, locale - * list, route resolution, archive entries). - * - * Everything is wrapped in React `cache()`, so within one request a value is - * fetched once no matter how many times it's asked for — which matters because - * Next runs generateMetadata and the page component separately, and both hit - * these. Crucially the Payload instance is cached *here*, once, so every helper - * shares it; that's why this is a factory and not loose functions importing a - * shared module. - * - * ```ts - * // src/lib/content.ts - * import { createContentHelpers } from 'ipal-kit' - * import config from '@/payload.config' - * import { contentConfig } from '@/content.config' - * - * export const { getCachedPayload, getSettings, getConfiguredLocales, resolveRoute, getEntries } = - * createContentHelpers({ config, content: contentConfig }) - * ``` - * - * `resolveRoute` and `getEntries` are separate on purpose: routing decides what - * a URL is, fetching gets the listing. Metadata generation needs the first and - * not the second, and a page component composes them in two obvious lines. - */ export function createContentHelpers({ baseUrl, config, content, i18n, pagesSlug = 'pages', settingsSlug = 'site-settings' }) { - const origin = baseUrl ?? process.env.NEXT_PUBLIC_SERVER_URL ?? ''; - const getCachedPayload = cache(async ()=>getPayload({ - config: await config - })); - const getConfiguredLocales = cache(async ()=>{ - const c = await config; - return c.localization ? c.localization.locales.map((l)=>l.code) : []; - }); - const getSettings = cache(async (locale)=>{ - const payload = await getCachedPayload(); - return payload.findGlobal({ - slug: settingsSlug, - depth: 2, - locale: locale - }); - }); - /** What does this URL point at? Routing only — no listing data. */ const resolveRoute = cache(async (locale, segments, page)=>{ - const payload = await getCachedPayload(); - return resolveRouteRaw({ - content, - locale, - page, - pagesSlug, - payload, - segments, - settingsSlug - }); - }); - /** One page of a collection's entries, for an archive listing. */ const getEntries = cache(async (collection, locale, page, perPage)=>{ - const payload = await getCachedPayload(); - return getArchiveEntries({ - collection, - locale, - page, - payload, - perPage - }); - }); - /** - * Ready-made handler for Next's `app/sitemap.ts` — every page and entry with - * per-URL hreflang and lastmod. Re-export it directly: - * - * ```ts - * // app/sitemap.ts - * export { sitemap as default } from '@/lib/content' - * ``` - */ const sitemap = cache(async ()=>{ - if (!i18n) { - throw new Error('[ipal] createContentHelpers: pass `i18n` to use the sitemap handler.'); - } - return buildSitemapEntries({ - baseUrl: origin, - config: i18n, - content, - pagesSlug, - payload: await getCachedPayload(), - settingsSlug - }); - }); - /** - * Ready-made handler for Next's `app/robots.ts`. Re-export directly: - * - * ```ts - * // app/robots.ts - * export { robots as default } from '@/lib/content' - * ``` - */ const robots = ()=>buildRobots({ - baseUrl: origin - }); - return { - getCachedPayload, - getConfiguredLocales, - getEntries, - getSettings, - resolveRoute, - robots, - sitemap - }; -} - -//# sourceMappingURL=createContentHelpers.js.map \ No newline at end of file diff --git a/dist/modules/frontend/createContentHelpers.js.map b/dist/modules/frontend/createContentHelpers.js.map deleted file mode 100644 index c0b4c3d..0000000 --- a/dist/modules/frontend/createContentHelpers.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/frontend/createContentHelpers.ts"],"sourcesContent":["import type { BasePayload, SanitizedConfig } from 'payload'\n\nimport { getPayload } from 'payload'\nimport { cache } from 'react'\n\nimport type { ArchiveEntries, ContentOption, ResolvedRoute } from '../content/index.js'\nimport type { I18nConfig } from '../i18n/index.js'\nimport type { RobotsRules, SitemapEntry } from '../seo/index.js'\n\nimport { getArchiveEntries, resolveRoute as resolveRouteRaw } from '../content/index.js'\nimport { buildRobots, buildSitemapEntries } from '../seo/index.js'\n\ntype CreateContentHelpersArgs = {\n /**\n * Absolute site origin for sitemap/robots URLs. Falls back to\n * NEXT_PUBLIC_SERVER_URL, then to a relative origin (which most crawlers\n * reject, so set one in production).\n */\n baseUrl?: string\n /**\n * The client's payload config promise (the default export of payload.config).\n * Passed in because the plugin never imports the client's config directly.\n */\n config: Promise | SanitizedConfig\n /** Archive-backed collections, same value as the plugin option. */\n content?: ContentOption\n /**\n * i18n config. Required only if you want the ready-made `sitemap` / `robots`\n * handlers — they need the locale list to emit hreflang.\n */\n i18n?: I18nConfig\n /** Pages collection slug. Defaults to 'pages'. */\n pagesSlug?: string\n /** SiteSettings global slug. Defaults to 'site-settings'. */\n settingsSlug?: string\n}\n\n/**\n * Bundles the per-request data helpers a frontend needs — the same cached\n * wrappers every project was writing by hand (getPayload, settings, locale\n * list, route resolution, archive entries).\n *\n * Everything is wrapped in React `cache()`, so within one request a value is\n * fetched once no matter how many times it's asked for — which matters because\n * Next runs generateMetadata and the page component separately, and both hit\n * these. Crucially the Payload instance is cached *here*, once, so every helper\n * shares it; that's why this is a factory and not loose functions importing a\n * shared module.\n *\n * ```ts\n * // src/lib/content.ts\n * import { createContentHelpers } from 'ipal-kit'\n * import config from '@/payload.config'\n * import { contentConfig } from '@/content.config'\n *\n * export const { getCachedPayload, getSettings, getConfiguredLocales, resolveRoute, getEntries } =\n * createContentHelpers({ config, content: contentConfig })\n * ```\n *\n * `resolveRoute` and `getEntries` are separate on purpose: routing decides what\n * a URL is, fetching gets the listing. Metadata generation needs the first and\n * not the second, and a page component composes them in two obvious lines.\n */\nexport function createContentHelpers({\n baseUrl,\n config,\n content,\n i18n,\n pagesSlug = 'pages',\n settingsSlug = 'site-settings',\n}: CreateContentHelpersArgs) {\n const origin = baseUrl ?? process.env.NEXT_PUBLIC_SERVER_URL ?? ''\n const getCachedPayload = cache(async (): Promise =>\n getPayload({ config: await config }),\n )\n\n const getConfiguredLocales = cache(async (): Promise => {\n const c = await config\n return c.localization ? c.localization.locales.map((l) => l.code) : []\n })\n\n const getSettings = cache(async (locale: string) => {\n const payload = await getCachedPayload()\n return payload.findGlobal({ slug: settingsSlug as never, depth: 2, locale: locale as never })\n })\n\n /** What does this URL point at? Routing only — no listing data. */\n const resolveRoute = cache(\n async (\n locale: string,\n segments: string[] | undefined,\n page: number,\n ): Promise => {\n const payload = await getCachedPayload()\n return resolveRouteRaw({ content, locale, page, pagesSlug, payload, segments, settingsSlug })\n },\n )\n\n /** One page of a collection's entries, for an archive listing. */\n const getEntries = cache(\n async (\n collection: string,\n locale: string,\n page: number,\n perPage: number,\n ): Promise => {\n const payload = await getCachedPayload()\n return getArchiveEntries({ collection, locale, page, payload, perPage })\n },\n )\n\n /**\n * Ready-made handler for Next's `app/sitemap.ts` — every page and entry with\n * per-URL hreflang and lastmod. Re-export it directly:\n *\n * ```ts\n * // app/sitemap.ts\n * export { sitemap as default } from '@/lib/content'\n * ```\n */\n const sitemap = cache(async (): Promise => {\n if (!i18n) {\n throw new Error('[ipal] createContentHelpers: pass `i18n` to use the sitemap handler.')\n }\n return buildSitemapEntries({\n baseUrl: origin,\n config: i18n,\n content,\n pagesSlug,\n payload: await getCachedPayload(),\n settingsSlug,\n })\n })\n\n /**\n * Ready-made handler for Next's `app/robots.ts`. Re-export directly:\n *\n * ```ts\n * // app/robots.ts\n * export { robots as default } from '@/lib/content'\n * ```\n */\n const robots = (): RobotsRules => buildRobots({ baseUrl: origin })\n\n return {\n getCachedPayload,\n getConfiguredLocales,\n getEntries,\n getSettings,\n resolveRoute,\n robots,\n sitemap,\n }\n}\n"],"names":["getPayload","cache","getArchiveEntries","resolveRoute","resolveRouteRaw","buildRobots","buildSitemapEntries","createContentHelpers","baseUrl","config","content","i18n","pagesSlug","settingsSlug","origin","process","env","NEXT_PUBLIC_SERVER_URL","getCachedPayload","getConfiguredLocales","c","localization","locales","map","l","code","getSettings","locale","payload","findGlobal","slug","depth","segments","page","getEntries","collection","perPage","sitemap","Error","robots"],"mappings":"AAEA,SAASA,UAAU,QAAQ,UAAS;AACpC,SAASC,KAAK,QAAQ,QAAO;AAM7B,SAASC,iBAAiB,EAAEC,gBAAgBC,eAAe,QAAQ,sBAAqB;AACxF,SAASC,WAAW,EAAEC,mBAAmB,QAAQ,kBAAiB;AA2BlE;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,SAASC,qBAAqB,EACnCC,OAAO,EACPC,MAAM,EACNC,OAAO,EACPC,IAAI,EACJC,YAAY,OAAO,EACnBC,eAAe,eAAe,EACL;IACzB,MAAMC,SAASN,WAAWO,QAAQC,GAAG,CAACC,sBAAsB,IAAI;IAChE,MAAMC,mBAAmBjB,MAAM,UAC7BD,WAAW;YAAES,QAAQ,MAAMA;QAAO;IAGpC,MAAMU,uBAAuBlB,MAAM;QACjC,MAAMmB,IAAI,MAAMX;QAChB,OAAOW,EAAEC,YAAY,GAAGD,EAAEC,YAAY,CAACC,OAAO,CAACC,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI,IAAI,EAAE;IACxE;IAEA,MAAMC,cAAczB,MAAM,OAAO0B;QAC/B,MAAMC,UAAU,MAAMV;QACtB,OAAOU,QAAQC,UAAU,CAAC;YAAEC,MAAMjB;YAAuBkB,OAAO;YAAGJ,QAAQA;QAAgB;IAC7F;IAEA,iEAAiE,GACjE,MAAMxB,eAAeF,MACnB,OACE0B,QACAK,UACAC;QAEA,MAAML,UAAU,MAAMV;QACtB,OAAOd,gBAAgB;YAAEM;YAASiB;YAAQM;YAAMrB;YAAWgB;YAASI;YAAUnB;QAAa;IAC7F;IAGF,gEAAgE,GAChE,MAAMqB,aAAajC,MACjB,OACEkC,YACAR,QACAM,MACAG;QAEA,MAAMR,UAAU,MAAMV;QACtB,OAAOhB,kBAAkB;YAAEiC;YAAYR;YAAQM;YAAML;YAASQ;QAAQ;IACxE;IAGF;;;;;;;;GAQC,GACD,MAAMC,UAAUpC,MAAM;QACpB,IAAI,CAACU,MAAM;YACT,MAAM,IAAI2B,MAAM;QAClB;QACA,OAAOhC,oBAAoB;YACzBE,SAASM;YACTL,QAAQE;YACRD;YACAE;YACAgB,SAAS,MAAMV;YACfL;QACF;IACF;IAEA;;;;;;;GAOC,GACD,MAAM0B,SAAS,IAAmBlC,YAAY;YAAEG,SAASM;QAAO;IAEhE,OAAO;QACLI;QACAC;QACAe;QACAR;QACAvB;QACAoC;QACAF;IACF;AACF"} \ No newline at end of file diff --git a/dist/modules/frontend/index.d.ts b/dist/modules/frontend/index.d.ts deleted file mode 100644 index 5c23c74..0000000 --- a/dist/modules/frontend/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export { createContentHelpers } from './createContentHelpers.js'; diff --git a/dist/modules/frontend/index.js b/dist/modules/frontend/index.js deleted file mode 100644 index 889f6c0..0000000 --- a/dist/modules/frontend/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { createContentHelpers } from './createContentHelpers.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/frontend/index.js.map b/dist/modules/frontend/index.js.map deleted file mode 100644 index 1f67e27..0000000 --- a/dist/modules/frontend/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/frontend/index.ts"],"sourcesContent":["export { createContentHelpers } from './createContentHelpers.js'\n"],"names":["createContentHelpers"],"mappings":"AAAA,SAASA,oBAAoB,QAAQ,4BAA2B"} \ No newline at end of file diff --git a/dist/modules/i18n/getLocalizedSlugs.d.ts b/dist/modules/i18n/getLocalizedSlugs.d.ts deleted file mode 100644 index 9347dc9..0000000 --- a/dist/modules/i18n/getLocalizedSlugs.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { LocalizedSlugs } from './localizedPath.js'; -import type { I18nConfig } from './types.js'; -/** - * A localized field as Payload returns it when queried with `locale: 'all'`: - * a map of locale code → value. - */ -type LocalizedField = Record; -type GetLocalizedSlugsArgs = { - config: I18nConfig; - /** - * The document's localized slug field, as returned by Payload with - * `locale: 'all'` — e.g. { pl: 'strona-glowna', en: 'home' }. - */ - slugField: LocalizedField | null | undefined; -}; -/** - * Normalizes a document's localized slug field into the LocalizedSlugs - * contract consumed by buildLocalizedPath / switchLocalePath. - * - * Pure function — the template fetches the document (payload.findByID with - * `locale: 'all'`, where the slug field must be `localized: true`) and passes - * the raw field in. The plugin never touches the database. - * - * Keeps only entries whose locale is configured and whose slug is a - * non-empty string, so callers get a clean, trustworthy map. - * - * @example - * const doc = await payload.findByID({ collection: 'pages', id, locale: 'all' }) - * const slugs = getLocalizedSlugs({ slugField: doc.slug, config }) - * // → { pl: 'strona-glowna', en: 'home' } - * switchLocalePath({ slugs, targetLocale: 'en', config }) // → '/en' - */ -export declare function getLocalizedSlugs({ config, slugField }: GetLocalizedSlugsArgs): LocalizedSlugs; -export {}; diff --git a/dist/modules/i18n/getLocalizedSlugs.js b/dist/modules/i18n/getLocalizedSlugs.js deleted file mode 100644 index c7a27f7..0000000 --- a/dist/modules/i18n/getLocalizedSlugs.js +++ /dev/null @@ -1,39 +0,0 @@ -import { isValidLocale } from './helpers.js'; -/** - * Normalizes a document's localized slug field into the LocalizedSlugs - * contract consumed by buildLocalizedPath / switchLocalePath. - * - * Pure function — the template fetches the document (payload.findByID with - * `locale: 'all'`, where the slug field must be `localized: true`) and passes - * the raw field in. The plugin never touches the database. - * - * Keeps only entries whose locale is configured and whose slug is a - * non-empty string, so callers get a clean, trustworthy map. - * - * @example - * const doc = await payload.findByID({ collection: 'pages', id, locale: 'all' }) - * const slugs = getLocalizedSlugs({ slugField: doc.slug, config }) - * // → { pl: 'strona-glowna', en: 'home' } - * switchLocalePath({ slugs, targetLocale: 'en', config }) // → '/en' - */ export function getLocalizedSlugs({ config, slugField }) { - const result = {}; - if (!slugField || typeof slugField !== 'object') { - return result; - } - for (const [locale, value] of Object.entries(slugField)){ - if (!isValidLocale(locale, config)) { - continue; - } - if (typeof value !== 'string') { - continue; - } - const slug = value.trim(); - if (!slug) { - continue; - } - result[locale] = slug; - } - return result; -} - -//# sourceMappingURL=getLocalizedSlugs.js.map \ No newline at end of file diff --git a/dist/modules/i18n/getLocalizedSlugs.js.map b/dist/modules/i18n/getLocalizedSlugs.js.map deleted file mode 100644 index 8885d1b..0000000 --- a/dist/modules/i18n/getLocalizedSlugs.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/i18n/getLocalizedSlugs.ts"],"sourcesContent":["import type { LocalizedSlugs } from './localizedPath.js'\nimport type { I18nConfig } from './types.js'\n\nimport { isValidLocale } from './helpers.js'\n\n/**\n * A localized field as Payload returns it when queried with `locale: 'all'`:\n * a map of locale code → value.\n */\ntype LocalizedField = Record\n\ntype GetLocalizedSlugsArgs = {\n config: I18nConfig\n /**\n * The document's localized slug field, as returned by Payload with\n * `locale: 'all'` — e.g. { pl: 'strona-glowna', en: 'home' }.\n */\n slugField: LocalizedField | null | undefined\n}\n\n/**\n * Normalizes a document's localized slug field into the LocalizedSlugs\n * contract consumed by buildLocalizedPath / switchLocalePath.\n *\n * Pure function — the template fetches the document (payload.findByID with\n * `locale: 'all'`, where the slug field must be `localized: true`) and passes\n * the raw field in. The plugin never touches the database.\n *\n * Keeps only entries whose locale is configured and whose slug is a\n * non-empty string, so callers get a clean, trustworthy map.\n *\n * @example\n * const doc = await payload.findByID({ collection: 'pages', id, locale: 'all' })\n * const slugs = getLocalizedSlugs({ slugField: doc.slug, config })\n * // → { pl: 'strona-glowna', en: 'home' }\n * switchLocalePath({ slugs, targetLocale: 'en', config }) // → '/en'\n */\nexport function getLocalizedSlugs({ config, slugField }: GetLocalizedSlugsArgs): LocalizedSlugs {\n const result: LocalizedSlugs = {}\n\n if (!slugField || typeof slugField !== 'object') {\n return result\n }\n\n for (const [locale, value] of Object.entries(slugField)) {\n if (!isValidLocale(locale, config)) {continue}\n if (typeof value !== 'string') {continue}\n\n const slug = value.trim()\n if (!slug) {continue}\n\n result[locale] = slug\n }\n\n return result\n}\n"],"names":["isValidLocale","getLocalizedSlugs","config","slugField","result","locale","value","Object","entries","slug","trim"],"mappings":"AAGA,SAASA,aAAa,QAAQ,eAAc;AAiB5C;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,SAASC,kBAAkB,EAAEC,MAAM,EAAEC,SAAS,EAAyB;IAC5E,MAAMC,SAAyB,CAAC;IAEhC,IAAI,CAACD,aAAa,OAAOA,cAAc,UAAU;QAC/C,OAAOC;IACT;IAEA,KAAK,MAAM,CAACC,QAAQC,MAAM,IAAIC,OAAOC,OAAO,CAACL,WAAY;QACvD,IAAI,CAACH,cAAcK,QAAQH,SAAS;YAAC;QAAQ;QAC7C,IAAI,OAAOI,UAAU,UAAU;YAAC;QAAQ;QAExC,MAAMG,OAAOH,MAAMI,IAAI;QACvB,IAAI,CAACD,MAAM;YAAC;QAAQ;QAEpBL,MAAM,CAACC,OAAO,GAAGI;IACnB;IAEA,OAAOL;AACT"} \ No newline at end of file diff --git a/dist/modules/i18n/helpers.d.ts b/dist/modules/i18n/helpers.d.ts deleted file mode 100644 index 11e70af..0000000 --- a/dist/modules/i18n/helpers.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { I18nConfig, LocaleDefinition } from './types.js'; -/** - * Returns all configured locale codes. - */ -export declare function getLocaleCodes(config: I18nConfig): string[]; -/** - * Returns the default locale code. - */ -export declare function getDefaultLocale(config: I18nConfig): string; -/** - * Checks if a string is a valid configured locale code. - */ -export declare function isValidLocale(code: string, config: I18nConfig): boolean; -/** - * Returns the full locale definition for a given code, or undefined if not found. - */ -export declare function getLocaleDefinition(code: string, config: I18nConfig): LocaleDefinition | undefined; diff --git a/dist/modules/i18n/helpers.js b/dist/modules/i18n/helpers.js deleted file mode 100644 index deec843..0000000 --- a/dist/modules/i18n/helpers.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Returns all configured locale codes. - */ export function getLocaleCodes(config) { - return config.locales.map((locale)=>locale.code); -} -/** - * Returns the default locale code. - */ export function getDefaultLocale(config) { - return config.defaultLocale; -} -/** - * Checks if a string is a valid configured locale code. - */ export function isValidLocale(code, config) { - return config.locales.some((locale)=>locale.code === code); -} -/** - * Returns the full locale definition for a given code, or undefined if not found. - */ export function getLocaleDefinition(code, config) { - return config.locales.find((locale)=>locale.code === code); -} - -//# sourceMappingURL=helpers.js.map \ No newline at end of file diff --git a/dist/modules/i18n/helpers.js.map b/dist/modules/i18n/helpers.js.map deleted file mode 100644 index 8db416a..0000000 --- a/dist/modules/i18n/helpers.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/i18n/helpers.ts"],"sourcesContent":["import type { I18nConfig, LocaleDefinition } from './types.js'\n\n/**\n * Returns all configured locale codes.\n */\nexport function getLocaleCodes(config: I18nConfig): string[] {\n return config.locales.map((locale) => locale.code)\n}\n\n/**\n * Returns the default locale code.\n */\nexport function getDefaultLocale(config: I18nConfig): string {\n return config.defaultLocale\n}\n\n/**\n * Checks if a string is a valid configured locale code.\n */\nexport function isValidLocale(code: string, config: I18nConfig): boolean {\n return config.locales.some((locale) => locale.code === code)\n}\n\n/**\n * Returns the full locale definition for a given code, or undefined if not found.\n */\nexport function getLocaleDefinition(\n code: string,\n config: I18nConfig,\n): LocaleDefinition | undefined {\n return config.locales.find((locale) => locale.code === code)\n}\n"],"names":["getLocaleCodes","config","locales","map","locale","code","getDefaultLocale","defaultLocale","isValidLocale","some","getLocaleDefinition","find"],"mappings":"AAEA;;CAEC,GACD,OAAO,SAASA,eAAeC,MAAkB;IAC/C,OAAOA,OAAOC,OAAO,CAACC,GAAG,CAAC,CAACC,SAAWA,OAAOC,IAAI;AACnD;AAEA;;CAEC,GACD,OAAO,SAASC,iBAAiBL,MAAkB;IACjD,OAAOA,OAAOM,aAAa;AAC7B;AAEA;;CAEC,GACD,OAAO,SAASC,cAAcH,IAAY,EAAEJ,MAAkB;IAC5D,OAAOA,OAAOC,OAAO,CAACO,IAAI,CAAC,CAACL,SAAWA,OAAOC,IAAI,KAAKA;AACzD;AAEA;;CAEC,GACD,OAAO,SAASK,oBACdL,IAAY,EACZJ,MAAkB;IAElB,OAAOA,OAAOC,OAAO,CAACS,IAAI,CAAC,CAACP,SAAWA,OAAOC,IAAI,KAAKA;AACzD"} \ No newline at end of file diff --git a/dist/modules/i18n/index.d.ts b/dist/modules/i18n/index.d.ts deleted file mode 100644 index 2db9940..0000000 --- a/dist/modules/i18n/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { getLocalizedSlugs } from './getLocalizedSlugs.js'; -export { getDefaultLocale, getLocaleCodes, getLocaleDefinition, isValidLocale } from './helpers.js'; -export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from './localeMiddleware.js'; -export type { LocaleMiddlewareResult } from './localeMiddleware.js'; -export { buildLocalizationConfig } from './localizationConfig.js'; -export { buildLocalizedPath, switchLocalePath } from './localizedPath.js'; -export type { LocalizedSlugs } from './localizedPath.js'; -export { LOCALE_COOKIE_NAME, matchAcceptLanguage, negotiateLocale } from './negotiateLocale.js'; -export type { I18nConfig, LocaleDefinition } from './types.js'; -export { validateI18nConfig } from './validation.js'; diff --git a/dist/modules/i18n/index.js b/dist/modules/i18n/index.js deleted file mode 100644 index 6dbd28a..0000000 --- a/dist/modules/i18n/index.js +++ /dev/null @@ -1,9 +0,0 @@ -export { getLocalizedSlugs } from './getLocalizedSlugs.js'; -export { getDefaultLocale, getLocaleCodes, getLocaleDefinition, isValidLocale } from './helpers.js'; -export { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from './localeMiddleware.js'; -export { buildLocalizationConfig } from './localizationConfig.js'; -export { buildLocalizedPath, switchLocalePath } from './localizedPath.js'; -export { LOCALE_COOKIE_NAME, matchAcceptLanguage, negotiateLocale } from './negotiateLocale.js'; -export { validateI18nConfig } from './validation.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/i18n/index.js.map b/dist/modules/i18n/index.js.map deleted file mode 100644 index 9708da6..0000000 --- a/dist/modules/i18n/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/i18n/index.ts"],"sourcesContent":["export { getLocalizedSlugs } from './getLocalizedSlugs.js'\nexport { getDefaultLocale, getLocaleCodes, getLocaleDefinition, isValidLocale } from './helpers.js'\nexport { createLocaleMiddleware, DEFAULT_MIDDLEWARE_MATCHER } from './localeMiddleware.js'\nexport type { LocaleMiddlewareResult } from './localeMiddleware.js'\nexport { buildLocalizationConfig } from './localizationConfig.js'\nexport { buildLocalizedPath, switchLocalePath } from './localizedPath.js'\nexport type { LocalizedSlugs } from './localizedPath.js'\nexport { LOCALE_COOKIE_NAME, matchAcceptLanguage, negotiateLocale } from './negotiateLocale.js'\nexport type { I18nConfig, LocaleDefinition } from './types.js'\nexport { validateI18nConfig } from './validation.js'\n"],"names":["getLocalizedSlugs","getDefaultLocale","getLocaleCodes","getLocaleDefinition","isValidLocale","createLocaleMiddleware","DEFAULT_MIDDLEWARE_MATCHER","buildLocalizationConfig","buildLocalizedPath","switchLocalePath","LOCALE_COOKIE_NAME","matchAcceptLanguage","negotiateLocale","validateI18nConfig"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,yBAAwB;AAC1D,SAASC,gBAAgB,EAAEC,cAAc,EAAEC,mBAAmB,EAAEC,aAAa,QAAQ,eAAc;AACnG,SAASC,sBAAsB,EAAEC,0BAA0B,QAAQ,wBAAuB;AAE1F,SAASC,uBAAuB,QAAQ,0BAAyB;AACjE,SAASC,kBAAkB,EAAEC,gBAAgB,QAAQ,qBAAoB;AAEzE,SAASC,kBAAkB,EAAEC,mBAAmB,EAAEC,eAAe,QAAQ,uBAAsB;AAE/F,SAASC,kBAAkB,QAAQ,kBAAiB"} \ No newline at end of file diff --git a/dist/modules/i18n/localeMiddleware.d.ts b/dist/modules/i18n/localeMiddleware.d.ts deleted file mode 100644 index cfc518f..0000000 --- a/dist/modules/i18n/localeMiddleware.d.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { I18nConfig } from './types.js'; -/** - * Minimal request shape the middleware reads. Kept structural so the plugin - * doesn't hard-depend on next/server types; a Next.js `NextRequest` satisfies it. - */ -type MiddlewareRequest = { - cookies: { - get: (name: string) => { - value: string; - } | undefined; - }; - headers: { - get: (name: string) => null | string; - }; - nextUrl: { - clone: () => URL; - pathname: string; - search: string; - }; - url: string; -}; -/** - * What the factory returns — the caller (in Next next-middleware.ts) decides how to - * act: `redirect` means send a 307 to `location` and set the locale cookie; - * `next` means let the request pass through untouched. - */ -export type LocaleMiddlewareResult = { - cookie: { - name: string; - value: string; - }; - location: string; - type: 'redirect'; -} | { - type: 'next'; -}; -type CreateLocaleMiddlewareArgs = { - config: I18nConfig; - /** Cookie name for the locale choice. Defaults to LOCALE_COOKIE_NAME. */ - cookieName?: string; -}; -/** - * Builds locale-routing logic for Next.js middleware. - * - * Behavior: - * - path already starts with a valid locale (/pl/...) → pass through - * - any other path (/, /o-nas) → redirect to /{locale}{path}, where locale - * comes from negotiateLocale (cookie → Accept-Language → default) - * - the chosen locale is written to a cookie so the next visit is stable - * - * The plugin returns a decision; the thin next-middleware.ts in the client project - * turns it into a NextResponse. This keeps all logic in the plugin while - * respecting that next-middleware.ts must physically live in the client app. - * - * @example - * // next-middleware.ts (client project) — one wiring file, no logic: - * import { NextResponse } from 'next/server' - * import { localeMiddleware } from './ipal.middleware' // created from this factory - * export function middleware(req) { - * const r = localeMiddleware(req) - * if (r.type === 'next') return NextResponse.next() - * const res = NextResponse.redirect(r.location) - * res.cookies.set(r.cookie.name, r.cookie.value) - * return res - * } - */ -export declare function createLocaleMiddleware({ config, cookieName, }: CreateLocaleMiddlewareArgs): (request: MiddlewareRequest) => LocaleMiddlewareResult; -/** - * Default Next.js middleware matcher: run on everything except API routes, the - * admin panel, Next internals, and files with an extension (static assets). - */ -export declare const DEFAULT_MIDDLEWARE_MATCHER: string[]; -export {}; diff --git a/dist/modules/i18n/localeMiddleware.js b/dist/modules/i18n/localeMiddleware.js deleted file mode 100644 index e251ad4..0000000 --- a/dist/modules/i18n/localeMiddleware.js +++ /dev/null @@ -1,67 +0,0 @@ -import { isValidLocale, LOCALE_COOKIE_NAME, negotiateLocale } from '../i18n/index.js'; -/** - * First path segment of a URL pathname, or '' for root. - * '/pl/o-nas' → 'pl', '/o-nas' → 'o-nas', '/' → ''. - */ function firstSegment(pathname) { - return pathname.split('/').filter(Boolean)[0] ?? ''; -} -/** - * Builds locale-routing logic for Next.js middleware. - * - * Behavior: - * - path already starts with a valid locale (/pl/...) → pass through - * - any other path (/, /o-nas) → redirect to /{locale}{path}, where locale - * comes from negotiateLocale (cookie → Accept-Language → default) - * - the chosen locale is written to a cookie so the next visit is stable - * - * The plugin returns a decision; the thin next-middleware.ts in the client project - * turns it into a NextResponse. This keeps all logic in the plugin while - * respecting that next-middleware.ts must physically live in the client app. - * - * @example - * // next-middleware.ts (client project) — one wiring file, no logic: - * import { NextResponse } from 'next/server' - * import { localeMiddleware } from './ipal.middleware' // created from this factory - * export function middleware(req) { - * const r = localeMiddleware(req) - * if (r.type === 'next') return NextResponse.next() - * const res = NextResponse.redirect(r.location) - * res.cookies.set(r.cookie.name, r.cookie.value) - * return res - * } - */ export function createLocaleMiddleware({ config, cookieName = LOCALE_COOKIE_NAME }) { - return function localeMiddleware(request) { - const { pathname } = request.nextUrl; - // Already locale-prefixed → nothing to do - if (isValidLocale(firstSegment(pathname), config)) { - return { - type: 'next' - }; - } - // Resolve the locale to use - const locale = negotiateLocale({ - acceptLanguage: request.headers.get('accept-language'), - config, - cookieLocale: request.cookies.get(cookieName)?.value ?? null - }); - // Redirect to the locale-prefixed path, preserving the rest - const url = request.nextUrl.clone(); - url.pathname = `/${locale}${pathname === '/' ? '' : pathname}`; - return { - type: 'redirect', - cookie: { - name: cookieName, - value: locale - }, - location: url.toString() - }; - }; -} -/** - * Default Next.js middleware matcher: run on everything except API routes, the - * admin panel, Next internals, and files with an extension (static assets). - */ export const DEFAULT_MIDDLEWARE_MATCHER = [ - '/((?!api|admin|_next|.*\\..*).*)' -]; - -//# sourceMappingURL=localeMiddleware.js.map \ No newline at end of file diff --git a/dist/modules/i18n/localeMiddleware.js.map b/dist/modules/i18n/localeMiddleware.js.map deleted file mode 100644 index de77963..0000000 --- a/dist/modules/i18n/localeMiddleware.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/i18n/localeMiddleware.ts"],"sourcesContent":["import type { I18nConfig } from './types.js'\n\nimport { isValidLocale, LOCALE_COOKIE_NAME, negotiateLocale } from '../i18n/index.js'\n\n/**\n * Minimal request shape the middleware reads. Kept structural so the plugin\n * doesn't hard-depend on next/server types; a Next.js `NextRequest` satisfies it.\n */\ntype MiddlewareRequest = {\n cookies: { get: (name: string) => { value: string } | undefined }\n headers: { get: (name: string) => null | string }\n nextUrl: { clone: () => URL; pathname: string; search: string }\n url: string\n}\n\n/**\n * What the factory returns — the caller (in Next next-middleware.ts) decides how to\n * act: `redirect` means send a 307 to `location` and set the locale cookie;\n * `next` means let the request pass through untouched.\n */\nexport type LocaleMiddlewareResult =\n { cookie: { name: string; value: string }; location: string; type: 'redirect' } | { type: 'next' }\n\ntype CreateLocaleMiddlewareArgs = {\n config: I18nConfig\n /** Cookie name for the locale choice. Defaults to LOCALE_COOKIE_NAME. */\n cookieName?: string\n}\n\n/**\n * First path segment of a URL pathname, or '' for root.\n * '/pl/o-nas' → 'pl', '/o-nas' → 'o-nas', '/' → ''.\n */\nfunction firstSegment(pathname: string): string {\n return pathname.split('/').filter(Boolean)[0] ?? ''\n}\n\n/**\n * Builds locale-routing logic for Next.js middleware.\n *\n * Behavior:\n * - path already starts with a valid locale (/pl/...) → pass through\n * - any other path (/, /o-nas) → redirect to /{locale}{path}, where locale\n * comes from negotiateLocale (cookie → Accept-Language → default)\n * - the chosen locale is written to a cookie so the next visit is stable\n *\n * The plugin returns a decision; the thin next-middleware.ts in the client project\n * turns it into a NextResponse. This keeps all logic in the plugin while\n * respecting that next-middleware.ts must physically live in the client app.\n *\n * @example\n * // next-middleware.ts (client project) — one wiring file, no logic:\n * import { NextResponse } from 'next/server'\n * import { localeMiddleware } from './ipal.middleware' // created from this factory\n * export function middleware(req) {\n * const r = localeMiddleware(req)\n * if (r.type === 'next') return NextResponse.next()\n * const res = NextResponse.redirect(r.location)\n * res.cookies.set(r.cookie.name, r.cookie.value)\n * return res\n * }\n */\nexport function createLocaleMiddleware({\n config,\n cookieName = LOCALE_COOKIE_NAME,\n}: CreateLocaleMiddlewareArgs) {\n return function localeMiddleware(request: MiddlewareRequest): LocaleMiddlewareResult {\n const { pathname } = request.nextUrl\n\n // Already locale-prefixed → nothing to do\n if (isValidLocale(firstSegment(pathname), config)) {\n return { type: 'next' }\n }\n\n // Resolve the locale to use\n const locale = negotiateLocale({\n acceptLanguage: request.headers.get('accept-language'),\n config,\n cookieLocale: request.cookies.get(cookieName)?.value ?? null,\n })\n\n // Redirect to the locale-prefixed path, preserving the rest\n const url = request.nextUrl.clone()\n url.pathname = `/${locale}${pathname === '/' ? '' : pathname}`\n\n return {\n type: 'redirect',\n cookie: { name: cookieName, value: locale },\n location: url.toString(),\n }\n }\n}\n\n/**\n * Default Next.js middleware matcher: run on everything except API routes, the\n * admin panel, Next internals, and files with an extension (static assets).\n */\nexport const DEFAULT_MIDDLEWARE_MATCHER = ['/((?!api|admin|_next|.*\\\\..*).*)']\n"],"names":["isValidLocale","LOCALE_COOKIE_NAME","negotiateLocale","firstSegment","pathname","split","filter","Boolean","createLocaleMiddleware","config","cookieName","localeMiddleware","request","nextUrl","type","locale","acceptLanguage","headers","get","cookieLocale","cookies","value","url","clone","cookie","name","location","toString","DEFAULT_MIDDLEWARE_MATCHER"],"mappings":"AAEA,SAASA,aAAa,EAAEC,kBAAkB,EAAEC,eAAe,QAAQ,mBAAkB;AA2BrF;;;CAGC,GACD,SAASC,aAAaC,QAAgB;IACpC,OAAOA,SAASC,KAAK,CAAC,KAAKC,MAAM,CAACC,QAAQ,CAAC,EAAE,IAAI;AACnD;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC,uBAAuB,EACrCC,MAAM,EACNC,aAAaT,kBAAkB,EACJ;IAC3B,OAAO,SAASU,iBAAiBC,OAA0B;QACzD,MAAM,EAAER,QAAQ,EAAE,GAAGQ,QAAQC,OAAO;QAEpC,0CAA0C;QAC1C,IAAIb,cAAcG,aAAaC,WAAWK,SAAS;YACjD,OAAO;gBAAEK,MAAM;YAAO;QACxB;QAEA,4BAA4B;QAC5B,MAAMC,SAASb,gBAAgB;YAC7Bc,gBAAgBJ,QAAQK,OAAO,CAACC,GAAG,CAAC;YACpCT;YACAU,cAAcP,QAAQQ,OAAO,CAACF,GAAG,CAACR,aAAaW,SAAS;QAC1D;QAEA,4DAA4D;QAC5D,MAAMC,MAAMV,QAAQC,OAAO,CAACU,KAAK;QACjCD,IAAIlB,QAAQ,GAAG,CAAC,CAAC,EAAEW,SAASX,aAAa,MAAM,KAAKA,UAAU;QAE9D,OAAO;YACLU,MAAM;YACNU,QAAQ;gBAAEC,MAAMf;gBAAYW,OAAON;YAAO;YAC1CW,UAAUJ,IAAIK,QAAQ;QACxB;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,MAAMC,6BAA6B;IAAC;CAAmC,CAAA"} \ No newline at end of file diff --git a/dist/modules/i18n/localizationConfig.d.ts b/dist/modules/i18n/localizationConfig.d.ts deleted file mode 100644 index 910a7fd..0000000 --- a/dist/modules/i18n/localizationConfig.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Config } from 'payload'; -import type { I18nConfig } from './types.js'; -type PayloadLocalizationConfig = NonNullable; -/** - * Transforms validated i18n config into Payload's localization object. - * Pure function — no side effects, no validation (caller validates first). - */ -export declare function buildLocalizationConfig(config: I18nConfig): PayloadLocalizationConfig; -export {}; diff --git a/dist/modules/i18n/localizationConfig.js b/dist/modules/i18n/localizationConfig.js deleted file mode 100644 index 8471755..0000000 --- a/dist/modules/i18n/localizationConfig.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Transforms validated i18n config into Payload's localization object. - * Pure function — no side effects, no validation (caller validates first). - */ export function buildLocalizationConfig(config) { - const { defaultLocale, fallback = true, locales } = config; - return { - defaultLocale, - fallback, - locales: locales.map((locale)=>({ - code: locale.code, - label: locale.label, - ...locale.rtl && { - rtl: true - } - })) - }; -} - -//# sourceMappingURL=localizationConfig.js.map \ No newline at end of file diff --git a/dist/modules/i18n/localizationConfig.js.map b/dist/modules/i18n/localizationConfig.js.map deleted file mode 100644 index 0beb8b9..0000000 --- a/dist/modules/i18n/localizationConfig.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/i18n/localizationConfig.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport type { I18nConfig } from './types.js'\n\ntype PayloadLocalizationConfig = NonNullable\n\n/**\n * Transforms validated i18n config into Payload's localization object.\n * Pure function — no side effects, no validation (caller validates first).\n */\nexport function buildLocalizationConfig(config: I18nConfig): PayloadLocalizationConfig {\n const { defaultLocale, fallback = true, locales } = config\n\n return {\n defaultLocale,\n fallback,\n locales: locales.map((locale) => ({\n code: locale.code,\n label: locale.label,\n ...(locale.rtl && { rtl: true }),\n })),\n }\n}\n"],"names":["buildLocalizationConfig","config","defaultLocale","fallback","locales","map","locale","code","label","rtl"],"mappings":"AAMA;;;CAGC,GACD,OAAO,SAASA,wBAAwBC,MAAkB;IACxD,MAAM,EAAEC,aAAa,EAAEC,WAAW,IAAI,EAAEC,OAAO,EAAE,GAAGH;IAEpD,OAAO;QACLC;QACAC;QACAC,SAASA,QAAQC,GAAG,CAAC,CAACC,SAAY,CAAA;gBAChCC,MAAMD,OAAOC,IAAI;gBACjBC,OAAOF,OAAOE,KAAK;gBACnB,GAAIF,OAAOG,GAAG,IAAI;oBAAEA,KAAK;gBAAK,CAAC;YACjC,CAAA;IACF;AACF"} \ No newline at end of file diff --git a/dist/modules/i18n/localizedPath.d.ts b/dist/modules/i18n/localizedPath.d.ts deleted file mode 100644 index 112706a..0000000 --- a/dist/modules/i18n/localizedPath.d.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { I18nConfig } from './types.js'; -/** - * Minimal shape the path builder needs from a document. - * - * The plugin doesn't know the client's Pages type, so it depends only on - * this contract: a map of locale code → slug for that locale. The template - * supplies it (e.g. by reading the localized slug field across locales). - */ -export type LocalizedSlugs = Record; -type BuildPathArgs = { - config: I18nConfig; - /** - * Slug that represents the site root (served at /{locale} with no trailing - * segment). Defaults to 'home'. Matched against the slug in the target locale. - */ - homeSlug?: string; - /** Target locale to build the path for */ - locale: string; - /** - * Localized segment the document lives under, e.g. - * `{ pl: 'artykuly', en: 'articles' }` → /pl/artykuly/moj-post. - * - * These are the slugs of the collection's archive page, so the prefix is - * whatever an editor named that page — and it differs per locale for free. - * A document under a prefix is never the home page, so homeSlug is ignored. - */ - prefix?: LocalizedSlugs; - /** slug per locale, e.g. { pl: 'strona-glowna', en: 'home' } */ - slugs: LocalizedSlugs; -}; -/** - * Builds a locale-prefixed path for a document in a target locale. - * - * Always prefixes the locale: /{locale} or /{locale}/{slug}. The home slug - * collapses to the locale root. Returns undefined if the document has no slug - * in the target locale (caller decides fallback behavior). - * - * @example - * buildLocalizedPath({ slugs: { pl: 'strona-glowna', en: 'home' }, locale: 'en', config }) - * // → '/en' (home slug collapses to root) - * - * buildLocalizedPath({ slugs: { pl: 'o-nas', en: 'about' }, locale: 'en', config }) - * // → '/en/about' - * - * buildLocalizedPath({ - * slugs: { pl: 'moj-post', en: 'my-post' }, - * prefix: { pl: 'artykuly', en: 'articles' }, - * locale: 'en', - * config, - * }) - * // → '/en/articles/my-post' - */ -export declare function buildLocalizedPath({ config, homeSlug, locale, prefix, slugs, }: BuildPathArgs): string | undefined; -type SwitchLocaleArgs = { - config: I18nConfig; - homeSlug?: string; - prefix?: LocalizedSlugs; - slugs: LocalizedSlugs; - targetLocale: string; -}; -/** - * Resolves the equivalent path for the same document in a different locale — - * the language-switcher use case (/pl/strona-glowna → /en/home). - * - * Never dead-ends on a 404. When the document has no slug in the target locale, - * falls back to the archive it belongs to (/en/articles) if there is one, and - * to the locale root otherwise — the closest place the visitor would want. - */ -export declare function switchLocalePath({ config, homeSlug, prefix, slugs, targetLocale, }: SwitchLocaleArgs): string; -export {}; diff --git a/dist/modules/i18n/localizedPath.js b/dist/modules/i18n/localizedPath.js deleted file mode 100644 index 40a37be..0000000 --- a/dist/modules/i18n/localizedPath.js +++ /dev/null @@ -1,71 +0,0 @@ -import { isValidLocale } from './helpers.js'; -/** - * Builds a locale-prefixed path for a document in a target locale. - * - * Always prefixes the locale: /{locale} or /{locale}/{slug}. The home slug - * collapses to the locale root. Returns undefined if the document has no slug - * in the target locale (caller decides fallback behavior). - * - * @example - * buildLocalizedPath({ slugs: { pl: 'strona-glowna', en: 'home' }, locale: 'en', config }) - * // → '/en' (home slug collapses to root) - * - * buildLocalizedPath({ slugs: { pl: 'o-nas', en: 'about' }, locale: 'en', config }) - * // → '/en/about' - * - * buildLocalizedPath({ - * slugs: { pl: 'moj-post', en: 'my-post' }, - * prefix: { pl: 'artykuly', en: 'articles' }, - * locale: 'en', - * config, - * }) - * // → '/en/articles/my-post' - */ export function buildLocalizedPath({ config, homeSlug = 'home', locale, prefix, slugs }) { - if (!isValidLocale(locale, config)) { - return undefined; - } - const slug = slugs[locale]; - if (!slug) { - return undefined; - } - if (prefix) { - const segment = prefix[locale]; - // No archive slug in this locale means the entry is unreachable there — - // there's no path to point at, so hreflang should omit it rather than - // invent /en/moj-post. - if (!segment) { - return undefined; - } - return `/${locale}/${segment}/${slug}`; - } - if (slug === homeSlug) { - return `/${locale}`; - } - return `/${locale}/${slug}`; -} -/** - * Resolves the equivalent path for the same document in a different locale — - * the language-switcher use case (/pl/strona-glowna → /en/home). - * - * Never dead-ends on a 404. When the document has no slug in the target locale, - * falls back to the archive it belongs to (/en/articles) if there is one, and - * to the locale root otherwise — the closest place the visitor would want. - */ export function switchLocalePath({ config, homeSlug = 'home', prefix, slugs, targetLocale }) { - const path = buildLocalizedPath({ - config, - homeSlug, - locale: targetLocale, - prefix, - slugs - }); - if (path) { - return path; - } - const archiveSegment = prefix?.[targetLocale]; - if (archiveSegment) { - return `/${targetLocale}/${archiveSegment}`; - } - return `/${targetLocale}`; -} - -//# sourceMappingURL=localizedPath.js.map \ No newline at end of file diff --git a/dist/modules/i18n/localizedPath.js.map b/dist/modules/i18n/localizedPath.js.map deleted file mode 100644 index 1f73694..0000000 --- a/dist/modules/i18n/localizedPath.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/i18n/localizedPath.ts"],"sourcesContent":["import type { I18nConfig } from './types.js'\n\nimport { isValidLocale } from './helpers.js'\n\n/**\n * Minimal shape the path builder needs from a document.\n *\n * The plugin doesn't know the client's Pages type, so it depends only on\n * this contract: a map of locale code → slug for that locale. The template\n * supplies it (e.g. by reading the localized slug field across locales).\n */\nexport type LocalizedSlugs = Record\n\ntype BuildPathArgs = {\n config: I18nConfig\n /**\n * Slug that represents the site root (served at /{locale} with no trailing\n * segment). Defaults to 'home'. Matched against the slug in the target locale.\n */\n homeSlug?: string\n /** Target locale to build the path for */\n locale: string\n /**\n * Localized segment the document lives under, e.g.\n * `{ pl: 'artykuly', en: 'articles' }` → /pl/artykuly/moj-post.\n *\n * These are the slugs of the collection's archive page, so the prefix is\n * whatever an editor named that page — and it differs per locale for free.\n * A document under a prefix is never the home page, so homeSlug is ignored.\n */\n prefix?: LocalizedSlugs\n /** slug per locale, e.g. { pl: 'strona-glowna', en: 'home' } */\n slugs: LocalizedSlugs\n}\n\n/**\n * Builds a locale-prefixed path for a document in a target locale.\n *\n * Always prefixes the locale: /{locale} or /{locale}/{slug}. The home slug\n * collapses to the locale root. Returns undefined if the document has no slug\n * in the target locale (caller decides fallback behavior).\n *\n * @example\n * buildLocalizedPath({ slugs: { pl: 'strona-glowna', en: 'home' }, locale: 'en', config })\n * // → '/en' (home slug collapses to root)\n *\n * buildLocalizedPath({ slugs: { pl: 'o-nas', en: 'about' }, locale: 'en', config })\n * // → '/en/about'\n *\n * buildLocalizedPath({\n * slugs: { pl: 'moj-post', en: 'my-post' },\n * prefix: { pl: 'artykuly', en: 'articles' },\n * locale: 'en',\n * config,\n * })\n * // → '/en/articles/my-post'\n */\nexport function buildLocalizedPath({\n config,\n homeSlug = 'home',\n locale,\n prefix,\n slugs,\n}: BuildPathArgs): string | undefined {\n if (!isValidLocale(locale, config)) {\n return undefined\n }\n\n const slug = slugs[locale]\n if (!slug) {\n return undefined\n }\n\n if (prefix) {\n const segment = prefix[locale]\n // No archive slug in this locale means the entry is unreachable there —\n // there's no path to point at, so hreflang should omit it rather than\n // invent /en/moj-post.\n if (!segment) {\n return undefined\n }\n return `/${locale}/${segment}/${slug}`\n }\n\n if (slug === homeSlug) {\n return `/${locale}`\n }\n\n return `/${locale}/${slug}`\n}\n\ntype SwitchLocaleArgs = {\n config: I18nConfig\n homeSlug?: string\n prefix?: LocalizedSlugs\n slugs: LocalizedSlugs\n targetLocale: string\n}\n\n/**\n * Resolves the equivalent path for the same document in a different locale —\n * the language-switcher use case (/pl/strona-glowna → /en/home).\n *\n * Never dead-ends on a 404. When the document has no slug in the target locale,\n * falls back to the archive it belongs to (/en/articles) if there is one, and\n * to the locale root otherwise — the closest place the visitor would want.\n */\nexport function switchLocalePath({\n config,\n homeSlug = 'home',\n prefix,\n slugs,\n targetLocale,\n}: SwitchLocaleArgs): string {\n const path = buildLocalizedPath({ config, homeSlug, locale: targetLocale, prefix, slugs })\n if (path) {return path}\n\n const archiveSegment = prefix?.[targetLocale]\n if (archiveSegment) {return `/${targetLocale}/${archiveSegment}`}\n\n return `/${targetLocale}`\n}\n"],"names":["isValidLocale","buildLocalizedPath","config","homeSlug","locale","prefix","slugs","undefined","slug","segment","switchLocalePath","targetLocale","path","archiveSegment"],"mappings":"AAEA,SAASA,aAAa,QAAQ,eAAc;AAiC5C;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,SAASC,mBAAmB,EACjCC,MAAM,EACNC,WAAW,MAAM,EACjBC,MAAM,EACNC,MAAM,EACNC,KAAK,EACS;IACd,IAAI,CAACN,cAAcI,QAAQF,SAAS;QAClC,OAAOK;IACT;IAEA,MAAMC,OAAOF,KAAK,CAACF,OAAO;IAC1B,IAAI,CAACI,MAAM;QACT,OAAOD;IACT;IAEA,IAAIF,QAAQ;QACV,MAAMI,UAAUJ,MAAM,CAACD,OAAO;QAC9B,wEAAwE;QACxE,sEAAsE;QACtE,uBAAuB;QACvB,IAAI,CAACK,SAAS;YACZ,OAAOF;QACT;QACA,OAAO,CAAC,CAAC,EAAEH,OAAO,CAAC,EAAEK,QAAQ,CAAC,EAAED,MAAM;IACxC;IAEA,IAAIA,SAASL,UAAU;QACrB,OAAO,CAAC,CAAC,EAAEC,QAAQ;IACrB;IAEA,OAAO,CAAC,CAAC,EAAEA,OAAO,CAAC,EAAEI,MAAM;AAC7B;AAUA;;;;;;;CAOC,GACD,OAAO,SAASE,iBAAiB,EAC/BR,MAAM,EACNC,WAAW,MAAM,EACjBE,MAAM,EACNC,KAAK,EACLK,YAAY,EACK;IACjB,MAAMC,OAAOX,mBAAmB;QAAEC;QAAQC;QAAUC,QAAQO;QAAcN;QAAQC;IAAM;IACxF,IAAIM,MAAM;QAAC,OAAOA;IAAI;IAEtB,MAAMC,iBAAiBR,QAAQ,CAACM,aAAa;IAC7C,IAAIE,gBAAgB;QAAC,OAAO,CAAC,CAAC,EAAEF,aAAa,CAAC,EAAEE,gBAAgB;IAAA;IAEhE,OAAO,CAAC,CAAC,EAAEF,cAAc;AAC3B"} \ No newline at end of file diff --git a/dist/modules/i18n/negotiateLocale.d.ts b/dist/modules/i18n/negotiateLocale.d.ts deleted file mode 100644 index 9527eb3..0000000 --- a/dist/modules/i18n/negotiateLocale.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { I18nConfig } from './types.js'; -/** Cookie name the template uses to persist a visitor's locale choice. */ -export declare const LOCALE_COOKIE_NAME = "ipal-locale"; -type NegotiateLocaleArgs = { - /** Raw Accept-Language header value */ - acceptLanguage?: null | string; - config: I18nConfig; - /** Value of the locale cookie, if present (from LOCALE_COOKIE_NAME) */ - cookieLocale?: null | string; -}; -/** - * Resolves which locale to serve, in priority order: - * 1. Cookie (explicit prior choice) - * 2. Accept-Language header (best match against configured locales) - * 3. Configured default locale - * - * Pure function — the template feeds it request data and acts on the result - * (redirect, cookie set). No Next.js or request objects here. - */ -export declare function negotiateLocale({ acceptLanguage, config, cookieLocale, }: NegotiateLocaleArgs): string; -/** - * Parses an Accept-Language header and returns the best-matching configured - * locale, or undefined if none match. - * - * Matches on the primary subtag (e.g. "en-US" matches configured "en"), - * respecting the header's quality-value ordering. - */ -export declare function matchAcceptLanguage(acceptLanguage: null | string | undefined, config: I18nConfig): string | undefined; -export {}; diff --git a/dist/modules/i18n/negotiateLocale.js b/dist/modules/i18n/negotiateLocale.js deleted file mode 100644 index 7996d0e..0000000 --- a/dist/modules/i18n/negotiateLocale.js +++ /dev/null @@ -1,68 +0,0 @@ -import { getLocaleCodes, isValidLocale } from './helpers.js'; -/** Cookie name the template uses to persist a visitor's locale choice. */ export const LOCALE_COOKIE_NAME = 'ipal-locale'; -/** - * Resolves which locale to serve, in priority order: - * 1. Cookie (explicit prior choice) - * 2. Accept-Language header (best match against configured locales) - * 3. Configured default locale - * - * Pure function — the template feeds it request data and acts on the result - * (redirect, cookie set). No Next.js or request objects here. - */ export function negotiateLocale({ acceptLanguage, config, cookieLocale }) { - // 1. Explicit prior choice wins - if (cookieLocale && isValidLocale(cookieLocale, config)) { - return cookieLocale; - } - // 2. Best match from Accept-Language - const fromHeader = matchAcceptLanguage(acceptLanguage, config); - if (fromHeader) { - return fromHeader; - } - // 3. Fall back to configured default - return config.defaultLocale; -} -/** - * Parses an Accept-Language header and returns the best-matching configured - * locale, or undefined if none match. - * - * Matches on the primary subtag (e.g. "en-US" matches configured "en"), - * respecting the header's quality-value ordering. - */ export function matchAcceptLanguage(acceptLanguage, config) { - if (!acceptLanguage) { - return undefined; - } - const available = getLocaleCodes(config); - const ranked = parseAcceptLanguage(acceptLanguage); - for (const tag of ranked){ - // Exact match (e.g. "pt-BR" === "pt-BR") - const exact = available.find((code)=>code.toLowerCase() === tag); - if (exact) { - return exact; - } - // Primary-subtag match (e.g. "en-us" → "en") - const primary = tag.split('-')[0]; - const partial = available.find((code)=>code.toLowerCase().split('-')[0] === primary); - if (partial) { - return partial; - } - } - return undefined; -} -/** - * Parses an Accept-Language header into locale tags ordered by descending - * quality value. Tags are lowercased for comparison. - * - * "en-US,en;q=0.9,pl;q=0.8" → ["en-us", "en", "pl"] - */ function parseAcceptLanguage(header) { - return header.split(',').map((part)=>{ - const [tag, ...params] = part.trim().split(';'); - const qParam = params.find((p)=>p.trim().startsWith('q=')); - const quality = qParam ? parseFloat(qParam.split('=')[1]) : 1; - return { - quality: Number.isNaN(quality) ? 0 : quality, - tag: tag.trim().toLowerCase() - }; - }).filter((entry)=>entry.tag && entry.tag !== '*').sort((a, b)=>b.quality - a.quality).map((entry)=>entry.tag); -} - -//# sourceMappingURL=negotiateLocale.js.map \ No newline at end of file diff --git a/dist/modules/i18n/negotiateLocale.js.map b/dist/modules/i18n/negotiateLocale.js.map deleted file mode 100644 index a5dd6ac..0000000 --- a/dist/modules/i18n/negotiateLocale.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/i18n/negotiateLocale.ts"],"sourcesContent":["import type { I18nConfig } from './types.js'\n\nimport { getLocaleCodes, isValidLocale } from './helpers.js'\n\n/** Cookie name the template uses to persist a visitor's locale choice. */\nexport const LOCALE_COOKIE_NAME = 'ipal-locale'\n\ntype NegotiateLocaleArgs = {\n /** Raw Accept-Language header value */\n acceptLanguage?: null | string\n config: I18nConfig\n /** Value of the locale cookie, if present (from LOCALE_COOKIE_NAME) */\n cookieLocale?: null | string\n}\n\n/**\n * Resolves which locale to serve, in priority order:\n * 1. Cookie (explicit prior choice)\n * 2. Accept-Language header (best match against configured locales)\n * 3. Configured default locale\n *\n * Pure function — the template feeds it request data and acts on the result\n * (redirect, cookie set). No Next.js or request objects here.\n */\nexport function negotiateLocale({\n acceptLanguage,\n config,\n cookieLocale,\n}: NegotiateLocaleArgs): string {\n // 1. Explicit prior choice wins\n if (cookieLocale && isValidLocale(cookieLocale, config)) {\n return cookieLocale\n }\n\n // 2. Best match from Accept-Language\n const fromHeader = matchAcceptLanguage(acceptLanguage, config)\n if (fromHeader) {\n return fromHeader\n }\n\n // 3. Fall back to configured default\n return config.defaultLocale\n}\n\n/**\n * Parses an Accept-Language header and returns the best-matching configured\n * locale, or undefined if none match.\n *\n * Matches on the primary subtag (e.g. \"en-US\" matches configured \"en\"),\n * respecting the header's quality-value ordering.\n */\nexport function matchAcceptLanguage(\n acceptLanguage: null | string | undefined,\n config: I18nConfig,\n): string | undefined {\n if (!acceptLanguage) {return undefined}\n\n const available = getLocaleCodes(config)\n const ranked = parseAcceptLanguage(acceptLanguage)\n\n for (const tag of ranked) {\n // Exact match (e.g. \"pt-BR\" === \"pt-BR\")\n const exact = available.find((code) => code.toLowerCase() === tag)\n if (exact) {return exact}\n\n // Primary-subtag match (e.g. \"en-us\" → \"en\")\n const primary = tag.split('-')[0]\n const partial = available.find((code) => code.toLowerCase().split('-')[0] === primary)\n if (partial) {return partial}\n }\n\n return undefined\n}\n\n/**\n * Parses an Accept-Language header into locale tags ordered by descending\n * quality value. Tags are lowercased for comparison.\n *\n * \"en-US,en;q=0.9,pl;q=0.8\" → [\"en-us\", \"en\", \"pl\"]\n */\nfunction parseAcceptLanguage(header: string): string[] {\n return header\n .split(',')\n .map((part) => {\n const [tag, ...params] = part.trim().split(';')\n const qParam = params.find((p) => p.trim().startsWith('q='))\n const quality = qParam ? parseFloat(qParam.split('=')[1]) : 1\n return { quality: Number.isNaN(quality) ? 0 : quality, tag: tag.trim().toLowerCase() }\n })\n .filter((entry) => entry.tag && entry.tag !== '*')\n .sort((a, b) => b.quality - a.quality)\n .map((entry) => entry.tag)\n}\n"],"names":["getLocaleCodes","isValidLocale","LOCALE_COOKIE_NAME","negotiateLocale","acceptLanguage","config","cookieLocale","fromHeader","matchAcceptLanguage","defaultLocale","undefined","available","ranked","parseAcceptLanguage","tag","exact","find","code","toLowerCase","primary","split","partial","header","map","part","params","trim","qParam","p","startsWith","quality","parseFloat","Number","isNaN","filter","entry","sort","a","b"],"mappings":"AAEA,SAASA,cAAc,EAAEC,aAAa,QAAQ,eAAc;AAE5D,wEAAwE,GACxE,OAAO,MAAMC,qBAAqB,cAAa;AAU/C;;;;;;;;CAQC,GACD,OAAO,SAASC,gBAAgB,EAC9BC,cAAc,EACdC,MAAM,EACNC,YAAY,EACQ;IACpB,gCAAgC;IAChC,IAAIA,gBAAgBL,cAAcK,cAAcD,SAAS;QACvD,OAAOC;IACT;IAEA,qCAAqC;IACrC,MAAMC,aAAaC,oBAAoBJ,gBAAgBC;IACvD,IAAIE,YAAY;QACd,OAAOA;IACT;IAEA,qCAAqC;IACrC,OAAOF,OAAOI,aAAa;AAC7B;AAEA;;;;;;CAMC,GACD,OAAO,SAASD,oBACdJ,cAAyC,EACzCC,MAAkB;IAElB,IAAI,CAACD,gBAAgB;QAAC,OAAOM;IAAS;IAEtC,MAAMC,YAAYX,eAAeK;IACjC,MAAMO,SAASC,oBAAoBT;IAEnC,KAAK,MAAMU,OAAOF,OAAQ;QACxB,yCAAyC;QACzC,MAAMG,QAAQJ,UAAUK,IAAI,CAAC,CAACC,OAASA,KAAKC,WAAW,OAAOJ;QAC9D,IAAIC,OAAO;YAAC,OAAOA;QAAK;QAExB,6CAA6C;QAC7C,MAAMI,UAAUL,IAAIM,KAAK,CAAC,IAAI,CAAC,EAAE;QACjC,MAAMC,UAAUV,UAAUK,IAAI,CAAC,CAACC,OAASA,KAAKC,WAAW,GAAGE,KAAK,CAAC,IAAI,CAAC,EAAE,KAAKD;QAC9E,IAAIE,SAAS;YAAC,OAAOA;QAAO;IAC9B;IAEA,OAAOX;AACT;AAEA;;;;;CAKC,GACD,SAASG,oBAAoBS,MAAc;IACzC,OAAOA,OACJF,KAAK,CAAC,KACNG,GAAG,CAAC,CAACC;QACJ,MAAM,CAACV,KAAK,GAAGW,OAAO,GAAGD,KAAKE,IAAI,GAAGN,KAAK,CAAC;QAC3C,MAAMO,SAASF,OAAOT,IAAI,CAAC,CAACY,IAAMA,EAAEF,IAAI,GAAGG,UAAU,CAAC;QACtD,MAAMC,UAAUH,SAASI,WAAWJ,OAAOP,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI;QAC5D,OAAO;YAAEU,SAASE,OAAOC,KAAK,CAACH,WAAW,IAAIA;YAAShB,KAAKA,IAAIY,IAAI,GAAGR,WAAW;QAAG;IACvF,GACCgB,MAAM,CAAC,CAACC,QAAUA,MAAMrB,GAAG,IAAIqB,MAAMrB,GAAG,KAAK,KAC7CsB,IAAI,CAAC,CAACC,GAAGC,IAAMA,EAAER,OAAO,GAAGO,EAAEP,OAAO,EACpCP,GAAG,CAAC,CAACY,QAAUA,MAAMrB,GAAG;AAC7B"} \ No newline at end of file diff --git a/dist/modules/i18n/types.d.ts b/dist/modules/i18n/types.d.ts deleted file mode 100644 index 764df84..0000000 --- a/dist/modules/i18n/types.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Single locale definition provided by the client project. - */ -export type LocaleDefinition = { - /** BCP-47 language code, e.g. 'pl', 'en', 'de' */ - code: string; - /** Human-readable label, e.g. 'Polski', 'English' */ - label: string; - /** Right-to-left script (defaults to false) */ - rtl?: boolean; -}; -/** - * Full i18n configuration passed through plugin options. - */ -export type I18nConfig = { - defaultLocale: string; - /** Enable locale fallback when content is missing (defaults to true) */ - fallback?: boolean; - locales: readonly [LocaleDefinition, ...LocaleDefinition[]]; -}; diff --git a/dist/modules/i18n/types.js b/dist/modules/i18n/types.js deleted file mode 100644 index 550d3a4..0000000 --- a/dist/modules/i18n/types.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Single locale definition provided by the client project. - */ /** - * Full i18n configuration passed through plugin options. - */ export { }; - -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/modules/i18n/types.js.map b/dist/modules/i18n/types.js.map deleted file mode 100644 index 5aaa89d..0000000 --- a/dist/modules/i18n/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/i18n/types.ts"],"sourcesContent":["/**\n * Single locale definition provided by the client project.\n */\nexport type LocaleDefinition = {\n /** BCP-47 language code, e.g. 'pl', 'en', 'de' */\n code: string\n /** Human-readable label, e.g. 'Polski', 'English' */\n label: string\n /** Right-to-left script (defaults to false) */\n rtl?: boolean\n}\n\n/**\n * Full i18n configuration passed through plugin options.\n */\nexport type I18nConfig = {\n defaultLocale: string\n /** Enable locale fallback when content is missing (defaults to true) */\n fallback?: boolean\n locales: readonly [LocaleDefinition, ...LocaleDefinition[]]\n}\n"],"names":[],"mappings":"AAAA;;CAEC,GAUD;;CAEC,GACD,WAKC"} \ No newline at end of file diff --git a/dist/modules/i18n/validation.d.ts b/dist/modules/i18n/validation.d.ts deleted file mode 100644 index 90734bc..0000000 --- a/dist/modules/i18n/validation.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { I18nConfig } from './types.js'; -/** - * Validates i18n config at plugin initialization. - * Throws descriptive errors — fail fast, no silent defaults. - */ -export declare function validateI18nConfig(config: I18nConfig): void; diff --git a/dist/modules/i18n/validation.js b/dist/modules/i18n/validation.js deleted file mode 100644 index 0f82961..0000000 --- a/dist/modules/i18n/validation.js +++ /dev/null @@ -1,30 +0,0 @@ -const LOCALE_CODE_PATTERN = /^[a-z]{2,3}(-[A-Z]{2})?$/; -/** - * Validates i18n config at plugin initialization. - * Throws descriptive errors — fail fast, no silent defaults. - */ export function validateI18nConfig(config) { - const { defaultLocale, locales } = config; - if (!locales?.length) { - throw new Error('[ipal] i18n: "locales" must contain at least one locale.'); - } - const codes = new Set(); - for (const locale of locales){ - if (!locale.code || !locale.label) { - throw new Error(`[ipal] i18n: Every locale must have "code" and "label". Received: ${JSON.stringify(locale)}`); - } - if (!LOCALE_CODE_PATTERN.test(locale.code)) { - throw new Error(`[ipal] i18n: Invalid locale code "${locale.code}". Expected format: "pl", "en", "pt-BR".`); - } - if (codes.has(locale.code)) { - throw new Error(`[ipal] i18n: Duplicate locale code "${locale.code}".`); - } - codes.add(locale.code); - } - if (!codes.has(defaultLocale)) { - throw new Error(`[ipal] i18n: defaultLocale "${defaultLocale}" not found in locales [${[ - ...codes - ].join(', ')}].`); - } -} - -//# sourceMappingURL=validation.js.map \ No newline at end of file diff --git a/dist/modules/i18n/validation.js.map b/dist/modules/i18n/validation.js.map deleted file mode 100644 index e231400..0000000 --- a/dist/modules/i18n/validation.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/i18n/validation.ts"],"sourcesContent":["import type { I18nConfig } from './types.js'\n\nconst LOCALE_CODE_PATTERN = /^[a-z]{2,3}(-[A-Z]{2})?$/\n\n/**\n * Validates i18n config at plugin initialization.\n * Throws descriptive errors — fail fast, no silent defaults.\n */\nexport function validateI18nConfig(config: I18nConfig): void {\n const { defaultLocale, locales } = config\n\n if (!locales?.length) {\n throw new Error('[ipal] i18n: \"locales\" must contain at least one locale.')\n }\n\n const codes = new Set()\n\n for (const locale of locales) {\n if (!locale.code || !locale.label) {\n throw new Error(\n `[ipal] i18n: Every locale must have \"code\" and \"label\". Received: ${JSON.stringify(locale)}`,\n )\n }\n\n if (!LOCALE_CODE_PATTERN.test(locale.code)) {\n throw new Error(\n `[ipal] i18n: Invalid locale code \"${locale.code}\". Expected format: \"pl\", \"en\", \"pt-BR\".`,\n )\n }\n\n if (codes.has(locale.code)) {\n throw new Error(`[ipal] i18n: Duplicate locale code \"${locale.code}\".`)\n }\n\n codes.add(locale.code)\n }\n\n if (!codes.has(defaultLocale)) {\n throw new Error(\n `[ipal] i18n: defaultLocale \"${defaultLocale}\" not found in locales [${[...codes].join(', ')}].`,\n )\n }\n}\n"],"names":["LOCALE_CODE_PATTERN","validateI18nConfig","config","defaultLocale","locales","length","Error","codes","Set","locale","code","label","JSON","stringify","test","has","add","join"],"mappings":"AAEA,MAAMA,sBAAsB;AAE5B;;;CAGC,GACD,OAAO,SAASC,mBAAmBC,MAAkB;IACnD,MAAM,EAAEC,aAAa,EAAEC,OAAO,EAAE,GAAGF;IAEnC,IAAI,CAACE,SAASC,QAAQ;QACpB,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAMC,QAAQ,IAAIC;IAElB,KAAK,MAAMC,UAAUL,QAAS;QAC5B,IAAI,CAACK,OAAOC,IAAI,IAAI,CAACD,OAAOE,KAAK,EAAE;YACjC,MAAM,IAAIL,MACR,CAAC,kEAAkE,EAAEM,KAAKC,SAAS,CAACJ,SAAS;QAEjG;QAEA,IAAI,CAACT,oBAAoBc,IAAI,CAACL,OAAOC,IAAI,GAAG;YAC1C,MAAM,IAAIJ,MACR,CAAC,kCAAkC,EAAEG,OAAOC,IAAI,CAAC,wCAAwC,CAAC;QAE9F;QAEA,IAAIH,MAAMQ,GAAG,CAACN,OAAOC,IAAI,GAAG;YAC1B,MAAM,IAAIJ,MAAM,CAAC,oCAAoC,EAAEG,OAAOC,IAAI,CAAC,EAAE,CAAC;QACxE;QAEAH,MAAMS,GAAG,CAACP,OAAOC,IAAI;IACvB;IAEA,IAAI,CAACH,MAAMQ,GAAG,CAACZ,gBAAgB;QAC7B,MAAM,IAAIG,MACR,CAAC,4BAA4B,EAAEH,cAAc,wBAAwB,EAAE;eAAII;SAAM,CAACU,IAAI,CAAC,MAAM,EAAE,CAAC;IAEpG;AACF"} \ No newline at end of file diff --git a/dist/modules/pages/getSystemPagePath.d.ts b/dist/modules/pages/getSystemPagePath.d.ts deleted file mode 100644 index 45532ec..0000000 --- a/dist/modules/pages/getSystemPagePath.d.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { I18nConfig } from '../i18n/index.js'; -/** - * The shape we need from an assigned system-page document: its localized slug - * field, as returned by Payload when the parent is read with `locale: 'all'`. - * - * The plugin doesn't know the client's Pages type, so it depends only on this - * minimal contract. - */ -type AssignedPage = { - slug?: null | Record; -}; -type GetSystemPagePathArgs = { - config: I18nConfig; - /** - * Slug that represents the site root. Defaults to 'home'. When the assigned - * page's slug in the target locale equals this, the path collapses to the - * locale root (/pl, /en). - */ - homeSlug?: string; - /** Target locale to build the path for. */ - locale: string; - /** - * The resolved system-page assignment from SiteSettings — the related - * document object (not just an ID), read with `locale: 'all'` so its slug - * is a locale→value map. Pass null/undefined if the role is unassigned. - */ - page: AssignedPage | null | undefined; -}; -/** - * Resolves a system-page assignment to a locale-aware path. - * - * Bridges the Pages module (which document plays a role) and the i18n module - * (how that document's localized slug becomes a URL). This is what powers - * "visit /pl → serve the homepage": read SiteSettings.homepage, pass the - * related document here, get /pl/strona-glowna (or /pl if it's the home slug). - * - * Returns undefined when the role is unassigned or the assigned page has no - * slug in the target locale — the caller decides the fallback (e.g. 404, - * redirect to default locale). - * - * @example - * const settings = await payload.findGlobal({ slug: 'site-settings', locale: 'all', depth: 1 }) - * getSystemPagePath({ page: settings.homepage, locale: 'pl', config }) - * // → '/pl' (home slug collapses to root) - * getSystemPagePath({ page: settings.privacyPolicy, locale: 'en', config }) - * // → '/en/privacy-policy' - */ -export declare function getSystemPagePath({ config, homeSlug, locale, page, }: GetSystemPagePathArgs): string | undefined; -export {}; diff --git a/dist/modules/pages/getSystemPagePath.js b/dist/modules/pages/getSystemPagePath.js deleted file mode 100644 index 2e69747..0000000 --- a/dist/modules/pages/getSystemPagePath.js +++ /dev/null @@ -1,36 +0,0 @@ -import { buildLocalizedPath, getLocalizedSlugs } from '../i18n/index.js'; -/** - * Resolves a system-page assignment to a locale-aware path. - * - * Bridges the Pages module (which document plays a role) and the i18n module - * (how that document's localized slug becomes a URL). This is what powers - * "visit /pl → serve the homepage": read SiteSettings.homepage, pass the - * related document here, get /pl/strona-glowna (or /pl if it's the home slug). - * - * Returns undefined when the role is unassigned or the assigned page has no - * slug in the target locale — the caller decides the fallback (e.g. 404, - * redirect to default locale). - * - * @example - * const settings = await payload.findGlobal({ slug: 'site-settings', locale: 'all', depth: 1 }) - * getSystemPagePath({ page: settings.homepage, locale: 'pl', config }) - * // → '/pl' (home slug collapses to root) - * getSystemPagePath({ page: settings.privacyPolicy, locale: 'en', config }) - * // → '/en/privacy-policy' - */ export function getSystemPagePath({ config, homeSlug = 'home', locale, page }) { - if (!page?.slug) { - return undefined; - } - const slugs = getLocalizedSlugs({ - config, - slugField: page.slug - }); - return buildLocalizedPath({ - config, - homeSlug, - locale, - slugs - }); -} - -//# sourceMappingURL=getSystemPagePath.js.map \ No newline at end of file diff --git a/dist/modules/pages/getSystemPagePath.js.map b/dist/modules/pages/getSystemPagePath.js.map deleted file mode 100644 index 5ca5cca..0000000 --- a/dist/modules/pages/getSystemPagePath.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/pages/getSystemPagePath.ts"],"sourcesContent":["import type { I18nConfig } from '../i18n/index.js'\nimport type { SystemPageRole } from './types.js'\n\nimport { buildLocalizedPath, getLocalizedSlugs } from '../i18n/index.js'\n\n/**\n * The shape we need from an assigned system-page document: its localized slug\n * field, as returned by Payload when the parent is read with `locale: 'all'`.\n *\n * The plugin doesn't know the client's Pages type, so it depends only on this\n * minimal contract.\n */\ntype AssignedPage = {\n slug?: null | Record\n}\n\ntype GetSystemPagePathArgs = {\n config: I18nConfig\n /**\n * Slug that represents the site root. Defaults to 'home'. When the assigned\n * page's slug in the target locale equals this, the path collapses to the\n * locale root (/pl, /en).\n */\n homeSlug?: string\n /** Target locale to build the path for. */\n locale: string\n /**\n * The resolved system-page assignment from SiteSettings — the related\n * document object (not just an ID), read with `locale: 'all'` so its slug\n * is a locale→value map. Pass null/undefined if the role is unassigned.\n */\n page: AssignedPage | null | undefined\n}\n\n/**\n * Resolves a system-page assignment to a locale-aware path.\n *\n * Bridges the Pages module (which document plays a role) and the i18n module\n * (how that document's localized slug becomes a URL). This is what powers\n * \"visit /pl → serve the homepage\": read SiteSettings.homepage, pass the\n * related document here, get /pl/strona-glowna (or /pl if it's the home slug).\n *\n * Returns undefined when the role is unassigned or the assigned page has no\n * slug in the target locale — the caller decides the fallback (e.g. 404,\n * redirect to default locale).\n *\n * @example\n * const settings = await payload.findGlobal({ slug: 'site-settings', locale: 'all', depth: 1 })\n * getSystemPagePath({ page: settings.homepage, locale: 'pl', config })\n * // → '/pl' (home slug collapses to root)\n * getSystemPagePath({ page: settings.privacyPolicy, locale: 'en', config })\n * // → '/en/privacy-policy'\n */\nexport function getSystemPagePath({\n config,\n homeSlug = 'home',\n locale,\n page,\n}: GetSystemPagePathArgs): string | undefined {\n if (!page?.slug) {\n return undefined\n }\n\n const slugs = getLocalizedSlugs({ config, slugField: page.slug })\n return buildLocalizedPath({ config, homeSlug, locale, slugs })\n}\n"],"names":["buildLocalizedPath","getLocalizedSlugs","getSystemPagePath","config","homeSlug","locale","page","slug","undefined","slugs","slugField"],"mappings":"AAGA,SAASA,kBAAkB,EAAEC,iBAAiB,QAAQ,mBAAkB;AA+BxE;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,kBAAkB,EAChCC,MAAM,EACNC,WAAW,MAAM,EACjBC,MAAM,EACNC,IAAI,EACkB;IACtB,IAAI,CAACA,MAAMC,MAAM;QACf,OAAOC;IACT;IAEA,MAAMC,QAAQR,kBAAkB;QAAEE;QAAQO,WAAWJ,KAAKC,IAAI;IAAC;IAC/D,OAAOP,mBAAmB;QAAEG;QAAQC;QAAUC;QAAQI;IAAM;AAC9D"} \ No newline at end of file diff --git a/dist/modules/pages/index.d.ts b/dist/modules/pages/index.d.ts deleted file mode 100644 index 8f64cb1..0000000 --- a/dist/modules/pages/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { getSystemPagePath } from './getSystemPagePath.js'; -export { buildSystemPagesFields } from './systemPagesFields.js'; -export type { PagesOption, SystemPageRole } from './types.js'; -export { ALL_SYSTEM_PAGE_ROLES } from './types.js'; diff --git a/dist/modules/pages/index.js b/dist/modules/pages/index.js deleted file mode 100644 index 1a4d929..0000000 --- a/dist/modules/pages/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export { getSystemPagePath } from './getSystemPagePath.js'; -export { buildSystemPagesFields } from './systemPagesFields.js'; -export { ALL_SYSTEM_PAGE_ROLES } from './types.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/pages/index.js.map b/dist/modules/pages/index.js.map deleted file mode 100644 index 14e6426..0000000 --- a/dist/modules/pages/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/pages/index.ts"],"sourcesContent":["export { getSystemPagePath } from './getSystemPagePath.js'\nexport { buildSystemPagesFields } from './systemPagesFields.js'\nexport type { PagesOption, SystemPageRole } from './types.js'\nexport { ALL_SYSTEM_PAGE_ROLES } from './types.js'\n"],"names":["getSystemPagePath","buildSystemPagesFields","ALL_SYSTEM_PAGE_ROLES"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,yBAAwB;AAC1D,SAASC,sBAAsB,QAAQ,yBAAwB;AAE/D,SAASC,qBAAqB,QAAQ,aAAY"} \ No newline at end of file diff --git a/dist/modules/pages/systemPagesFields.d.ts b/dist/modules/pages/systemPagesFields.d.ts deleted file mode 100644 index 818f9ef..0000000 --- a/dist/modules/pages/systemPagesFields.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Field } from 'payload'; -import type { PagesOption } from './types.js'; -/** - * Builds one relationship field per configured system-page role. - * - * Each field points at the client's Pages collection (via the provided slug) - * and holds a single document reference. The referenced document's slug is - * localized, so the same assignment resolves to /pl/strona-glowna and - * /en/home at runtime — role assignment is locale-agnostic, path resolution - * is locale-aware (see getSystemPagePath). - */ -export declare function buildSystemPagesFields(pages: PagesOption): Field[]; diff --git a/dist/modules/pages/systemPagesFields.js b/dist/modules/pages/systemPagesFields.js deleted file mode 100644 index 8ce13d4..0000000 --- a/dist/modules/pages/systemPagesFields.js +++ /dev/null @@ -1,36 +0,0 @@ -import { ALL_SYSTEM_PAGE_ROLES } from './types.js'; -/** Admin-facing labels per role. */ const ROLE_LABELS = { - cookiePolicy: 'Cookie Policy', - homepage: 'Homepage', - privacyPolicy: 'Privacy Policy' -}; -/** Admin descriptions per role. */ const ROLE_DESCRIPTIONS = { - cookiePolicy: 'Page linked from the cookie consent banner.', - homepage: 'Page served at the locale root (e.g. /pl, /en).', - privacyPolicy: 'Page linked as the privacy policy.' -}; -/** - * Builds one relationship field per configured system-page role. - * - * Each field points at the client's Pages collection (via the provided slug) - * and holds a single document reference. The referenced document's slug is - * localized, so the same assignment resolves to /pl/strona-glowna and - * /en/home at runtime — role assignment is locale-agnostic, path resolution - * is locale-aware (see getSystemPagePath). - */ export function buildSystemPagesFields(pages) { - const roles = pages.roles?.length ? pages.roles : [ - ...ALL_SYSTEM_PAGE_ROLES - ]; - return roles.map((role)=>({ - name: role, - type: 'relationship', - admin: { - description: ROLE_DESCRIPTIONS[role] - }, - label: ROLE_LABELS[role], - maxDepth: 1, - relationTo: pages.slug - })); -} - -//# sourceMappingURL=systemPagesFields.js.map \ No newline at end of file diff --git a/dist/modules/pages/systemPagesFields.js.map b/dist/modules/pages/systemPagesFields.js.map deleted file mode 100644 index c8cfe91..0000000 --- a/dist/modules/pages/systemPagesFields.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/pages/systemPagesFields.ts"],"sourcesContent":["import type { Field, RelationshipField } from 'payload'\n\nimport type { PagesOption, SystemPageRole } from './types.js'\n\nimport { ALL_SYSTEM_PAGE_ROLES } from './types.js'\n\n/** Admin-facing labels per role. */\nconst ROLE_LABELS: Record = {\n cookiePolicy: 'Cookie Policy',\n homepage: 'Homepage',\n privacyPolicy: 'Privacy Policy',\n}\n\n/** Admin descriptions per role. */\nconst ROLE_DESCRIPTIONS: Record = {\n cookiePolicy: 'Page linked from the cookie consent banner.',\n homepage: 'Page served at the locale root (e.g. /pl, /en).',\n privacyPolicy: 'Page linked as the privacy policy.',\n}\n\n/**\n * Builds one relationship field per configured system-page role.\n *\n * Each field points at the client's Pages collection (via the provided slug)\n * and holds a single document reference. The referenced document's slug is\n * localized, so the same assignment resolves to /pl/strona-glowna and\n * /en/home at runtime — role assignment is locale-agnostic, path resolution\n * is locale-aware (see getSystemPagePath).\n */\nexport function buildSystemPagesFields(pages: PagesOption): Field[] {\n const roles = pages.roles?.length ? pages.roles : [...ALL_SYSTEM_PAGE_ROLES]\n\n return roles.map((role): RelationshipField => ({\n name: role,\n type: 'relationship',\n admin: {\n description: ROLE_DESCRIPTIONS[role],\n },\n label: ROLE_LABELS[role],\n maxDepth: 1,\n relationTo: pages.slug,\n }))\n}\n"],"names":["ALL_SYSTEM_PAGE_ROLES","ROLE_LABELS","cookiePolicy","homepage","privacyPolicy","ROLE_DESCRIPTIONS","buildSystemPagesFields","pages","roles","length","map","role","name","type","admin","description","label","maxDepth","relationTo","slug"],"mappings":"AAIA,SAASA,qBAAqB,QAAQ,aAAY;AAElD,kCAAkC,GAClC,MAAMC,cAA8C;IAClDC,cAAc;IACdC,UAAU;IACVC,eAAe;AACjB;AAEA,iCAAiC,GACjC,MAAMC,oBAAoD;IACxDH,cAAc;IACdC,UAAU;IACVC,eAAe;AACjB;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASE,uBAAuBC,KAAkB;IACvD,MAAMC,QAAQD,MAAMC,KAAK,EAAEC,SAASF,MAAMC,KAAK,GAAG;WAAIR;KAAsB;IAE5E,OAAOQ,MAAME,GAAG,CAAC,CAACC,OAA6B,CAAA;YAC7CC,MAAMD;YACNE,MAAM;YACNC,OAAO;gBACLC,aAAaV,iBAAiB,CAACM,KAAK;YACtC;YACAK,OAAOf,WAAW,CAACU,KAAK;YACxBM,UAAU;YACVC,YAAYX,MAAMY,IAAI;QACxB,CAAA;AACF"} \ No newline at end of file diff --git a/dist/modules/pages/types.d.ts b/dist/modules/pages/types.d.ts deleted file mode 100644 index 9b62a00..0000000 --- a/dist/modules/pages/types.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Built-in "system page" roles — documents from the client's Pages - * collection that the site references by function rather than by slug. - */ -export type SystemPageRole = 'cookiePolicy' | 'homepage' | 'privacyPolicy'; -/** - * All system roles in a stable order (used when no subset is configured). - */ -export declare const ALL_SYSTEM_PAGE_ROLES: readonly SystemPageRole[]; -/** - * Plugin option enabling system-page assignments in SiteSettings. - * - * The plugin doesn't own the Pages collection — the client creates it in - * their own project. This option hands the plugin the collection's slug so - * it can build relationship fields pointing at it. No hardcoding: the slug - * always comes from the client. - */ -export type PagesOption = { - /** - * Which system roles to expose as assignable fields. - * Defaults to all roles when omitted. - */ - roles?: SystemPageRole[]; - /** Slug of the client's Pages collection, e.g. 'pages'. */ - slug: string; -}; diff --git a/dist/modules/pages/types.js b/dist/modules/pages/types.js deleted file mode 100644 index c29e5d6..0000000 --- a/dist/modules/pages/types.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Built-in "system page" roles — documents from the client's Pages - * collection that the site references by function rather than by slug. - */ /** - * All system roles in a stable order (used when no subset is configured). - */ export const ALL_SYSTEM_PAGE_ROLES = [ - 'homepage', - 'privacyPolicy', - 'cookiePolicy' -]; - -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/modules/pages/types.js.map b/dist/modules/pages/types.js.map deleted file mode 100644 index ae8ef81..0000000 --- a/dist/modules/pages/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/pages/types.ts"],"sourcesContent":["/**\n * Built-in \"system page\" roles — documents from the client's Pages\n * collection that the site references by function rather than by slug.\n */\nexport type SystemPageRole = 'cookiePolicy' | 'homepage' | 'privacyPolicy'\n\n/**\n * All system roles in a stable order (used when no subset is configured).\n */\nexport const ALL_SYSTEM_PAGE_ROLES: readonly SystemPageRole[] = [\n 'homepage',\n 'privacyPolicy',\n 'cookiePolicy',\n]\n\n/**\n * Plugin option enabling system-page assignments in SiteSettings.\n *\n * The plugin doesn't own the Pages collection — the client creates it in\n * their own project. This option hands the plugin the collection's slug so\n * it can build relationship fields pointing at it. No hardcoding: the slug\n * always comes from the client.\n */\nexport type PagesOption = {\n /**\n * Which system roles to expose as assignable fields.\n * Defaults to all roles when omitted.\n */\n roles?: SystemPageRole[]\n /** Slug of the client's Pages collection, e.g. 'pages'. */\n slug: string\n}\n"],"names":["ALL_SYSTEM_PAGE_ROLES"],"mappings":"AAAA;;;CAGC,GAGD;;CAEC,GACD,OAAO,MAAMA,wBAAmD;IAC9D;IACA;IACA;CACD,CAAA"} \ No newline at end of file diff --git a/dist/modules/payload/getGlobal.d.ts b/dist/modules/payload/getGlobal.d.ts deleted file mode 100644 index c3e6be2..0000000 --- a/dist/modules/payload/getGlobal.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { BasePayload } from 'payload'; -/** Locale argument accepted by the global helpers. */ -export type GlobalQueryOptions = { - /** Relationship population depth. Defaults to Payload's config default. */ - depth?: number; - /** Locale to fetch, or 'all' for every locale's values. Defaults to Payload's default. */ - locale?: string; -}; -/** - * Thin wrapper over payload.findGlobal that keeps locale/depth handling in one - * place. The plugin never calls getPayload itself — the caller passes the - * instance (from getPayload in their app, or req.payload in a hook), matching - * the plugin's data-access rule: logic here, instance from outside. - */ -export declare function getGlobal>(payload: BasePayload, slug: string, options?: GlobalQueryOptions): Promise; diff --git a/dist/modules/payload/getGlobal.js b/dist/modules/payload/getGlobal.js deleted file mode 100644 index 4f4dcbf..0000000 --- a/dist/modules/payload/getGlobal.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Thin wrapper over payload.findGlobal that keeps locale/depth handling in one - * place. The plugin never calls getPayload itself — the caller passes the - * instance (from getPayload in their app, or req.payload in a hook), matching - * the plugin's data-access rule: logic here, instance from outside. - */ export async function getGlobal(payload, slug, options = {}) { - const result = await payload.findGlobal({ - slug, - ...options.locale ? { - locale: options.locale - } : {}, - ...typeof options.depth === 'number' ? { - depth: options.depth - } : {} - }); - return result; -} - -//# sourceMappingURL=getGlobal.js.map \ No newline at end of file diff --git a/dist/modules/payload/getGlobal.js.map b/dist/modules/payload/getGlobal.js.map deleted file mode 100644 index f2be8f4..0000000 --- a/dist/modules/payload/getGlobal.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/payload/getGlobal.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\n/** Locale argument accepted by the global helpers. */\nexport type GlobalQueryOptions = {\n /** Relationship population depth. Defaults to Payload's config default. */\n depth?: number\n /** Locale to fetch, or 'all' for every locale's values. Defaults to Payload's default. */\n locale?: string\n}\n\n/**\n * Thin wrapper over payload.findGlobal that keeps locale/depth handling in one\n * place. The plugin never calls getPayload itself — the caller passes the\n * instance (from getPayload in their app, or req.payload in a hook), matching\n * the plugin's data-access rule: logic here, instance from outside.\n */\nexport async function getGlobal>(\n payload: BasePayload,\n slug: string,\n options: GlobalQueryOptions = {},\n): Promise {\n const result = await payload.findGlobal({\n slug,\n ...(options.locale ? { locale: options.locale as never } : {}),\n ...(typeof options.depth === 'number' ? { depth: options.depth } : {}),\n })\n\n return result as T\n}\n"],"names":["getGlobal","payload","slug","options","result","findGlobal","locale","depth"],"mappings":"AAUA;;;;;CAKC,GACD,OAAO,eAAeA,UACpBC,OAAoB,EACpBC,IAAY,EACZC,UAA8B,CAAC,CAAC;IAEhC,MAAMC,SAAS,MAAMH,QAAQI,UAAU,CAAC;QACtCH;QACA,GAAIC,QAAQG,MAAM,GAAG;YAAEA,QAAQH,QAAQG,MAAM;QAAU,IAAI,CAAC,CAAC;QAC7D,GAAI,OAAOH,QAAQI,KAAK,KAAK,WAAW;YAAEA,OAAOJ,QAAQI,KAAK;QAAC,IAAI,CAAC,CAAC;IACvE;IAEA,OAAOH;AACT"} \ No newline at end of file diff --git a/dist/modules/payload/getSiteIntegrations.d.ts b/dist/modules/payload/getSiteIntegrations.d.ts deleted file mode 100644 index a8c9d30..0000000 --- a/dist/modules/payload/getSiteIntegrations.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { GlobalQueryOptions } from './getGlobal.js'; -/** Slug of the SiteIntegrations global defined by the plugin. */ -export declare const SITE_INTEGRATIONS_SLUG = "site-integrations"; -/** - * Fetches the SiteIntegrations global. - * - * This global is admin-only through access control, but the Local API bypasses - * access control by default (overrideAccess: true), so server-side callers get - * the secrets they need (SMTP password, Turnstile secret, R2 keys). Never - * expose the raw result to the client — read the specific values you need - * server-side and pass only what's safe to the browser. - * - * Generic over the return type so the client can pass their generated - * `SiteIntegration` type. - * - * @example - * import type { SiteIntegration } from '@/payload-types' - * const integrations = await getSiteIntegrations(payload) - */ -export declare function getSiteIntegrations>(payload: BasePayload, options?: GlobalQueryOptions): Promise; diff --git a/dist/modules/payload/getSiteIntegrations.js b/dist/modules/payload/getSiteIntegrations.js deleted file mode 100644 index e8b85ae..0000000 --- a/dist/modules/payload/getSiteIntegrations.js +++ /dev/null @@ -1,22 +0,0 @@ -import { getGlobal } from './getGlobal.js'; -/** Slug of the SiteIntegrations global defined by the plugin. */ export const SITE_INTEGRATIONS_SLUG = 'site-integrations'; -/** - * Fetches the SiteIntegrations global. - * - * This global is admin-only through access control, but the Local API bypasses - * access control by default (overrideAccess: true), so server-side callers get - * the secrets they need (SMTP password, Turnstile secret, R2 keys). Never - * expose the raw result to the client — read the specific values you need - * server-side and pass only what's safe to the browser. - * - * Generic over the return type so the client can pass their generated - * `SiteIntegration` type. - * - * @example - * import type { SiteIntegration } from '@/payload-types' - * const integrations = await getSiteIntegrations(payload) - */ export function getSiteIntegrations(payload, options) { - return getGlobal(payload, SITE_INTEGRATIONS_SLUG, options); -} - -//# sourceMappingURL=getSiteIntegrations.js.map \ No newline at end of file diff --git a/dist/modules/payload/getSiteIntegrations.js.map b/dist/modules/payload/getSiteIntegrations.js.map deleted file mode 100644 index 63c6b31..0000000 --- a/dist/modules/payload/getSiteIntegrations.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/payload/getSiteIntegrations.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { GlobalQueryOptions } from './getGlobal.js'\n\nimport { getGlobal } from './getGlobal.js'\n\n/** Slug of the SiteIntegrations global defined by the plugin. */\nexport const SITE_INTEGRATIONS_SLUG = 'site-integrations'\n\n/**\n * Fetches the SiteIntegrations global.\n *\n * This global is admin-only through access control, but the Local API bypasses\n * access control by default (overrideAccess: true), so server-side callers get\n * the secrets they need (SMTP password, Turnstile secret, R2 keys). Never\n * expose the raw result to the client — read the specific values you need\n * server-side and pass only what's safe to the browser.\n *\n * Generic over the return type so the client can pass their generated\n * `SiteIntegration` type.\n *\n * @example\n * import type { SiteIntegration } from '@/payload-types'\n * const integrations = await getSiteIntegrations(payload)\n */\nexport function getSiteIntegrations>(\n payload: BasePayload,\n options?: GlobalQueryOptions,\n): Promise {\n return getGlobal(payload, SITE_INTEGRATIONS_SLUG, options)\n}\n"],"names":["getGlobal","SITE_INTEGRATIONS_SLUG","getSiteIntegrations","payload","options"],"mappings":"AAIA,SAASA,SAAS,QAAQ,iBAAgB;AAE1C,+DAA+D,GAC/D,OAAO,MAAMC,yBAAyB,oBAAmB;AAEzD;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC,oBACdC,OAAoB,EACpBC,OAA4B;IAE5B,OAAOJ,UAAaG,SAASF,wBAAwBG;AACvD"} \ No newline at end of file diff --git a/dist/modules/payload/getSiteSettings.d.ts b/dist/modules/payload/getSiteSettings.d.ts deleted file mode 100644 index 56f84f1..0000000 --- a/dist/modules/payload/getSiteSettings.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { GlobalQueryOptions } from './getGlobal.js'; -/** Slug of the SiteSettings global defined by the plugin. */ -export declare const SITE_SETTINGS_SLUG = "site-settings"; -/** - * Fetches the SiteSettings global. - * - * Generic over the return type so the client can pass their generated - * `SiteSetting` type for full type safety, while still working without it. - * - * @example - * import type { SiteSetting } from '@/payload-types' - * const settings = await getSiteSettings(payload, { locale: 'pl' }) - */ -export declare function getSiteSettings>(payload: BasePayload, options?: GlobalQueryOptions): Promise; diff --git a/dist/modules/payload/getSiteSettings.js b/dist/modules/payload/getSiteSettings.js deleted file mode 100644 index bbea669..0000000 --- a/dist/modules/payload/getSiteSettings.js +++ /dev/null @@ -1,16 +0,0 @@ -import { getGlobal } from './getGlobal.js'; -/** Slug of the SiteSettings global defined by the plugin. */ export const SITE_SETTINGS_SLUG = 'site-settings'; -/** - * Fetches the SiteSettings global. - * - * Generic over the return type so the client can pass their generated - * `SiteSetting` type for full type safety, while still working without it. - * - * @example - * import type { SiteSetting } from '@/payload-types' - * const settings = await getSiteSettings(payload, { locale: 'pl' }) - */ export function getSiteSettings(payload, options) { - return getGlobal(payload, SITE_SETTINGS_SLUG, options); -} - -//# sourceMappingURL=getSiteSettings.js.map \ No newline at end of file diff --git a/dist/modules/payload/getSiteSettings.js.map b/dist/modules/payload/getSiteSettings.js.map deleted file mode 100644 index daeec8f..0000000 --- a/dist/modules/payload/getSiteSettings.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/payload/getSiteSettings.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { GlobalQueryOptions } from './getGlobal.js'\n\nimport { getGlobal } from './getGlobal.js'\n\n/** Slug of the SiteSettings global defined by the plugin. */\nexport const SITE_SETTINGS_SLUG = 'site-settings'\n\n/**\n * Fetches the SiteSettings global.\n *\n * Generic over the return type so the client can pass their generated\n * `SiteSetting` type for full type safety, while still working without it.\n *\n * @example\n * import type { SiteSetting } from '@/payload-types'\n * const settings = await getSiteSettings(payload, { locale: 'pl' })\n */\nexport function getSiteSettings>(\n payload: BasePayload,\n options?: GlobalQueryOptions,\n): Promise {\n return getGlobal(payload, SITE_SETTINGS_SLUG, options)\n}\n"],"names":["getGlobal","SITE_SETTINGS_SLUG","getSiteSettings","payload","options"],"mappings":"AAIA,SAASA,SAAS,QAAQ,iBAAgB;AAE1C,2DAA2D,GAC3D,OAAO,MAAMC,qBAAqB,gBAAe;AAEjD;;;;;;;;;CASC,GACD,OAAO,SAASC,gBACdC,OAAoB,EACpBC,OAA4B;IAE5B,OAAOJ,UAAaG,SAASF,oBAAoBG;AACnD"} \ No newline at end of file diff --git a/dist/modules/payload/index.d.ts b/dist/modules/payload/index.d.ts deleted file mode 100644 index ece927a..0000000 --- a/dist/modules/payload/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type { GlobalQueryOptions } from './getGlobal.js'; -export { getGlobal } from './getGlobal.js'; -export { getSiteIntegrations, SITE_INTEGRATIONS_SLUG } from './getSiteIntegrations.js'; -export { getSiteSettings, SITE_SETTINGS_SLUG } from './getSiteSettings.js'; diff --git a/dist/modules/payload/index.js b/dist/modules/payload/index.js deleted file mode 100644 index f85eef0..0000000 --- a/dist/modules/payload/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export { getGlobal } from './getGlobal.js'; -export { getSiteIntegrations, SITE_INTEGRATIONS_SLUG } from './getSiteIntegrations.js'; -export { getSiteSettings, SITE_SETTINGS_SLUG } from './getSiteSettings.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/payload/index.js.map b/dist/modules/payload/index.js.map deleted file mode 100644 index 46813a1..0000000 --- a/dist/modules/payload/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/payload/index.ts"],"sourcesContent":["export type { GlobalQueryOptions } from './getGlobal.js'\nexport { getGlobal } from './getGlobal.js'\nexport { getSiteIntegrations, SITE_INTEGRATIONS_SLUG } from './getSiteIntegrations.js'\nexport { getSiteSettings, SITE_SETTINGS_SLUG } from './getSiteSettings.js'\n"],"names":["getGlobal","getSiteIntegrations","SITE_INTEGRATIONS_SLUG","getSiteSettings","SITE_SETTINGS_SLUG"],"mappings":"AACA,SAASA,SAAS,QAAQ,iBAAgB;AAC1C,SAASC,mBAAmB,EAAEC,sBAAsB,QAAQ,2BAA0B;AACtF,SAASC,eAAe,EAAEC,kBAAkB,QAAQ,uBAAsB"} \ No newline at end of file diff --git a/dist/modules/seo/autoFillMeta.d.ts b/dist/modules/seo/autoFillMeta.d.ts deleted file mode 100644 index e927d54..0000000 --- a/dist/modules/seo/autoFillMeta.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { CollectionBeforeChangeHook } from 'payload'; -/** - * Maps document fields to SEO meta fields for auto-fill. - * Defaults match the Payload website template (title → meta.title). - */ -export type AutoFillMapping = { - /** Document field used to fill meta.description when empty. */ - description?: string; - /** Document field used to fill meta.title when empty. Default: 'title'. */ - title?: string; -}; -/** - * Builds a beforeChange hook that fills empty SEO meta fields from document - * content. Only fills when the meta field is blank — never overwrites what an - * editor typed. This runs server-side on every create/update, so editors get - * sensible meta without clicking "auto-generate". - * - * The plugin doesn't know the client's collection shape, so the field mapping - * is configurable; defaults follow the website template. - */ -export declare function buildAutoFillMetaHook(mapping?: AutoFillMapping): CollectionBeforeChangeHook; diff --git a/dist/modules/seo/autoFillMeta.js b/dist/modules/seo/autoFillMeta.js deleted file mode 100644 index 5f548da..0000000 --- a/dist/modules/seo/autoFillMeta.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Reads a possibly-localized field value into a plain string. - * With localized fields at this hook stage the value is the active-locale - * string, so we just coerce defensively. - */ function readString(value) { - if (typeof value === 'string' && value.trim()) { - return value.trim(); - } - return undefined; -} -/** - * Builds a beforeChange hook that fills empty SEO meta fields from document - * content. Only fills when the meta field is blank — never overwrites what an - * editor typed. This runs server-side on every create/update, so editors get - * sensible meta without clicking "auto-generate". - * - * The plugin doesn't know the client's collection shape, so the field mapping - * is configurable; defaults follow the website template. - */ export function buildAutoFillMetaHook(mapping = {}) { - const titleField = mapping.title ?? 'title'; - const descriptionField = mapping.description; - return ({ data })=>{ - if (!data) { - return data; - } - // plugin-seo stores meta as a group under `meta` - const meta = data.meta ?? {}; - // Fill meta.title from the document title when empty - if (!readString(meta.title)) { - const sourceTitle = readString(data[titleField]); - if (sourceTitle) { - meta.title = sourceTitle; - } - } - // Fill meta.description from a configured field when empty - if (descriptionField && !readString(meta.description)) { - const sourceDescription = readString(data[descriptionField]); - if (sourceDescription) { - meta.description = sourceDescription; - } - } - data.meta = meta; - return data; - }; -} - -//# sourceMappingURL=autoFillMeta.js.map \ No newline at end of file diff --git a/dist/modules/seo/autoFillMeta.js.map b/dist/modules/seo/autoFillMeta.js.map deleted file mode 100644 index 3239280..0000000 --- a/dist/modules/seo/autoFillMeta.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/autoFillMeta.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook } from 'payload'\n\n/**\n * Maps document fields to SEO meta fields for auto-fill.\n * Defaults match the Payload website template (title → meta.title).\n */\nexport type AutoFillMapping = {\n /** Document field used to fill meta.description when empty. */\n description?: string\n /** Document field used to fill meta.title when empty. Default: 'title'. */\n title?: string\n}\n\n/**\n * Reads a possibly-localized field value into a plain string.\n * With localized fields at this hook stage the value is the active-locale\n * string, so we just coerce defensively.\n */\nfunction readString(value: unknown): string | undefined {\n if (typeof value === 'string' && value.trim()) {return value.trim()}\n return undefined\n}\n\n/**\n * Builds a beforeChange hook that fills empty SEO meta fields from document\n * content. Only fills when the meta field is blank — never overwrites what an\n * editor typed. This runs server-side on every create/update, so editors get\n * sensible meta without clicking \"auto-generate\".\n *\n * The plugin doesn't know the client's collection shape, so the field mapping\n * is configurable; defaults follow the website template.\n */\nexport function buildAutoFillMetaHook(mapping: AutoFillMapping = {}): CollectionBeforeChangeHook {\n const titleField = mapping.title ?? 'title'\n const descriptionField = mapping.description\n\n return ({ data }) => {\n if (!data) {return data}\n\n // plugin-seo stores meta as a group under `meta`\n const meta = (data.meta as Record | undefined) ?? {}\n\n // Fill meta.title from the document title when empty\n if (!readString(meta.title)) {\n const sourceTitle = readString(data[titleField])\n if (sourceTitle) {meta.title = sourceTitle}\n }\n\n // Fill meta.description from a configured field when empty\n if (descriptionField && !readString(meta.description)) {\n const sourceDescription = readString(data[descriptionField])\n if (sourceDescription) {meta.description = sourceDescription}\n }\n\n data.meta = meta\n return data\n }\n}\n"],"names":["readString","value","trim","undefined","buildAutoFillMetaHook","mapping","titleField","title","descriptionField","description","data","meta","sourceTitle","sourceDescription"],"mappings":"AAaA;;;;CAIC,GACD,SAASA,WAAWC,KAAc;IAChC,IAAI,OAAOA,UAAU,YAAYA,MAAMC,IAAI,IAAI;QAAC,OAAOD,MAAMC,IAAI;IAAE;IACnE,OAAOC;AACT;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASC,sBAAsBC,UAA2B,CAAC,CAAC;IACjE,MAAMC,aAAaD,QAAQE,KAAK,IAAI;IACpC,MAAMC,mBAAmBH,QAAQI,WAAW;IAE5C,OAAO,CAAC,EAAEC,IAAI,EAAE;QACd,IAAI,CAACA,MAAM;YAAC,OAAOA;QAAI;QAEvB,iDAAiD;QACjD,MAAMC,OAAO,AAACD,KAAKC,IAAI,IAA4C,CAAC;QAEpE,qDAAqD;QACrD,IAAI,CAACX,WAAWW,KAAKJ,KAAK,GAAG;YAC3B,MAAMK,cAAcZ,WAAWU,IAAI,CAACJ,WAAW;YAC/C,IAAIM,aAAa;gBAACD,KAAKJ,KAAK,GAAGK;YAAW;QAC5C;QAEA,2DAA2D;QAC3D,IAAIJ,oBAAoB,CAACR,WAAWW,KAAKF,WAAW,GAAG;YACrD,MAAMI,oBAAoBb,WAAWU,IAAI,CAACF,iBAAiB;YAC3D,IAAIK,mBAAmB;gBAACF,KAAKF,WAAW,GAAGI;YAAiB;QAC9D;QAEAH,KAAKC,IAAI,GAAGA;QACZ,OAAOD;IACT;AACF"} \ No newline at end of file diff --git a/dist/modules/seo/buildMetadata.d.ts b/dist/modules/seo/buildMetadata.d.ts deleted file mode 100644 index 9d30e57..0000000 --- a/dist/modules/seo/buildMetadata.d.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { I18nConfig, LocalizedSlugs } from '../i18n/index.js'; -import type { TitleOrder } from './composeTitle.js'; -import type { SeoMeta } from './types.js'; -/** - * Subset of Next.js `Metadata` this helper produces. Kept local so the plugin - * doesn't depend on `next` types; the shape is assignable to Next's Metadata. - */ -export type PageMetadata = { - alternates?: { - canonical?: string; - languages?: Record; - }; - description?: string; - openGraph?: { - description?: string; - images?: { - url: string; - }[]; - locale?: string; - title: string; - }; - title: string; -}; -type BuildMetadataArgs = { - /** Absolute site origin, e.g. 'https://example.com'. */ - baseUrl?: string; - config: I18nConfig; - /** Home slug that collapses to the locale root. Defaults to 'home'. */ - homeSlug?: string; - /** Resolved OG image URL (page image or site defaultShareImage). */ - imageUrl?: null | string; - /** Current locale being rendered. */ - locale: string; - /** SEO meta from the document (plugin-seo group). */ - meta?: null | SeoMeta; - /** Page title or site name first. Defaults to 'page-first'. */ - order?: TitleOrder; - /** - * Localized segment the document lives under (an archive page's slugs). - * Feeds both canonical and hreflang, so /pl/artykuly/moj-post and - * /en/articles/my-post point at each other correctly. - */ - prefix?: LocalizedSlugs; - /** - * Query string appended to canonical and every hreflang, e.g. '?page=2'. - * - * A paginated listing must be canonical to itself — pointing page 2 at page 1 - * tells Google the entries on it don't exist. Alternates carry the same page, - * since /pl/artykuly?page=2 corresponds to /en/articles?page=2. - */ - query?: string; - /** Separator between page title and site name. Defaults to ' | '. */ - separator?: string; - /** Site name for title composition and OG. */ - siteName?: null | string; - /** slug per locale for this document — drives canonical + hreflang. */ - slugs: LocalizedSlugs; -}; -/** - * Assembles a Next.js-compatible Metadata object from document SEO fields and - * site-level data. Locale-aware: canonical points at the current locale's - * path, and hreflang alternates cover every locale the document exists in. - * - * Designed for use inside Next.js `generateMetadata`. The caller resolves the - * pieces (meta group, site name, image URL, localized slugs) and passes them - * in — the plugin composes, it doesn't fetch. - */ -export declare function buildMetadata({ baseUrl, config, homeSlug, imageUrl, locale, meta, order, prefix, query, separator, siteName, slugs, }: BuildMetadataArgs): PageMetadata; -export {}; diff --git a/dist/modules/seo/buildMetadata.js b/dist/modules/seo/buildMetadata.js deleted file mode 100644 index 1a08dc3..0000000 --- a/dist/modules/seo/buildMetadata.js +++ /dev/null @@ -1,76 +0,0 @@ -import { buildLocalizedPath } from '../i18n/index.js'; -import { composeTitle } from './composeTitle.js'; -import { buildHreflangAlternates } from './hreflang.js'; -/** - * Assembles a Next.js-compatible Metadata object from document SEO fields and - * site-level data. Locale-aware: canonical points at the current locale's - * path, and hreflang alternates cover every locale the document exists in. - * - * Designed for use inside Next.js `generateMetadata`. The caller resolves the - * pieces (meta group, site name, image URL, localized slugs) and passes them - * in — the plugin composes, it doesn't fetch. - */ export function buildMetadata({ baseUrl, config, homeSlug = 'home', imageUrl, locale, meta, order, prefix, query, separator, siteName, slugs }) { - // titleOverride wins outright: an editor who filled it in wants that exact - // string in the tab, not a composition. - const override = meta?.titleOverride?.trim(); - const title = override || composeTitle({ - order, - pageTitle: meta?.title, - separator, - siteName - }); - const description = meta?.description?.trim() || undefined; - const origin = baseUrl?.replace(/\/$/, '') ?? ''; - const suffix = query ?? ''; - const currentPath = buildLocalizedPath({ - config, - homeSlug, - locale, - prefix, - slugs - }); - const canonical = currentPath ? `${origin}${currentPath}${suffix}` : undefined; - const languages = buildHreflangAlternates({ - baseUrl, - config, - homeSlug, - prefix, - slugs - }); - if (suffix) { - for (const code of Object.keys(languages)){ - languages[code] = `${languages[code]}${suffix}`; - } - } - const images = imageUrl ? [ - { - url: imageUrl - } - ] : undefined; - return { - title, - ...description && { - description - }, - alternates: { - ...canonical && { - canonical - }, - ...Object.keys(languages).length > 0 && { - languages - } - }, - openGraph: { - title, - ...description && { - description - }, - ...images && { - images - }, - locale - } - }; -} - -//# sourceMappingURL=buildMetadata.js.map \ No newline at end of file diff --git a/dist/modules/seo/buildMetadata.js.map b/dist/modules/seo/buildMetadata.js.map deleted file mode 100644 index 5b52d9a..0000000 --- a/dist/modules/seo/buildMetadata.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/buildMetadata.ts"],"sourcesContent":["import type { I18nConfig, LocalizedSlugs } from '../i18n/index.js'\nimport type { TitleOrder } from './composeTitle.js'\nimport type { SeoMeta } from './types.js'\n\nimport { buildLocalizedPath } from '../i18n/index.js'\nimport { composeTitle } from './composeTitle.js'\nimport { buildHreflangAlternates } from './hreflang.js'\n\n/**\n * Subset of Next.js `Metadata` this helper produces. Kept local so the plugin\n * doesn't depend on `next` types; the shape is assignable to Next's Metadata.\n */\nexport type PageMetadata = {\n alternates?: {\n canonical?: string\n languages?: Record\n }\n description?: string\n openGraph?: {\n description?: string\n images?: { url: string }[]\n locale?: string\n title: string\n }\n title: string\n}\n\ntype BuildMetadataArgs = {\n /** Absolute site origin, e.g. 'https://example.com'. */\n baseUrl?: string\n config: I18nConfig\n /** Home slug that collapses to the locale root. Defaults to 'home'. */\n homeSlug?: string\n /** Resolved OG image URL (page image or site defaultShareImage). */\n imageUrl?: null | string\n /** Current locale being rendered. */\n locale: string\n /** SEO meta from the document (plugin-seo group). */\n meta?: null | SeoMeta\n /** Page title or site name first. Defaults to 'page-first'. */\n order?: TitleOrder\n /**\n * Localized segment the document lives under (an archive page's slugs).\n * Feeds both canonical and hreflang, so /pl/artykuly/moj-post and\n * /en/articles/my-post point at each other correctly.\n */\n prefix?: LocalizedSlugs\n /**\n * Query string appended to canonical and every hreflang, e.g. '?page=2'.\n *\n * A paginated listing must be canonical to itself — pointing page 2 at page 1\n * tells Google the entries on it don't exist. Alternates carry the same page,\n * since /pl/artykuly?page=2 corresponds to /en/articles?page=2.\n */\n query?: string\n /** Separator between page title and site name. Defaults to ' | '. */\n separator?: string\n /** Site name for title composition and OG. */\n siteName?: null | string\n /** slug per locale for this document — drives canonical + hreflang. */\n slugs: LocalizedSlugs\n}\n\n/**\n * Assembles a Next.js-compatible Metadata object from document SEO fields and\n * site-level data. Locale-aware: canonical points at the current locale's\n * path, and hreflang alternates cover every locale the document exists in.\n *\n * Designed for use inside Next.js `generateMetadata`. The caller resolves the\n * pieces (meta group, site name, image URL, localized slugs) and passes them\n * in — the plugin composes, it doesn't fetch.\n */\nexport function buildMetadata({\n baseUrl,\n config,\n homeSlug = 'home',\n imageUrl,\n locale,\n meta,\n order,\n prefix,\n query,\n separator,\n siteName,\n slugs,\n}: BuildMetadataArgs): PageMetadata {\n // titleOverride wins outright: an editor who filled it in wants that exact\n // string in the tab, not a composition.\n const override = meta?.titleOverride?.trim()\n const title = override || composeTitle({ order, pageTitle: meta?.title, separator, siteName })\n const description = meta?.description?.trim() || undefined\n const origin = baseUrl?.replace(/\\/$/, '') ?? ''\n\n const suffix = query ?? ''\n\n const currentPath = buildLocalizedPath({ config, homeSlug, locale, prefix, slugs })\n const canonical = currentPath ? `${origin}${currentPath}${suffix}` : undefined\n\n const languages = buildHreflangAlternates({ baseUrl, config, homeSlug, prefix, slugs })\n if (suffix) {\n for (const code of Object.keys(languages)) {\n languages[code] = `${languages[code]}${suffix}`\n }\n }\n\n const images = imageUrl ? [{ url: imageUrl }] : undefined\n\n return {\n title,\n ...(description && { description }),\n alternates: {\n ...(canonical && { canonical }),\n ...(Object.keys(languages).length > 0 && { languages }),\n },\n openGraph: {\n title,\n ...(description && { description }),\n ...(images && { images }),\n locale,\n },\n }\n}\n"],"names":["buildLocalizedPath","composeTitle","buildHreflangAlternates","buildMetadata","baseUrl","config","homeSlug","imageUrl","locale","meta","order","prefix","query","separator","siteName","slugs","override","titleOverride","trim","title","pageTitle","description","undefined","origin","replace","suffix","currentPath","canonical","languages","code","Object","keys","images","url","alternates","length","openGraph"],"mappings":"AAIA,SAASA,kBAAkB,QAAQ,mBAAkB;AACrD,SAASC,YAAY,QAAQ,oBAAmB;AAChD,SAASC,uBAAuB,QAAQ,gBAAe;AAyDvD;;;;;;;;CAQC,GACD,OAAO,SAASC,cAAc,EAC5BC,OAAO,EACPC,MAAM,EACNC,WAAW,MAAM,EACjBC,QAAQ,EACRC,MAAM,EACNC,IAAI,EACJC,KAAK,EACLC,MAAM,EACNC,KAAK,EACLC,SAAS,EACTC,QAAQ,EACRC,KAAK,EACa;IAClB,2EAA2E;IAC3E,wCAAwC;IACxC,MAAMC,WAAWP,MAAMQ,eAAeC;IACtC,MAAMC,QAAQH,YAAYf,aAAa;QAAES;QAAOU,WAAWX,MAAMU;QAAON;QAAWC;IAAS;IAC5F,MAAMO,cAAcZ,MAAMY,aAAaH,UAAUI;IACjD,MAAMC,SAASnB,SAASoB,QAAQ,OAAO,OAAO;IAE9C,MAAMC,SAASb,SAAS;IAExB,MAAMc,cAAc1B,mBAAmB;QAAEK;QAAQC;QAAUE;QAAQG;QAAQI;IAAM;IACjF,MAAMY,YAAYD,cAAc,GAAGH,SAASG,cAAcD,QAAQ,GAAGH;IAErE,MAAMM,YAAY1B,wBAAwB;QAAEE;QAASC;QAAQC;QAAUK;QAAQI;IAAM;IACrF,IAAIU,QAAQ;QACV,KAAK,MAAMI,QAAQC,OAAOC,IAAI,CAACH,WAAY;YACzCA,SAAS,CAACC,KAAK,GAAG,GAAGD,SAAS,CAACC,KAAK,GAAGJ,QAAQ;QACjD;IACF;IAEA,MAAMO,SAASzB,WAAW;QAAC;YAAE0B,KAAK1B;QAAS;KAAE,GAAGe;IAEhD,OAAO;QACLH;QACA,GAAIE,eAAe;YAAEA;QAAY,CAAC;QAClCa,YAAY;YACV,GAAIP,aAAa;gBAAEA;YAAU,CAAC;YAC9B,GAAIG,OAAOC,IAAI,CAACH,WAAWO,MAAM,GAAG,KAAK;gBAAEP;YAAU,CAAC;QACxD;QACAQ,WAAW;YACTjB;YACA,GAAIE,eAAe;gBAAEA;YAAY,CAAC;YAClC,GAAIW,UAAU;gBAAEA;YAAO,CAAC;YACxBxB;QACF;IACF;AACF"} \ No newline at end of file diff --git a/dist/modules/seo/buildRobots.d.ts b/dist/modules/seo/buildRobots.d.ts deleted file mode 100644 index 6369a0a..0000000 --- a/dist/modules/seo/buildRobots.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -type BuildRobotsArgs = { - /** Absolute origin, e.g. 'https://example.com'. */ - baseUrl: string; - /** Paths to disallow. Defaults to Payload's admin and API. */ - disallow?: string[]; - /** Whether to advertise the sitemap. Defaults to true. */ - sitemap?: boolean; -}; -/** - * Shape for Next's `app/robots.ts`. - */ -export type RobotsRules = { - host?: string; - rules: { - allow?: string; - disallow?: string[]; - userAgent: string; - }; - sitemap?: string; -}; -/** - * Builds robots rules that allow crawling everything except the admin panel and - * API, and point crawlers at the sitemap. - * - * ```ts - * // app/robots.ts - * export default function robots() { - * return buildRobots({ baseUrl: process.env.NEXT_PUBLIC_SERVER_URL! }) - * } - * ``` - * - * The admin and API are disallowed by default — no reason for them in the index, - * and it keeps crawl budget on real content. - */ -export declare function buildRobots({ baseUrl, disallow, sitemap, }: BuildRobotsArgs): RobotsRules; -export {}; diff --git a/dist/modules/seo/buildRobots.js b/dist/modules/seo/buildRobots.js deleted file mode 100644 index 226441f..0000000 --- a/dist/modules/seo/buildRobots.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Builds robots rules that allow crawling everything except the admin panel and - * API, and point crawlers at the sitemap. - * - * ```ts - * // app/robots.ts - * export default function robots() { - * return buildRobots({ baseUrl: process.env.NEXT_PUBLIC_SERVER_URL! }) - * } - * ``` - * - * The admin and API are disallowed by default — no reason for them in the index, - * and it keeps crawl budget on real content. - */ export function buildRobots({ baseUrl, disallow = [ - '/admin', - '/api' -], sitemap = true }) { - const origin = baseUrl.replace(/\/$/, ''); - return { - rules: { - allow: '/', - disallow, - userAgent: '*' - }, - ...sitemap ? { - sitemap: `${origin}/sitemap.xml` - } : {}, - host: origin - }; -} - -//# sourceMappingURL=buildRobots.js.map \ No newline at end of file diff --git a/dist/modules/seo/buildRobots.js.map b/dist/modules/seo/buildRobots.js.map deleted file mode 100644 index e27a67b..0000000 --- a/dist/modules/seo/buildRobots.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/buildRobots.ts"],"sourcesContent":["type BuildRobotsArgs = {\n /** Absolute origin, e.g. 'https://example.com'. */\n baseUrl: string\n /** Paths to disallow. Defaults to Payload's admin and API. */\n disallow?: string[]\n /** Whether to advertise the sitemap. Defaults to true. */\n sitemap?: boolean\n}\n\n/**\n * Shape for Next's `app/robots.ts`.\n */\nexport type RobotsRules = {\n host?: string\n rules: { allow?: string; disallow?: string[]; userAgent: string }\n sitemap?: string\n}\n\n/**\n * Builds robots rules that allow crawling everything except the admin panel and\n * API, and point crawlers at the sitemap.\n *\n * ```ts\n * // app/robots.ts\n * export default function robots() {\n * return buildRobots({ baseUrl: process.env.NEXT_PUBLIC_SERVER_URL! })\n * }\n * ```\n *\n * The admin and API are disallowed by default — no reason for them in the index,\n * and it keeps crawl budget on real content.\n */\nexport function buildRobots({\n baseUrl,\n disallow = ['/admin', '/api'],\n sitemap = true,\n}: BuildRobotsArgs): RobotsRules {\n const origin = baseUrl.replace(/\\/$/, '')\n\n return {\n rules: {\n allow: '/',\n disallow,\n userAgent: '*',\n },\n ...(sitemap ? { sitemap: `${origin}/sitemap.xml` } : {}),\n host: origin,\n }\n}\n"],"names":["buildRobots","baseUrl","disallow","sitemap","origin","replace","rules","allow","userAgent","host"],"mappings":"AAkBA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASA,YAAY,EAC1BC,OAAO,EACPC,WAAW;IAAC;IAAU;CAAO,EAC7BC,UAAU,IAAI,EACE;IAChB,MAAMC,SAASH,QAAQI,OAAO,CAAC,OAAO;IAEtC,OAAO;QACLC,OAAO;YACLC,OAAO;YACPL;YACAM,WAAW;QACb;QACA,GAAIL,UAAU;YAAEA,SAAS,GAAGC,OAAO,YAAY,CAAC;QAAC,IAAI,CAAC,CAAC;QACvDK,MAAML;IACR;AACF"} \ No newline at end of file diff --git a/dist/modules/seo/buildSitemapEntries.d.ts b/dist/modules/seo/buildSitemapEntries.d.ts deleted file mode 100644 index c739317..0000000 --- a/dist/modules/seo/buildSitemapEntries.d.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { ContentOption } from '../content/index.js'; -import type { I18nConfig } from '../i18n/index.js'; -/** - * One sitemap entry, shaped for Next's `app/sitemap.ts`. - * - * `alternates.languages` is the important part for a multilingual site: Next - * renders it as `` per URL, which is - * exactly what Google uses to connect language versions. A sitemap without it - * is the common, weaker kind. - */ -export type SitemapEntry = { - alternates?: { - languages: Record; - }; - changeFrequency?: 'always' | 'daily' | 'hourly' | 'monthly' | 'never' | 'weekly' | 'yearly'; - lastModified?: Date | string; - priority?: number; - url: string; -}; -type BuildSitemapArgs = { - /** Absolute origin, e.g. 'https://example.com'. Required for valid sitemap URLs. */ - baseUrl: string; - changeFrequency?: SitemapEntry['changeFrequency']; - config: I18nConfig; - /** Archive-backed collections, same value as the plugin option. */ - content?: ContentOption; - /** - * Slug of the page that is the site root (collapses to /{locale}). - * Read from System Pages when omitted. - */ - homeSlug?: string; - /** Pages collection slug. Defaults to 'pages'. */ - pagesSlug?: string; - payload: BasePayload; - /** SiteSettings global slug. Defaults to 'site-settings'. */ - settingsSlug?: string; -}; -/** - * Collects every public URL — pages and archive entries — as sitemap entries - * with per-URL hreflang and lastmod. - * - * Reuses the same path/hreflang builders as page metadata, so the sitemap can't - * drift from what the pages actually render (a classic source of sitemap bugs: - * a URL listed one way and served another). Drafts and noindex documents are - * omitted. - * - * ```ts - * // app/sitemap.ts - * export default async function sitemap() { - * return buildSitemapEntries({ - * payload: await getPayload({ config }), - * config: i18nConfig, - * baseUrl: process.env.NEXT_PUBLIC_SERVER_URL!, - * content: contentConfig, - * }) - * } - * ``` - */ -export declare function buildSitemapEntries({ baseUrl, changeFrequency, config, content, homeSlug, pagesSlug, payload, settingsSlug, }: BuildSitemapArgs): Promise; -export {}; diff --git a/dist/modules/seo/buildSitemapEntries.js b/dist/modules/seo/buildSitemapEntries.js deleted file mode 100644 index 53f83ce..0000000 --- a/dist/modules/seo/buildSitemapEntries.js +++ /dev/null @@ -1,158 +0,0 @@ -import { archiveFieldName } from '../content/index.js'; -import { buildLocalizedPath, getLocalizedSlugs } from '../i18n/index.js'; -import { buildHreflangAlternates } from './hreflang.js'; -/** Skip drafts and anything flagged noindex in the SEO tab. */ function isIndexable(doc) { - if (doc._status && doc._status !== 'published') { - return false; - } - if (doc.meta?.noindex) { - return false; - } - return true; -} -/** - * Turns one document into a sitemap entry per default locale isn't needed — - * one entry with all languages as alternates is the correct, de-duplicated - * shape. The `url` is the current locale's path; `alternates.languages` carries - * every locale (including itself, per Google's guidance). - */ function entryFor(doc, locale, config, baseUrl, homeSlug, prefix, changeFrequency) { - const slugs = doc.slug && typeof doc.slug === 'object' ? getLocalizedSlugs({ - config, - slugField: doc.slug - }) : {}; - const path = buildLocalizedPath({ - config, - homeSlug, - locale, - prefix, - slugs - }); - if (!path) { - return null; - } - const origin = baseUrl.replace(/\/$/, ''); - const languages = buildHreflangAlternates({ - baseUrl, - config, - homeSlug, - prefix, - slugs - }); - return { - url: `${origin}${path}`, - ...doc.updatedAt ? { - lastModified: doc.updatedAt - } : {}, - ...changeFrequency ? { - changeFrequency - } : {}, - ...Object.keys(languages).length ? { - alternates: { - languages - } - } : {} - }; -} -/** - * Collects every public URL — pages and archive entries — as sitemap entries - * with per-URL hreflang and lastmod. - * - * Reuses the same path/hreflang builders as page metadata, so the sitemap can't - * drift from what the pages actually render (a classic source of sitemap bugs: - * a URL listed one way and served another). Drafts and noindex documents are - * omitted. - * - * ```ts - * // app/sitemap.ts - * export default async function sitemap() { - * return buildSitemapEntries({ - * payload: await getPayload({ config }), - * config: i18nConfig, - * baseUrl: process.env.NEXT_PUBLIC_SERVER_URL!, - * content: contentConfig, - * }) - * } - * ``` - */ export async function buildSitemapEntries({ baseUrl, changeFrequency = 'weekly', config, content, homeSlug, pagesSlug = 'pages', payload, settingsSlug = 'site-settings' }) { - const locales = config.locales.map((l)=>l.code); - const defaultLocale = config.defaultLocale; - // Resolve homeSlug and archive prefixes from System Pages (read once, in all - // locales so archive prefixes are available per language). - const settings = await payload.findGlobal({ - slug: settingsSlug, - depth: 1, - locale: 'all' - }); - const resolvedHomeSlug = homeSlug ?? extractSlugInLocale(settings.homepage, defaultLocale) ?? 'home'; - // Which collections to walk: pages (no prefix) + each content collection with - // its archive-page slugs as the localized prefix. - const collections = [ - { - slug: pagesSlug - } - ]; - for (const c of content?.collections ?? []){ - const archive = settings[archiveFieldName(c.slug)]; - const prefixSlugs = archive ? slugMapAllLocales(archive, locales) : undefined; - collections.push({ - slug: c.slug, - prefixSlugs - }); - } - const entries = []; - for (const collection of collections){ - // Read every doc once, across locales, so one row yields all its language - // alternates without re-querying per locale. - const result = await payload.find({ - collection: collection.slug, - depth: 0, - limit: 0, - locale: 'all', - pagination: false - }); - for (const raw of result.docs){ - if (!isIndexable(raw)) { - continue; - } - // Emit the entry under the default locale's URL; alternates cover the rest. - const entry = entryFor(raw, defaultLocale, config, baseUrl, resolvedHomeSlug, collection.prefixSlugs, changeFrequency); - if (entry) { - entries.push(entry); - } - } - } - return entries; -} -/** Pulls a slug string from a populated relationship in a specific locale. */ function extractSlugInLocale(rel, locale) { - if (!rel || typeof rel !== 'object') { - return undefined; - } - const slug = rel.slug; - if (typeof slug === 'string') { - return slug; - } - if (slug && typeof slug === 'object') { - const v = slug[locale]; - return typeof v === 'string' ? v : undefined; - } - return undefined; -} -/** Builds a locale→slug map from a populated archive relationship. */ function slugMapAllLocales(rel, locales) { - if (!rel || typeof rel !== 'object') { - return undefined; - } - const slug = rel.slug; - if (!slug || typeof slug !== 'object') { - return undefined; - } - const map = {}; - for (const locale of locales){ - const v = slug[locale]; - if (typeof v === 'string') { - map[locale] = v; - } - } - return Object.keys(map).length ? map : undefined; -} - -//# sourceMappingURL=buildSitemapEntries.js.map \ No newline at end of file diff --git a/dist/modules/seo/buildSitemapEntries.js.map b/dist/modules/seo/buildSitemapEntries.js.map deleted file mode 100644 index ccf0101..0000000 --- a/dist/modules/seo/buildSitemapEntries.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/buildSitemapEntries.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { ContentOption } from '../content/index.js'\nimport type { I18nConfig } from '../i18n/index.js'\n\nimport { archiveFieldName } from '../content/index.js'\nimport { buildLocalizedPath, getLocalizedSlugs } from '../i18n/index.js'\nimport { buildHreflangAlternates } from './hreflang.js'\n\n/**\n * One sitemap entry, shaped for Next's `app/sitemap.ts`.\n *\n * `alternates.languages` is the important part for a multilingual site: Next\n * renders it as `` per URL, which is\n * exactly what Google uses to connect language versions. A sitemap without it\n * is the common, weaker kind.\n */\nexport type SitemapEntry = {\n alternates?: { languages: Record }\n changeFrequency?: 'always' | 'daily' | 'hourly' | 'monthly' | 'never' | 'weekly' | 'yearly'\n lastModified?: Date | string\n priority?: number\n url: string\n}\n\ntype CollectionEntry = {\n /** Localized segment for entries (archive page slugs), when applicable. */\n prefixSlugs?: Record\n slug: string\n}\n\ntype BuildSitemapArgs = {\n /** Absolute origin, e.g. 'https://example.com'. Required for valid sitemap URLs. */\n baseUrl: string\n changeFrequency?: SitemapEntry['changeFrequency']\n config: I18nConfig\n /** Archive-backed collections, same value as the plugin option. */\n content?: ContentOption\n /**\n * Slug of the page that is the site root (collapses to /{locale}).\n * Read from System Pages when omitted.\n */\n homeSlug?: string\n /** Pages collection slug. Defaults to 'pages'. */\n pagesSlug?: string\n payload: BasePayload\n /** SiteSettings global slug. Defaults to 'site-settings'. */\n settingsSlug?: string\n}\n\ntype DocRow = {\n _status?: string\n id: number | string\n meta?: { noindex?: boolean } | null\n slug?: unknown\n updatedAt?: string\n}\n\n/** Skip drafts and anything flagged noindex in the SEO tab. */\nfunction isIndexable(doc: DocRow): boolean {\n if (doc._status && doc._status !== 'published') {return false}\n if (doc.meta?.noindex) {return false}\n return true\n}\n\n/**\n * Turns one document into a sitemap entry per default locale isn't needed —\n * one entry with all languages as alternates is the correct, de-duplicated\n * shape. The `url` is the current locale's path; `alternates.languages` carries\n * every locale (including itself, per Google's guidance).\n */\nfunction entryFor(\n doc: DocRow,\n locale: string,\n config: I18nConfig,\n baseUrl: string,\n homeSlug: string | undefined,\n prefix: Record | undefined,\n changeFrequency: SitemapEntry['changeFrequency'],\n): null | SitemapEntry {\n const slugs =\n doc.slug && typeof doc.slug === 'object'\n ? getLocalizedSlugs({ config, slugField: doc.slug as Record })\n : {}\n\n const path = buildLocalizedPath({ config, homeSlug, locale, prefix, slugs })\n if (!path) {return null}\n\n const origin = baseUrl.replace(/\\/$/, '')\n const languages = buildHreflangAlternates({ baseUrl, config, homeSlug, prefix, slugs })\n\n return {\n url: `${origin}${path}`,\n ...(doc.updatedAt ? { lastModified: doc.updatedAt } : {}),\n ...(changeFrequency ? { changeFrequency } : {}),\n ...(Object.keys(languages).length ? { alternates: { languages } } : {}),\n }\n}\n\n/**\n * Collects every public URL — pages and archive entries — as sitemap entries\n * with per-URL hreflang and lastmod.\n *\n * Reuses the same path/hreflang builders as page metadata, so the sitemap can't\n * drift from what the pages actually render (a classic source of sitemap bugs:\n * a URL listed one way and served another). Drafts and noindex documents are\n * omitted.\n *\n * ```ts\n * // app/sitemap.ts\n * export default async function sitemap() {\n * return buildSitemapEntries({\n * payload: await getPayload({ config }),\n * config: i18nConfig,\n * baseUrl: process.env.NEXT_PUBLIC_SERVER_URL!,\n * content: contentConfig,\n * })\n * }\n * ```\n */\nexport async function buildSitemapEntries({\n baseUrl,\n changeFrequency = 'weekly',\n config,\n content,\n homeSlug,\n pagesSlug = 'pages',\n payload,\n settingsSlug = 'site-settings',\n}: BuildSitemapArgs): Promise {\n const locales = config.locales.map((l) => l.code)\n const defaultLocale = config.defaultLocale\n\n // Resolve homeSlug and archive prefixes from System Pages (read once, in all\n // locales so archive prefixes are available per language).\n const settings = (await payload.findGlobal({\n slug: settingsSlug as never,\n depth: 1,\n locale: 'all' as never,\n })) as Record\n\n const resolvedHomeSlug =\n homeSlug ?? extractSlugInLocale(settings.homepage, defaultLocale) ?? 'home'\n\n // Which collections to walk: pages (no prefix) + each content collection with\n // its archive-page slugs as the localized prefix.\n const collections: CollectionEntry[] = [{ slug: pagesSlug }]\n\n for (const c of content?.collections ?? []) {\n const archive = settings[archiveFieldName(c.slug)]\n const prefixSlugs = archive ? slugMapAllLocales(archive, locales) : undefined\n collections.push({ slug: c.slug, prefixSlugs })\n }\n\n const entries: SitemapEntry[] = []\n\n for (const collection of collections) {\n // Read every doc once, across locales, so one row yields all its language\n // alternates without re-querying per locale.\n const result = await payload.find({\n collection: collection.slug as never,\n depth: 0,\n limit: 0, // no pagination — sitemap wants everything\n locale: 'all' as never,\n pagination: false as never,\n })\n\n for (const raw of result.docs as DocRow[]) {\n if (!isIndexable(raw)) {continue}\n\n // Emit the entry under the default locale's URL; alternates cover the rest.\n const entry = entryFor(\n raw,\n defaultLocale,\n config,\n baseUrl,\n resolvedHomeSlug,\n collection.prefixSlugs,\n changeFrequency,\n )\n if (entry) {entries.push(entry)}\n }\n }\n\n return entries\n}\n\n/** Pulls a slug string from a populated relationship in a specific locale. */\nfunction extractSlugInLocale(rel: unknown, locale: string): string | undefined {\n if (!rel || typeof rel !== 'object') {return undefined}\n const slug = (rel as { slug?: unknown }).slug\n if (typeof slug === 'string') {return slug}\n if (slug && typeof slug === 'object') {\n const v = (slug as Record)[locale]\n return typeof v === 'string' ? v : undefined\n }\n return undefined\n}\n\n/** Builds a locale→slug map from a populated archive relationship. */\nfunction slugMapAllLocales(rel: unknown, locales: string[]): Record | undefined {\n if (!rel || typeof rel !== 'object') {return undefined}\n const slug = (rel as { slug?: unknown }).slug\n if (!slug || typeof slug !== 'object') {return undefined}\n\n const map: Record = {}\n for (const locale of locales) {\n const v = (slug as Record)[locale]\n if (typeof v === 'string') {map[locale] = v}\n }\n return Object.keys(map).length ? map : undefined\n}\n"],"names":["archiveFieldName","buildLocalizedPath","getLocalizedSlugs","buildHreflangAlternates","isIndexable","doc","_status","meta","noindex","entryFor","locale","config","baseUrl","homeSlug","prefix","changeFrequency","slugs","slug","slugField","path","origin","replace","languages","url","updatedAt","lastModified","Object","keys","length","alternates","buildSitemapEntries","content","pagesSlug","payload","settingsSlug","locales","map","l","code","defaultLocale","settings","findGlobal","depth","resolvedHomeSlug","extractSlugInLocale","homepage","collections","c","archive","prefixSlugs","slugMapAllLocales","undefined","push","entries","collection","result","find","limit","pagination","raw","docs","entry","rel","v"],"mappings":"AAKA,SAASA,gBAAgB,QAAQ,sBAAqB;AACtD,SAASC,kBAAkB,EAAEC,iBAAiB,QAAS,mBAAkB;AACzE,SAASC,uBAAuB,QAAQ,gBAAe;AAmDvD,6DAA6D,GAC7D,SAASC,YAAYC,GAAW;IAC9B,IAAIA,IAAIC,OAAO,IAAID,IAAIC,OAAO,KAAK,aAAa;QAAC,OAAO;IAAK;IAC7D,IAAID,IAAIE,IAAI,EAAEC,SAAS;QAAC,OAAO;IAAK;IACpC,OAAO;AACT;AAEA;;;;;CAKC,GACD,SAASC,SACPJ,GAAW,EACXK,MAAc,EACdC,MAAkB,EAClBC,OAAe,EACfC,QAA4B,EAC5BC,MAA0C,EAC1CC,eAAgD;IAEhD,MAAMC,QACJX,IAAIY,IAAI,IAAI,OAAOZ,IAAIY,IAAI,KAAK,WAC5Bf,kBAAkB;QAAES;QAAQO,WAAWb,IAAIY,IAAI;IAA4B,KAC3E,CAAC;IAEP,MAAME,OAAOlB,mBAAmB;QAAEU;QAAQE;QAAUH;QAAQI;QAAQE;IAAM;IAC1E,IAAI,CAACG,MAAM;QAAC,OAAO;IAAI;IAEvB,MAAMC,SAASR,QAAQS,OAAO,CAAC,OAAO;IACtC,MAAMC,YAAYnB,wBAAwB;QAAES;QAASD;QAAQE;QAAUC;QAAQE;IAAM;IAErF,OAAO;QACLO,KAAK,GAAGH,SAASD,MAAM;QACvB,GAAId,IAAImB,SAAS,GAAG;YAAEC,cAAcpB,IAAImB,SAAS;QAAC,IAAI,CAAC,CAAC;QACxD,GAAIT,kBAAkB;YAAEA;QAAgB,IAAI,CAAC,CAAC;QAC9C,GAAIW,OAAOC,IAAI,CAACL,WAAWM,MAAM,GAAG;YAAEC,YAAY;gBAAEP;YAAU;QAAE,IAAI,CAAC,CAAC;IACxE;AACF;AAEA;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,eAAeQ,oBAAoB,EACxClB,OAAO,EACPG,kBAAkB,QAAQ,EAC1BJ,MAAM,EACNoB,OAAO,EACPlB,QAAQ,EACRmB,YAAY,OAAO,EACnBC,OAAO,EACPC,eAAe,eAAe,EACb;IACjB,MAAMC,UAAUxB,OAAOwB,OAAO,CAACC,GAAG,CAAC,CAACC,IAAMA,EAAEC,IAAI;IAChD,MAAMC,gBAAgB5B,OAAO4B,aAAa;IAE1C,6EAA6E;IAC7E,2DAA2D;IAC3D,MAAMC,WAAY,MAAMP,QAAQQ,UAAU,CAAC;QACzCxB,MAAMiB;QACNQ,OAAO;QACPhC,QAAQ;IACV;IAEA,MAAMiC,mBACJ9B,YAAY+B,oBAAoBJ,SAASK,QAAQ,EAAEN,kBAAkB;IAEvE,8EAA8E;IAC9E,kDAAkD;IAClD,MAAMO,cAAiC;QAAC;YAAE7B,MAAMe;QAAU;KAAE;IAE5D,KAAK,MAAMe,KAAKhB,SAASe,eAAe,EAAE,CAAE;QAC1C,MAAME,UAAUR,QAAQ,CAACxC,iBAAiB+C,EAAE9B,IAAI,EAAE;QAClD,MAAMgC,cAAcD,UAAUE,kBAAkBF,SAASb,WAAWgB;QACpEL,YAAYM,IAAI,CAAC;YAAEnC,MAAM8B,EAAE9B,IAAI;YAAEgC;QAAY;IAC/C;IAEA,MAAMI,UAA0B,EAAE;IAElC,KAAK,MAAMC,cAAcR,YAAa;QACpC,0EAA0E;QAC1E,6CAA6C;QAC7C,MAAMS,SAAS,MAAMtB,QAAQuB,IAAI,CAAC;YAChCF,YAAYA,WAAWrC,IAAI;YAC3ByB,OAAO;YACPe,OAAO;YACP/C,QAAQ;YACRgD,YAAY;QACd;QAEA,KAAK,MAAMC,OAAOJ,OAAOK,IAAI,CAAc;YACzC,IAAI,CAACxD,YAAYuD,MAAM;gBAAC;YAAQ;YAEhC,4EAA4E;YAC5E,MAAME,QAAQpD,SACZkD,KACApB,eACA5B,QACAC,SACA+B,kBACAW,WAAWL,WAAW,EACtBlC;YAEF,IAAI8C,OAAO;gBAACR,QAAQD,IAAI,CAACS;YAAM;QACjC;IACF;IAEA,OAAOR;AACT;AAEA,4EAA4E,GAC5E,SAAST,oBAAoBkB,GAAY,EAAEpD,MAAc;IACvD,IAAI,CAACoD,OAAO,OAAOA,QAAQ,UAAU;QAAC,OAAOX;IAAS;IACtD,MAAMlC,OAAO,AAAC6C,IAA2B7C,IAAI;IAC7C,IAAI,OAAOA,SAAS,UAAU;QAAC,OAAOA;IAAI;IAC1C,IAAIA,QAAQ,OAAOA,SAAS,UAAU;QACpC,MAAM8C,IAAI,AAAC9C,IAAgC,CAACP,OAAO;QACnD,OAAO,OAAOqD,MAAM,WAAWA,IAAIZ;IACrC;IACA,OAAOA;AACT;AAEA,oEAAoE,GACpE,SAASD,kBAAkBY,GAAY,EAAE3B,OAAiB;IACxD,IAAI,CAAC2B,OAAO,OAAOA,QAAQ,UAAU;QAAC,OAAOX;IAAS;IACtD,MAAMlC,OAAO,AAAC6C,IAA2B7C,IAAI;IAC7C,IAAI,CAACA,QAAQ,OAAOA,SAAS,UAAU;QAAC,OAAOkC;IAAS;IAExD,MAAMf,MAA8B,CAAC;IACrC,KAAK,MAAM1B,UAAUyB,QAAS;QAC5B,MAAM4B,IAAI,AAAC9C,IAAgC,CAACP,OAAO;QACnD,IAAI,OAAOqD,MAAM,UAAU;YAAC3B,GAAG,CAAC1B,OAAO,GAAGqD;QAAC;IAC7C;IACA,OAAOrC,OAAOC,IAAI,CAACS,KAAKR,MAAM,GAAGQ,MAAMe;AACzC"} \ No newline at end of file diff --git a/dist/modules/seo/composeTitle.d.ts b/dist/modules/seo/composeTitle.d.ts deleted file mode 100644 index 3c92e24..0000000 --- a/dist/modules/seo/composeTitle.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** Which part comes first in a composed title. */ -export type TitleOrder = 'page-first' | 'site-first'; -type ComposeTitleArgs = { - /** Defaults to 'page-first' — the page title is what a visitor scans for. */ - order?: TitleOrder; - /** Page-specific title, e.g. 'About Us'. */ - pageTitle?: null | string; - /** Separator between page title and site name. Defaults to ' | '. */ - separator?: string; - /** Site name, e.g. 'Acme Inc'. */ - siteName?: null | string; -}; -/** - * Composes a full document title from a page title and the site name. - * - * - both, page-first: "About Us | Acme Inc" - * - both, site-first: "Acme Inc | About Us" - * - page only: "About Us" - * - site only: "Acme Inc" - * - neither: "" - * - * Pure function — no dependency on Payload or request state. - */ -export declare function composeTitle({ order, pageTitle, separator, siteName, }: ComposeTitleArgs): string; -export {}; diff --git a/dist/modules/seo/composeTitle.js b/dist/modules/seo/composeTitle.js deleted file mode 100644 index 5d5b958..0000000 --- a/dist/modules/seo/composeTitle.js +++ /dev/null @@ -1,20 +0,0 @@ -/** Which part comes first in a composed title. */ /** - * Composes a full document title from a page title and the site name. - * - * - both, page-first: "About Us | Acme Inc" - * - both, site-first: "Acme Inc | About Us" - * - page only: "About Us" - * - site only: "Acme Inc" - * - neither: "" - * - * Pure function — no dependency on Payload or request state. - */ export function composeTitle({ order = 'page-first', pageTitle, separator = ' | ', siteName }) { - const page = pageTitle?.trim(); - const site = siteName?.trim(); - if (page && site) { - return order === 'site-first' ? `${site}${separator}${page}` : `${page}${separator}${site}`; - } - return page || site || ''; -} - -//# sourceMappingURL=composeTitle.js.map \ No newline at end of file diff --git a/dist/modules/seo/composeTitle.js.map b/dist/modules/seo/composeTitle.js.map deleted file mode 100644 index bfefe1e..0000000 --- a/dist/modules/seo/composeTitle.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/composeTitle.ts"],"sourcesContent":["/** Which part comes first in a composed title. */\nexport type TitleOrder = 'page-first' | 'site-first'\n\ntype ComposeTitleArgs = {\n /** Defaults to 'page-first' — the page title is what a visitor scans for. */\n order?: TitleOrder\n /** Page-specific title, e.g. 'About Us'. */\n pageTitle?: null | string\n /** Separator between page title and site name. Defaults to ' | '. */\n separator?: string\n /** Site name, e.g. 'Acme Inc'. */\n siteName?: null | string\n}\n\n/**\n * Composes a full document title from a page title and the site name.\n *\n * - both, page-first: \"About Us | Acme Inc\"\n * - both, site-first: \"Acme Inc | About Us\"\n * - page only: \"About Us\"\n * - site only: \"Acme Inc\"\n * - neither: \"\"\n *\n * Pure function — no dependency on Payload or request state.\n */\nexport function composeTitle({\n order = 'page-first',\n pageTitle,\n separator = ' | ',\n siteName,\n}: ComposeTitleArgs): string {\n const page = pageTitle?.trim()\n const site = siteName?.trim()\n\n if (page && site) {\n return order === 'site-first' ? `${site}${separator}${page}` : `${page}${separator}${site}`\n }\n return page || site || ''\n}\n"],"names":["composeTitle","order","pageTitle","separator","siteName","page","trim","site"],"mappings":"AAAA,gDAAgD,GAchD;;;;;;;;;;CAUC,GACD,OAAO,SAASA,aAAa,EAC3BC,QAAQ,YAAY,EACpBC,SAAS,EACTC,YAAY,KAAK,EACjBC,QAAQ,EACS;IACjB,MAAMC,OAAOH,WAAWI;IACxB,MAAMC,OAAOH,UAAUE;IAEvB,IAAID,QAAQE,MAAM;QAChB,OAAON,UAAU,eAAe,GAAGM,OAAOJ,YAAYE,MAAM,GAAG,GAAGA,OAAOF,YAAYI,MAAM;IAC7F;IACA,OAAOF,QAAQE,QAAQ;AACzB"} \ No newline at end of file diff --git a/dist/modules/seo/createMetadataGenerator.d.ts b/dist/modules/seo/createMetadataGenerator.d.ts deleted file mode 100644 index ffe4467..0000000 --- a/dist/modules/seo/createMetadataGenerator.d.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { I18nConfig } from '../i18n/index.js'; -import type { PageMetadata } from './buildMetadata.js'; -import type { SeoMeta } from './types.js'; -/** - * A document as needed for metadata: its SEO meta group and localized slug. - */ -type MetadataDocument = { - meta?: null | SeoMeta; - slug?: null | Record; -}; -type CreateMetadataGeneratorArgs = { - /** Absolute site origin, e.g. 'https://example.com'. */ - baseUrl?: string; - config: I18nConfig; - /** Home slug that collapses to the locale root. Defaults to 'home'. */ - homeSlug?: string; - /** - * Resolves the document to build metadata for, given route params. - * The client supplies this (they own the collections and routing); it should - * fetch with `locale: 'all'` so the slug field is a locale→value map. - */ - resolveDocument: (args: { - locale: string; - params: Record; - payload: BasePayload; - }) => Promise; - /** Resolves the OG image URL for the document, if any. */ - resolveImageUrl?: (args: { - doc: MetadataDocument; - locale: string; - payload: BasePayload; - }) => Promise; - /** Resolves the site name (e.g. from SiteSettings). */ - resolveSiteName?: (args: { - locale: string; - payload: BasePayload; - }) => Promise; -}; -/** - * Builds a metadata generator, collapsing the usual generateMetadata - * boilerplate into a single wired-up function. - * - * The client provides resolvers (they own collections/routing); the plugin - * owns the assembly (title composition, canonical, hreflang, OG). - * - * The returned function takes `{ payload, params, locale }` — Next calls - * `generateMetadata({ params })` without those, and the plugin never calls - * getPayload itself, so the client wraps it in their route file: - * - * ```ts - * // app/(frontend)/[locale]/[[...slug]]/page.tsx - * const generate = createMetadataGenerator({ - * config: i18nConfig, - * baseUrl: process.env.NEXT_PUBLIC_SERVER_URL, - * resolveDocument: async ({ payload, params, locale }) => { ... }, - * }) - * - * export async function generateMetadata({ params }) { - * const { locale, slug } = await params - * const payload = await getPayload({ config: await config }) - * return generate({ payload, params: { slug: slug ?? [] }, locale }) - * } - * ``` - * - * `resolveDocument` should fetch with `locale: 'all'` so the slug field comes - * back as a locale→value map — that's what hreflang alternates are built from. - */ -export declare function createMetadataGenerator(args: CreateMetadataGeneratorArgs): (context: { - locale: string; - params: Record; - payload: BasePayload; -}) => Promise; -export {}; diff --git a/dist/modules/seo/createMetadataGenerator.js b/dist/modules/seo/createMetadataGenerator.js deleted file mode 100644 index 4ce37ab..0000000 --- a/dist/modules/seo/createMetadataGenerator.js +++ /dev/null @@ -1,68 +0,0 @@ -import { getLocalizedSlugs } from '../i18n/index.js'; -import { buildMetadata } from './buildMetadata.js'; -/** - * Builds a metadata generator, collapsing the usual generateMetadata - * boilerplate into a single wired-up function. - * - * The client provides resolvers (they own collections/routing); the plugin - * owns the assembly (title composition, canonical, hreflang, OG). - * - * The returned function takes `{ payload, params, locale }` — Next calls - * `generateMetadata({ params })` without those, and the plugin never calls - * getPayload itself, so the client wraps it in their route file: - * - * ```ts - * // app/(frontend)/[locale]/[[...slug]]/page.tsx - * const generate = createMetadataGenerator({ - * config: i18nConfig, - * baseUrl: process.env.NEXT_PUBLIC_SERVER_URL, - * resolveDocument: async ({ payload, params, locale }) => { ... }, - * }) - * - * export async function generateMetadata({ params }) { - * const { locale, slug } = await params - * const payload = await getPayload({ config: await config }) - * return generate({ payload, params: { slug: slug ?? [] }, locale }) - * } - * ``` - * - * `resolveDocument` should fetch with `locale: 'all'` so the slug field comes - * back as a locale→value map — that's what hreflang alternates are built from. - */ export function createMetadataGenerator(args) { - const { baseUrl, config, homeSlug, resolveDocument, resolveImageUrl, resolveSiteName } = args; - return async function generateMetadata(context) { - const { locale, params, payload } = context; - const doc = await resolveDocument({ - locale, - params, - payload - }); - const slugs = doc?.slug ? getLocalizedSlugs({ - config, - slugField: doc.slug - }) : {}; - const [siteName, imageUrl] = await Promise.all([ - resolveSiteName?.({ - locale, - payload - }) ?? Promise.resolve(null), - doc && resolveImageUrl ? resolveImageUrl({ - doc, - locale, - payload - }) : Promise.resolve(null) - ]); - return buildMetadata({ - baseUrl, - config, - homeSlug, - imageUrl, - locale, - meta: doc?.meta, - siteName, - slugs - }); - }; -} - -//# sourceMappingURL=createMetadataGenerator.js.map \ No newline at end of file diff --git a/dist/modules/seo/createMetadataGenerator.js.map b/dist/modules/seo/createMetadataGenerator.js.map deleted file mode 100644 index bd001e5..0000000 --- a/dist/modules/seo/createMetadataGenerator.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/createMetadataGenerator.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { I18nConfig, LocalizedSlugs } from '../i18n/index.js'\nimport type { PageMetadata } from './buildMetadata.js'\nimport type { SeoMeta } from './types.js'\n\nimport { getLocalizedSlugs } from '../i18n/index.js'\nimport { buildMetadata } from './buildMetadata.js'\n\n/**\n * A document as needed for metadata: its SEO meta group and localized slug.\n */\ntype MetadataDocument = {\n meta?: null | SeoMeta\n slug?: null | Record\n}\n\ntype CreateMetadataGeneratorArgs = {\n /** Absolute site origin, e.g. 'https://example.com'. */\n baseUrl?: string\n config: I18nConfig\n /** Home slug that collapses to the locale root. Defaults to 'home'. */\n homeSlug?: string\n /**\n * Resolves the document to build metadata for, given route params.\n * The client supplies this (they own the collections and routing); it should\n * fetch with `locale: 'all'` so the slug field is a locale→value map.\n */\n resolveDocument: (args: {\n locale: string\n params: Record\n payload: BasePayload\n }) => Promise\n /** Resolves the OG image URL for the document, if any. */\n resolveImageUrl?: (args: {\n doc: MetadataDocument\n locale: string\n payload: BasePayload\n }) => Promise\n /** Resolves the site name (e.g. from SiteSettings). */\n resolveSiteName?: (args: { locale: string; payload: BasePayload }) => Promise\n}\n\n/**\n * Builds a metadata generator, collapsing the usual generateMetadata\n * boilerplate into a single wired-up function.\n *\n * The client provides resolvers (they own collections/routing); the plugin\n * owns the assembly (title composition, canonical, hreflang, OG).\n *\n * The returned function takes `{ payload, params, locale }` — Next calls\n * `generateMetadata({ params })` without those, and the plugin never calls\n * getPayload itself, so the client wraps it in their route file:\n *\n * ```ts\n * // app/(frontend)/[locale]/[[...slug]]/page.tsx\n * const generate = createMetadataGenerator({\n * config: i18nConfig,\n * baseUrl: process.env.NEXT_PUBLIC_SERVER_URL,\n * resolveDocument: async ({ payload, params, locale }) => { ... },\n * })\n *\n * export async function generateMetadata({ params }) {\n * const { locale, slug } = await params\n * const payload = await getPayload({ config: await config })\n * return generate({ payload, params: { slug: slug ?? [] }, locale })\n * }\n * ```\n *\n * `resolveDocument` should fetch with `locale: 'all'` so the slug field comes\n * back as a locale→value map — that's what hreflang alternates are built from.\n */\nexport function createMetadataGenerator(args: CreateMetadataGeneratorArgs) {\n const { baseUrl, config, homeSlug, resolveDocument, resolveImageUrl, resolveSiteName } = args\n\n return async function generateMetadata(context: {\n locale: string\n params: Record\n payload: BasePayload\n }): Promise {\n const { locale, params, payload } = context\n\n const doc = await resolveDocument({ locale, params, payload })\n\n const slugs: LocalizedSlugs = doc?.slug\n ? getLocalizedSlugs({ config, slugField: doc.slug })\n : {}\n\n const [siteName, imageUrl] = await Promise.all([\n resolveSiteName?.({ locale, payload }) ?? Promise.resolve(null),\n doc && resolveImageUrl ? resolveImageUrl({ doc, locale, payload }) : Promise.resolve(null),\n ])\n\n return buildMetadata({\n baseUrl,\n config,\n homeSlug,\n imageUrl,\n locale,\n meta: doc?.meta,\n siteName,\n slugs,\n })\n }\n}\n"],"names":["getLocalizedSlugs","buildMetadata","createMetadataGenerator","args","baseUrl","config","homeSlug","resolveDocument","resolveImageUrl","resolveSiteName","generateMetadata","context","locale","params","payload","doc","slugs","slug","slugField","siteName","imageUrl","Promise","all","resolve","meta"],"mappings":"AAMA,SAASA,iBAAiB,QAAQ,mBAAkB;AACpD,SAASC,aAAa,QAAQ,qBAAoB;AAoClD;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,OAAO,SAASC,wBAAwBC,IAAiC;IACvE,MAAM,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,eAAe,EAAEC,eAAe,EAAEC,eAAe,EAAE,GAAGN;IAEzF,OAAO,eAAeO,iBAAiBC,OAItC;QACC,MAAM,EAAEC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAE,GAAGH;QAEpC,MAAMI,MAAM,MAAMR,gBAAgB;YAAEK;YAAQC;YAAQC;QAAQ;QAE5D,MAAME,QAAwBD,KAAKE,OAC/BjB,kBAAkB;YAAEK;YAAQa,WAAWH,IAAIE,IAAI;QAAC,KAChD,CAAC;QAEL,MAAM,CAACE,UAAUC,SAAS,GAAG,MAAMC,QAAQC,GAAG,CAAC;YAC7Cb,kBAAkB;gBAAEG;gBAAQE;YAAQ,MAAMO,QAAQE,OAAO,CAAC;YAC1DR,OAAOP,kBAAkBA,gBAAgB;gBAAEO;gBAAKH;gBAAQE;YAAQ,KAAKO,QAAQE,OAAO,CAAC;SACtF;QAED,OAAOtB,cAAc;YACnBG;YACAC;YACAC;YACAc;YACAR;YACAY,MAAMT,KAAKS;YACXL;YACAH;QACF;IACF;AACF"} \ No newline at end of file diff --git a/dist/modules/seo/createPageMetadata.d.ts b/dist/modules/seo/createPageMetadata.d.ts deleted file mode 100644 index 8bc8dbd..0000000 --- a/dist/modules/seo/createPageMetadata.d.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { ContentOption } from '../content/index.js'; -import type { I18nConfig } from '../i18n/index.js'; -import type { PageMetadata } from './buildMetadata.js'; -type CreatePageMetadataArgs = { - /** Absolute site origin, e.g. 'https://example.com'. */ - baseUrl?: string; - /** Collection holding pages. Defaults to 'pages'. */ - collection?: string; - config: I18nConfig; - /** - * Archive-backed collections, same value as the plugin option. Pass it and - * entry URLs (/pl/artykuly/moj-post) get correct canonical and hreflang; - * omit it and only pages are handled. - */ - content?: ContentOption; - /** SiteSettings global slug. Defaults to 'site-settings'. */ - settingsSlug?: string; - /** Field on SiteSettings holding the site name. Defaults to 'siteName'. */ - siteNameField?: string; -}; -type PageMetadataContext = { - locale: string; - /** - * Page number from ?page= on an archive listing. Pass it and page 2 gets a - * canonical to itself; leave it out and every page claims to be page 1. - */ - page?: number; - payload: BasePayload; - /** Route slug segments; empty/undefined means the locale root. */ - slug?: string[]; -}; -/** - * Metadata for the page route, with every resolver already wired. - * - * `createMetadataGenerator` asks the client for resolvers because it can't know - * their collections. But for the plugin's own conventions it does know: pages - * live in one collection, the site name sits on SiteSettings, the OG image sits - * on the plugin-seo `meta` group, the home page is whatever System Pages points - * at, and — with `content` — entries live under their collection's archive page. - * Re-declaring all that in every project is copy-paste, so this resolves it. - * - * Reach for `createMetadataGenerator` instead when a route doesn't follow those - * conventions — custom image logic, a different global, hand-rolled paths. - * - * The plugin never calls getPayload, and Next calls generateMetadata without a - * payload, so the client keeps a small wrapper: - * - * ```ts - * const pageMetadata = createPageMetadata({ config: i18nConfig, baseUrl, content }) - * - * export async function generateMetadata({ params }) { - * const { locale, slug } = await params - * return pageMetadata({ payload: await getPayload({ config }), locale, slug }) - * } - * ``` - */ -export declare function createPageMetadata(args: CreatePageMetadataArgs): ({ slug, locale, page, payload, }: PageMetadataContext) => Promise; -export {}; diff --git a/dist/modules/seo/createPageMetadata.js b/dist/modules/seo/createPageMetadata.js deleted file mode 100644 index 0880024..0000000 --- a/dist/modules/seo/createPageMetadata.js +++ /dev/null @@ -1,100 +0,0 @@ -import { resolveRoute } from '../content/index.js'; -import { buildMetadata } from './buildMetadata.js'; -import { readSiteMetaConfig } from './readSiteMetaConfig.js'; -import { slugsAcrossLocales } from './slugsAcrossLocales.js'; -/** plugin-seo stores the OG image as an upload relationship. */ function resolveOgImage(doc) { - const image = doc.meta?.image; - if (image && typeof image === 'object' && 'url' in image) { - return image.url ?? null; - } - return null; -} -/** - * Metadata for the page route, with every resolver already wired. - * - * `createMetadataGenerator` asks the client for resolvers because it can't know - * their collections. But for the plugin's own conventions it does know: pages - * live in one collection, the site name sits on SiteSettings, the OG image sits - * on the plugin-seo `meta` group, the home page is whatever System Pages points - * at, and — with `content` — entries live under their collection's archive page. - * Re-declaring all that in every project is copy-paste, so this resolves it. - * - * Reach for `createMetadataGenerator` instead when a route doesn't follow those - * conventions — custom image logic, a different global, hand-rolled paths. - * - * The plugin never calls getPayload, and Next calls generateMetadata without a - * payload, so the client keeps a small wrapper: - * - * ```ts - * const pageMetadata = createPageMetadata({ config: i18nConfig, baseUrl, content }) - * - * export async function generateMetadata({ params }) { - * const { locale, slug } = await params - * return pageMetadata({ payload: await getPayload({ config }), locale, slug }) - * } - * ``` - */ export function createPageMetadata(args) { - const { baseUrl, collection = 'pages', config, content, settingsSlug = 'site-settings', siteNameField = 'siteName' } = args; - return async function pageMetadata({ slug, locale, page, payload }) { - const site = await readSiteMetaConfig({ - locale, - payload, - settingsSlug, - siteNameField - }); - const base = { - baseUrl, - config, - homeSlug: site.homeSlug, - locale, - order: site.order, - separator: site.separator, - siteName: site.siteName - }; - const route = await resolveRoute({ - content, - locale, - page, - pagesSlug: collection, - payload, - segments: slug, - settingsSlug - }); - // Unknown route (the page component will 404) — still return something - // coherent rather than throwing during metadata generation. - if (!route) { - return buildMetadata({ - ...base, - meta: null, - slugs: {} - }); - } - const doc = route.doc; - // An entry sits under its archive, so its URLs need that segment — and the - // segment differs per locale, since it's the archive page's own slug. - const prefix = route.type === 'entry' ? await slugsAcrossLocales({ - id: route.archive.id, - collection, - config, - payload - }) : undefined; - const slugs = await slugsAcrossLocales({ - id: doc.id, - collection: route.type === 'entry' ? route.collection : collection, - config, - payload - }); - // Page 2 of a listing is its own URL, not a variant of page 1. - const query = route.type === 'archive' && route.page > 1 ? `?page=${route.page}` : undefined; - return buildMetadata({ - ...base, - imageUrl: resolveOgImage(doc), - meta: doc.meta, - prefix, - query, - slugs - }); - }; -} - -//# sourceMappingURL=createPageMetadata.js.map \ No newline at end of file diff --git a/dist/modules/seo/createPageMetadata.js.map b/dist/modules/seo/createPageMetadata.js.map deleted file mode 100644 index e1b3faf..0000000 --- a/dist/modules/seo/createPageMetadata.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/createPageMetadata.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { ContentOption } from '../content/index.js'\nimport type { I18nConfig } from '../i18n/index.js'\nimport type { PageMetadata } from './buildMetadata.js'\nimport type { SeoMeta } from './types.js'\n\nimport { resolveRoute } from '../content/index.js'\nimport { buildMetadata } from './buildMetadata.js'\nimport { readSiteMetaConfig } from './readSiteMetaConfig.js'\nimport { slugsAcrossLocales } from './slugsAcrossLocales.js'\n\ntype CreatePageMetadataArgs = {\n /** Absolute site origin, e.g. 'https://example.com'. */\n baseUrl?: string\n /** Collection holding pages. Defaults to 'pages'. */\n collection?: string\n config: I18nConfig\n /**\n * Archive-backed collections, same value as the plugin option. Pass it and\n * entry URLs (/pl/artykuly/moj-post) get correct canonical and hreflang;\n * omit it and only pages are handled.\n */\n content?: ContentOption\n /** SiteSettings global slug. Defaults to 'site-settings'. */\n settingsSlug?: string\n /** Field on SiteSettings holding the site name. Defaults to 'siteName'. */\n siteNameField?: string\n}\n\ntype PageMetadataContext = {\n locale: string\n /**\n * Page number from ?page= on an archive listing. Pass it and page 2 gets a\n * canonical to itself; leave it out and every page claims to be page 1.\n */\n page?: number\n payload: BasePayload\n /** Route slug segments; empty/undefined means the locale root. */\n slug?: string[]\n}\n\ntype DocShape = {\n id: number | string\n meta?: null | SeoMeta\n}\n\n/** plugin-seo stores the OG image as an upload relationship. */\nfunction resolveOgImage(doc: DocShape): null | string {\n const image = (doc.meta as { image?: unknown } | null | undefined)?.image\n if (image && typeof image === 'object' && 'url' in image) {\n return (image as { url: string }).url ?? null\n }\n return null\n}\n\n/**\n * Metadata for the page route, with every resolver already wired.\n *\n * `createMetadataGenerator` asks the client for resolvers because it can't know\n * their collections. But for the plugin's own conventions it does know: pages\n * live in one collection, the site name sits on SiteSettings, the OG image sits\n * on the plugin-seo `meta` group, the home page is whatever System Pages points\n * at, and — with `content` — entries live under their collection's archive page.\n * Re-declaring all that in every project is copy-paste, so this resolves it.\n *\n * Reach for `createMetadataGenerator` instead when a route doesn't follow those\n * conventions — custom image logic, a different global, hand-rolled paths.\n *\n * The plugin never calls getPayload, and Next calls generateMetadata without a\n * payload, so the client keeps a small wrapper:\n *\n * ```ts\n * const pageMetadata = createPageMetadata({ config: i18nConfig, baseUrl, content })\n *\n * export async function generateMetadata({ params }) {\n * const { locale, slug } = await params\n * return pageMetadata({ payload: await getPayload({ config }), locale, slug })\n * }\n * ```\n */\nexport function createPageMetadata(args: CreatePageMetadataArgs) {\n const {\n baseUrl,\n collection = 'pages',\n config,\n content,\n settingsSlug = 'site-settings',\n siteNameField = 'siteName',\n } = args\n\n return async function pageMetadata({\n slug,\n locale,\n page,\n payload,\n }: PageMetadataContext): Promise {\n const site = await readSiteMetaConfig({ locale, payload, settingsSlug, siteNameField })\n\n const base = {\n baseUrl,\n config,\n homeSlug: site.homeSlug,\n locale,\n order: site.order,\n separator: site.separator,\n siteName: site.siteName,\n }\n\n const route = await resolveRoute({\n content,\n locale,\n page,\n pagesSlug: collection,\n payload,\n segments: slug,\n settingsSlug,\n })\n\n // Unknown route (the page component will 404) — still return something\n // coherent rather than throwing during metadata generation.\n if (!route) {\n return buildMetadata({ ...base, meta: null, slugs: {} })\n }\n\n const doc = route.doc as DocShape\n\n // An entry sits under its archive, so its URLs need that segment — and the\n // segment differs per locale, since it's the archive page's own slug.\n const prefix =\n route.type === 'entry'\n ? await slugsAcrossLocales({\n id: (route.archive as DocShape).id,\n collection,\n config,\n payload,\n })\n : undefined\n\n const slugs = await slugsAcrossLocales({\n id: doc.id,\n collection: route.type === 'entry' ? route.collection : collection,\n config,\n payload,\n })\n\n // Page 2 of a listing is its own URL, not a variant of page 1.\n const query = route.type === 'archive' && route.page > 1 ? `?page=${route.page}` : undefined\n\n return buildMetadata({\n ...base,\n imageUrl: resolveOgImage(doc),\n meta: doc.meta,\n prefix,\n query,\n slugs,\n })\n }\n}\n"],"names":["resolveRoute","buildMetadata","readSiteMetaConfig","slugsAcrossLocales","resolveOgImage","doc","image","meta","url","createPageMetadata","args","baseUrl","collection","config","content","settingsSlug","siteNameField","pageMetadata","slug","locale","page","payload","site","base","homeSlug","order","separator","siteName","route","pagesSlug","segments","slugs","prefix","type","id","archive","undefined","query","imageUrl"],"mappings":"AAOA,SAASA,YAAY,QAAQ,sBAAqB;AAClD,SAASC,aAAa,QAAQ,qBAAoB;AAClD,SAASC,kBAAkB,QAAQ,0BAAyB;AAC5D,SAASC,kBAAkB,QAAQ,0BAAyB;AAqC5D,8DAA8D,GAC9D,SAASC,eAAeC,GAAa;IACnC,MAAMC,QAASD,IAAIE,IAAI,EAA6CD;IACpE,IAAIA,SAAS,OAAOA,UAAU,YAAY,SAASA,OAAO;QACxD,OAAO,AAACA,MAA0BE,GAAG,IAAI;IAC3C;IACA,OAAO;AACT;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC,mBAAmBC,IAA4B;IAC7D,MAAM,EACJC,OAAO,EACPC,aAAa,OAAO,EACpBC,MAAM,EACNC,OAAO,EACPC,eAAe,eAAe,EAC9BC,gBAAgB,UAAU,EAC3B,GAAGN;IAEJ,OAAO,eAAeO,aAAa,EACjCC,IAAI,EACJC,MAAM,EACNC,IAAI,EACJC,OAAO,EACa;QACpB,MAAMC,OAAO,MAAMpB,mBAAmB;YAAEiB;YAAQE;YAASN;YAAcC;QAAc;QAErF,MAAMO,OAAO;YACXZ;YACAE;YACAW,UAAUF,KAAKE,QAAQ;YACvBL;YACAM,OAAOH,KAAKG,KAAK;YACjBC,WAAWJ,KAAKI,SAAS;YACzBC,UAAUL,KAAKK,QAAQ;QACzB;QAEA,MAAMC,QAAQ,MAAM5B,aAAa;YAC/Bc;YACAK;YACAC;YACAS,WAAWjB;YACXS;YACAS,UAAUZ;YACVH;QACF;QAEA,uEAAuE;QACvE,4DAA4D;QAC5D,IAAI,CAACa,OAAO;YACV,OAAO3B,cAAc;gBAAE,GAAGsB,IAAI;gBAAEhB,MAAM;gBAAMwB,OAAO,CAAC;YAAE;QACxD;QAEA,MAAM1B,MAAMuB,MAAMvB,GAAG;QAErB,2EAA2E;QAC3E,sEAAsE;QACtE,MAAM2B,SACJJ,MAAMK,IAAI,KAAK,UACX,MAAM9B,mBAAmB;YACvB+B,IAAI,AAACN,MAAMO,OAAO,CAAcD,EAAE;YAClCtB;YACAC;YACAQ;QACF,KACAe;QAEN,MAAML,QAAQ,MAAM5B,mBAAmB;YACrC+B,IAAI7B,IAAI6B,EAAE;YACVtB,YAAYgB,MAAMK,IAAI,KAAK,UAAUL,MAAMhB,UAAU,GAAGA;YACxDC;YACAQ;QACF;QAEA,+DAA+D;QAC/D,MAAMgB,QAAQT,MAAMK,IAAI,KAAK,aAAaL,MAAMR,IAAI,GAAG,IAAI,CAAC,MAAM,EAAEQ,MAAMR,IAAI,EAAE,GAAGgB;QAEnF,OAAOnC,cAAc;YACnB,GAAGsB,IAAI;YACPe,UAAUlC,eAAeC;YACzBE,MAAMF,IAAIE,IAAI;YACdyB;YACAK;YACAN;QACF;IACF;AACF"} \ No newline at end of file diff --git a/dist/modules/seo/hreflang.d.ts b/dist/modules/seo/hreflang.d.ts deleted file mode 100644 index f559f0d..0000000 --- a/dist/modules/seo/hreflang.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { I18nConfig, LocalizedSlugs } from '../i18n/index.js'; -type BuildHreflangArgs = { - /** Absolute site origin, e.g. 'https://example.com'. Omit for relative paths. */ - baseUrl?: string; - config: I18nConfig; - /** Home slug that collapses to the locale root. Defaults to 'home'. */ - homeSlug?: string; - /** - * Localized segment the document lives under (an archive page's slugs), - * e.g. { pl: 'artykuly', en: 'articles' }. Locales missing from the prefix - * are omitted — an entry with no archive in that language has no URL there. - */ - prefix?: LocalizedSlugs; - /** slug per locale for the current document, e.g. { pl: 'o-nas', en: 'about' } */ - slugs: LocalizedSlugs; -}; -/** - * Builds a map of locale → URL for hreflang alternate links, suitable for - * Next.js Metadata `alternates.languages`. - * - * Bridges SEO and i18n: for each configured locale that the document has a - * slug in, it produces the locale-aware path (via buildLocalizedPath), - * optionally prefixed with an absolute origin. - * - * @example - * buildHreflangAlternates({ - * slugs: { pl: 'o-nas', en: 'about' }, - * config, - * baseUrl: 'https://example.com', - * }) - * // → { pl: 'https://example.com/pl/o-nas', en: 'https://example.com/en/about' } - */ -export declare function buildHreflangAlternates({ baseUrl, config, homeSlug, prefix, slugs, }: BuildHreflangArgs): Record; -export {}; diff --git a/dist/modules/seo/hreflang.js b/dist/modules/seo/hreflang.js deleted file mode 100644 index 0a74d4e..0000000 --- a/dist/modules/seo/hreflang.js +++ /dev/null @@ -1,35 +0,0 @@ -import { buildLocalizedPath, getLocaleCodes } from '../i18n/index.js'; -/** - * Builds a map of locale → URL for hreflang alternate links, suitable for - * Next.js Metadata `alternates.languages`. - * - * Bridges SEO and i18n: for each configured locale that the document has a - * slug in, it produces the locale-aware path (via buildLocalizedPath), - * optionally prefixed with an absolute origin. - * - * @example - * buildHreflangAlternates({ - * slugs: { pl: 'o-nas', en: 'about' }, - * config, - * baseUrl: 'https://example.com', - * }) - * // → { pl: 'https://example.com/pl/o-nas', en: 'https://example.com/en/about' } - */ export function buildHreflangAlternates({ baseUrl, config, homeSlug = 'home', prefix, slugs }) { - const origin = baseUrl?.replace(/\/$/, '') ?? ''; - const alternates = {}; - for (const locale of getLocaleCodes(config)){ - const path = buildLocalizedPath({ - config, - homeSlug, - locale, - prefix, - slugs - }); - if (path) { - alternates[locale] = `${origin}${path}`; - } - } - return alternates; -} - -//# sourceMappingURL=hreflang.js.map \ No newline at end of file diff --git a/dist/modules/seo/hreflang.js.map b/dist/modules/seo/hreflang.js.map deleted file mode 100644 index 4ca97fe..0000000 --- a/dist/modules/seo/hreflang.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/hreflang.ts"],"sourcesContent":["import type { I18nConfig, LocalizedSlugs } from '../i18n/index.js'\n\nimport { buildLocalizedPath, getLocaleCodes } from '../i18n/index.js'\n\ntype BuildHreflangArgs = {\n /** Absolute site origin, e.g. 'https://example.com'. Omit for relative paths. */\n baseUrl?: string\n config: I18nConfig\n /** Home slug that collapses to the locale root. Defaults to 'home'. */\n homeSlug?: string\n /**\n * Localized segment the document lives under (an archive page's slugs),\n * e.g. { pl: 'artykuly', en: 'articles' }. Locales missing from the prefix\n * are omitted — an entry with no archive in that language has no URL there.\n */\n prefix?: LocalizedSlugs\n /** slug per locale for the current document, e.g. { pl: 'o-nas', en: 'about' } */\n slugs: LocalizedSlugs\n}\n\n/**\n * Builds a map of locale → URL for hreflang alternate links, suitable for\n * Next.js Metadata `alternates.languages`.\n *\n * Bridges SEO and i18n: for each configured locale that the document has a\n * slug in, it produces the locale-aware path (via buildLocalizedPath),\n * optionally prefixed with an absolute origin.\n *\n * @example\n * buildHreflangAlternates({\n * slugs: { pl: 'o-nas', en: 'about' },\n * config,\n * baseUrl: 'https://example.com',\n * })\n * // → { pl: 'https://example.com/pl/o-nas', en: 'https://example.com/en/about' }\n */\nexport function buildHreflangAlternates({\n baseUrl,\n config,\n homeSlug = 'home',\n prefix,\n slugs,\n}: BuildHreflangArgs): Record {\n const origin = baseUrl?.replace(/\\/$/, '') ?? ''\n const alternates: Record = {}\n\n for (const locale of getLocaleCodes(config)) {\n const path = buildLocalizedPath({ config, homeSlug, locale, prefix, slugs })\n if (path) {\n alternates[locale] = `${origin}${path}`\n }\n }\n\n return alternates\n}\n"],"names":["buildLocalizedPath","getLocaleCodes","buildHreflangAlternates","baseUrl","config","homeSlug","prefix","slugs","origin","replace","alternates","locale","path"],"mappings":"AAEA,SAASA,kBAAkB,EAAEC,cAAc,QAAQ,mBAAkB;AAkBrE;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC,wBAAwB,EACtCC,OAAO,EACPC,MAAM,EACNC,WAAW,MAAM,EACjBC,MAAM,EACNC,KAAK,EACa;IAClB,MAAMC,SAASL,SAASM,QAAQ,OAAO,OAAO;IAC9C,MAAMC,aAAqC,CAAC;IAE5C,KAAK,MAAMC,UAAUV,eAAeG,QAAS;QAC3C,MAAMQ,OAAOZ,mBAAmB;YAAEI;YAAQC;YAAUM;YAAQL;YAAQC;QAAM;QAC1E,IAAIK,MAAM;YACRF,UAAU,CAACC,OAAO,GAAG,GAAGH,SAASI,MAAM;QACzC;IACF;IAEA,OAAOF;AACT"} \ No newline at end of file diff --git a/dist/modules/seo/index.d.ts b/dist/modules/seo/index.d.ts deleted file mode 100644 index 46841ff..0000000 --- a/dist/modules/seo/index.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -export { buildAutoFillMetaHook } from './autoFillMeta.js'; -export type { AutoFillMapping } from './autoFillMeta.js'; -export { buildMetadata } from './buildMetadata.js'; -export type { PageMetadata } from './buildMetadata.js'; -export { buildRobots } from './buildRobots.js'; -export type { RobotsRules } from './buildRobots.js'; -export { buildSitemapEntries } from './buildSitemapEntries.js'; -export type { SitemapEntry } from './buildSitemapEntries.js'; -export { composeTitle } from './composeTitle.js'; -export type { TitleOrder } from './composeTitle.js'; -export { createMetadataGenerator } from './createMetadataGenerator.js'; -export { createPageMetadata } from './createPageMetadata.js'; -export { buildHreflangAlternates } from './hreflang.js'; -export { injectAutoFillMeta } from './injectAutoFillMeta.js'; -export { injectSeoTabs } from './injectSeoTabs.js'; -export { readSiteMetaConfig } from './readSiteMetaConfig.js'; -export type { SiteMetaConfig } from './readSiteMetaConfig.js'; -export { buildSeoPlugin } from './seoPluginConfig.js'; -export { slugsAcrossLocales } from './slugsAcrossLocales.js'; -export type { SeoMeta, SeoOption } from './types.js'; diff --git a/dist/modules/seo/index.js b/dist/modules/seo/index.js deleted file mode 100644 index ef3cc66..0000000 --- a/dist/modules/seo/index.js +++ /dev/null @@ -1,15 +0,0 @@ -export { buildAutoFillMetaHook } from './autoFillMeta.js'; -export { buildMetadata } from './buildMetadata.js'; -export { buildRobots } from './buildRobots.js'; -export { buildSitemapEntries } from './buildSitemapEntries.js'; -export { composeTitle } from './composeTitle.js'; -export { createMetadataGenerator } from './createMetadataGenerator.js'; -export { createPageMetadata } from './createPageMetadata.js'; -export { buildHreflangAlternates } from './hreflang.js'; -export { injectAutoFillMeta } from './injectAutoFillMeta.js'; -export { injectSeoTabs } from './injectSeoTabs.js'; -export { readSiteMetaConfig } from './readSiteMetaConfig.js'; -export { buildSeoPlugin } from './seoPluginConfig.js'; -export { slugsAcrossLocales } from './slugsAcrossLocales.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/seo/index.js.map b/dist/modules/seo/index.js.map deleted file mode 100644 index 7917a96..0000000 --- a/dist/modules/seo/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/index.ts"],"sourcesContent":["export { buildAutoFillMetaHook } from './autoFillMeta.js'\nexport type { AutoFillMapping } from './autoFillMeta.js'\nexport { buildMetadata } from './buildMetadata.js'\nexport type { PageMetadata } from './buildMetadata.js'\nexport { buildRobots } from './buildRobots.js'\nexport type { RobotsRules } from './buildRobots.js'\nexport { buildSitemapEntries } from './buildSitemapEntries.js'\nexport type { SitemapEntry } from './buildSitemapEntries.js'\nexport { composeTitle } from './composeTitle.js'\nexport type { TitleOrder } from './composeTitle.js'\nexport { createMetadataGenerator } from './createMetadataGenerator.js'\nexport { createPageMetadata } from './createPageMetadata.js'\nexport { buildHreflangAlternates } from './hreflang.js'\nexport { injectAutoFillMeta } from './injectAutoFillMeta.js'\nexport { injectSeoTabs } from './injectSeoTabs.js'\nexport { readSiteMetaConfig } from './readSiteMetaConfig.js'\nexport type { SiteMetaConfig } from './readSiteMetaConfig.js'\nexport { buildSeoPlugin } from './seoPluginConfig.js'\nexport { slugsAcrossLocales } from './slugsAcrossLocales.js'\nexport type { SeoMeta, SeoOption } from './types.js'\n"],"names":["buildAutoFillMetaHook","buildMetadata","buildRobots","buildSitemapEntries","composeTitle","createMetadataGenerator","createPageMetadata","buildHreflangAlternates","injectAutoFillMeta","injectSeoTabs","readSiteMetaConfig","buildSeoPlugin","slugsAcrossLocales"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,oBAAmB;AAEzD,SAASC,aAAa,QAAQ,qBAAoB;AAElD,SAASC,WAAW,QAAQ,mBAAkB;AAE9C,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9D,SAASC,YAAY,QAAQ,oBAAmB;AAEhD,SAASC,uBAAuB,QAAQ,+BAA8B;AACtE,SAASC,kBAAkB,QAAQ,0BAAyB;AAC5D,SAASC,uBAAuB,QAAQ,gBAAe;AACvD,SAASC,kBAAkB,QAAQ,0BAAyB;AAC5D,SAASC,aAAa,QAAQ,qBAAoB;AAClD,SAASC,kBAAkB,QAAQ,0BAAyB;AAE5D,SAASC,cAAc,QAAQ,uBAAsB;AACrD,SAASC,kBAAkB,QAAQ,0BAAyB"} \ No newline at end of file diff --git a/dist/modules/seo/injectAutoFillMeta.d.ts b/dist/modules/seo/injectAutoFillMeta.d.ts deleted file mode 100644 index 5f4aa09..0000000 --- a/dist/modules/seo/injectAutoFillMeta.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Config } from 'payload'; -import type { SeoOption } from './types.js'; -/** - * Injects the auto-fill meta hook into every SEO-enabled collection. - * - * Runs after @payloadcms/plugin-seo has added the meta group, appending a - * beforeChange hook that fills empty meta from document content. Existing - * hooks are preserved (plugin hook runs last, so editor input and other hooks - * win first). - */ -export declare function injectAutoFillMeta(config: Config, seo: SeoOption): Config; diff --git a/dist/modules/seo/injectAutoFillMeta.js b/dist/modules/seo/injectAutoFillMeta.js deleted file mode 100644 index cdb8518..0000000 --- a/dist/modules/seo/injectAutoFillMeta.js +++ /dev/null @@ -1,35 +0,0 @@ -import { buildAutoFillMetaHook } from './autoFillMeta.js'; -/** - * Injects the auto-fill meta hook into every SEO-enabled collection. - * - * Runs after @payloadcms/plugin-seo has added the meta group, appending a - * beforeChange hook that fills empty meta from document content. Existing - * hooks are preserved (plugin hook runs last, so editor input and other hooks - * win first). - */ export function injectAutoFillMeta(config, seo) { - // autoFill disabled explicitly — leave collections untouched - if (seo.autoFill === false) { - return config; - } - const hook = buildAutoFillMetaHook(seo.autoFill); - return { - ...config, - collections: (config.collections ?? []).map((collection)=>{ - if (!seo.collections.includes(collection.slug)) { - return collection; - } - return { - ...collection, - hooks: { - ...collection.hooks, - beforeChange: [ - ...collection.hooks?.beforeChange ?? [], - hook - ] - } - }; - }) - }; -} - -//# sourceMappingURL=injectAutoFillMeta.js.map \ No newline at end of file diff --git a/dist/modules/seo/injectAutoFillMeta.js.map b/dist/modules/seo/injectAutoFillMeta.js.map deleted file mode 100644 index 8d2a54b..0000000 --- a/dist/modules/seo/injectAutoFillMeta.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/injectAutoFillMeta.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport type { SeoOption } from './types.js'\n\nimport { buildAutoFillMetaHook } from './autoFillMeta.js'\n\n/**\n * Injects the auto-fill meta hook into every SEO-enabled collection.\n *\n * Runs after @payloadcms/plugin-seo has added the meta group, appending a\n * beforeChange hook that fills empty meta from document content. Existing\n * hooks are preserved (plugin hook runs last, so editor input and other hooks\n * win first).\n */\nexport function injectAutoFillMeta(config: Config, seo: SeoOption): Config {\n // autoFill disabled explicitly — leave collections untouched\n if (seo.autoFill === false) {\n return config\n }\n\n const hook = buildAutoFillMetaHook(seo.autoFill)\n\n return {\n ...config,\n collections: (config.collections ?? []).map((collection) => {\n if (!seo.collections.includes(collection.slug)) {\n return collection\n }\n return {\n ...collection,\n hooks: {\n ...collection.hooks,\n beforeChange: [...(collection.hooks?.beforeChange ?? []), hook],\n },\n }\n }),\n }\n}\n"],"names":["buildAutoFillMetaHook","injectAutoFillMeta","config","seo","autoFill","hook","collections","map","collection","includes","slug","hooks","beforeChange"],"mappings":"AAIA,SAASA,qBAAqB,QAAQ,oBAAmB;AAEzD;;;;;;;CAOC,GACD,OAAO,SAASC,mBAAmBC,MAAc,EAAEC,GAAc;IAC/D,6DAA6D;IAC7D,IAAIA,IAAIC,QAAQ,KAAK,OAAO;QAC1B,OAAOF;IACT;IAEA,MAAMG,OAAOL,sBAAsBG,IAAIC,QAAQ;IAE/C,OAAO;QACL,GAAGF,MAAM;QACTI,aAAa,AAACJ,CAAAA,OAAOI,WAAW,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC;YAC3C,IAAI,CAACL,IAAIG,WAAW,CAACG,QAAQ,CAACD,WAAWE,IAAI,GAAG;gBAC9C,OAAOF;YACT;YACA,OAAO;gBACL,GAAGA,UAAU;gBACbG,OAAO;oBACL,GAAGH,WAAWG,KAAK;oBACnBC,cAAc;2BAAKJ,WAAWG,KAAK,EAAEC,gBAAgB,EAAE;wBAAGP;qBAAK;gBACjE;YACF;QACF;IACF;AACF"} \ No newline at end of file diff --git a/dist/modules/seo/injectSeoTabs.d.ts b/dist/modules/seo/injectSeoTabs.d.ts deleted file mode 100644 index a725771..0000000 --- a/dist/modules/seo/injectSeoTabs.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Config } from 'payload'; -import type { SeoOption } from './types.js'; -/** - * Wraps each SEO-enabled collection's fields into a tabbed UI: a "Content" tab - * holding the collection's own fields, and an "SEO" tab holding the `meta` - * group that @payloadcms/plugin-seo added. - * - * This replaces plugin-seo's own `tabbedUI`, which merges tabs by inspecting - * the first field and breaks when other plugins/fields (roles, slug) already - * sit in the collection — producing an empty SEO tab. Running this AFTER - * plugin-seo (so `meta` already exists) and after other field injections lets - * us build the tabs deterministically. - * - * If a collection's first field is already a tabs field, the SEO tab is - * appended to it instead of creating a new wrapper. - */ -export declare function injectSeoTabs(config: Config, seo: SeoOption): Config; diff --git a/dist/modules/seo/injectSeoTabs.js b/dist/modules/seo/injectSeoTabs.js deleted file mode 100644 index c7ba4c4..0000000 --- a/dist/modules/seo/injectSeoTabs.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Wraps each SEO-enabled collection's fields into a tabbed UI: a "Content" tab - * holding the collection's own fields, and an "SEO" tab holding the `meta` - * group that @payloadcms/plugin-seo added. - * - * This replaces plugin-seo's own `tabbedUI`, which merges tabs by inspecting - * the first field and breaks when other plugins/fields (roles, slug) already - * sit in the collection — producing an empty SEO tab. Running this AFTER - * plugin-seo (so `meta` already exists) and after other field injections lets - * us build the tabs deterministically. - * - * If a collection's first field is already a tabs field, the SEO tab is - * appended to it instead of creating a new wrapper. - */ export function injectSeoTabs(config, seo) { - return { - ...config, - collections: (config.collections ?? []).map((collection)=>{ - if (!seo.collections.includes(collection.slug)) { - return collection; - } - const fields = collection.fields ?? []; - // Separate the meta group (added by plugin-seo) from the rest. - const metaField = fields.find((f)=>'name' in f && f.name === 'meta'); - const otherFields = fields.filter((f)=>!('name' in f && f.name === 'meta')); - // Nothing to do if plugin-seo hasn't added meta (shouldn't happen). - if (!metaField) { - return collection; - } - // If the collection already leads with a tabs field, append an SEO tab. - const firstField = otherFields[0]; - if (firstField && firstField.type === 'tabs') { - const existingTabs = firstField; - const withSeoTab = { - ...existingTabs, - tabs: [ - ...existingTabs.tabs, - { - fields: [ - metaField - ], - label: 'SEO' - } - ] - }; - return { - ...collection, - fields: [ - withSeoTab, - ...otherFields.slice(1) - ] - }; - } - // Otherwise wrap everything: Content tab + SEO tab. - const tabs = { - type: 'tabs', - tabs: [ - { - fields: otherFields, - label: collection.labels?.singular?.toString() || 'Content' - }, - { - fields: [ - metaField - ], - label: 'SEO' - } - ] - }; - return { - ...collection, - fields: [ - tabs - ] - }; - }) - }; -} - -//# sourceMappingURL=injectSeoTabs.js.map \ No newline at end of file diff --git a/dist/modules/seo/injectSeoTabs.js.map b/dist/modules/seo/injectSeoTabs.js.map deleted file mode 100644 index 0b5dcfc..0000000 --- a/dist/modules/seo/injectSeoTabs.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/injectSeoTabs.ts"],"sourcesContent":["import type { Config, Field, TabsField } from 'payload'\n\nimport type { SeoOption } from './types.js'\n\n/**\n * Wraps each SEO-enabled collection's fields into a tabbed UI: a \"Content\" tab\n * holding the collection's own fields, and an \"SEO\" tab holding the `meta`\n * group that @payloadcms/plugin-seo added.\n *\n * This replaces plugin-seo's own `tabbedUI`, which merges tabs by inspecting\n * the first field and breaks when other plugins/fields (roles, slug) already\n * sit in the collection — producing an empty SEO tab. Running this AFTER\n * plugin-seo (so `meta` already exists) and after other field injections lets\n * us build the tabs deterministically.\n *\n * If a collection's first field is already a tabs field, the SEO tab is\n * appended to it instead of creating a new wrapper.\n */\nexport function injectSeoTabs(config: Config, seo: SeoOption): Config {\n return {\n ...config,\n collections: (config.collections ?? []).map((collection) => {\n if (!seo.collections.includes(collection.slug)) {\n return collection\n }\n\n const fields = collection.fields ?? []\n\n // Separate the meta group (added by plugin-seo) from the rest.\n const metaField = fields.find(\n (f): f is { name: string } & Field => 'name' in f && f.name === 'meta',\n )\n const otherFields = fields.filter(\n (f) => !('name' in f && f.name === 'meta'),\n )\n\n // Nothing to do if plugin-seo hasn't added meta (shouldn't happen).\n if (!metaField) {return collection}\n\n // If the collection already leads with a tabs field, append an SEO tab.\n const firstField = otherFields[0]\n if (firstField && firstField.type === 'tabs') {\n const existingTabs = firstField\n const withSeoTab: TabsField = {\n ...existingTabs,\n tabs: [...existingTabs.tabs, { fields: [metaField], label: 'SEO' }],\n }\n return {\n ...collection,\n fields: [withSeoTab, ...otherFields.slice(1)],\n }\n }\n\n // Otherwise wrap everything: Content tab + SEO tab.\n const tabs: TabsField = {\n type: 'tabs',\n tabs: [\n { fields: otherFields, label: collection.labels?.singular?.toString() || 'Content' },\n { fields: [metaField], label: 'SEO' },\n ],\n }\n\n return {\n ...collection,\n fields: [tabs],\n }\n }),\n }\n}\n"],"names":["injectSeoTabs","config","seo","collections","map","collection","includes","slug","fields","metaField","find","f","name","otherFields","filter","firstField","type","existingTabs","withSeoTab","tabs","label","slice","labels","singular","toString"],"mappings":"AAIA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASA,cAAcC,MAAc,EAAEC,GAAc;IAC1D,OAAO;QACL,GAAGD,MAAM;QACTE,aAAa,AAACF,CAAAA,OAAOE,WAAW,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC;YAC3C,IAAI,CAACH,IAAIC,WAAW,CAACG,QAAQ,CAACD,WAAWE,IAAI,GAAG;gBAC9C,OAAOF;YACT;YAEA,MAAMG,SAASH,WAAWG,MAAM,IAAI,EAAE;YAEtC,+DAA+D;YAC/D,MAAMC,YAAYD,OAAOE,IAAI,CAC3B,CAACC,IAAqC,UAAUA,KAAKA,EAAEC,IAAI,KAAK;YAElE,MAAMC,cAAcL,OAAOM,MAAM,CAC/B,CAACH,IAAM,CAAE,CAAA,UAAUA,KAAKA,EAAEC,IAAI,KAAK,MAAK;YAG1C,oEAAoE;YACpE,IAAI,CAACH,WAAW;gBAAC,OAAOJ;YAAU;YAElC,wEAAwE;YACxE,MAAMU,aAAaF,WAAW,CAAC,EAAE;YACjC,IAAIE,cAAcA,WAAWC,IAAI,KAAK,QAAQ;gBAC5C,MAAMC,eAAeF;gBACrB,MAAMG,aAAwB;oBAC5B,GAAGD,YAAY;oBACfE,MAAM;2BAAIF,aAAaE,IAAI;wBAAE;4BAAEX,QAAQ;gCAACC;6BAAU;4BAAEW,OAAO;wBAAM;qBAAE;gBACrE;gBACA,OAAO;oBACL,GAAGf,UAAU;oBACbG,QAAQ;wBAACU;2BAAeL,YAAYQ,KAAK,CAAC;qBAAG;gBAC/C;YACF;YAEA,oDAAoD;YACpD,MAAMF,OAAkB;gBACtBH,MAAM;gBACNG,MAAM;oBACJ;wBAAEX,QAAQK;wBAAaO,OAAOf,WAAWiB,MAAM,EAAEC,UAAUC,cAAc;oBAAU;oBACnF;wBAAEhB,QAAQ;4BAACC;yBAAU;wBAAEW,OAAO;oBAAM;iBACrC;YACH;YAEA,OAAO;gBACL,GAAGf,UAAU;gBACbG,QAAQ;oBAACW;iBAAK;YAChB;QACF;IACF;AACF"} \ No newline at end of file diff --git a/dist/modules/seo/readSiteMetaConfig.d.ts b/dist/modules/seo/readSiteMetaConfig.d.ts deleted file mode 100644 index a7ca12b..0000000 --- a/dist/modules/seo/readSiteMetaConfig.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { TitleOrder } from './composeTitle.js'; -export type SiteMetaConfig = { - /** Slug of the page assigned as Homepage; collapses to the locale root. */ - homeSlug?: string; - order?: TitleOrder; - /** Padded separator, e.g. ' | ' — the panel stores the bare character. */ - separator?: string; - siteName: null | string; -}; -/** - * Reads the site-wide inputs to title and URL composition from SiteSettings. - * - * Split out of metadata assembly because these are one concern with one source: - * whatever an editor set in the panel. Metadata generation shouldn't also know - * that the separator arrives unpadded, or that the home slug hides inside a - * relationship — it should receive a resolved config. - */ -export declare function readSiteMetaConfig({ locale, payload, settingsSlug, siteNameField, }: { - locale: string; - payload: BasePayload; - settingsSlug?: string; - siteNameField?: string; -}): Promise; diff --git a/dist/modules/seo/readSiteMetaConfig.js b/dist/modules/seo/readSiteMetaConfig.js deleted file mode 100644 index f6f8b61..0000000 --- a/dist/modules/seo/readSiteMetaConfig.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Reads the site-wide inputs to title and URL composition from SiteSettings. - * - * Split out of metadata assembly because these are one concern with one source: - * whatever an editor set in the panel. Metadata generation shouldn't also know - * that the separator arrives unpadded, or that the home slug hides inside a - * relationship — it should receive a resolved config. - */ export async function readSiteMetaConfig({ locale, payload, settingsSlug = 'site-settings', siteNameField = 'siteName' }) { - // depth 1 populates the homepage relationship, so its slug is available - // without a second query. - const settings = await payload.findGlobal({ - slug: settingsSlug, - depth: 1, - locale: locale - }); - const homepage = settings.homepage && typeof settings.homepage === 'object' ? settings.homepage : null; - return { - siteName: settings[siteNameField] ?? null, - // The panel stores '|'; titles need it padded. - homeSlug: typeof homepage?.slug === 'string' ? homepage.slug : undefined, - order: settings.titleOrder ?? undefined, - separator: settings.titleSeparator ? ` ${settings.titleSeparator} ` : undefined - }; -} - -//# sourceMappingURL=readSiteMetaConfig.js.map \ No newline at end of file diff --git a/dist/modules/seo/readSiteMetaConfig.js.map b/dist/modules/seo/readSiteMetaConfig.js.map deleted file mode 100644 index 940557a..0000000 --- a/dist/modules/seo/readSiteMetaConfig.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/readSiteMetaConfig.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { TitleOrder } from './composeTitle.js'\n\ntype SettingsShape = {\n [key: string]: unknown\n homepage?: { id: number | string; slug?: unknown } | null | number | string\n titleOrder?: null | TitleOrder\n titleSeparator?: null | string\n}\n\nexport type SiteMetaConfig = {\n /** Slug of the page assigned as Homepage; collapses to the locale root. */\n homeSlug?: string\n order?: TitleOrder\n /** Padded separator, e.g. ' | ' — the panel stores the bare character. */\n separator?: string\n siteName: null | string\n}\n\n/**\n * Reads the site-wide inputs to title and URL composition from SiteSettings.\n *\n * Split out of metadata assembly because these are one concern with one source:\n * whatever an editor set in the panel. Metadata generation shouldn't also know\n * that the separator arrives unpadded, or that the home slug hides inside a\n * relationship — it should receive a resolved config.\n */\nexport async function readSiteMetaConfig({\n locale,\n payload,\n settingsSlug = 'site-settings',\n siteNameField = 'siteName',\n}: {\n locale: string\n payload: BasePayload\n settingsSlug?: string\n siteNameField?: string\n}): Promise {\n // depth 1 populates the homepage relationship, so its slug is available\n // without a second query.\n const settings = (await payload.findGlobal({\n slug: settingsSlug as never,\n depth: 1,\n locale: locale as never,\n })) as SettingsShape\n\n const homepage =\n settings.homepage && typeof settings.homepage === 'object' ? settings.homepage : null\n\n return {\n siteName: (settings[siteNameField] as string | undefined) ?? null,\n // The panel stores '|'; titles need it padded.\n homeSlug: typeof homepage?.slug === 'string' ? homepage.slug : undefined,\n order: settings.titleOrder ?? undefined,\n separator: settings.titleSeparator ? ` ${settings.titleSeparator} ` : undefined,\n }\n}\n"],"names":["readSiteMetaConfig","locale","payload","settingsSlug","siteNameField","settings","findGlobal","slug","depth","homepage","siteName","homeSlug","undefined","order","titleOrder","separator","titleSeparator"],"mappings":"AAoBA;;;;;;;CAOC,GACD,OAAO,eAAeA,mBAAmB,EACvCC,MAAM,EACNC,OAAO,EACPC,eAAe,eAAe,EAC9BC,gBAAgB,UAAU,EAM3B;IACC,wEAAwE;IACxE,0BAA0B;IAC1B,MAAMC,WAAY,MAAMH,QAAQI,UAAU,CAAC;QACzCC,MAAMJ;QACNK,OAAO;QACPP,QAAQA;IACV;IAEA,MAAMQ,WACJJ,SAASI,QAAQ,IAAI,OAAOJ,SAASI,QAAQ,KAAK,WAAWJ,SAASI,QAAQ,GAAG;IAEnF,OAAO;QACLC,UAAU,AAACL,QAAQ,CAACD,cAAc,IAA2B;QAC7D,+CAA+C;QAC/CO,UAAU,OAAOF,UAAUF,SAAS,WAAWE,SAASF,IAAI,GAAGK;QAC/DC,OAAOR,SAASS,UAAU,IAAIF;QAC9BG,WAAWV,SAASW,cAAc,GAAG,CAAC,CAAC,EAAEX,SAASW,cAAc,CAAC,CAAC,CAAC,GAAGJ;IACxE;AACF"} \ No newline at end of file diff --git a/dist/modules/seo/seoPluginConfig.d.ts b/dist/modules/seo/seoPluginConfig.d.ts deleted file mode 100644 index 8060fc1..0000000 --- a/dist/modules/seo/seoPluginConfig.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Plugin } from 'payload'; -import type { SeoOption } from './types.js'; -type BuildSeoPluginArgs = { - seo: SeoOption; - /** Upload collection slug for the meta image (typically 'media'). */ - uploadsCollection?: string; -}; -/** - * Configures @payloadcms/plugin-seo from IPAL's SeoOption. - * - * Adds the `meta` field group (title, description, image) to the client's - * chosen collections. tabbedUI is intentionally NOT used — it merges tabs by - * inspecting the first field, which breaks when other plugins/fields (roles, - * slug) already sit in the collection, leaving an empty SEO tab. Instead the - * SEO UI fields are injected into a controlled tab by injectSeoTabs. - */ -export declare function buildSeoPlugin({ seo, uploadsCollection }: BuildSeoPluginArgs): Plugin; -export {}; diff --git a/dist/modules/seo/seoPluginConfig.js b/dist/modules/seo/seoPluginConfig.js deleted file mode 100644 index 7976dc2..0000000 --- a/dist/modules/seo/seoPluginConfig.js +++ /dev/null @@ -1,40 +0,0 @@ -import { seoPlugin } from '@payloadcms/plugin-seo'; -/** - * Escape hatch from automatic title composition: whatever is typed here becomes - * the entire title — no site name, no separator. Useful on a home page, where - * composing would produce "Intecion Software | Intecion Software". - */ const titleOverrideField = { - name: 'titleOverride', - type: 'text', - admin: { - description: 'Use this exact text as the browser-tab title — no site name, no separator. Leave empty to compose the title automatically.' - }, - localized: true -}; -/** - * Configures @payloadcms/plugin-seo from IPAL's SeoOption. - * - * Adds the `meta` field group (title, description, image) to the client's - * chosen collections. tabbedUI is intentionally NOT used — it merges tabs by - * inspecting the first field, which breaks when other plugins/fields (roles, - * slug) already sit in the collection, leaving an empty SEO tab. Instead the - * SEO UI fields are injected into a controlled tab by injectSeoTabs. - */ export function buildSeoPlugin({ seo, uploadsCollection = 'media' }) { - return seoPlugin({ - collections: seo.collections, - fields: ({ defaultFields })=>[ - ...defaultFields, - titleOverrideField, - ...seo.fields ?? [] - ], - uploadsCollection, - ...seo.generateTitle && { - generateTitle: seo.generateTitle - }, - ...seo.generateDescription && { - generateDescription: seo.generateDescription - } - }); -} - -//# sourceMappingURL=seoPluginConfig.js.map \ No newline at end of file diff --git a/dist/modules/seo/seoPluginConfig.js.map b/dist/modules/seo/seoPluginConfig.js.map deleted file mode 100644 index f6d6337..0000000 --- a/dist/modules/seo/seoPluginConfig.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/seoPluginConfig.ts"],"sourcesContent":["import type { Field, Plugin } from 'payload'\n\nimport { seoPlugin } from '@payloadcms/plugin-seo'\n\nimport type { SeoOption } from './types.js'\n\ntype BuildSeoPluginArgs = {\n seo: SeoOption\n /** Upload collection slug for the meta image (typically 'media'). */\n uploadsCollection?: string\n}\n\n/**\n * Escape hatch from automatic title composition: whatever is typed here becomes\n * the entire title — no site name, no separator. Useful on a home page, where\n * composing would produce \"Intecion Software | Intecion Software\".\n */\nconst titleOverrideField: Field = {\n name: 'titleOverride',\n type: 'text',\n admin: {\n description:\n 'Use this exact text as the browser-tab title — no site name, no separator. Leave empty to compose the title automatically.',\n },\n localized: true,\n}\n\n/**\n * Configures @payloadcms/plugin-seo from IPAL's SeoOption.\n *\n * Adds the `meta` field group (title, description, image) to the client's\n * chosen collections. tabbedUI is intentionally NOT used — it merges tabs by\n * inspecting the first field, which breaks when other plugins/fields (roles,\n * slug) already sit in the collection, leaving an empty SEO tab. Instead the\n * SEO UI fields are injected into a controlled tab by injectSeoTabs.\n */\nexport function buildSeoPlugin({ seo, uploadsCollection = 'media' }: BuildSeoPluginArgs): Plugin {\n return seoPlugin({\n collections: seo.collections,\n fields: ({ defaultFields }) => [...defaultFields, titleOverrideField, ...(seo.fields ?? [])],\n uploadsCollection,\n ...(seo.generateTitle && { generateTitle: seo.generateTitle }),\n ...(seo.generateDescription && { generateDescription: seo.generateDescription }),\n })\n}\n"],"names":["seoPlugin","titleOverrideField","name","type","admin","description","localized","buildSeoPlugin","seo","uploadsCollection","collections","fields","defaultFields","generateTitle","generateDescription"],"mappings":"AAEA,SAASA,SAAS,QAAQ,yBAAwB;AAUlD;;;;CAIC,GACD,MAAMC,qBAA4B;IAChCC,MAAM;IACNC,MAAM;IACNC,OAAO;QACLC,aACE;IACJ;IACAC,WAAW;AACb;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASC,eAAe,EAAEC,GAAG,EAAEC,oBAAoB,OAAO,EAAsB;IACrF,OAAOT,UAAU;QACfU,aAAaF,IAAIE,WAAW;QAC5BC,QAAQ,CAAC,EAAEC,aAAa,EAAE,GAAK;mBAAIA;gBAAeX;mBAAwBO,IAAIG,MAAM,IAAI,EAAE;aAAE;QAC5FF;QACA,GAAID,IAAIK,aAAa,IAAI;YAAEA,eAAeL,IAAIK,aAAa;QAAC,CAAC;QAC7D,GAAIL,IAAIM,mBAAmB,IAAI;YAAEA,qBAAqBN,IAAIM,mBAAmB;QAAC,CAAC;IACjF;AACF"} \ No newline at end of file diff --git a/dist/modules/seo/slugsAcrossLocales.d.ts b/dist/modules/seo/slugsAcrossLocales.d.ts deleted file mode 100644 index 2f1df3c..0000000 --- a/dist/modules/seo/slugsAcrossLocales.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { BasePayload } from 'payload'; -import type { I18nConfig, LocalizedSlugs } from '../i18n/index.js'; -/** - * Reads a document's slug in every locale — the map hreflang alternates are - * built from. - * - * Needs its own query because a document fetched in one locale returns `slug` - * as a plain string. Fetching with `locale: 'all'` turns *every* localized - * field into a locale→value map, which is right for the slug and wrong for - * everything else (a title map would break title composition), so this asks - * only for what it needs and at depth 0. - */ -export declare function slugsAcrossLocales({ id, collection, config, payload, }: { - collection: string; - config: I18nConfig; - id: number | string; - payload: BasePayload; -}): Promise; diff --git a/dist/modules/seo/slugsAcrossLocales.js b/dist/modules/seo/slugsAcrossLocales.js deleted file mode 100644 index b01ba98..0000000 --- a/dist/modules/seo/slugsAcrossLocales.js +++ /dev/null @@ -1,24 +0,0 @@ -import { getLocalizedSlugs } from '../i18n/index.js'; -/** - * Reads a document's slug in every locale — the map hreflang alternates are - * built from. - * - * Needs its own query because a document fetched in one locale returns `slug` - * as a plain string. Fetching with `locale: 'all'` turns *every* localized - * field into a locale→value map, which is right for the slug and wrong for - * everything else (a title map would break title composition), so this asks - * only for what it needs and at depth 0. - */ export async function slugsAcrossLocales({ id, collection, config, payload }) { - const doc = await payload.findByID({ - id, - collection: collection, - depth: 0, - locale: 'all' - }); - return doc.slug && typeof doc.slug === 'object' ? getLocalizedSlugs({ - config, - slugField: doc.slug - }) : {}; -} - -//# sourceMappingURL=slugsAcrossLocales.js.map \ No newline at end of file diff --git a/dist/modules/seo/slugsAcrossLocales.js.map b/dist/modules/seo/slugsAcrossLocales.js.map deleted file mode 100644 index 28ab795..0000000 --- a/dist/modules/seo/slugsAcrossLocales.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/slugsAcrossLocales.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nimport type { I18nConfig, LocalizedSlugs } from '../i18n/index.js'\n\nimport { getLocalizedSlugs } from '../i18n/index.js'\n\n/**\n * Reads a document's slug in every locale — the map hreflang alternates are\n * built from.\n *\n * Needs its own query because a document fetched in one locale returns `slug`\n * as a plain string. Fetching with `locale: 'all'` turns *every* localized\n * field into a locale→value map, which is right for the slug and wrong for\n * everything else (a title map would break title composition), so this asks\n * only for what it needs and at depth 0.\n */\nexport async function slugsAcrossLocales({\n id,\n collection,\n config,\n payload,\n}: {\n collection: string\n config: I18nConfig\n id: number | string\n payload: BasePayload\n}): Promise {\n const doc = (await payload.findByID({\n id,\n collection: collection as never,\n depth: 0,\n locale: 'all',\n })) as { slug?: unknown }\n\n return doc.slug && typeof doc.slug === 'object'\n ? getLocalizedSlugs({ config, slugField: doc.slug as Record })\n : {}\n}\n"],"names":["getLocalizedSlugs","slugsAcrossLocales","id","collection","config","payload","doc","findByID","depth","locale","slug","slugField"],"mappings":"AAIA,SAASA,iBAAiB,QAAQ,mBAAkB;AAEpD;;;;;;;;;CASC,GACD,OAAO,eAAeC,mBAAmB,EACvCC,EAAE,EACFC,UAAU,EACVC,MAAM,EACNC,OAAO,EAMR;IACC,MAAMC,MAAO,MAAMD,QAAQE,QAAQ,CAAC;QAClCL;QACAC,YAAYA;QACZK,OAAO;QACPC,QAAQ;IACV;IAEA,OAAOH,IAAII,IAAI,IAAI,OAAOJ,IAAII,IAAI,KAAK,WACnCV,kBAAkB;QAAEI;QAAQO,WAAWL,IAAII,IAAI;IAA4B,KAC3E,CAAC;AACP"} \ No newline at end of file diff --git a/dist/modules/seo/types.d.ts b/dist/modules/seo/types.d.ts deleted file mode 100644 index 1965441..0000000 --- a/dist/modules/seo/types.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { Field } from 'payload'; -import type { AutoFillMapping } from './autoFillMeta.js'; -/** - * SEO configuration. - * - * The plugin wires @payloadcms/plugin-seo into the client's config and adds - * locale-aware metadata helpers on top. Collections come from options because - * the plugin doesn't own the client's content collections (e.g. Pages). - */ -export type SeoOption = { - /** - * Auto-fill empty meta from document fields on save. Defaults to the website - * template mapping (title → meta.title). Set to false to disable. - */ - autoFill?: AutoFillMapping | false; - /** Collection slugs that receive SEO meta fields, e.g. ['pages', 'posts']. */ - collections: string[]; - /** Extra fields appended to the SEO group in the admin. */ - fields?: Field[]; - /** Optional: customize how meta descriptions are generated. */ - generateDescription?: (args: { - doc: Record; - }) => string; - /** Optional: customize how meta titles are generated in the admin preview. */ - generateTitle?: (args: { - doc: Record; - }) => string; -}; -/** - * Minimal shape of the SEO meta group as stored on a document by - * @payloadcms/plugin-seo. The client's generated types are richer; helpers - * depend only on this. - */ -export type SeoMeta = { - description?: null | string; - image?: unknown; - title?: null | string; - /** When set, used as the whole title — no site name, no separator. */ - titleOverride?: null | string; -}; diff --git a/dist/modules/seo/types.js b/dist/modules/seo/types.js deleted file mode 100644 index 7544304..0000000 --- a/dist/modules/seo/types.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Minimal shape of the SEO meta group as stored on a document by - * @payloadcms/plugin-seo. The client's generated types are richer; helpers - * depend only on this. - */ export { }; - -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/modules/seo/types.js.map b/dist/modules/seo/types.js.map deleted file mode 100644 index 6d93404..0000000 --- a/dist/modules/seo/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/seo/types.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nimport type { AutoFillMapping } from './autoFillMeta.js'\n\n/**\n * SEO configuration.\n *\n * The plugin wires @payloadcms/plugin-seo into the client's config and adds\n * locale-aware metadata helpers on top. Collections come from options because\n * the plugin doesn't own the client's content collections (e.g. Pages).\n */\nexport type SeoOption = {\n /**\n * Auto-fill empty meta from document fields on save. Defaults to the website\n * template mapping (title → meta.title). Set to false to disable.\n */\n autoFill?: AutoFillMapping | false\n /** Collection slugs that receive SEO meta fields, e.g. ['pages', 'posts']. */\n collections: string[]\n /** Extra fields appended to the SEO group in the admin. */\n fields?: Field[]\n /** Optional: customize how meta descriptions are generated. */\n generateDescription?: (args: { doc: Record }) => string\n /** Optional: customize how meta titles are generated in the admin preview. */\n generateTitle?: (args: { doc: Record }) => string\n}\n\n/**\n * Minimal shape of the SEO meta group as stored on a document by\n * @payloadcms/plugin-seo. The client's generated types are richer; helpers\n * depend only on this.\n */\nexport type SeoMeta = {\n description?: null | string\n image?: unknown\n title?: null | string\n /** When set, used as the whole title — no site name, no separator. */\n titleOverride?: null | string\n}\n"],"names":[],"mappings":"AA2BA;;;;CAIC,GACD,WAMC"} \ No newline at end of file diff --git a/dist/modules/slug/buildSlugField.d.ts b/dist/modules/slug/buildSlugField.d.ts deleted file mode 100644 index c91bfcd..0000000 --- a/dist/modules/slug/buildSlugField.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { Field, TextField } from 'payload'; -/** - * Normalizes a string into a URL slug. slugify handles diacritics out of the - * box (Polish included: "Strona główna" → "strona-glowna"). - */ -export declare function toSlug(input: string): string; -type BuildSlugFieldOptions = { - /** Source field to derive the slug from. Default: 'title'. */ - from?: string; - /** Whether the slug is localized (per-locale). Default: true. */ - localized?: boolean; - /** Field name for the slug. Default: 'slug'. */ - name?: string; - /** Override or extend the generated field config. */ - overrides?: Partial; - /** Whether the slug is required. Default: true. */ - required?: boolean; -}; -/** - * Builds a slug field that auto-generates from a source field (default 'title') - * only when left empty — an editor's manual slug is never overwritten (mode 4a). - * - * Localized-safe: on a localized field Payload runs the hook per locale, so - * `value` is the slug for the active locale and `siblingData[from]` is the - * source in that same locale. Editing the doc in 'pl' fills slug.pl from - * title.pl; editing in 'en' fills slug.en from title.en — giving genuinely - * per-locale slugs (strona-glowna / homepage) that the language switcher needs. - */ -export declare function buildSlugField(options?: BuildSlugFieldOptions): Field; -export {}; diff --git a/dist/modules/slug/buildSlugField.js b/dist/modules/slug/buildSlugField.js deleted file mode 100644 index 4352966..0000000 --- a/dist/modules/slug/buildSlugField.js +++ /dev/null @@ -1,55 +0,0 @@ -import slugify from 'slugify'; -/** - * Normalizes a string into a URL slug. slugify handles diacritics out of the - * box (Polish included: "Strona główna" → "strona-glowna"). - */ export function toSlug(input) { - return slugify(input, { - lower: true, - strict: true, - trim: true - }); -} -/** - * Builds a slug field that auto-generates from a source field (default 'title') - * only when left empty — an editor's manual slug is never overwritten (mode 4a). - * - * Localized-safe: on a localized field Payload runs the hook per locale, so - * `value` is the slug for the active locale and `siblingData[from]` is the - * source in that same locale. Editing the doc in 'pl' fills slug.pl from - * title.pl; editing in 'en' fills slug.en from title.en — giving genuinely - * per-locale slugs (strona-glowna / homepage) that the language switcher needs. - */ export function buildSlugField(options = {}) { - const { name = 'slug', from = 'title', localized = true, overrides = {}, required = true } = options; - const autoGenerate = ({ originalDoc, siblingData, value })=>{ - // Respect a manually entered slug — only generate when empty (mode 4a). - if (typeof value === 'string' && value.trim()) { - return toSlug(value) // still normalize what the editor typed - ; - } - // Derive from the source field in the current locale. - const source = siblingData?.[from] ?? originalDoc?.[from]; - if (typeof source === 'string' && source.trim()) { - return toSlug(source); - } - return value; - }; - return { - name, - type: 'text', - admin: { - description: 'Auto-generated from the title when left empty. You can override it.', - ...overrides.admin - }, - hooks: { - beforeValidate: [ - autoGenerate - ] - }, - index: true, - localized, - required, - ...overrides - }; -} - -//# sourceMappingURL=buildSlugField.js.map \ No newline at end of file diff --git a/dist/modules/slug/buildSlugField.js.map b/dist/modules/slug/buildSlugField.js.map deleted file mode 100644 index a6e53f5..0000000 --- a/dist/modules/slug/buildSlugField.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/slug/buildSlugField.ts"],"sourcesContent":["import type { Field, FieldHook, TextField } from 'payload'\n\nimport slugify from 'slugify'\n\n/**\n * Normalizes a string into a URL slug. slugify handles diacritics out of the\n * box (Polish included: \"Strona główna\" → \"strona-glowna\").\n */\nexport function toSlug(input: string): string {\n return slugify(input, {\n lower: true,\n strict: true, // drop characters that aren't url-safe\n trim: true,\n })\n}\n\ntype BuildSlugFieldOptions = {\n /** Source field to derive the slug from. Default: 'title'. */\n from?: string\n /** Whether the slug is localized (per-locale). Default: true. */\n localized?: boolean\n /** Field name for the slug. Default: 'slug'. */\n name?: string\n /** Override or extend the generated field config. */\n overrides?: Partial\n /** Whether the slug is required. Default: true. */\n required?: boolean\n}\n\n/**\n * Builds a slug field that auto-generates from a source field (default 'title')\n * only when left empty — an editor's manual slug is never overwritten (mode 4a).\n *\n * Localized-safe: on a localized field Payload runs the hook per locale, so\n * `value` is the slug for the active locale and `siblingData[from]` is the\n * source in that same locale. Editing the doc in 'pl' fills slug.pl from\n * title.pl; editing in 'en' fills slug.en from title.en — giving genuinely\n * per-locale slugs (strona-glowna / homepage) that the language switcher needs.\n */\nexport function buildSlugField(options: BuildSlugFieldOptions = {}): Field {\n const {\n name = 'slug',\n from = 'title',\n localized = true,\n overrides = {},\n required = true,\n } = options\n\n const autoGenerate: FieldHook = ({ originalDoc, siblingData, value }) => {\n // Respect a manually entered slug — only generate when empty (mode 4a).\n if (typeof value === 'string' && value.trim()) {\n return toSlug(value) // still normalize what the editor typed\n }\n\n // Derive from the source field in the current locale.\n const source =\n (siblingData as Record)?.[from] ??\n (originalDoc as Record)?.[from]\n\n if (typeof source === 'string' && source.trim()) {\n return toSlug(source)\n }\n\n return value\n }\n\n return {\n name,\n type: 'text',\n admin: {\n description: 'Auto-generated from the title when left empty. You can override it.',\n ...overrides.admin,\n },\n hooks: {\n beforeValidate: [autoGenerate],\n },\n index: true,\n localized,\n required,\n ...overrides,\n } as TextField\n}\n"],"names":["slugify","toSlug","input","lower","strict","trim","buildSlugField","options","name","from","localized","overrides","required","autoGenerate","originalDoc","siblingData","value","source","type","admin","description","hooks","beforeValidate","index"],"mappings":"AAEA,OAAOA,aAAa,UAAS;AAE7B;;;CAGC,GACD,OAAO,SAASC,OAAOC,KAAa;IAClC,OAAOF,QAAQE,OAAO;QACpBC,OAAO;QACPC,QAAQ;QACRC,MAAM;IACR;AACF;AAeA;;;;;;;;;CASC,GACD,OAAO,SAASC,eAAeC,UAAiC,CAAC,CAAC;IAChE,MAAM,EACJC,OAAO,MAAM,EACbC,OAAO,OAAO,EACdC,YAAY,IAAI,EAChBC,YAAY,CAAC,CAAC,EACdC,WAAW,IAAI,EAChB,GAAGL;IAEJ,MAAMM,eAA0B,CAAC,EAAEC,WAAW,EAAEC,WAAW,EAAEC,KAAK,EAAE;QAClE,wEAAwE;QACxE,IAAI,OAAOA,UAAU,YAAYA,MAAMX,IAAI,IAAI;YAC7C,OAAOJ,OAAOe,OAAO,wCAAwC;;QAC/D;QAEA,sDAAsD;QACtD,MAAMC,SACJ,AAACF,aAAyC,CAACN,KAAK,IAC/CK,aAAyC,CAACL,KAAK;QAElD,IAAI,OAAOQ,WAAW,YAAYA,OAAOZ,IAAI,IAAI;YAC/C,OAAOJ,OAAOgB;QAChB;QAEA,OAAOD;IACT;IAEA,OAAO;QACLR;QACAU,MAAM;QACNC,OAAO;YACLC,aAAa;YACb,GAAGT,UAAUQ,KAAK;QACpB;QACAE,OAAO;YACLC,gBAAgB;gBAACT;aAAa;QAChC;QACAU,OAAO;QACPb;QACAE;QACA,GAAGD,SAAS;IACd;AACF"} \ No newline at end of file diff --git a/dist/modules/slug/index.d.ts b/dist/modules/slug/index.d.ts deleted file mode 100644 index 4152718..0000000 --- a/dist/modules/slug/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export { buildSlugField, toSlug } from './buildSlugField.js'; diff --git a/dist/modules/slug/index.js b/dist/modules/slug/index.js deleted file mode 100644 index acfd35f..0000000 --- a/dist/modules/slug/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { buildSlugField, toSlug } from './buildSlugField.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/slug/index.js.map b/dist/modules/slug/index.js.map deleted file mode 100644 index a337955..0000000 --- a/dist/modules/slug/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/slug/index.ts"],"sourcesContent":["export { buildSlugField, toSlug } from './buildSlugField.js'\n"],"names":["buildSlugField","toSlug"],"mappings":"AAAA,SAASA,cAAc,EAAEC,MAAM,QAAQ,sBAAqB"} \ No newline at end of file diff --git a/dist/modules/turnstile/Turnstile.d.ts b/dist/modules/turnstile/Turnstile.d.ts deleted file mode 100644 index 1e77d64..0000000 --- a/dist/modules/turnstile/Turnstile.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -declare global { - interface Window { - turnstile?: { - render: (el: HTMLElement, opts: Record) => string; - reset: (id?: string) => void; - }; - } -} -export type TurnstileProps = { - /** Called with the token once solved, or null on expiry/error. */ - onToken: (token: null | string) => void; - /** - * Public Turnstile site key. The client project reads it server-side from - * SiteIntegrations (turnstileSiteKey) and passes it in — the widget is a pure - * client component and can't read Payload itself. - */ - siteKey: string; - theme?: 'auto' | 'dark' | 'light'; -}; -/** - * Cloudflare Turnstile widget — reusable across any form. Renders the challenge - * and reports the token via onToken. The token must then be verified - * server-side (see verifyTurnstile) before a submission is trusted. - * - * siteKey is a prop rather than an env var so all Turnstile config lives in one - * place (SiteIntegrations), consistent with the plugin's panel-managed model. - * The script is injected directly (no next/script dependency) so the widget - * stays framework-agnostic. - */ -export declare function Turnstile({ onToken, siteKey, theme }: TurnstileProps): import("react/jsx-runtime").JSX.Element | null; diff --git a/dist/modules/turnstile/Turnstile.js b/dist/modules/turnstile/Turnstile.js deleted file mode 100644 index fc4ed53..0000000 --- a/dist/modules/turnstile/Turnstile.js +++ /dev/null @@ -1,64 +0,0 @@ -'use client'; -import { jsx as _jsx } from "react/jsx-runtime"; -import { useEffect, useRef } from 'react'; -const SCRIPT_SRC = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; -/** Loads the Turnstile script once, shared across all widget instances. */ function ensureScript() { - if (typeof document === 'undefined') { - return; - } - if (document.querySelector(`script[src="${SCRIPT_SRC}"]`)) { - return; - } - const script = document.createElement('script'); - script.src = SCRIPT_SRC; - script.async = true; - script.defer = true; - document.head.appendChild(script); -} -/** - * Cloudflare Turnstile widget — reusable across any form. Renders the challenge - * and reports the token via onToken. The token must then be verified - * server-side (see verifyTurnstile) before a submission is trusted. - * - * siteKey is a prop rather than an env var so all Turnstile config lives in one - * place (SiteIntegrations), consistent with the plugin's panel-managed model. - * The script is injected directly (no next/script dependency) so the widget - * stays framework-agnostic. - */ export function Turnstile({ onToken, siteKey, theme = 'auto' }) { - const ref = useRef(null); - const widgetId = useRef(null); - useEffect(()=>{ - if (!siteKey) { - return; - } - ensureScript(); - function render() { - if (!ref.current || !window.turnstile || widgetId.current) { - return; - } - widgetId.current = window.turnstile.render(ref.current, { - callback: (token)=>onToken(token), - 'error-callback': ()=>onToken(null), - 'expired-callback': ()=>onToken(null), - sitekey: siteKey, - theme - }); - } - render(); - // Script may load after mount — retry briefly until ready. - const interval = setInterval(render, 300); - return ()=>clearInterval(interval); - }, [ - siteKey, - theme, - onToken - ]); - if (!siteKey) { - return null; - } - return /*#__PURE__*/ _jsx("div", { - ref: ref - }); -} - -//# sourceMappingURL=Turnstile.js.map \ No newline at end of file diff --git a/dist/modules/turnstile/Turnstile.js.map b/dist/modules/turnstile/Turnstile.js.map deleted file mode 100644 index 093dfa4..0000000 --- a/dist/modules/turnstile/Turnstile.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/turnstile/Turnstile.tsx"],"sourcesContent":["'use client'\nimport { useEffect, useRef } from 'react'\n\ndeclare global {\n interface Window {\n turnstile?: {\n render: (el: HTMLElement, opts: Record) => string\n reset: (id?: string) => void\n }\n }\n}\n\nconst SCRIPT_SRC = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'\n\n/** Loads the Turnstile script once, shared across all widget instances. */\nfunction ensureScript(): void {\n if (typeof document === 'undefined') {return}\n if (document.querySelector(`script[src=\"${SCRIPT_SRC}\"]`)) {return}\n const script = document.createElement('script')\n script.src = SCRIPT_SRC\n script.async = true\n script.defer = true\n document.head.appendChild(script)\n}\n\nexport type TurnstileProps = {\n /** Called with the token once solved, or null on expiry/error. */\n onToken: (token: null | string) => void\n /**\n * Public Turnstile site key. The client project reads it server-side from\n * SiteIntegrations (turnstileSiteKey) and passes it in — the widget is a pure\n * client component and can't read Payload itself.\n */\n siteKey: string\n theme?: 'auto' | 'dark' | 'light'\n}\n\n/**\n * Cloudflare Turnstile widget — reusable across any form. Renders the challenge\n * and reports the token via onToken. The token must then be verified\n * server-side (see verifyTurnstile) before a submission is trusted.\n *\n * siteKey is a prop rather than an env var so all Turnstile config lives in one\n * place (SiteIntegrations), consistent with the plugin's panel-managed model.\n * The script is injected directly (no next/script dependency) so the widget\n * stays framework-agnostic.\n */\nexport function Turnstile({ onToken, siteKey, theme = 'auto' }: TurnstileProps) {\n const ref = useRef(null)\n const widgetId = useRef(null)\n\n useEffect(() => {\n if (!siteKey) {return}\n\n ensureScript()\n\n function render() {\n if (!ref.current || !window.turnstile || widgetId.current) {return}\n widgetId.current = window.turnstile.render(ref.current, {\n callback: (token: string) => onToken(token),\n 'error-callback': () => onToken(null),\n 'expired-callback': () => onToken(null),\n sitekey: siteKey,\n theme,\n })\n }\n\n render()\n // Script may load after mount — retry briefly until ready.\n const interval = setInterval(render, 300)\n return () => clearInterval(interval)\n }, [siteKey, theme, onToken])\n\n if (!siteKey) {return null}\n\n return
\n}\n"],"names":["useEffect","useRef","SCRIPT_SRC","ensureScript","document","querySelector","script","createElement","src","async","defer","head","appendChild","Turnstile","onToken","siteKey","theme","ref","widgetId","render","current","window","turnstile","callback","token","sitekey","interval","setInterval","clearInterval","div"],"mappings":"AAAA;;AACA,SAASA,SAAS,EAAEC,MAAM,QAAQ,QAAO;AAWzC,MAAMC,aAAa;AAEnB,yEAAyE,GACzE,SAASC;IACP,IAAI,OAAOC,aAAa,aAAa;QAAC;IAAM;IAC5C,IAAIA,SAASC,aAAa,CAAC,CAAC,YAAY,EAAEH,WAAW,EAAE,CAAC,GAAG;QAAC;IAAM;IAClE,MAAMI,SAASF,SAASG,aAAa,CAAC;IACtCD,OAAOE,GAAG,GAAGN;IACbI,OAAOG,KAAK,GAAG;IACfH,OAAOI,KAAK,GAAG;IACfN,SAASO,IAAI,CAACC,WAAW,CAACN;AAC5B;AAcA;;;;;;;;;CASC,GACD,OAAO,SAASO,UAAU,EAAEC,OAAO,EAAEC,OAAO,EAAEC,QAAQ,MAAM,EAAkB;IAC5E,MAAMC,MAAMhB,OAAuB;IACnC,MAAMiB,WAAWjB,OAAsB;IAEvCD,UAAU;QACR,IAAI,CAACe,SAAS;YAAC;QAAM;QAErBZ;QAEA,SAASgB;YACP,IAAI,CAACF,IAAIG,OAAO,IAAI,CAACC,OAAOC,SAAS,IAAIJ,SAASE,OAAO,EAAE;gBAAC;YAAM;YAClEF,SAASE,OAAO,GAAGC,OAAOC,SAAS,CAACH,MAAM,CAACF,IAAIG,OAAO,EAAE;gBACtDG,UAAU,CAACC,QAAkBV,QAAQU;gBACrC,kBAAkB,IAAMV,QAAQ;gBAChC,oBAAoB,IAAMA,QAAQ;gBAClCW,SAASV;gBACTC;YACF;QACF;QAEAG;QACA,2DAA2D;QAC3D,MAAMO,WAAWC,YAAYR,QAAQ;QACrC,OAAO,IAAMS,cAAcF;IAC7B,GAAG;QAACX;QAASC;QAAOF;KAAQ;IAE5B,IAAI,CAACC,SAAS;QAAC,OAAO;IAAI;IAE1B,qBAAO,KAACc;QAAIZ,KAAKA;;AACnB"} \ No newline at end of file diff --git a/dist/modules/turnstile/client.d.ts b/dist/modules/turnstile/client.d.ts deleted file mode 100644 index 7180b99..0000000 --- a/dist/modules/turnstile/client.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Turnstile } from './Turnstile.js'; -export type { TurnstileProps } from './Turnstile.js'; diff --git a/dist/modules/turnstile/client.js b/dist/modules/turnstile/client.js deleted file mode 100644 index 157876e..0000000 --- a/dist/modules/turnstile/client.js +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; -// Client-only exports — the Turnstile widget. Kept separate from index.ts so -// the server-only verify never leaks into a browser bundle. -export { Turnstile } from './Turnstile.js'; - -//# sourceMappingURL=client.js.map \ No newline at end of file diff --git a/dist/modules/turnstile/client.js.map b/dist/modules/turnstile/client.js.map deleted file mode 100644 index 6beab7c..0000000 --- a/dist/modules/turnstile/client.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/turnstile/client.ts"],"sourcesContent":["'use client'\n// Client-only exports — the Turnstile widget. Kept separate from index.ts so\n// the server-only verify never leaks into a browser bundle.\nexport { Turnstile } from './Turnstile.js'\nexport type { TurnstileProps } from './Turnstile.js'\n"],"names":["Turnstile"],"mappings":"AAAA;AACA,6EAA6E;AAC7E,4DAA4D;AAC5D,SAASA,SAAS,QAAQ,iBAAgB"} \ No newline at end of file diff --git a/dist/modules/turnstile/index.d.ts b/dist/modules/turnstile/index.d.ts deleted file mode 100644 index fe1cc66..0000000 --- a/dist/modules/turnstile/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export { verifyTurnstile } from './verify.js'; diff --git a/dist/modules/turnstile/index.js b/dist/modules/turnstile/index.js deleted file mode 100644 index 69979b6..0000000 --- a/dist/modules/turnstile/index.js +++ /dev/null @@ -1,5 +0,0 @@ -// Server-only exports. verify.ts imports 'server-only', so this must never be -// imported from a client component — use ./client for the widget instead. -export { verifyTurnstile } from './verify.js'; - -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/modules/turnstile/index.js.map b/dist/modules/turnstile/index.js.map deleted file mode 100644 index acf77d2..0000000 --- a/dist/modules/turnstile/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/turnstile/index.ts"],"sourcesContent":["// Server-only exports. verify.ts imports 'server-only', so this must never be\n// imported from a client component — use ./client for the widget instead.\nexport { verifyTurnstile } from './verify.js'\n"],"names":["verifyTurnstile"],"mappings":"AAAA,8EAA8E;AAC9E,0EAA0E;AAC1E,SAASA,eAAe,QAAQ,cAAa"} \ No newline at end of file diff --git a/dist/modules/turnstile/verify.d.ts b/dist/modules/turnstile/verify.d.ts deleted file mode 100644 index 36d2dfb..0000000 --- a/dist/modules/turnstile/verify.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -import 'server-only'; -import type { BasePayload } from 'payload'; -type VerifyTurnstileArgs = { - /** Optional client IP for stricter verification. */ - ip?: string; - /** Payload instance — used to read the secret from SiteIntegrations. */ - payload: BasePayload; - /** Token produced by the client-side widget. */ - token: string; -}; -/** - * Verifies a Turnstile token with Cloudflare, server-side only. - * - * The secret comes from the SiteIntegrations global (editor-managed, per the - * plugin's "secrets in the panel" model), read via the Local API which bypasses - * access control. `server-only` guarantees this never reaches the browser - * bundle, keeping the secret off the client. - * - * Returns false on any failure (missing secret/token, network error, rejected - * challenge) — callers treat false as "do not trust this submission". - */ -export declare function verifyTurnstile({ ip, payload, token, }: VerifyTurnstileArgs): Promise; -export {}; diff --git a/dist/modules/turnstile/verify.js b/dist/modules/turnstile/verify.js deleted file mode 100644 index 250d69c..0000000 --- a/dist/modules/turnstile/verify.js +++ /dev/null @@ -1,41 +0,0 @@ -import 'server-only'; -import { getSiteIntegrations } from '../payload/index.js'; -/** - * Verifies a Turnstile token with Cloudflare, server-side only. - * - * The secret comes from the SiteIntegrations global (editor-managed, per the - * plugin's "secrets in the panel" model), read via the Local API which bypasses - * access control. `server-only` guarantees this never reaches the browser - * bundle, keeping the secret off the client. - * - * Returns false on any failure (missing secret/token, network error, rejected - * challenge) — callers treat false as "do not trust this submission". - */ export async function verifyTurnstile({ ip, payload, token }) { - if (!token) { - return false; - } - const integrations = await getSiteIntegrations(payload); - const secret = integrations.turnstileSecretKey; - if (!secret) { - return false; - } - const body = new URLSearchParams({ - response: token, - secret - }); - if (ip) { - body.append('remoteip', ip); - } - try { - const res = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { - body, - method: 'POST' - }); - const data = await res.json(); - return data.success; - } catch { - return false; - } -} - -//# sourceMappingURL=verify.js.map \ No newline at end of file diff --git a/dist/modules/turnstile/verify.js.map b/dist/modules/turnstile/verify.js.map deleted file mode 100644 index 91d58b7..0000000 --- a/dist/modules/turnstile/verify.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../src/modules/turnstile/verify.ts"],"sourcesContent":["import 'server-only'\n\nimport type { BasePayload } from 'payload'\n\nimport { getSiteIntegrations } from '../payload/index.js'\n\ntype SiteverifyResponse = {\n challenge_ts?: string\n 'error-codes'?: string[]\n hostname?: string\n success: boolean\n}\n\ntype IntegrationsWithTurnstile = {\n turnstileSecretKey?: null | string\n}\n\ntype VerifyTurnstileArgs = {\n /** Optional client IP for stricter verification. */\n ip?: string\n /** Payload instance — used to read the secret from SiteIntegrations. */\n payload: BasePayload\n /** Token produced by the client-side widget. */\n token: string\n}\n\n/**\n * Verifies a Turnstile token with Cloudflare, server-side only.\n *\n * The secret comes from the SiteIntegrations global (editor-managed, per the\n * plugin's \"secrets in the panel\" model), read via the Local API which bypasses\n * access control. `server-only` guarantees this never reaches the browser\n * bundle, keeping the secret off the client.\n *\n * Returns false on any failure (missing secret/token, network error, rejected\n * challenge) — callers treat false as \"do not trust this submission\".\n */\nexport async function verifyTurnstile({\n ip,\n payload,\n token,\n}: VerifyTurnstileArgs): Promise {\n if (!token) {return false}\n\n const integrations = await getSiteIntegrations(payload)\n const secret = integrations.turnstileSecretKey\n if (!secret) {return false}\n\n const body = new URLSearchParams({ response: token, secret })\n if (ip) {body.append('remoteip', ip)}\n\n try {\n const res = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {\n body,\n method: 'POST',\n })\n const data = (await res.json()) as SiteverifyResponse\n return data.success\n } catch {\n return false\n }\n}\n"],"names":["getSiteIntegrations","verifyTurnstile","ip","payload","token","integrations","secret","turnstileSecretKey","body","URLSearchParams","response","append","res","fetch","method","data","json","success"],"mappings":"AAAA,OAAO,cAAa;AAIpB,SAASA,mBAAmB,QAAQ,sBAAqB;AAsBzD;;;;;;;;;;CAUC,GACD,OAAO,eAAeC,gBAAgB,EACpCC,EAAE,EACFC,OAAO,EACPC,KAAK,EACe;IACpB,IAAI,CAACA,OAAO;QAAC,OAAO;IAAK;IAEzB,MAAMC,eAAe,MAAML,oBAA+CG;IAC1E,MAAMG,SAASD,aAAaE,kBAAkB;IAC9C,IAAI,CAACD,QAAQ;QAAC,OAAO;IAAK;IAE1B,MAAME,OAAO,IAAIC,gBAAgB;QAAEC,UAAUN;QAAOE;IAAO;IAC3D,IAAIJ,IAAI;QAACM,KAAKG,MAAM,CAAC,YAAYT;IAAG;IAEpC,IAAI;QACF,MAAMU,MAAM,MAAMC,MAAM,6DAA6D;YACnFL;YACAM,QAAQ;QACV;QACA,MAAMC,OAAQ,MAAMH,IAAII,IAAI;QAC5B,OAAOD,KAAKE,OAAO;IACrB,EAAE,OAAM;QACN,OAAO;IACT;AACF"} \ No newline at end of file diff --git a/dist/plugin.d.ts b/dist/plugin.d.ts deleted file mode 100644 index 6550968..0000000 --- a/dist/plugin.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Plugin } from 'payload'; -import type { IpalOptions } from './types.js'; -/** - * IPAL (Intecion Payload Advanced Library) plugin for Payload CMS 3. - * - * @example - * ```ts - * import { ipalKit } from 'ipal-kit' - * - * export default buildConfig({ - * plugins: [ - * ipalKit({ - * i18n: { - * locales: [ - * { code: 'pl', label: 'Polski' }, - * { code: 'en', label: 'English' }, - * ], - * defaultLocale: 'pl', - * }, - * access: { authCollection: 'users' }, - * }), - * ], - * }) - * ``` - */ -export declare const ipalKit: (options: IpalOptions) => Plugin; diff --git a/dist/plugin.js b/dist/plugin.js deleted file mode 100644 index fa81ed0..0000000 --- a/dist/plugin.js +++ /dev/null @@ -1,100 +0,0 @@ -import { buildCookieSettings } from './globals/CookieSettings/index.js'; -import { buildSiteIntegrations } from './globals/SiteIntegrations/index.js'; -import { buildSiteSettings } from './globals/SiteSettings/index.js'; -import { injectRoles } from './modules/access/index.js'; -import { buildArchiveFields } from './modules/content/index.js'; -import { buildFormsPlugin } from './modules/forms/formsPluginConfig.js'; -import { buildLocalizationConfig, validateI18nConfig } from './modules/i18n/index.js'; -import { buildSystemPagesFields } from './modules/pages/index.js'; -import { buildSeoPlugin, injectAutoFillMeta, injectSeoTabs } from './modules/seo/index.js'; -/** - * IPAL (Intecion Payload Advanced Library) plugin for Payload CMS 3. - * - * @example - * ```ts - * import { ipalKit } from 'ipal-kit' - * - * export default buildConfig({ - * plugins: [ - * ipalKit({ - * i18n: { - * locales: [ - * { code: 'pl', label: 'Polski' }, - * { code: 'en', label: 'English' }, - * ], - * defaultLocale: 'pl', - * }, - * access: { authCollection: 'users' }, - * }), - * ], - * }) - * ``` - */ export const ipalKit = (options)=>{ - // Validate eagerly — fail fast before Payload boots - validateI18nConfig(options.i18n); - return async (incomingConfig)=>{ - // Early return when disabled — schema stays, behavior off - if (options.enabled === false) { - return incomingConfig; - } - let config = { - ...incomingConfig - }; - // --- i18n --- - config.localization = buildLocalizationConfig(options.i18n); - // --- access: inject roles into the client's auth collection --- - if (options.access) { - config = injectRoles(config, options.access); - } - // --- seo: apply @payloadcms/plugin-seo directly --- - // NOTE: apply the plugin function to the config immediately rather than - // pushing it onto config.plugins. Payload has already iterated the plugins - // array by the time IPAL runs, so nested plugins added to that list are - // never executed. Calling the plugin as (config) => config applies its - // transform now. - if (options.seo) { - config = await buildSeoPlugin({ - seo: options.seo - })(config); - // Auto-fill empty meta from document content on save - config = injectAutoFillMeta(config, options.seo); - // Wrap fields into Content + SEO tabs (replaces plugin-seo's tabbedUI, - // which breaks when other fields already exist in the collection) - config = injectSeoTabs(config, options.seo); - } - // --- forms: apply @payloadcms/plugin-form-builder directly --- - if (options.forms) { - config = await buildFormsPlugin(options.forms)(config); - } - // --- globals --- - // The System Pages tab collects every "which page plays this role" - // assignment. Composing it here keeps SiteSettings unaware of which modules - // are enabled — it just renders the fields it's given. - const systemPageFields = [ - ...options.pages ? buildSystemPagesFields(options.pages) : [], - ...options.content && options.pages ? buildArchiveFields(options.content, options.pages.slug) : [] - ]; - config.globals = [ - ...config.globals ?? [], - buildSiteSettings({ - additionalFields: options.siteSettingsFields, - systemPageFields - }), - buildSiteIntegrations({ - additionalFields: options.integrationsFields - }), - buildCookieSettings() - ]; - // --- hooks: onInit --- - const incomingOnInit = config.onInit; - config.onInit = async (payload)=>{ - if (incomingOnInit) { - await incomingOnInit(payload); - } - payload.logger.info('[ipal] Plugin initialized.'); - }; - return config; - }; -}; - -//# sourceMappingURL=plugin.js.map \ No newline at end of file diff --git a/dist/plugin.js.map b/dist/plugin.js.map deleted file mode 100644 index 915cbe6..0000000 --- a/dist/plugin.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { Config, Plugin } from 'payload'\n\nimport type { IpalOptions } from './types.js'\n\nimport { buildCookieSettings } from './globals/CookieSettings/index.js'\nimport { buildSiteIntegrations } from './globals/SiteIntegrations/index.js'\nimport { buildSiteSettings } from './globals/SiteSettings/index.js'\nimport { injectRoles } from './modules/access/index.js'\nimport { buildArchiveFields } from './modules/content/index.js'\nimport { buildFormsPlugin } from './modules/forms/formsPluginConfig.js'\nimport { buildLocalizationConfig, validateI18nConfig } from './modules/i18n/index.js'\nimport { buildSystemPagesFields } from './modules/pages/index.js'\nimport { buildSeoPlugin, injectAutoFillMeta, injectSeoTabs } from './modules/seo/index.js'\n\n/**\n * IPAL (Intecion Payload Advanced Library) plugin for Payload CMS 3.\n *\n * @example\n * ```ts\n * import { ipalKit } from 'ipal-kit'\n *\n * export default buildConfig({\n * plugins: [\n * ipalKit({\n * i18n: {\n * locales: [\n * { code: 'pl', label: 'Polski' },\n * { code: 'en', label: 'English' },\n * ],\n * defaultLocale: 'pl',\n * },\n * access: { authCollection: 'users' },\n * }),\n * ],\n * })\n * ```\n */\nexport const ipalKit = (options: IpalOptions): Plugin => {\n // Validate eagerly — fail fast before Payload boots\n validateI18nConfig(options.i18n)\n\n return async (incomingConfig: Config): Promise => {\n // Early return when disabled — schema stays, behavior off\n if (options.enabled === false) {\n return incomingConfig\n }\n\n let config = { ...incomingConfig }\n\n // --- i18n ---\n config.localization = buildLocalizationConfig(options.i18n)\n\n // --- access: inject roles into the client's auth collection ---\n if (options.access) {\n config = injectRoles(config, options.access)\n }\n\n // --- seo: apply @payloadcms/plugin-seo directly ---\n // NOTE: apply the plugin function to the config immediately rather than\n // pushing it onto config.plugins. Payload has already iterated the plugins\n // array by the time IPAL runs, so nested plugins added to that list are\n // never executed. Calling the plugin as (config) => config applies its\n // transform now.\n if (options.seo) {\n config = await buildSeoPlugin({ seo: options.seo })(config)\n // Auto-fill empty meta from document content on save\n config = injectAutoFillMeta(config, options.seo)\n // Wrap fields into Content + SEO tabs (replaces plugin-seo's tabbedUI,\n // which breaks when other fields already exist in the collection)\n config = injectSeoTabs(config, options.seo)\n }\n\n // --- forms: apply @payloadcms/plugin-form-builder directly ---\n if (options.forms) {\n config = await buildFormsPlugin(options.forms)(config)\n }\n\n // --- globals ---\n // The System Pages tab collects every \"which page plays this role\"\n // assignment. Composing it here keeps SiteSettings unaware of which modules\n // are enabled — it just renders the fields it's given.\n const systemPageFields = [\n ...(options.pages ? buildSystemPagesFields(options.pages) : []),\n ...(options.content && options.pages\n ? buildArchiveFields(options.content, options.pages.slug)\n : []),\n ]\n\n config.globals = [\n ...(config.globals ?? []),\n buildSiteSettings({\n additionalFields: options.siteSettingsFields,\n systemPageFields,\n }),\n buildSiteIntegrations({ additionalFields: options.integrationsFields }),\n buildCookieSettings(),\n ]\n\n // --- hooks: onInit ---\n const incomingOnInit = config.onInit\n config.onInit = async (payload) => {\n if (incomingOnInit) {await incomingOnInit(payload)}\n payload.logger.info('[ipal] Plugin initialized.')\n }\n\n return config\n }\n}\n"],"names":["buildCookieSettings","buildSiteIntegrations","buildSiteSettings","injectRoles","buildArchiveFields","buildFormsPlugin","buildLocalizationConfig","validateI18nConfig","buildSystemPagesFields","buildSeoPlugin","injectAutoFillMeta","injectSeoTabs","ipalKit","options","i18n","incomingConfig","enabled","config","localization","access","seo","forms","systemPageFields","pages","content","slug","globals","additionalFields","siteSettingsFields","integrationsFields","incomingOnInit","onInit","payload","logger","info"],"mappings":"AAIA,SAASA,mBAAmB,QAAQ,oCAAmC;AACvE,SAASC,qBAAqB,QAAQ,sCAAqC;AAC3E,SAASC,iBAAiB,QAAQ,kCAAiC;AACnE,SAASC,WAAW,QAAQ,4BAA2B;AACvD,SAASC,kBAAkB,QAAQ,6BAA4B;AAC/D,SAASC,gBAAgB,QAAQ,uCAAsC;AACvE,SAASC,uBAAuB,EAAEC,kBAAkB,QAAQ,0BAAyB;AACrF,SAASC,sBAAsB,QAAQ,2BAA0B;AACjE,SAASC,cAAc,EAAEC,kBAAkB,EAAEC,aAAa,QAAQ,yBAAwB;AAE1F;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACD,OAAO,MAAMC,UAAU,CAACC;IACtB,oDAAoD;IACpDN,mBAAmBM,QAAQC,IAAI;IAE/B,OAAO,OAAOC;QACZ,0DAA0D;QAC1D,IAAIF,QAAQG,OAAO,KAAK,OAAO;YAC7B,OAAOD;QACT;QAEA,IAAIE,SAAS;YAAE,GAAGF,cAAc;QAAC;QAEjC,eAAe;QACfE,OAAOC,YAAY,GAAGZ,wBAAwBO,QAAQC,IAAI;QAE1D,iEAAiE;QACjE,IAAID,QAAQM,MAAM,EAAE;YAClBF,SAASd,YAAYc,QAAQJ,QAAQM,MAAM;QAC7C;QAEA,qDAAqD;QACrD,wEAAwE;QACxE,2EAA2E;QAC3E,wEAAwE;QACxE,uEAAuE;QACvE,iBAAiB;QACjB,IAAIN,QAAQO,GAAG,EAAE;YACfH,SAAS,MAAMR,eAAe;gBAAEW,KAAKP,QAAQO,GAAG;YAAC,GAAGH;YACpD,qDAAqD;YACrDA,SAASP,mBAAmBO,QAAQJ,QAAQO,GAAG;YAC/C,uEAAuE;YACvE,kEAAkE;YAClEH,SAASN,cAAcM,QAAQJ,QAAQO,GAAG;QAC5C;QAEA,gEAAgE;QAChE,IAAIP,QAAQQ,KAAK,EAAE;YACjBJ,SAAS,MAAMZ,iBAAiBQ,QAAQQ,KAAK,EAAEJ;QACjD;QAEA,kBAAkB;QAClB,mEAAmE;QACnE,4EAA4E;QAC5E,uDAAuD;QACvD,MAAMK,mBAAmB;eACnBT,QAAQU,KAAK,GAAGf,uBAAuBK,QAAQU,KAAK,IAAI,EAAE;eAC1DV,QAAQW,OAAO,IAAIX,QAAQU,KAAK,GAChCnB,mBAAmBS,QAAQW,OAAO,EAAEX,QAAQU,KAAK,CAACE,IAAI,IACtD,EAAE;SACP;QAEDR,OAAOS,OAAO,GAAG;eACXT,OAAOS,OAAO,IAAI,EAAE;YACxBxB,kBAAkB;gBAChByB,kBAAkBd,QAAQe,kBAAkB;gBAC5CN;YACF;YACArB,sBAAsB;gBAAE0B,kBAAkBd,QAAQgB,kBAAkB;YAAC;YACrE7B;SACD;QAED,wBAAwB;QACxB,MAAM8B,iBAAiBb,OAAOc,MAAM;QACpCd,OAAOc,MAAM,GAAG,OAAOC;YACrB,IAAIF,gBAAgB;gBAAC,MAAMA,eAAeE;YAAQ;YAClDA,QAAQC,MAAM,CAACC,IAAI,CAAC;QACtB;QAEA,OAAOjB;IACT;AACF,EAAC"} \ No newline at end of file diff --git a/dist/types.d.ts b/dist/types.d.ts deleted file mode 100644 index 3287ee5..0000000 --- a/dist/types.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { Field } from 'payload'; -import type { AccessOption } from './modules/access/types.js'; -import type { ContentOption } from './modules/content/types.js'; -import type { FormsOption } from './modules/forms/types.js'; -import type { I18nConfig } from './modules/i18n/types.js'; -import type { PagesOption } from './modules/pages/types.js'; -import type { SeoOption } from './modules/seo/types.js'; -/** - * Configuration options for the IPAL plugin. - * Passed by the client project in payload.config.ts. - */ -export type IpalOptions = { - /** - * Role-based access control. Injects a fixed `roles` field - * (admin > editor > user) into the client's auth collection. - */ - access?: AccessOption; - /** - * Collections whose entries live under an archive page — blog posts, case - * studies, anything with a listing. Adds an "archive page" assignment per - * collection in SiteSettings; the assigned page's localized slug becomes the - * URL segment (/pl/artykuly/moj-post, /en/articles/my-post). Requires `pages`. - */ - content?: ContentOption; - /** Disable the plugin without uninstalling (keeps DB schema intact) */ - enabled?: boolean; - /** - * Forms — form-builder collections (forms, form-submissions) plus the - * callable submitForm (Turnstile + persistence + SMTP-from-panel email). - */ - forms?: FormsOption; - /** Internationalization — locales, default locale, fallback behavior */ - i18n: I18nConfig; - /** Additional fields injected into SiteIntegrations global */ - integrationsFields?: Field[]; - /** - * System-page assignments (homepage, privacy, cookies) in SiteSettings. - * Provide the slug of the client's Pages collection to enable. - */ - pages?: PagesOption; - /** - * SEO — adds meta fields to chosen collections (via @payloadcms/plugin-seo) - * and enables locale-aware metadata helpers. - */ - seo?: SeoOption; - /** Additional fields injected into SiteSettings global */ - siteSettingsFields?: Field[]; -}; diff --git a/dist/types.js b/dist/types.js deleted file mode 100644 index 3601f21..0000000 --- a/dist/types.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Configuration options for the IPAL plugin. - * Passed by the client project in payload.config.ts. - */ export { }; - -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/types.js.map b/dist/types.js.map deleted file mode 100644 index 7c3b73f..0000000 --- a/dist/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nimport type { AccessOption } from './modules/access/types.js'\nimport type { ContentOption } from './modules/content/types.js'\nimport type { FormsOption } from './modules/forms/types.js'\nimport type { I18nConfig } from './modules/i18n/types.js'\nimport type { PagesOption } from './modules/pages/types.js'\nimport type { SeoOption } from './modules/seo/types.js'\n\n/**\n * Configuration options for the IPAL plugin.\n * Passed by the client project in payload.config.ts.\n */\nexport type IpalOptions = {\n /**\n * Role-based access control. Injects a fixed `roles` field\n * (admin > editor > user) into the client's auth collection.\n */\n access?: AccessOption\n\n /**\n * Collections whose entries live under an archive page — blog posts, case\n * studies, anything with a listing. Adds an \"archive page\" assignment per\n * collection in SiteSettings; the assigned page's localized slug becomes the\n * URL segment (/pl/artykuly/moj-post, /en/articles/my-post). Requires `pages`.\n */\n content?: ContentOption\n\n /** Disable the plugin without uninstalling (keeps DB schema intact) */\n enabled?: boolean\n\n /**\n * Forms — form-builder collections (forms, form-submissions) plus the\n * callable submitForm (Turnstile + persistence + SMTP-from-panel email).\n */\n forms?: FormsOption\n\n /** Internationalization — locales, default locale, fallback behavior */\n i18n: I18nConfig\n\n /** Additional fields injected into SiteIntegrations global */\n integrationsFields?: Field[]\n\n /**\n * System-page assignments (homepage, privacy, cookies) in SiteSettings.\n * Provide the slug of the client's Pages collection to enable.\n */\n pages?: PagesOption\n\n /**\n * SEO — adds meta fields to chosen collections (via @payloadcms/plugin-seo)\n * and enables locale-aware metadata helpers.\n */\n seo?: SeoOption\n\n /** Additional fields injected into SiteSettings global */\n siteSettingsFields?: Field[]\n}\n"],"names":[],"mappings":"AASA;;;CAGC,GACD,WA4CC"} \ No newline at end of file