gate locale behind functional consent
This commit is contained in:
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
/** Requires at least the given role, for field-level access. */ export { };
|
||||
|
||||
//# sourceMappingURL=access.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["access.d.ts"],"sourcesContent":["import type { Access, FieldAccess } from 'payload';\nimport type { Role } from './types.js';\n/**\n * Collection-level access (returns boolean | Where).\n * Use in collection `access.read/create/update/delete`.\n */\nexport declare const adminOnly: Access;\nexport declare const adminOrEditor: Access;\nexport declare const authenticated: Access;\n/** Requires at least the given role. */\nexport declare const requireRole: (minimum: Role) => Access;\n/** Admins see all; others are constrained to their own document. */\nexport declare const adminOrSelf: Access;\n/**\n * Field-level access (returns boolean only — no Where support).\n * Use in field `access.read/update`.\n */\nexport declare const adminOnlyField: FieldAccess;\nexport declare const adminOrEditorField: FieldAccess;\n/** Requires at least the given role, for field-level access. */\nexport declare const requireRoleField: (minimum: Role) => FieldAccess;\n"],"names":[],"mappings":"AAmBA,8DAA8D,GAC9D,WAAsE"}
|
||||
Vendored
-7
@@ -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.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.d.ts"],"sourcesContent":["export { adminOnly, adminOnlyField, adminOrEditor, adminOrEditorField, adminOrSelf, authenticated, requireRole, requireRoleField, } 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,SAASA,SAAS,EAAEC,cAAc,EAAEC,aAAa,EAAEC,kBAAkB,EAAEC,WAAW,EAAEC,aAAa,EAAEC,WAAW,EAAEC,gBAAgB,QAAS,cAAc;AACvJ,SAASC,WAAW,QAAQ,mBAAmB;AAC/C,SAASC,cAAc,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,kBAAkB;AACpE,SAASC,eAAe,QAAQ,kBAAkB;AAElD,SAASC,cAAc,QAAQ,aAAa"}
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* 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 { };
|
||||
|
||||
//# sourceMappingURL=injectRoles.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["injectRoles.d.ts"],"sourcesContent":["import type { Config } from 'payload';\nimport type { AccessOption } from './types.js';\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 declare function injectRoles(config: Config, access: AccessOption): Config;\n"],"names":[],"mappings":"AAEA;;;;;;CAMC,GACD,WAAkF"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=predicates.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["predicates.d.ts"],"sourcesContent":["import type { Role } from './types.js';\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 = {\n roles?: null | Role[];\n} | null | Record<string, unknown> | undefined;\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 declare function hasMinimumRole(user: MaybeUser, minimum: Role): boolean;\n/** True if the user is an admin. */\nexport declare function isAdmin(user: MaybeUser): boolean;\n/** True if the user is an editor or higher (editor, admin). */\nexport declare function isEditor(user: MaybeUser): boolean;\nexport {};\n"],"names":[],"mappings":"AAmBA,WAAU"}
|
||||
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* 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 { };
|
||||
|
||||
//# sourceMappingURL=rolesField.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["rolesField.d.ts"],"sourcesContent":["import type { Field } from 'payload';\nimport type { Role } from './types.js';\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 declare function buildRolesField(defaultRole?: Role): Field;\n"],"names":[],"mappings":"AAEA;;;;;CAKC,GACD,WAAmE"}
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Role hierarchy, lowest to highest privilege.
|
||||
* A higher role satisfies any requirement met by a lower one.
|
||||
*/ /**
|
||||
* 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 { };
|
||||
|
||||
//# sourceMappingURL=types.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["types.d.ts"],"sourcesContent":["/**\n * Role hierarchy, lowest to highest privilege.\n * A higher role satisfies any requirement met by a lower one.\n */\nexport declare const ROLE_HIERARCHY: readonly [\"user\", \"editor\", \"admin\"];\nexport type Role = (typeof ROLE_HIERARCHY)[number];\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":[],"mappings":"AAAA;;;CAGC,GAGD;;;;;;CAMC,GACD,WAKE"}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* 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 { };
|
||||
|
||||
//# sourceMappingURL=Analytics.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["Analytics.d.ts"],"sourcesContent":["import type { AnalyticsConfig } from './types.js';\ndeclare global {\n interface Window {\n dataLayer?: unknown[];\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 declare function Analytics({ ga4MeasurementId, gtmContainerId }: AnalyticsConfig): null;\n"],"names":[],"mappings":"AAMA;;;;;;;;;;;CAWC,GACD,WAA+F"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { Analytics } from './Analytics.js';
|
||||
|
||||
//# sourceMappingURL=client.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["client.d.ts"],"sourcesContent":["export { Analytics } from './Analytics.js';\n"],"names":["Analytics"],"mappings":"AAAA,SAASA,SAAS,QAAQ,iBAAiB"}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Reads the public analytics IDs from SiteIntegrations, server-side.
|
||||
*
|
||||
* Returns only the two public IDs (GA4 / GTM) — safe to pass to the client
|
||||
* <Analytics> component. Does NOT expose any secret from SiteIntegrations.
|
||||
*/ export { };
|
||||
|
||||
//# sourceMappingURL=getAnalyticsConfig.d.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["getAnalyticsConfig.d.ts"],"sourcesContent":["import type { BasePayload } from 'payload';\nimport type { AnalyticsConfig } from './types.js';\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 * <Analytics> component. Does NOT expose any secret from SiteIntegrations.\n */\nexport declare function getAnalyticsConfig(payload: BasePayload): Promise<AnalyticsConfig>;\n"],"names":[],"mappings":"AAEA;;;;;CAKC,GACD,WAA2F"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { getAnalyticsConfig } from './getAnalyticsConfig.js';
|
||||
|
||||
//# sourceMappingURL=index.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.d.ts"],"sourcesContent":["export type { AnalyticsConfig } from './types.js';\nexport { getAnalyticsConfig } from './getAnalyticsConfig.js';\n"],"names":["getAnalyticsConfig"],"mappings":"AACA,SAASA,kBAAkB,QAAQ,0BAA0B"}
|
||||
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
export type { AnalyticsConfig } from './types.js';
|
||||
export { getAnalyticsConfig } from './getAnalyticsConfig.js';
|
||||
export type { AnalyticsConfig } from './types.js';
|
||||
|
||||
Vendored
-2
@@ -1,5 +1,3 @@
|
||||
// Server-safe: config type + the helper that reads IDs from SiteIntegrations.
|
||||
// The <Analytics> component is client-side — exported via ./client.
|
||||
export { getAnalyticsConfig } from './getAnalyticsConfig.js';
|
||||
|
||||
//# sourceMappingURL=index.js.map
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"sources":["../../../src/modules/analytics/index.ts"],"sourcesContent":["// Server-safe: config type + the helper that reads IDs from SiteIntegrations.\n// The <Analytics> 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"}
|
||||
{"version":3,"sources":["../../../src/modules/analytics/index.ts"],"sourcesContent":["export { getAnalyticsConfig } from './getAnalyticsConfig.js'\n// Server-safe: config type + the helper that reads IDs from SiteIntegrations.\n// The <Analytics> component is client-side — exported via ./client.\nexport type { AnalyticsConfig } from './types.js'\n"],"names":["getAnalyticsConfig"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,0BAAyB"}
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* Analytics IDs, read from SiteIntegrations (public — safe on the client).
|
||||
*/ export { };
|
||||
|
||||
//# sourceMappingURL=types.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["types.d.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,WAKE"}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* 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 <RenderBlocks> 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 { };
|
||||
|
||||
//# sourceMappingURL=RenderBlocks.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["RenderBlocks.d.ts"],"sourcesContent":["import type { BlockComponentMap, BlockData, EnhanceProps } from './types.js';\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 * 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 <RenderBlocks> 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 declare function RenderBlocks({ blocks, components, enhanceProps }: RenderBlocksProps): import(\"react/jsx-runtime\").JSX.Element | null;\n"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;;CAkBC,GACD,WAA8I"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { RenderBlocks } from './RenderBlocks.js';
|
||||
|
||||
//# sourceMappingURL=index.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.d.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,oBAAoB"}
|
||||
Vendored
-9
@@ -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.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["types.d.ts"],"sourcesContent":["import type { ComponentType } from 'react';\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 * 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<string, ComponentType<any>>;\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<string, unknown>;\n"],"names":[],"mappings":"AAcA;;;;;;CAMC,GACD,WAI8B"}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=ConsentContext.d.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["ConsentContext.d.ts"],"sourcesContent":["import { type ReactNode } from 'react';\nimport type { ConsentTexts } from './texts.js';\nimport { useConsent } from './useConsent.js';\ntype ConsentContextValue = {\n texts: ConsentTexts;\n} & ReturnType<typeof useConsent>;\nexport declare function ConsentProvider({ children, texts }: {\n children: ReactNode;\n texts: ConsentTexts;\n}): import(\"react/jsx-runtime\").JSX.Element;\nexport declare function useConsentContext(): ConsentContextValue;\nexport {};\n"],"names":[],"mappings":"AAWA,WAAU"}
|
||||
-6
@@ -1,6 +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 { };
|
||||
|
||||
//# sourceMappingURL=CookieBanner.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["CookieBanner.d.ts"],"sourcesContent":["/**\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};\nexport declare function CookieBanner({ classNames }?: {\n classNames?: CookieBannerClassNames;\n}): import(\"react/jsx-runtime\").JSX.Element | null;\n"],"names":[],"mappings":"AAAA;;;CAGC,GAMD,WAEmD"}
|
||||
-12
@@ -1,12 +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 { };
|
||||
|
||||
//# sourceMappingURL=CookieButton.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["CookieButton.d.ts"],"sourcesContent":["/**\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 declare function CookieButton({ className }?: {\n className?: string;\n}): import(\"react/jsx-runtime\").JSX.Element | null;\n"],"names":[],"mappings":"AAAA;;;;;;;;;CASC,GACD,WAEmD"}
|
||||
Vendored
-6
@@ -1,6 +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 { };
|
||||
|
||||
//# sourceMappingURL=categories.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["categories.d.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 declare const CONSENT_CATEGORIES: readonly [\"necessary\", \"functional\", \"analytics\", \"marketing\"];\nexport type ConsentCategory = (typeof CONSENT_CATEGORIES)[number];\nexport type ConsentState = Record<ConsentCategory, boolean>;\nexport declare const DEFAULT_CONSENT: ConsentState;\nexport declare const ACCEPT_ALL_CONSENT: ConsentState;\nexport declare const REJECT_ALL_CONSENT: ConsentState;\n"],"names":[],"mappings":"AAAA;;;CAGC,GAMD,WAAsD"}
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
export { ConsentProvider, useConsentContext } from './ConsentContext.js';
|
||||
export { CookieBanner } from './CookieBanner.js';
|
||||
export { CookieButton } from './CookieButton.js';
|
||||
export { useConsent } from './useConsent.js';
|
||||
|
||||
//# sourceMappingURL=client.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["client.d.ts"],"sourcesContent":["export { ConsentProvider, useConsentContext } from './ConsentContext.js';\nexport { CookieBanner } from './CookieBanner.js';\nexport type { CookieBannerClassNames } from './CookieBanner.js';\nexport { CookieButton } from './CookieButton.js';\nexport { useConsent } from './useConsent.js';\n"],"names":["ConsentProvider","useConsentContext","CookieBanner","CookieButton","useConsent"],"mappings":"AAAA,SAASA,eAAe,EAAEC,iBAAiB,QAAQ,sBAAsB;AACzE,SAASC,YAAY,QAAQ,oBAAoB;AAEjD,SAASC,YAAY,QAAQ,oBAAoB;AACjD,SAASC,UAAU,QAAQ,kBAAkB"}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=getConsentTexts.d.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["getConsentTexts.d.ts"],"sourcesContent":["import type { BasePayload } from 'payload';\nimport type { I18nConfig } from '../i18n/index.js';\nimport type { ConsentTexts } from './texts.js';\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: {\n slug?: null | Record<string, unknown>;\n } | null;\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 declare function getConsentTexts({ config, locale, payload, privacyPolicy, }: GetConsentTextsArgs): Promise<ConsentTexts>;\nexport {};\n"],"names":[],"mappings":"AAwBA,WAAU"}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
/** Updates consent after the user decides. */ export { };
|
||||
|
||||
//# sourceMappingURL=googleConsent.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["googleConsent.d.ts"],"sourcesContent":["import type { ConsentState } from './categories.js';\ndeclare global {\n interface Window {\n dataLayer?: unknown[];\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 declare function gtag(..._args: unknown[]): void;\n/** Sets the default consent state (call before Google tags load). */\nexport declare function setDefaultConsent(state: ConsentState): void;\n/** Updates consent after the user decides. */\nexport declare function updateConsent(state: ConsentState): void;\n"],"names":[],"mappings":"AAqBA,4CAA4C,GAC5C,WAAiE"}
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
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.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.d.ts"],"sourcesContent":["export { ACCEPT_ALL_CONSENT, CONSENT_CATEGORIES, DEFAULT_CONSENT, REJECT_ALL_CONSENT, } from './categories.js';\nexport type { ConsentCategory, ConsentState } from './categories.js';\nexport { getConsentTexts } from './getConsentTexts.js';\nexport { setDefaultConsent, updateConsent } from './googleConsent.js';\nexport { CONSENT_COOKIE, CONSENT_MAX_AGE, CONSENT_VERSION, parseConsent, serializeConsent, } 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,SAASA,kBAAkB,EAAEC,kBAAkB,EAAEC,eAAe,EAAEC,kBAAkB,QAAS,kBAAkB;AAE/G,SAASC,eAAe,QAAQ,uBAAuB;AACvD,SAASC,iBAAiB,EAAEC,aAAa,QAAQ,qBAAqB;AACtE,SAASC,cAAc,EAAEC,eAAe,EAAEC,eAAe,EAAEC,YAAY,EAAEC,gBAAgB,QAAS,eAAe"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=storage.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["storage.d.ts"],"sourcesContent":["import type { ConsentState } from './categories.js';\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 declare const CONSENT_COOKIE = \"cookie-consent\";\nexport declare const CONSENT_VERSION = 1;\nexport declare const CONSENT_MAX_AGE: number;\nexport declare function serializeConsent(state: ConsentState): string;\nexport declare function parseConsent(raw: string | undefined): ConsentState | null;\n"],"names":[],"mappings":"AAUA,WAAmF"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=texts.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["texts.d.ts"],"sourcesContent":["import type { ConsentCategory } from './categories.js';\nexport type ConsentTexts = {\n buttons: {\n acceptAll: string;\n back: string;\n reject: string;\n save: string;\n settings: string;\n };\n categories: Record<ConsentCategory, {\n description: string;\n title: string;\n }>;\n message: string;\n /** null = no privacy-policy link shown */\n privacyLink: {\n href: string;\n label: string;\n } | null;\n settingsTitle: string;\n};\n"],"names":[],"mappings":"AACA,WAmBE"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=useConsent.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["useConsent.d.ts"],"sourcesContent":["import type { ConsentCategory, ConsentState } from './categories.js';\nexport declare function useConsent(): {\n acceptAll: () => void;\n decided: boolean;\n rejectAll: () => void;\n reopen: () => void;\n savePreferences: (choices: Partial<Record<ConsentCategory, boolean>>) => void;\n state: ConsentState;\n};\n"],"names":[],"mappings":"AACA,WAOE"}
|
||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
||||
import type { ConsentCategory, ConsentState } from './categories.js';
|
||||
export declare function useConsent(): {
|
||||
export declare function useConsent(cookieMap?: Partial<Record<ConsentCategory, string[]>>): {
|
||||
acceptAll: () => void;
|
||||
decided: boolean;
|
||||
rejectAll: () => void;
|
||||
|
||||
Vendored
+31
-2
@@ -1,9 +1,22 @@
|
||||
'use client';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { LOCALE_COOKIE_NAME } from '../i18n/index.js';
|
||||
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() {
|
||||
/**
|
||||
* Cookies to delete when consent for a category is withdrawn. Keyed by
|
||||
* category. The plugin knows about its own functional cookie (locale); clients
|
||||
* can extend this via the `cookieMap` arg to useConsent if they set their own.
|
||||
*/ const DEFAULT_COOKIE_MAP = {
|
||||
functional: [
|
||||
LOCALE_COOKIE_NAME
|
||||
]
|
||||
};
|
||||
/** Expire a cookie now, across the whole site. */ function deleteCookie(name) {
|
||||
document.cookie = `${name}=;path=/;max-age=0;samesite=lax`;
|
||||
}
|
||||
export function useConsent(cookieMap = DEFAULT_COOKIE_MAP) {
|
||||
const [state, setState] = useState(DEFAULT_CONSENT);
|
||||
const [decided, setDecided] = useState(false);
|
||||
useEffect(()=>{
|
||||
@@ -15,6 +28,19 @@ export function useConsent() {
|
||||
}
|
||||
}, []);
|
||||
const persist = useCallback((next)=>{
|
||||
// Withdrawal cleanup: for any category flipping true → false, delete the
|
||||
// cookies tied to it. GDPR: withdrawing consent must stop the processing,
|
||||
// so a functional cookie (e.g. locale) written under consent has to go when
|
||||
// that consent is revoked. Uses the current `state` as the previous value.
|
||||
for (const cat of Object.keys(cookieMap)){
|
||||
const wasGranted = state[cat] === true;
|
||||
const nowDenied = next[cat] !== true;
|
||||
if (wasGranted && nowDenied) {
|
||||
for (const name of cookieMap[cat] ?? []){
|
||||
deleteCookie(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
document.cookie = `${CONSENT_COOKIE}=${serializeConsent(next)};path=/;max-age=${CONSENT_MAX_AGE};samesite=lax`;
|
||||
setState(next);
|
||||
setDecided(true);
|
||||
@@ -23,7 +49,10 @@ export function useConsent() {
|
||||
// the rest of the session and never write their cookies — the banner would
|
||||
// look like it worked while nothing changed.
|
||||
updateConsent(next);
|
||||
}, []);
|
||||
}, [
|
||||
state,
|
||||
cookieMap
|
||||
]);
|
||||
const acceptAll = useCallback(()=>persist(ACCEPT_ALL_CONSENT), [
|
||||
persist
|
||||
]);
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
-12
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* 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 { };
|
||||
|
||||
//# sourceMappingURL=archiveFields.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["archiveFields.d.ts"],"sourcesContent":["import type { Field } from 'payload';\nimport type { ContentOption } from './types.js';\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 declare function buildArchiveFields(content: ContentOption, pagesSlug: string): Field[];\n"],"names":[],"mappings":"AAEA;;;;;;;;;CASC,GACD,WAA+F"}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=getArchiveEntries.d.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["getArchiveEntries.d.ts"],"sourcesContent":["import type { BasePayload } from 'payload';\nexport type ArchiveEntries<T = Record<string, unknown>> = {\n docs: T[];\n hasNextPage: boolean;\n hasPrevPage: boolean;\n /** 1-based. */\n page: number;\n totalDocs: number;\n totalPages: number;\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<string, unknown>;\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 declare function getArchiveEntries<T = Record<string, unknown>>({ collection, depth, locale, page, payload, perPage, sort, where, }: GetArchiveEntriesArgs): Promise<ArchiveEntries<T>>;\nexport {};\n"],"names":[],"mappings":"AAsCA,WAAU"}
|
||||
Vendored
-7
@@ -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.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.d.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,qBAAqB;AACxD,SAASC,iBAAiB,QAAQ,yBAAyB;AAE3D,SAASC,gBAAgB,EAAEC,cAAc,EAAEC,cAAc,QAAQ,aAAa;AAC9E,SAASC,YAAY,QAAQ,oBAAoB;AAGjD,SAASC,gBAAgB,QAAQ,aAAa"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=paths.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["paths.d.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 * 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 declare function buildArchivePath({ archiveSlug, locale, page }: ArchivePathArgs): string;\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/** Path to a single entry: /pl/artykuly/moj-post. */\nexport declare function buildEntryPath({ archiveSlug, entrySlug, locale }: EntryPathArgs): string;\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 declare function parsePageParam(value: string | string[] | undefined): number;\nexport {};\n"],"names":[],"mappings":"AAkCA,WAAU"}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=resolveRoute.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["resolveRoute.d.ts"],"sourcesContent":["import type { BasePayload } from 'payload';\nimport type { ContentOption } from './types.js';\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<string, unknown>;\n collection: string;\n doc: Record<string, unknown>;\n type: 'entry';\n}\n/** An ordinary page. */\n | {\n collection: string;\n doc: Record<string, unknown>;\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 | {\n doc: Record<string, unknown>;\n type: 'home';\n}\n/** A single entry, e.g. /pl/artykuly/moj-post. */\n | {\n doc: Record<string, unknown>;\n 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 * 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 declare function resolveRoute({ content, locale, page, pagesSlug, payload, segments, settingsSlug, }: ResolveRouteArgs): Promise<null | ResolvedRoute>;\nexport {};\n"],"names":[],"mappings":"AAmEA,WAAU"}
|
||||
Vendored
-15
@@ -1,15 +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 { };
|
||||
|
||||
//# sourceMappingURL=types.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["types.d.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 * Plugin option wiring archive-backed collections into routing.\n */\nexport type ContentOption = {\n collections: ContentCollectionOption[];\n};\n/**\n * Field name holding a collection's archive page in SiteSettings.\n * `posts` → `postsArchive`.\n */\nexport declare function archiveFieldName(collectionSlug: string): string;\n"],"names":[],"mappings":"AAAA;;;;;;;;;CASC,GAeD;;;CAGC,GACD,WAAyE"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { sendEmail } from './sendEmail.js';
|
||||
|
||||
//# sourceMappingURL=index.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.d.ts"],"sourcesContent":["export { sendEmail } from './sendEmail.js';\nexport type { SendEmailArgs, SendEmailResult } from './sendEmail.js';\n"],"names":["sendEmail"],"mappings":"AAAA,SAASA,SAAS,QAAQ,iBAAiB"}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* 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 { };
|
||||
|
||||
//# sourceMappingURL=panelSmtpAdapter.d.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["panelSmtpAdapter.d.ts"],"sourcesContent":["import type { PayloadEmailAdapter } from 'payload';\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 * 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 declare const panelSmtpAdapter: (args?: PanelSmtpAdapterArgs) => PayloadEmailAdapter;\n"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,WAA4F"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
import 'server-only';
|
||||
|
||||
//# sourceMappingURL=sendEmail.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["sendEmail.d.ts"],"sourcesContent":["import 'server-only';\nimport type { BasePayload } from 'payload';\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};\nexport type SendEmailResult = {\n error: string;\n sent: false;\n} | {\n messageId: string;\n 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 declare function sendEmail({ from, html, payload, replyTo, subject, text, to, }: SendEmailArgs): Promise<SendEmailResult>;\n"],"names":[],"mappings":"AAAA,OAAO,cAAc"}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* 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 { };
|
||||
|
||||
//# sourceMappingURL=formsPluginConfig.d.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["formsPluginConfig.d.ts"],"sourcesContent":["import type { Plugin } from 'payload';\nimport type { FormsOption } from './types.js';\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 declare function buildFormsPlugin(forms: FormsOption): Plugin;\n"],"names":[],"mappings":"AAEA;;;;;;;CAOC,GACD,WAAqE"}
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
export { buildFormsPlugin } from './formsPluginConfig.js';
|
||||
export { checkRateLimit } from './rateLimit.js';
|
||||
export { submitForm } from './submitForm.js';
|
||||
export { validateSubmission } from './validateSubmission.js';
|
||||
|
||||
//# sourceMappingURL=index.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.d.ts"],"sourcesContent":["export { 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,SAASA,gBAAgB,QAAQ,yBAAyB;AAC1D,SAASC,cAAc,QAAQ,iBAAiB;AAEhD,SAASC,UAAU,QAAQ,kBAAkB;AAG7C,SAASC,kBAAkB,QAAQ,0BAA0B"}
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* 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 { };
|
||||
|
||||
//# sourceMappingURL=rateLimit.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["rateLimit.d.ts"],"sourcesContent":["export 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 * 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 declare function checkRateLimit({ key, max, windowMs }: RateLimitArgs): boolean;\n"],"names":[],"mappings":"AAQA;;;;CAIC,GACD,WAAuF"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
import 'server-only';
|
||||
|
||||
//# sourceMappingURL=submitForm.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["submitForm.d.ts"],"sourcesContent":["import 'server-only';\nimport type { BasePayload } from 'payload';\nexport type SubmitFormArgs = {\n /** Submitted field data — shape matches the form's fields. */\n data: Record<string, unknown>;\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 * 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 /** 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';\n success: false;\n} | {\n reason: 'not_found';\n success: false;\n} | {\n reason: 'rate_limited';\n success: false;\n} | {\n reason: 'turnstile';\n success: false;\n};\nexport type SubmitFormResult = {\n submissionId: number | string;\n success: true;\n} | SubmitFailure;\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 declare function submitForm({ data, formId, ip, maxPerMinute, payload, turnstileToken, }: SubmitFormArgs): Promise<SubmitFormResult>;\n"],"names":[],"mappings":"AAAA,OAAO,cAAc"}
|
||||
Vendored
-6
@@ -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.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["types.d.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload';\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: {\n defaultFields: Field[];\n}) => Field[];\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<Omit<CollectionConfig, 'fields'>>;\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":"AAeA;;;CAGC,GACD,WA6BE"}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=validateSubmission.d.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["validateSubmission.d.ts"],"sourcesContent":["import type { BasePayload } from 'payload';\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};\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 * 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 /** 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<string, unknown>;\n form: FormDoc;\n ok: true;\n} | {\n ok: false;\n reason: 'not_found';\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 declare function validateSubmission(payload: BasePayload, formId: string, data: Record<string, unknown>): Promise<FormValidationResult>;\nexport {};\n"],"names":[],"mappings":"AA+CA,WAAU"}
|
||||
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=createContentHelpers.d.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["createContentHelpers.d.ts"],"sourcesContent":["import type { BasePayload, SanitizedConfig } from 'payload';\nimport type { ArchiveEntries, ContentOption, ResolvedRoute } from '../content/index.js';\nimport type { I18nConfig } from '../i18n/index.js';\nimport type { RobotsRules, SitemapEntry } from '../seo/index.js';\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> | 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 * 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 declare function createContentHelpers({ baseUrl, config, content, i18n, pagesSlug, settingsSlug, }: CreateContentHelpersArgs): {\n getCachedPayload: () => Promise<BasePayload>;\n getConfiguredLocales: () => Promise<string[]>;\n getEntries: (collection: string, locale: string, page: number, perPage: number) => Promise<ArchiveEntries>;\n getSettings: (locale: string) => Promise<import(\"payload\").JsonObject>;\n resolveRoute: (locale: string, segments: string[] | undefined, page: number) => Promise<null | ResolvedRoute>;\n robots: () => RobotsRules;\n sitemap: () => Promise<SitemapEntry[]>;\n};\nexport {};\n"],"names":[],"mappings":"AA+DA,WAAU"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export { createContentHelpers } from './createContentHelpers.js';
|
||||
|
||||
//# sourceMappingURL=index.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.d.ts"],"sourcesContent":["export { createContentHelpers } from './createContentHelpers.js';\n"],"names":["createContentHelpers"],"mappings":"AAAA,SAASA,oBAAoB,QAAQ,4BAA4B"}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=getLocalizedSlugs.d.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["getLocalizedSlugs.d.ts"],"sourcesContent":["import type { LocalizedSlugs } from './localizedPath.js';\nimport type { I18nConfig } from './types.js';\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<string, unknown>;\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 * 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 declare function getLocalizedSlugs({ config, slugField }: GetLocalizedSlugsArgs): LocalizedSlugs;\nexport {};\n"],"names":[],"mappings":"AAiCA,WAAU"}
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* Returns the full locale definition for a given code, or undefined if not found.
|
||||
*/ export { };
|
||||
|
||||
//# sourceMappingURL=helpers.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["helpers.d.ts"],"sourcesContent":["import type { I18nConfig, LocaleDefinition } from './types.js';\n/**\n * Returns all configured locale codes.\n */\nexport declare function getLocaleCodes(config: I18nConfig): string[];\n/**\n * Returns the default locale code.\n */\nexport declare function getDefaultLocale(config: I18nConfig): string;\n/**\n * Checks if a string is a valid configured locale code.\n */\nexport declare function isValidLocale(code: string, config: I18nConfig): boolean;\n/**\n * Returns the full locale definition for a given code, or undefined if not found.\n */\nexport declare function getLocaleDefinition(code: string, config: I18nConfig): LocaleDefinition | undefined;\n"],"names":[],"mappings":"AAaA;;CAEC,GACD,WAA4G"}
|
||||
Vendored
-9
@@ -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.d.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.d.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,yBAAyB;AAC3D,SAASC,gBAAgB,EAAEC,cAAc,EAAEC,mBAAmB,EAAEC,aAAa,QAAQ,eAAe;AACpG,SAASC,sBAAsB,EAAEC,0BAA0B,QAAQ,wBAAwB;AAE3F,SAASC,uBAAuB,QAAQ,0BAA0B;AAClE,SAASC,kBAAkB,EAAEC,gBAAgB,QAAQ,qBAAqB;AAE1E,SAASC,kBAAkB,EAAEC,mBAAmB,EAAEC,eAAe,QAAQ,uBAAuB;AAEhG,SAASC,kBAAkB,QAAQ,kBAAkB"}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export { };
|
||||
|
||||
//# sourceMappingURL=localeMiddleware.d.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["localeMiddleware.d.ts"],"sourcesContent":["import type { I18nConfig } from './types.js';\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: {\n get: (name: string) => {\n value: string;\n } | undefined;\n };\n headers: {\n get: (name: string) => null | string;\n };\n nextUrl: {\n clone: () => URL;\n pathname: string;\n search: string;\n };\n url: string;\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: {\n name: string;\n value: string;\n };\n location: string;\n type: 'redirect';\n} | {\n 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 * 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 declare function createLocaleMiddleware({ config, cookieName, }: CreateLocaleMiddlewareArgs): (request: MiddlewareRequest) => LocaleMiddlewareResult;\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 declare const DEFAULT_MIDDLEWARE_MATCHER: string[];\nexport {};\n"],"names":[],"mappings":"AAwEA,WAAU"}
|
||||
+20
-8
@@ -20,12 +20,12 @@ type MiddlewareRequest = {
|
||||
url: string;
|
||||
};
|
||||
/**
|
||||
* What the factory returns — the caller (in Next next-middleware.ts) decides how to
|
||||
* What the factory returns — the caller (in 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: {
|
||||
cookie?: {
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
@@ -36,6 +36,16 @@ export type LocaleMiddlewareResult = {
|
||||
};
|
||||
type CreateLocaleMiddlewareArgs = {
|
||||
config: I18nConfig;
|
||||
/**
|
||||
* Consent category that gates *persisting* the locale cookie. The locale is
|
||||
* always detected (routing works regardless), but the choice is only written
|
||||
* to a cookie once the visitor has consented to this category. Defaults to
|
||||
* 'functional'. Pass 'necessary' to always persist (treat locale as strictly
|
||||
* necessary), which restores the pre-consent behaviour.
|
||||
*/
|
||||
consentCategory?: 'functional' | 'necessary';
|
||||
/** Name of the consent cookie to read. Defaults to CONSENT_COOKIE. */
|
||||
consentCookieName?: string;
|
||||
/** Cookie name for the locale choice. Defaults to LOCALE_COOKIE_NAME. */
|
||||
cookieName?: string;
|
||||
};
|
||||
@@ -48,23 +58,25 @@ type CreateLocaleMiddlewareArgs = {
|
||||
* 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
|
||||
* The plugin returns a decision; the thin 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.
|
||||
* respecting that middleware.ts must physically live in the client app.
|
||||
*
|
||||
* @example
|
||||
* // next-middleware.ts (client project) — one wiring file, no logic:
|
||||
* // 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) {
|
||||
* export function proxy(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)
|
||||
* // cookie is optional: only present when the visitor consented to the
|
||||
* // gating category (functional by default). Guard before setting.
|
||||
* if (r.cookie) res.cookies.set(r.cookie.name, r.cookie.value)
|
||||
* return res
|
||||
* }
|
||||
*/
|
||||
export declare function createLocaleMiddleware({ config, cookieName, }: CreateLocaleMiddlewareArgs): (request: MiddlewareRequest) => LocaleMiddlewareResult;
|
||||
export declare function createLocaleMiddleware({ config, consentCategory, consentCookieName, 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).
|
||||
|
||||
+28
-11
@@ -1,3 +1,4 @@
|
||||
import { CONSENT_COOKIE, parseConsent } from '../consent/storage.js';
|
||||
import { isValidLocale, LOCALE_COOKIE_NAME, negotiateLocale } from '../i18n/index.js';
|
||||
/**
|
||||
* First path segment of a URL pathname, or '' for root.
|
||||
@@ -14,22 +15,24 @@ import { isValidLocale, LOCALE_COOKIE_NAME, negotiateLocale } from '../i18n/inde
|
||||
* 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
|
||||
* The plugin returns a decision; the thin 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.
|
||||
* respecting that middleware.ts must physically live in the client app.
|
||||
*
|
||||
* @example
|
||||
* // next-middleware.ts (client project) — one wiring file, no logic:
|
||||
* // 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) {
|
||||
* export function proxy(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)
|
||||
* // cookie is optional: only present when the visitor consented to the
|
||||
* // gating category (functional by default). Guard before setting.
|
||||
* if (r.cookie) res.cookies.set(r.cookie.name, r.cookie.value)
|
||||
* return res
|
||||
* }
|
||||
*/ export function createLocaleMiddleware({ config, cookieName = LOCALE_COOKIE_NAME }) {
|
||||
*/ export function createLocaleMiddleware({ config, consentCategory = 'functional', consentCookieName = CONSENT_COOKIE, cookieName = LOCALE_COOKIE_NAME }) {
|
||||
return function localeMiddleware(request) {
|
||||
const { pathname } = request.nextUrl;
|
||||
// Already locale-prefixed → nothing to do
|
||||
@@ -47,13 +50,27 @@ import { isValidLocale, LOCALE_COOKIE_NAME, negotiateLocale } from '../i18n/inde
|
||||
// Redirect to the locale-prefixed path, preserving the rest
|
||||
const url = request.nextUrl.clone();
|
||||
url.pathname = `/${locale}${pathname === '/' ? '' : pathname}`;
|
||||
// Persist the locale choice ONLY if the visitor consented to the gating
|
||||
// category. 'necessary' is always granted, so passing consentCategory:
|
||||
// 'necessary' always persists. For 'functional' (default), we read the
|
||||
// consent cookie and only write the locale cookie when functional is true.
|
||||
// Without consent the locale is still detected each request (routing works),
|
||||
// it just isn't remembered across visits — which is the whole point of
|
||||
// gating a functional cookie behind consent.
|
||||
let mayPersist = consentCategory === 'necessary';
|
||||
if (!mayPersist) {
|
||||
const consent = parseConsent(request.cookies.get(consentCookieName)?.value);
|
||||
mayPersist = consent?.[consentCategory] === true;
|
||||
}
|
||||
return {
|
||||
type: 'redirect',
|
||||
cookie: {
|
||||
name: cookieName,
|
||||
value: locale
|
||||
},
|
||||
location: url.toString()
|
||||
location: url.toString(),
|
||||
...mayPersist ? {
|
||||
cookie: {
|
||||
name: cookieName,
|
||||
value: locale
|
||||
}
|
||||
} : {}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user