// The layer's pagination and HTML-sitemap module options normalised for
// the runtime config (lib/seo-module.ts) and their readers in app and
// server code. Pure, so `node --test` drives it.
export interface SeoPaginationOption {
form: 'query' | 'path'
param: string
segments: Record
}
export type SeoHtmlSitemapOption = { paths: Record } | false
/** The pagination option with its defaults, as the runtime config carries it. */
export function paginationOption(value: unknown): SeoPaginationOption {
const v = value !== null && typeof value === 'object' ? (value as Record) : {}
const segments: Record = {}
const raw = v.segments !== null && typeof v.segments === 'object' ? (v.segments as Record) : {}
for (const [locale, segment] of Object.entries(raw)) if (typeof segment === 'string' && /^[\p{L}\p{N}_-]+$/u.test(segment)) segments[locale] = segment
return {
form: v.form === 'path' ? 'path' : 'query',
param: typeof v.param === 'string' && /^[A-Za-z0-9_-]+$/.test(v.param) ? v.param : 'page',
segments,
}
}
/** The HTML sitemap option: false, or the paths (each '/' plus at least one character). */
export function htmlSitemapOption(value: unknown): SeoHtmlSitemapOption {
if (value === false) return false
const v = value !== null && typeof value === 'object' ? (value as Record) : {}
const paths: Record = {}
const raw = v.paths !== null && typeof v.paths === 'object' ? (v.paths as Record) : {}
for (const [locale, path] of Object.entries(raw)) {
if (typeof path === 'string' && /^\/[^?#\s]+$/.test(path) && !path.includes(':') && !path.includes('*')) paths[locale] = path.replace(/\/+$/, '')
}
return { paths }
}