init commit for iPAL-kit plugin
This commit is contained in:
@@ -1,40 +1,37 @@
|
||||
import type { Field, GlobalConfig } from 'payload'
|
||||
|
||||
import type { ContentOption } from '../../modules/content/index.js'
|
||||
import type { PagesOption } from '../../modules/pages/index.js'
|
||||
|
||||
import { buildArchiveFields } from '../../modules/content/index.js'
|
||||
import { buildSystemPagesFields } from '../../modules/pages/index.js'
|
||||
import { generalFields } from './fields/general.js'
|
||||
import { themeFields } from './fields/theme.js'
|
||||
|
||||
type BuildSiteSettingsArgs = {
|
||||
/** Extra fields injected by the client project */
|
||||
additionalFields?: Field[]
|
||||
/** Archive-page assignments for content collections — joins the same tab */
|
||||
content?: ContentOption
|
||||
/** System-page assignments — adds a "System Pages" tab when provided */
|
||||
pages?: PagesOption
|
||||
/**
|
||||
* 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.
|
||||
* siteSettings.general.siteName). Client-provided fields land in their own
|
||||
* "Custom" tab so core data paths never shift.
|
||||
*/
|
||||
export function buildSiteSettings({
|
||||
additionalFields,
|
||||
content,
|
||||
pages,
|
||||
systemPageFields,
|
||||
}: BuildSiteSettingsArgs = {}): GlobalConfig {
|
||||
// Archive assignments sit with the system pages: both answer "which page
|
||||
// plays this role", and both turn into URLs through the page's own slug.
|
||||
const systemPageFields = [
|
||||
...(pages ? buildSystemPagesFields(pages) : []),
|
||||
...(content && pages ? buildArchiveFields(content, pages.slug) : []),
|
||||
]
|
||||
return {
|
||||
slug: 'site-settings',
|
||||
access: {
|
||||
@@ -55,7 +52,9 @@ export function buildSiteSettings({
|
||||
fields: themeFields,
|
||||
label: 'Theme',
|
||||
},
|
||||
...(systemPageFields.length ? [{ fields: systemPageFields, label: 'System Pages' }] : []),
|
||||
...(systemPageFields?.length
|
||||
? [{ fields: systemPageFields, label: 'System Pages' }]
|
||||
: []),
|
||||
...(additionalFields?.length ? [{ fields: additionalFields, label: 'Custom' }] : []),
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import type { BasePayload } from 'payload'
|
||||
|
||||
import type { ArchiveEntries } from './getArchiveEntries.js'
|
||||
import type { ContentOption } from './types.js'
|
||||
|
||||
import { getArchiveEntries } from './getArchiveEntries.js'
|
||||
import { archiveFieldName } from './types.js'
|
||||
|
||||
type ArchivePage = { id: number | string; slug?: unknown }
|
||||
@@ -21,8 +19,6 @@ export type ResolvedRoute =
|
||||
| {
|
||||
collection: string
|
||||
doc: Record<string, unknown>
|
||||
/** Present only when resolved with `withEntries` — metadata doesn't need them. */
|
||||
entries?: ArchiveEntries
|
||||
/** 1-based, from ?page=. */
|
||||
page: number
|
||||
perPage: number
|
||||
@@ -45,17 +41,18 @@ type ResolveRouteArgs = {
|
||||
segments?: string[]
|
||||
/** SiteSettings global slug. Defaults to 'site-settings'. */
|
||||
settingsSlug?: string
|
||||
/**
|
||||
* Fetch the archive's entries too. The page component wants them; metadata
|
||||
* generation doesn't, and would pay for a query it throws away.
|
||||
*/
|
||||
withEntries?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -77,7 +74,6 @@ export async function resolveRoute({
|
||||
payload,
|
||||
segments,
|
||||
settingsSlug = 'site-settings',
|
||||
withEntries = false,
|
||||
}: ResolveRouteArgs): Promise<null | ResolvedRoute> {
|
||||
const settings = (await payload.findGlobal({
|
||||
slug: settingsSlug,
|
||||
@@ -94,9 +90,9 @@ export async function resolveRoute({
|
||||
return null
|
||||
}
|
||||
|
||||
// Which archive (if any) does the first segment name?
|
||||
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}
|
||||
@@ -106,24 +102,12 @@ export async function resolveRoute({
|
||||
|
||||
// /pl/artykuly → the archive page itself.
|
||||
if (rest.length === 0) {
|
||||
const perPage = collection.perPage ?? 10
|
||||
return {
|
||||
type: 'archive',
|
||||
collection: collection.slug,
|
||||
doc: archiveDoc as Record<string, unknown>,
|
||||
page,
|
||||
perPage,
|
||||
...(withEntries
|
||||
? {
|
||||
entries: await getArchiveEntries({
|
||||
collection: collection.slug,
|
||||
locale,
|
||||
page,
|
||||
payload,
|
||||
perPage,
|
||||
}),
|
||||
}
|
||||
: {}),
|
||||
perPage: collection.perPage ?? 10,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import type { BasePayload, SanitizedConfig } from 'payload'
|
||||
import { getPayload } from 'payload'
|
||||
import { cache } from 'react'
|
||||
|
||||
import type { ContentOption, ResolvedRoute } from '../content/index.js'
|
||||
import type { ArchiveEntries, ContentOption, ResolvedRoute } from '../content/index.js'
|
||||
|
||||
import { resolveRoute as resolveRouteRaw } from '../content/index.js'
|
||||
import { getArchiveEntries, resolveRoute as resolveRouteRaw } from '../content/index.js'
|
||||
|
||||
type CreateContentHelpersArgs = {
|
||||
/**
|
||||
@@ -24,7 +24,7 @@ type CreateContentHelpersArgs = {
|
||||
/**
|
||||
* 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).
|
||||
* 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
|
||||
@@ -39,9 +39,13 @@ type CreateContentHelpersArgs = {
|
||||
* import config from '@/payload.config'
|
||||
* import { contentConfig } from '@/content.config'
|
||||
*
|
||||
* export const { getCachedPayload, getSettings, getConfiguredLocales, resolveRoute } =
|
||||
* 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({
|
||||
config,
|
||||
@@ -63,31 +67,30 @@ export function createContentHelpers({
|
||||
return payload.findGlobal({ slug: settingsSlug as never, depth: 2, locale: locale as never })
|
||||
})
|
||||
|
||||
/**
|
||||
* Resolves a URL to a page / archive / entry. Pass `withEntries` on the page
|
||||
* component (it needs the listing); omit it for metadata (it doesn't, and the
|
||||
* query would be wasted).
|
||||
*/
|
||||
/** What does this URL point at? Routing only — no listing data. */
|
||||
const resolveRoute = cache(
|
||||
async (
|
||||
locale: string,
|
||||
segments: string[] | undefined,
|
||||
page: number,
|
||||
withEntries = false,
|
||||
): Promise<null | ResolvedRoute> => {
|
||||
const payload = await getCachedPayload()
|
||||
return resolveRouteRaw({
|
||||
content,
|
||||
locale,
|
||||
page,
|
||||
pagesSlug,
|
||||
payload,
|
||||
segments,
|
||||
settingsSlug,
|
||||
withEntries,
|
||||
})
|
||||
return resolveRouteRaw({ content, locale, page, pagesSlug, payload, segments, settingsSlug })
|
||||
},
|
||||
)
|
||||
|
||||
return { getCachedPayload, getConfiguredLocales, getSettings, resolveRoute }
|
||||
/** One page of a collection's entries, for an archive listing. */
|
||||
const getEntries = cache(
|
||||
async (
|
||||
collection: string,
|
||||
locale: string,
|
||||
page: number,
|
||||
perPage: number,
|
||||
): Promise<ArchiveEntries> => {
|
||||
const payload = await getCachedPayload()
|
||||
return getArchiveEntries({ collection, locale, page, payload, perPage })
|
||||
},
|
||||
)
|
||||
|
||||
return { getCachedPayload, getConfiguredLocales, getEntries, getSettings, resolveRoute }
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ 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'
|
||||
import type { TitleOrder } from './composeTitle.js'
|
||||
import type { SeoMeta } from './types.js'
|
||||
|
||||
import { resolveRoute } from '../content/index.js'
|
||||
import { getLocalizedSlugs } from '../i18n/index.js'
|
||||
import { buildMetadata } from './buildMetadata.js'
|
||||
import { readSiteMetaConfig } from './readSiteMetaConfig.js'
|
||||
import { slugsAcrossLocales } from './slugsAcrossLocales.js'
|
||||
|
||||
type CreatePageMetadataArgs = {
|
||||
/** Absolute site origin, e.g. 'https://example.com'. */
|
||||
@@ -40,37 +40,18 @@ type PageMetadataContext = {
|
||||
slug?: string[]
|
||||
}
|
||||
|
||||
type SettingsShape = {
|
||||
[key: string]: unknown
|
||||
homepage?: { id: number | string; slug?: unknown } | null | number | string
|
||||
titleOrder?: null | TitleOrder
|
||||
titleSeparator?: null | string
|
||||
}
|
||||
|
||||
type DocShape = {
|
||||
id: number | string
|
||||
meta?: null | SeoMeta
|
||||
/** A string when read in one locale, a locale→value map when read with 'all'. */
|
||||
slug?: unknown
|
||||
}
|
||||
|
||||
/** Reads a document again across locales — the slug map hreflang needs. */
|
||||
async function slugsAcrossLocales(
|
||||
payload: BasePayload,
|
||||
collection: string,
|
||||
id: number | string,
|
||||
config: I18nConfig,
|
||||
) {
|
||||
const doc = (await payload.findByID({
|
||||
id,
|
||||
collection: collection as never,
|
||||
depth: 0,
|
||||
locale: 'all',
|
||||
})) as DocShape
|
||||
|
||||
return doc.slug && typeof doc.slug === 'object'
|
||||
? getLocalizedSlugs({ config, slugField: doc.slug as Record<string, unknown> })
|
||||
: {}
|
||||
/** plugin-seo stores the OG image as an upload relationship. */
|
||||
function resolveOgImage(doc: DocShape): null | string {
|
||||
const image = (doc.meta as { image?: unknown } | null | undefined)?.image
|
||||
if (image && typeof image === 'object' && 'url' in image) {
|
||||
return (image as { url: string }).url ?? null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,35 +95,17 @@ export function createPageMetadata(args: CreatePageMetadataArgs) {
|
||||
page,
|
||||
payload,
|
||||
}: PageMetadataContext): Promise<PageMetadata> {
|
||||
const settings = (await payload.findGlobal({
|
||||
slug: settingsSlug,
|
||||
depth: 1,
|
||||
locale: locale as never,
|
||||
})) as SettingsShape
|
||||
const site = await readSiteMetaConfig({ locale, payload, settingsSlug, siteNameField })
|
||||
|
||||
const siteName = (settings[siteNameField] as string | undefined) ?? null
|
||||
|
||||
// The panel stores the bare character ('|'); titles need it padded.
|
||||
const separator = settings.titleSeparator ? ` ${settings.titleSeparator} ` : undefined
|
||||
const order = settings.titleOrder ?? undefined
|
||||
|
||||
const homepage =
|
||||
settings.homepage && typeof settings.homepage === 'object' ? settings.homepage : null
|
||||
// The home page's slug collapses to the locale root (/pl, not /pl/homepage).
|
||||
const homeSlug = typeof homepage?.slug === 'string' ? homepage.slug : undefined
|
||||
|
||||
const empty = () =>
|
||||
buildMetadata({
|
||||
baseUrl,
|
||||
config,
|
||||
homeSlug,
|
||||
locale,
|
||||
meta: null,
|
||||
order,
|
||||
separator,
|
||||
siteName,
|
||||
slugs: {},
|
||||
})
|
||||
const base = {
|
||||
baseUrl,
|
||||
config,
|
||||
homeSlug: site.homeSlug,
|
||||
locale,
|
||||
order: site.order,
|
||||
separator: site.separator,
|
||||
siteName: site.siteName,
|
||||
}
|
||||
|
||||
const route = await resolveRoute({
|
||||
content,
|
||||
@@ -156,7 +119,9 @@ export function createPageMetadata(args: CreatePageMetadataArgs) {
|
||||
|
||||
// Unknown route (the page component will 404) — still return something
|
||||
// coherent rather than throwing during metadata generation.
|
||||
if (!route) {return empty()}
|
||||
if (!route) {
|
||||
return buildMetadata({ ...base, meta: null, slugs: {} })
|
||||
}
|
||||
|
||||
const doc = route.doc as DocShape
|
||||
|
||||
@@ -164,34 +129,30 @@ export function createPageMetadata(args: CreatePageMetadataArgs) {
|
||||
// segment differs per locale, since it's the archive page's own slug.
|
||||
const prefix =
|
||||
route.type === 'entry'
|
||||
? await slugsAcrossLocales(payload, collection, (route.archive as DocShape).id, config)
|
||||
? await slugsAcrossLocales({
|
||||
id: (route.archive as DocShape).id,
|
||||
collection,
|
||||
config,
|
||||
payload,
|
||||
})
|
||||
: undefined
|
||||
|
||||
const docCollection = route.type === 'entry' ? route.collection : collection
|
||||
const slugs = await slugsAcrossLocales(payload, docCollection, doc.id, config)
|
||||
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
|
||||
|
||||
// plugin-seo stores the OG image as an upload relationship.
|
||||
const image = (doc.meta as { image?: unknown } | null | undefined)?.image
|
||||
const imageUrl =
|
||||
image && typeof image === 'object' && 'url' in image
|
||||
? ((image as { url: string }).url ?? null)
|
||||
: null
|
||||
|
||||
return buildMetadata({
|
||||
baseUrl,
|
||||
config,
|
||||
homeSlug,
|
||||
imageUrl,
|
||||
locale,
|
||||
...base,
|
||||
imageUrl: resolveOgImage(doc),
|
||||
meta: doc.meta,
|
||||
order,
|
||||
prefix,
|
||||
query,
|
||||
separator,
|
||||
siteName,
|
||||
slugs,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,5 +9,8 @@ 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'
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { BasePayload } from 'payload'
|
||||
|
||||
import type { TitleOrder } from './composeTitle.js'
|
||||
|
||||
type SettingsShape = {
|
||||
[key: string]: unknown
|
||||
homepage?: { id: number | string; slug?: unknown } | null | number | string
|
||||
titleOrder?: null | TitleOrder
|
||||
titleSeparator?: null | string
|
||||
}
|
||||
|
||||
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 async function readSiteMetaConfig({
|
||||
locale,
|
||||
payload,
|
||||
settingsSlug = 'site-settings',
|
||||
siteNameField = 'siteName',
|
||||
}: {
|
||||
locale: string
|
||||
payload: BasePayload
|
||||
settingsSlug?: string
|
||||
siteNameField?: string
|
||||
}): Promise<SiteMetaConfig> {
|
||||
// depth 1 populates the homepage relationship, so its slug is available
|
||||
// without a second query.
|
||||
const settings = (await payload.findGlobal({
|
||||
slug: settingsSlug as never,
|
||||
depth: 1,
|
||||
locale: locale as never,
|
||||
})) as SettingsShape
|
||||
|
||||
const homepage =
|
||||
settings.homepage && typeof settings.homepage === 'object' ? settings.homepage : null
|
||||
|
||||
return {
|
||||
siteName: (settings[siteNameField] as string | undefined) ?? 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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { BasePayload } from 'payload'
|
||||
|
||||
import type { I18nConfig, LocalizedSlugs } from '../i18n/index.js'
|
||||
|
||||
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,
|
||||
}: {
|
||||
collection: string
|
||||
config: I18nConfig
|
||||
id: number | string
|
||||
payload: BasePayload
|
||||
}): Promise<LocalizedSlugs> {
|
||||
const doc = (await payload.findByID({
|
||||
id,
|
||||
collection: collection as never,
|
||||
depth: 0,
|
||||
locale: 'all',
|
||||
})) as { slug?: unknown }
|
||||
|
||||
return doc.slug && typeof doc.slug === 'object'
|
||||
? getLocalizedSlugs({ config, slugField: doc.slug as Record<string, unknown> })
|
||||
: {}
|
||||
}
|
||||
+14
-4
@@ -6,8 +6,10 @@ 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'
|
||||
|
||||
/**
|
||||
@@ -33,7 +35,7 @@ import { buildSeoPlugin, injectAutoFillMeta, injectSeoTabs } from './modules/seo
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
const ipalKit = (options: IpalOptions): Plugin => {
|
||||
export const ipalKit = (options: IpalOptions): Plugin => {
|
||||
// Validate eagerly — fail fast before Payload boots
|
||||
validateI18nConfig(options.i18n)
|
||||
|
||||
@@ -74,12 +76,21 @@ const ipalKit = (options: IpalOptions): Plugin => {
|
||||
}
|
||||
|
||||
// --- 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,
|
||||
content: options.content,
|
||||
pages: options.pages,
|
||||
systemPageFields,
|
||||
}),
|
||||
buildSiteIntegrations({ additionalFields: options.integrationsFields }),
|
||||
buildCookieSettings(),
|
||||
@@ -95,4 +106,3 @@ const ipalKit = (options: IpalOptions): Plugin => {
|
||||
return config
|
||||
}
|
||||
}
|
||||
export default ipalKit
|
||||
|
||||
Reference in New Issue
Block a user