@better-seo/core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -0
- package/dist/index.cjs +652 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +322 -0
- package/dist/index.d.ts +322 -0
- package/dist/index.js +620 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/plugins.ts","../src/schema-dedupe.ts","../src/core.ts","../src/schema.ts","../src/serialize.ts","../src/render.ts","../src/validate.ts","../src/adapters/registry.ts","../src/context.ts","../src/singleton.ts","../src/voila.ts","../src/rules.ts","../src/migrate.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/** Stable codes for programmatic handling (enterprise / observability). */\nexport type SEOErrorCode =\n | \"VALIDATION\"\n | \"ADAPTER_NOT_FOUND\"\n | \"MIGRATE_NOT_IMPLEMENTED\"\n | \"USE_SEO_NOT_AVAILABLE\"\n\nconst messages: Record<SEOErrorCode, string> = {\n VALIDATION: \"Invalid or incomplete SEO input.\",\n ADAPTER_NOT_FOUND:\n \"No adapter registered for this framework. Import the adapter package (e.g. @better-seo/next) before calling seoForFramework.\",\n MIGRATE_NOT_IMPLEMENTED:\n \"Migration helpers are not implemented yet. Track Roadmap Wave 12 / FEATURES C15.\",\n USE_SEO_NOT_AVAILABLE:\n \"useSEO is provided by @better-seo/react (Roadmap Wave 5 / FEATURES V3). App Router metadata should use seo() / prepareNextSeo from @better-seo/next.\",\n}\n\nexport class SEOError extends Error {\n readonly code: SEOErrorCode\n override readonly cause?: unknown\n\n constructor(code: SEOErrorCode, message?: string, options?: { cause?: unknown }) {\n const base = message ?? messages[code]\n super(`[@better-seo/core] [${code}]: ${base}`)\n this.name = \"SEOError\"\n this.code = code\n this.cause = options?.cause\n }\n}\n\nexport function isSEOError(e: unknown): e is SEOError {\n return e instanceof SEOError\n}\n","import type { SEO, SEOConfig, SEOInput, SEOPlugin } from \"./types.js\"\n\nexport function defineSEOPlugin(plugin: SEOPlugin): SEOPlugin {\n return plugin\n}\n\nexport function runBeforeMergePlugins(input: SEOInput, config?: SEOConfig): SEOInput {\n const list = config?.plugins ?? []\n let acc = input\n for (const p of list) {\n acc = p.beforeMerge?.(acc, { config }) ?? acc\n }\n return acc\n}\n\nexport function runAfterMergePlugins(seo: SEO, config?: SEOConfig): SEO {\n const list = config?.plugins ?? []\n let acc = seo\n for (const p of list) {\n acc = p.afterMerge?.(acc, { config }) ?? acc\n }\n return acc\n}\n","import type { JSONLD } from \"./types.js\"\n\n/**\n * When the same `@id` + `@type` appears more than once, keep the last occurrence (child-over-parent semantics).\n * Nodes without both `@id` and `@type` string are kept in order.\n */\nexport function dedupeSchemaByIdAndType(schemas: readonly JSONLD[]): JSONLD[] {\n type Key = string\n const lastIndexByKey = new Map<Key, number>()\n for (let i = 0; i < schemas.length; i++) {\n const node = schemas[i]\n if (!node) continue\n const id = node[\"@id\"]\n const type = node[\"@type\"]\n if (typeof id === \"string\" && typeof type === \"string\") {\n lastIndexByKey.set(`${type}::${id}`, i)\n }\n }\n const drop = new Set<number>()\n for (let i = 0; i < schemas.length; i++) {\n const node = schemas[i]\n if (!node) continue\n const id = node[\"@id\"]\n const type = node[\"@type\"]\n if (typeof id === \"string\" && typeof type === \"string\") {\n const k = `${type}::${id}`\n if (lastIndexByKey.get(k) !== i) drop.add(i)\n }\n }\n return schemas.filter((_, i) => !drop.has(i))\n}\n","import { SEOError } from \"./errors.js\"\nimport type { JSONLD, SEO, SEOConfig, SEOInput } from \"./types.js\"\nimport { runAfterMergePlugins, runBeforeMergePlugins } from \"./plugins.js\"\nimport { dedupeSchemaByIdAndType } from \"./schema-dedupe.js\"\n\nfunction applyTitleTemplate(title: string, template: string | undefined): string {\n if (!template) return title\n return template.includes(\"%s\") ? template.replace(/%s/g, title) : `${title} ${template}`.trim()\n}\n\nfunction resolveCanonical(\n canonical: string | undefined,\n baseUrl: string | undefined,\n): string | undefined {\n if (!canonical) return undefined\n if (canonical.startsWith(\"http://\") || canonical.startsWith(\"https://\")) return canonical\n if (!baseUrl) return canonical\n const base = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl\n const path = canonical.startsWith(\"/\") ? canonical : `/${canonical}`\n return `${base}${path}`\n}\n\nfunction mergeSchema(parent: readonly JSONLD[], child: readonly JSONLD[]): JSONLD[] {\n return [...parent, ...child]\n}\n\n/** Normalize partial + config into a canonical `SEO` document (Wave 1 baseline). */\nexport function createSEO(input: SEOInput, config?: SEOConfig): SEO {\n const mergedInput = runBeforeMergePlugins(input, config)\n\n const rawTitle = mergedInput.meta?.title ?? mergedInput.title\n const title = typeof rawTitle === \"string\" ? rawTitle.trim() : \"\"\n if (!title) {\n throw new SEOError(\"VALIDATION\", \"title is required (set meta.title or top-level title).\")\n }\n\n const description = mergedInput.meta?.description ?? mergedInput.description\n const canonical = resolveCanonical(\n mergedInput.meta?.canonical ?? mergedInput.canonical,\n config?.baseUrl,\n )\n const robots = mergedInput.meta?.robots ?? mergedInput.robots ?? config?.defaultRobots\n const langMap = mergedInput.meta?.alternates?.languages\n const alternates =\n langMap !== undefined && Object.keys(langMap).length > 0 ? { languages: langMap } : undefined\n\n const meta: SEO[\"meta\"] = {\n title: applyTitleTemplate(title, config?.titleTemplate),\n ...(description !== undefined ? { description } : {}),\n ...(canonical !== undefined ? { canonical } : {}),\n ...(robots !== undefined ? { robots } : {}),\n ...(alternates !== undefined ? { alternates } : {}),\n }\n\n const mergeOg = config?.features?.openGraphMerge !== false\n const ogBase = mergedInput.openGraph ?? {}\n const ogTitle = mergeOg\n ? (mergedInput.openGraph?.title ?? meta.title)\n : mergedInput.openGraph?.title\n const ogDesc = mergeOg\n ? (mergedInput.openGraph?.description ?? meta.description)\n : mergedInput.openGraph?.description\n\n let openGraph: SEO[\"openGraph\"] | undefined\n if (mergeOg) {\n openGraph = {\n ...ogBase,\n title: ogTitle ?? meta.title,\n ...(ogDesc !== undefined ? { description: ogDesc } : {}),\n }\n } else if (Object.keys(ogBase).length > 0 || ogTitle !== undefined || ogDesc !== undefined) {\n openGraph = {\n ...ogBase,\n ...(ogTitle !== undefined ? { title: ogTitle } : {}),\n ...(ogDesc !== undefined ? { description: ogDesc } : {}),\n }\n }\n\n const ogFirstImage = mergedInput.openGraph?.images?.[0]?.url\n const twTitle = mergedInput.twitter?.title ?? meta.title\n const twDesc = mergedInput.twitter?.description ?? meta.description\n const twImage = mergedInput.twitter?.image ?? (mergeOg ? ogFirstImage : undefined)\n const twitter: NonNullable<SEO[\"twitter\"]> = {\n ...(mergedInput.twitter ?? {}),\n card: mergedInput.twitter?.card ?? \"summary_large_image\",\n title: twTitle,\n ...(twDesc !== undefined ? { description: twDesc } : {}),\n ...(twImage !== undefined ? { image: twImage } : {}),\n }\n\n const parentSchema: readonly JSONLD[] = []\n let childSchema = mergedInput.schema ?? []\n if (\n config?.schemaMerge &&\n typeof config.schemaMerge === \"object\" &&\n config.schemaMerge.dedupeByIdAndType === true\n ) {\n childSchema = dedupeSchemaByIdAndType(childSchema)\n }\n const schema = mergeSchema(parentSchema, childSchema)\n\n const doc: SEO = {\n meta,\n ...(openGraph !== undefined ? { openGraph } : {}),\n twitter,\n schema,\n }\n\n let out = runAfterMergePlugins(doc, config)\n if (config?.features?.jsonLd === false) {\n out = { ...out, schema: [] }\n }\n return out\n}\n\n/** Child wins for scalars; schemas concatenate (baseline). */\nfunction mergeLanguageAlternates(\n parent?: Readonly<Record<string, string>>,\n child?: Readonly<Record<string, string>>,\n): Readonly<Record<string, string>> | undefined {\n const merged = { ...parent, ...child }\n return Object.keys(merged).length > 0 ? merged : undefined\n}\n\n/** Merge parent `SEO` with child input (featured as `withSEO` in docs / Next package). */\nexport function withSEO(parent: SEO, child: SEOInput, config?: SEOConfig): SEO {\n return mergeSEO(parent, child, config)\n}\n\nexport function mergeSEO(parent: SEO, child: SEOInput, config?: SEOConfig): SEO {\n const pMeta = parent.meta\n const cMeta = child.meta ?? {}\n const { alternates: pAlt, ...pRest } = pMeta\n const { alternates: cAlt, ...cRest } = cMeta\n const mergedLang = mergeLanguageAlternates(pAlt?.languages, cAlt?.languages)\n const mergedAlternates = mergedLang !== undefined ? { languages: mergedLang } : undefined\n const mergedChild: SEOInput = {\n ...child,\n meta: {\n ...pRest,\n ...cRest,\n title: cRest.title ?? child.title ?? pMeta.title,\n description: cRest.description ?? child.description ?? pMeta.description,\n canonical: cRest.canonical ?? child.canonical ?? pMeta.canonical,\n robots: cRest.robots ?? child.robots ?? pMeta.robots,\n ...(mergedAlternates !== undefined ? { alternates: mergedAlternates } : {}),\n },\n openGraph: { ...(parent.openGraph ?? {}), ...child.openGraph },\n twitter: { ...(parent.twitter ?? {}), ...child.twitter },\n schema: [...parent.schema, ...(child.schema ?? [])],\n }\n return createSEO(mergedChild, config)\n}\n","import type { JSONLD } from \"./types.js\"\n\n/** WebPage helper — ensures `@context` + `@type`. */\nexport function webPage(parts: {\n readonly name: string\n readonly description?: string\n readonly url: string\n}): JSONLD {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"WebPage\",\n name: parts.name,\n ...(parts.description !== undefined ? { description: parts.description } : {}),\n url: parts.url,\n }\n}\n\nexport function article(parts: {\n readonly headline: string\n readonly description?: string\n readonly datePublished?: string\n readonly url: string\n}): JSONLD {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Article\",\n headline: parts.headline,\n ...(parts.description !== undefined ? { description: parts.description } : {}),\n ...(parts.datePublished !== undefined ? { datePublished: parts.datePublished } : {}),\n url: parts.url,\n }\n}\n\nexport function organization(parts: {\n readonly name: string\n readonly url?: string\n readonly logo?: string\n}): JSONLD {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Organization\",\n name: parts.name,\n ...(parts.url !== undefined ? { url: parts.url } : {}),\n ...(parts.logo !== undefined ? { logo: parts.logo } : {}),\n }\n}\n\nexport function person(parts: { readonly name: string; readonly url?: string }): JSONLD {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Person\",\n name: parts.name,\n ...(parts.url !== undefined ? { url: parts.url } : {}),\n }\n}\n\nexport function product(parts: {\n readonly name: string\n readonly description?: string\n readonly sku?: string\n readonly image?: string\n readonly url: string\n}): JSONLD {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Product\",\n name: parts.name,\n url: parts.url,\n ...(parts.description !== undefined ? { description: parts.description } : {}),\n ...(parts.sku !== undefined ? { sku: parts.sku } : {}),\n ...(parts.image !== undefined ? { image: parts.image } : {}),\n }\n}\n\nexport function breadcrumbList(parts: {\n readonly items: ReadonlyArray<{ readonly name: string; readonly url: string }>\n}): JSONLD {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"BreadcrumbList\",\n itemListElement: parts.items.map((item, i) => ({\n \"@type\": \"ListItem\",\n position: i + 1,\n name: item.name,\n item: item.url,\n })),\n }\n}\n\nexport function faqPage(parts: {\n readonly questions: ReadonlyArray<{ readonly question: string; readonly answer: string }>\n}): JSONLD {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"FAQPage\",\n mainEntity: parts.questions.map((q) => ({\n \"@type\": \"Question\",\n name: q.question,\n acceptedAnswer: { \"@type\": \"Answer\", text: q.answer },\n })),\n }\n}\n\n/** Technical / how-to article (docs templates, PRD §2.5). */\nexport function techArticle(parts: {\n readonly headline: string\n readonly description?: string\n readonly datePublished?: string\n readonly url: string\n}): JSONLD {\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"TechArticle\",\n headline: parts.headline,\n ...(parts.description !== undefined ? { description: parts.description } : {}),\n ...(parts.datePublished !== undefined ? { datePublished: parts.datePublished } : {}),\n url: parts.url,\n }\n}\n\n/** Escape hatch for custom `@type` graphs (caller owns validity). */\nexport function customSchema(node: JSONLD): JSONLD {\n return node\n}\n","import type { JSONLD } from \"./types.js\"\nimport { SEOError } from \"./errors.js\"\n\n/**\n * Validates JSON-LD node structure before serialization.\n * Prevents prototype pollution and ensures @context safety.\n *\n * @param node - The JSON-LD node to validate\n * @param path - Current path in the object (for error messages)\n * @throws {SEOError} If validation fails\n */\nfunction validateJSONLDNode(node: unknown, path = \"root\"): void {\n if (node === null || node === undefined) {\n throw new SEOError(\"VALIDATION\", `JSON-LD node at ${path} is null or undefined`)\n }\n\n if (typeof node !== \"object\") {\n throw new SEOError(\"VALIDATION\", `JSON-LD node at ${path} must be an object`)\n }\n\n const obj = node as Record<string, unknown>\n\n // Validate @context if present\n if (\"@context\" in obj) {\n const context = obj[\"@context\"]\n if (typeof context === \"string\") {\n // Only allow schema.org context\n if (context !== \"https://schema.org\" && !context.startsWith(\"https://schema.org/\")) {\n console.warn(\n `[@better-seo/core] Non-standard @context at ${path}: ${context}. ` +\n \"Only https://schema.org is recommended for security.\",\n )\n }\n } else if (typeof context !== \"object\" || context === null) {\n throw new SEOError(\"VALIDATION\", `@context at ${path} must be a string or object`)\n }\n }\n\n // Validate @type if present\n if (\"@type\" in obj) {\n const type = obj[\"@type\"]\n if (typeof type !== \"string\" || type.length === 0) {\n throw new SEOError(\"VALIDATION\", `@type at ${path} must be a non-empty string`)\n }\n // Warn on potentially dangerous types with HTML/special chars\n if (type.includes(\"<\") || type.includes(\">\") || type.includes('\"')) {\n throw new SEOError(\"VALIDATION\", `@type at ${path} contains invalid characters: ${type}`)\n }\n }\n\n // Recursively validate nested objects and prevent prototype pollution.\n // Use Reflect.ownKeys so JSON.parse-shaped payloads keep own \"__proto__\" keys\n // (object literal `__proto__:` sets [[Prototype]] and is invisible to Object.entries).\n for (const key of Reflect.ownKeys(obj)) {\n if (typeof key !== \"string\") {\n throw new SEOError(\"VALIDATION\", `JSON-LD keys at ${path} must be strings`)\n }\n const value = obj[key]\n if (key === \"__proto__\" || key === \"constructor\" || key === \"prototype\") {\n throw new SEOError(\"VALIDATION\", `Dangerous key \"${key}\" detected at ${path}`)\n }\n if (typeof value === \"object\" && value !== null) {\n validateJSONLDNode(value, `${path}.${key}`)\n }\n }\n}\n\n/**\n * Single JSON-LD serialization path — `JSON.stringify` on the whole graph only (ARCHITECTURE §7).\n * Includes validation to prevent prototype pollution and XSS attacks.\n * For multiple nodes, callers may pass an array; consumers typically emit one script tag per call.\n *\n * @param data - JSON-LD node or array of nodes to serialize\n * @returns JSON string suitable for embedding in <script type=\"application/ld+json\">\n * @throws {SEOError} If validation fails\n *\n * @security Validates against prototype pollution, ensures @context is schema.org,\n * and properly escapes all user content via JSON.stringify\n */\nexport function serializeJSONLD(data: JSONLD | readonly JSONLD[]): string {\n // Validate structure before serialization\n if (Array.isArray(data)) {\n data.forEach((node, index) => validateJSONLDNode(node, `array[${index}]`))\n } else {\n validateJSONLDNode(data, \"root\")\n }\n\n // Deep clone to prevent prototype pollution\n const sanitized = JSON.parse(JSON.stringify(data)) as JSONLD | JSONLD[]\n\n // Final serialization with proper escaping for HTML embedding\n // JSON.stringify escapes </script> as <\\/script> and handles U+2028/U+2029\n return JSON.stringify(sanitized)\n}\n","import type { SEO, TagDescriptor } from \"./types.js\"\nimport { serializeJSONLD } from \"./serialize.js\"\n\n/** Vanilla tag list for snapshots and non-framework hosts (ARCHITECTURE §8). */\nexport function renderTags(seo: SEO): TagDescriptor[] {\n const tags: TagDescriptor[] = []\n tags.push({ kind: \"meta\", name: \"title\", content: seo.meta.title })\n if (seo.meta.description) {\n tags.push({ kind: \"meta\", name: \"description\", content: seo.meta.description })\n }\n if (seo.meta.canonical) {\n tags.push({ kind: \"link\", rel: \"canonical\", href: seo.meta.canonical })\n }\n if (seo.meta.robots) {\n tags.push({ kind: \"meta\", name: \"robots\", content: seo.meta.robots })\n }\n const langs = seo.meta.alternates?.languages\n if (langs) {\n for (const [hreflang, href] of Object.entries(langs)) {\n tags.push({ kind: \"link\", rel: \"alternate\", hreflang, href })\n }\n }\n if (seo.openGraph?.title) {\n tags.push({ kind: \"meta\", property: \"og:title\", content: seo.openGraph.title })\n }\n if (seo.openGraph?.description) {\n tags.push({ kind: \"meta\", property: \"og:description\", content: seo.openGraph.description })\n }\n const ogImages = seo.openGraph?.images\n if (ogImages?.length) {\n const first = ogImages[0]\n if (first?.url) tags.push({ kind: \"meta\", property: \"og:image\", content: first.url })\n if (first?.width !== undefined) {\n tags.push({ kind: \"meta\", property: \"og:image:width\", content: String(first.width) })\n }\n if (first?.height !== undefined) {\n tags.push({ kind: \"meta\", property: \"og:image:height\", content: String(first.height) })\n }\n if (first?.alt) tags.push({ kind: \"meta\", property: \"og:image:alt\", content: first.alt })\n }\n if (seo.twitter?.card) {\n tags.push({ kind: \"meta\", name: \"twitter:card\", content: seo.twitter.card })\n }\n if (seo.twitter?.title) {\n tags.push({ kind: \"meta\", name: \"twitter:title\", content: seo.twitter.title })\n }\n if (seo.twitter?.description) {\n tags.push({ kind: \"meta\", name: \"twitter:description\", content: seo.twitter.description })\n }\n if (seo.twitter?.image) {\n tags.push({ kind: \"meta\", name: \"twitter:image\", content: seo.twitter.image })\n }\n for (const node of seo.schema) {\n tags.push({ kind: \"script-jsonld\", json: serializeJSONLD(node) })\n }\n return tags\n}\n","import type { SEO } from \"./types.js\"\n\nexport type ValidationSeverity = \"warning\" | \"error\"\n\n/** Stable machine-readable codes (PRD §3.5 / observability). */\nexport type ValidationIssueCode =\n | \"TITLE_EMPTY\"\n | \"TITLE_TOO_LONG\"\n | \"DESCRIPTION_MISSING\"\n | \"DESCRIPTION_REQUIRED\"\n | \"DESCRIPTION_TOO_LONG\"\n | \"OG_IMAGE_NARROW\"\n | \"SCHEMA_MISSING_TYPE\"\n\nexport interface ValidationIssue {\n readonly code: ValidationIssueCode\n readonly field: string\n readonly message: string\n readonly severity: ValidationSeverity\n}\n\nexport interface ValidateSEOOptions {\n /** When false, validation is a no-op (production / Edge bundles). */\n readonly enabled?: boolean\n /** When true, missing description is `error` instead of `warning`. */\n readonly requireDescription?: boolean\n /** In non-production only: when not false, log each issue to console.warn (default: true). */\n readonly log?: boolean\n readonly titleMaxLength?: number\n readonly descriptionMaxLength?: number\n}\n\nfunction shouldRun(options?: ValidateSEOOptions): boolean {\n if (options?.enabled === false) return false\n\n // Safe environment check - handles Edge, browser, and bundler polyfills\n const isProduction =\n typeof process !== \"undefined\" &&\n typeof process.env === \"object\" &&\n process.env !== null &&\n process.env.NODE_ENV === \"production\"\n\n if (isProduction) return false\n return true\n}\n\n/**\n * Development-oriented checks — no heavy deps (ARCHITECTURE §12).\n * Returns structured issues; set `log: false` to consume programmatically without console noise.\n */\nexport function validateSEO(seo: SEO, options?: ValidateSEOOptions): readonly ValidationIssue[] {\n if (!shouldRun(options)) return []\n\n const titleMax = options?.titleMaxLength ?? 60\n const descMax = options?.descriptionMaxLength ?? 165\n const log = options?.log !== false\n const requireDesc = options?.requireDescription === true\n const issues: ValidationIssue[] = []\n\n if (!seo.meta.title?.trim()) {\n issues.push({\n code: \"TITLE_EMPTY\",\n field: \"meta.title\",\n message: \"empty title\",\n severity: \"error\",\n })\n } else if (seo.meta.title.length > titleMax) {\n issues.push({\n code: \"TITLE_TOO_LONG\",\n field: \"meta.title\",\n message: `length ${seo.meta.title.length} exceeds recommended ${titleMax}`,\n severity: \"warning\",\n })\n }\n\n if (!seo.meta.description?.trim()) {\n issues.push({\n code: requireDesc ? \"DESCRIPTION_REQUIRED\" : \"DESCRIPTION_MISSING\",\n field: \"meta.description\",\n message: requireDesc ? \"description is required\" : \"missing description\",\n severity: requireDesc ? \"error\" : \"warning\",\n })\n } else if (seo.meta.description.length > descMax) {\n issues.push({\n code: \"DESCRIPTION_TOO_LONG\",\n field: \"meta.description\",\n message: `length ${seo.meta.description.length} exceeds recommended ${descMax}`,\n severity: \"warning\",\n })\n }\n\n const firstOg = seo.openGraph?.images?.[0]\n if (firstOg?.width !== undefined && firstOg.width < 1200) {\n issues.push({\n code: \"OG_IMAGE_NARROW\",\n field: \"openGraph.images[0].width\",\n message: \"OG image width under 1200px\",\n severity: \"warning\",\n })\n }\n\n for (let i = 0; i < seo.schema.length; i++) {\n const node = seo.schema[i]\n if (!node?.[\"@type\"]) {\n issues.push({\n code: \"SCHEMA_MISSING_TYPE\",\n field: `schema[${i}]`,\n message: \"missing @type\",\n severity: \"error\",\n })\n }\n }\n\n if (log) {\n for (const i of issues) {\n console.warn(\n `[@better-seo/core] validateSEO [${i.severity}] ${i.code} ${i.field}: ${i.message}`,\n )\n }\n }\n\n return issues\n}\n","import type { SEOAdapter } from \"../types.js\"\nimport { SEOError } from \"../errors.js\"\n\nconst adapters = new Map<string, SEOAdapter<unknown>>()\n\n/**\n * Validates adapter ID to prevent namespace collisions and malicious overrides.\n *\n * @param id - The adapter ID to validate\n * @throws {SEOError} If adapter ID is invalid\n */\nfunction validateAdapterId(id: string): void {\n if (!id || typeof id !== \"string\") {\n throw new SEOError(\"VALIDATION\", \"Adapter ID must be a non-empty string\")\n }\n\n // Prevent extremely long IDs (DoS prevention)\n if (id.length > 64) {\n throw new SEOError(\"VALIDATION\", `Adapter ID too long: ${id.length} chars (max 64)`)\n }\n\n // Only allow alphanumeric, dash, underscore (prevents path traversal, etc.)\n if (!/^[a-zA-Z0-9_-]+$/.test(id)) {\n throw new SEOError(\"VALIDATION\", `Adapter ID contains invalid characters: ${id}`)\n }\n\n // Warn on overriding existing adapter (supply chain attack detection)\n if (adapters.has(id)) {\n console.warn(\n `[@better-seo/core] Overriding existing adapter: \"${id}\". ` +\n \"This may indicate a dependency conflict or malicious package.\",\n )\n }\n}\n\n/**\n * Register an adapter implementation.\n *\n * ⚠️ **SECURITY**: Only register adapters from trusted sources.\n * Malicious adapters can intercept and modify SEO data.\n *\n * @param adapter - The adapter to register\n * @throws {SEOError} If adapter ID is invalid or adapter is not an object\n *\n * @example\n * ```ts\n * import { registerAdapter } from '@better-seo/core'\n *\n * registerAdapter({\n * id: 'my-framework',\n * toFramework: (seo) => { /* conversion logic *\\/ }\n * })\n * ```\n */\nexport function registerAdapter<T>(adapter: SEOAdapter<T>): void {\n if (!adapter || typeof adapter !== \"object\") {\n throw new SEOError(\"VALIDATION\", \"Adapter must be an object\")\n }\n\n validateAdapterId(adapter.id)\n adapters.set(adapter.id, adapter)\n}\n\nexport function getAdapter<T = unknown>(id: string): SEOAdapter<T> | undefined {\n return adapters.get(id) as SEOAdapter<T> | undefined\n}\n\nexport function listAdapterIds(): string[] {\n return [...adapters.keys()]\n}\n","import { createSEO, mergeSEO } from \"./core.js\"\nimport type { SEO, SEOConfig, SEOInput } from \"./types.js\"\n\nexport interface SEOContext {\n readonly config: SEOConfig\n /** Request-scoped `createSEO` with bound config + plugins. */\n readonly createSEO: (input: SEOInput) => SEO\n readonly mergeSEO: (parent: SEO, child: SEOInput) => SEO\n}\n\n/**\n * Preferred production pattern for multi-tenant / Edge — explicit config, no filesystem inference (ARCHITECTURE §13).\n */\nexport function createSEOContext(config: SEOConfig): SEOContext {\n return {\n config,\n createSEO: (input: SEOInput) => createSEO(input, config),\n mergeSEO: (parent: SEO, child: SEOInput) => mergeSEO(parent, child, config),\n }\n}\n","import type { SEOConfig } from \"./types.js\"\n\nlet globalConfig: SEOConfig | undefined\n\n/**\n * ⚠️ **SECURITY WARNING**: Global state is NOT safe for multi-tenant or serverless environments.\n *\n * This function stores config in module-level global state, which can leak between:\n * - Different users in serverless functions (Vercel, Netlify, Cloudflare Workers)\n * - Concurrent requests in Node.js servers\n * - Different tenants in multi-tenant applications\n *\n * **DO NOT USE** in:\n * - Server-side rendering (SSR) with multiple users\n * - Edge functions or Workers\n * - Multi-tenant SaaS applications\n * - Any environment where config must be isolated per request\n *\n * **SAFE ALTERNATIVE:** Use `createSEOContext()` for request-scoped configuration.\n *\n * @deprecated Use `createSEOContext()` for production applications.\n * Only suitable for single-user, static sites or development.\n *\n * @param config - Global SEO configuration to store\n *\n * @see {@link createSEOContext} for request-scoped configuration\n * @see {@link internal-docs/ARCHITECTURE.md} §10 for runtime matrix\n * @see {@link internal-docs/USAGE.md} for security best practices\n */\nexport function initSEO(config: SEOConfig): void {\n // Security warning in development\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\") {\n console.warn(\n \"[@better-seo/core] ⚠️ initSEO() uses global state and is NOT safe for \" +\n \"multi-tenant or serverless environments. Use createSEOContext() instead. \" +\n \"See: https://github.com/OWNER/better-seo-js/blob/main/internal-docs/ARCHITECTURE.md\",\n )\n }\n\n globalConfig = config\n}\n\n/**\n * Gets the global SEO config.\n *\n * @deprecated Use `createSEOContext()` for production applications.\n *\n * @returns The global SEO config, or undefined if not initialized\n *\n * @see {@link createSEOContext} for request-scoped configuration\n */\nexport function getGlobalSEOConfig(): SEOConfig | undefined {\n return globalConfig\n}\n\n/**\n * Resets the global SEO config.\n *\n * @internal Used for testing only\n */\nexport function resetSEOConfigForTests(): void {\n globalConfig = undefined\n}\n","import { createSEO } from \"./core.js\"\nimport { getAdapter } from \"./adapters/registry.js\"\nimport { SEOError } from \"./errors.js\"\nimport type { SEOConfig, SEOInput } from \"./types.js\"\n\n/**\n * Resolve via registered adapter — prefer `@better-seo/next`’s `seo()` for the Next voilà path.\n * @throws {SEOError} ADAPTER_NOT_FOUND when the adapter id was never registered\n */\nexport function seoForFramework<T>(adapterId: string, input: SEOInput, config?: SEOConfig): T {\n const adapter = getAdapter(adapterId)\n if (!adapter) {\n throw new SEOError(\n \"ADAPTER_NOT_FOUND\",\n `no adapter \"${adapterId}\" registered (import your framework package, e.g. @better-seo/next).`,\n )\n }\n const doc = createSEO(input, config)\n return adapter.toFramework(doc) as T\n}\n\n/**\n * Stub until `@better-seo/react` (Wave 5 / V3). Calling this makes missing peer support obvious in dev.\n * @throws {SEOError} USE_SEO_NOT_AVAILABLE\n */\nexport function useSEO(): never {\n throw new SEOError(\"USE_SEO_NOT_AVAILABLE\")\n}\n","import { createSEO, mergeSEO } from \"./core.js\"\nimport type { SEO, SEOConfig, SEOInput, SEORule } from \"./types.js\"\n\n/** Normalize path for comparison (leading slash, collapse slashes, trim trailing except root). */\nexport function normalizeRoutePath(path: string): string {\n if (!path || path === \"/\") return \"/\"\n const t = path.startsWith(\"/\") ? path : `/${path}`\n const c = t.replace(/\\/+/g, \"/\")\n return c.endsWith(\"/\") && c.length > 1 ? c.slice(0, -1) : c\n}\n\nfunction pathSegments(path: string): string[] {\n const n = normalizeRoutePath(path)\n if (n === \"/\") return []\n return n.slice(1).split(\"/\")\n}\n\nfunction matchSegment(pat: string, seg: string): boolean {\n if (pat === \"*\") return true\n if (pat.endsWith(\"*\") && pat.length > 1) {\n return seg.startsWith(pat.slice(0, -1))\n }\n return pat === seg\n}\n\n/** Segment glob: multi-segment and single-segment wildcards (see rules.test.ts). */\nexport function matchRouteGlob(pattern: string, route: string): boolean {\n const ps = pathSegments(pattern)\n const rs = pathSegments(route)\n const dfs = (pi: number, ri: number): boolean => {\n if (pi === ps.length) return ri === rs.length\n if (ps[pi] === \"**\") {\n if (dfs(pi + 1, ri)) return true\n if (ri < rs.length && dfs(pi, ri + 1)) return true\n return false\n }\n if (ri === rs.length) return false\n if (!matchSegment(ps[pi]!, rs[ri]!)) return false\n return dfs(pi + 1, ri + 1)\n }\n return dfs(0, 0)\n}\n\nfunction matchRouteLegacyStar(pattern: string, route: string): boolean {\n const prefix = pattern.slice(0, -1)\n const r = normalizeRoutePath(route)\n const p = normalizeRoutePath(prefix)\n if (r === p) return true\n return r.startsWith(prefix) || r.startsWith(`${p}/`)\n}\n\n/** One `*` per path segment (not `**`). */\nfunction matchSingleSegmentStars(pattern: string, route: string): boolean {\n const ps = pathSegments(pattern)\n const rs = pathSegments(route)\n if (ps.length !== rs.length) return false\n for (let i = 0; i < ps.length; i++) {\n if (!matchSegment(ps[i]!, rs[i]!)) return false\n }\n return true\n}\n\nexport function matchRoute(pattern: string, route: string): boolean {\n if (pattern === \"*\") return true\n const r = route || \"/\"\n if (pattern.includes(\"**\")) {\n return matchRouteGlob(pattern, r)\n }\n /** Trailing `*` only → legacy full-path prefix (`/blog/*`, `/api*`). */\n if (pattern.endsWith(\"*\") && pattern.indexOf(\"*\") === pattern.length - 1) {\n return matchRouteLegacyStar(pattern, r)\n }\n if (pattern.includes(\"*\")) {\n return matchSingleSegmentStars(pattern, r)\n }\n return normalizeRoutePath(r) === normalizeRoutePath(pattern)\n}\n\n/**\n * Pure rule matcher — `**` segment globs + legacy `prefix*` path match (ARCHITECTURE §11 subset).\n */\nexport function applyRules(route: string, rules: readonly SEORule[]): Partial<SEOInput> {\n /** Lower priority merged first; higher `priority` wins on duplicate keys (ARCHITECTURE §11). */\n const sorted = [...rules].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0))\n let acc: Partial<SEOInput> = {}\n for (const rule of sorted) {\n if (matchRoute(rule.match, route)) {\n acc = { ...acc, ...rule.seo }\n }\n }\n return acc\n}\n\n/** Merge rule output into a base document (helper for adapters). */\nexport function applyRulesToSEO(\n route: string,\n base: SEO,\n rules: readonly SEORule[],\n config?: SEOConfig,\n): SEO {\n const partial = applyRules(route, rules)\n if (Object.keys(partial).length === 0) return base\n return mergeSEO(base, partial, config)\n}\n\n/** Convenience: rules → `createSEO`. */\nexport function createSEOForRoute(\n route: string,\n input: SEOInput,\n rules: readonly SEORule[],\n config?: SEOConfig,\n): SEO {\n const mergedInput: SEOInput = { ...applyRules(route, rules), ...input }\n return createSEO(mergedInput, config)\n}\n","import { SEOError } from \"./errors.js\"\n\n/**\n * Codemod-oriented helpers — stub until Wave 12 / CLI `migrate` (FEATURES C15).\n * @throws {SEOError} MIGRATE_NOT_IMPLEMENTED\n */\nexport function fromNextSeo(_nextSeoExport: unknown): never {\n void _nextSeoExport\n throw new SEOError(\"MIGRATE_NOT_IMPLEMENTED\")\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/** Strict JSON-LD values (no `any` on public surface). */
|
|
2
|
+
type JSONLDValue = string | number | boolean | null | JSONLDValue[] | {
|
|
3
|
+
readonly [k: string]: JSONLDValue;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* JSON-LD node. Helpers must ensure `@context` and `@type` before serialization when required by consumers.
|
|
7
|
+
*/
|
|
8
|
+
type JSONLD = {
|
|
9
|
+
readonly "@context"?: string | Record<string, unknown>;
|
|
10
|
+
readonly "@type": string;
|
|
11
|
+
readonly "@id"?: string;
|
|
12
|
+
} & {
|
|
13
|
+
readonly [k: string]: JSONLDValue | Record<string, unknown> | undefined;
|
|
14
|
+
};
|
|
15
|
+
interface SEOPlugin {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly beforeMerge?: (draft: SEOInput, ctx: {
|
|
18
|
+
readonly config?: SEOConfig;
|
|
19
|
+
}) => SEOInput;
|
|
20
|
+
readonly afterMerge?: (seo: SEO, ctx: {
|
|
21
|
+
readonly config?: SEOConfig;
|
|
22
|
+
}) => SEO;
|
|
23
|
+
}
|
|
24
|
+
interface SEOConfig {
|
|
25
|
+
readonly titleTemplate?: string;
|
|
26
|
+
readonly baseUrl?: string;
|
|
27
|
+
readonly defaultRobots?: string;
|
|
28
|
+
readonly schemaMerge?: "concat" | {
|
|
29
|
+
readonly dedupeByIdAndType?: boolean;
|
|
30
|
+
};
|
|
31
|
+
readonly features?: Partial<{
|
|
32
|
+
readonly jsonLd: boolean;
|
|
33
|
+
readonly openGraphMerge: boolean;
|
|
34
|
+
}>;
|
|
35
|
+
/** Hooks run in order; prefer `createSEOContext` for request-scoped registration (future hardening). */
|
|
36
|
+
readonly plugins?: readonly SEOPlugin[];
|
|
37
|
+
}
|
|
38
|
+
/** hreflang → absolute or path URL (adapter maps to framework expectations). */
|
|
39
|
+
interface SEOAlternates {
|
|
40
|
+
readonly languages?: Readonly<Record<string, string>>;
|
|
41
|
+
}
|
|
42
|
+
interface SEOMeta {
|
|
43
|
+
readonly title: string;
|
|
44
|
+
readonly description?: string;
|
|
45
|
+
readonly canonical?: string;
|
|
46
|
+
readonly robots?: string;
|
|
47
|
+
readonly alternates?: SEOAlternates;
|
|
48
|
+
}
|
|
49
|
+
interface SEOImage {
|
|
50
|
+
readonly url: string;
|
|
51
|
+
readonly width?: number;
|
|
52
|
+
readonly height?: number;
|
|
53
|
+
readonly alt?: string;
|
|
54
|
+
}
|
|
55
|
+
interface SEO {
|
|
56
|
+
readonly meta: SEOMeta;
|
|
57
|
+
readonly openGraph?: {
|
|
58
|
+
readonly title?: string;
|
|
59
|
+
readonly description?: string;
|
|
60
|
+
readonly url?: string;
|
|
61
|
+
readonly type?: string;
|
|
62
|
+
readonly images?: readonly SEOImage[];
|
|
63
|
+
};
|
|
64
|
+
readonly twitter?: {
|
|
65
|
+
readonly card?: "summary" | "summary_large_image";
|
|
66
|
+
readonly title?: string;
|
|
67
|
+
readonly description?: string;
|
|
68
|
+
readonly image?: string;
|
|
69
|
+
};
|
|
70
|
+
readonly schema: readonly JSONLD[];
|
|
71
|
+
}
|
|
72
|
+
type SEOAdapter<TOutput = unknown> = {
|
|
73
|
+
readonly id: string;
|
|
74
|
+
toFramework(seo: SEO): TOutput;
|
|
75
|
+
};
|
|
76
|
+
/** Loosely-accepted shape for `createSEO` / voilà entrypoints. */
|
|
77
|
+
type SEOInput = Omit<Partial<SEO>, "meta" | "schema" | "openGraph" | "twitter"> & {
|
|
78
|
+
readonly meta?: Partial<SEOMeta>;
|
|
79
|
+
readonly title?: string;
|
|
80
|
+
readonly description?: string;
|
|
81
|
+
readonly canonical?: string;
|
|
82
|
+
readonly robots?: string;
|
|
83
|
+
readonly openGraph?: SEO["openGraph"];
|
|
84
|
+
readonly twitter?: SEO["twitter"];
|
|
85
|
+
readonly schema?: readonly JSONLD[];
|
|
86
|
+
};
|
|
87
|
+
/** Rule output merges into `Partial<SEOInput>` before `createSEO`. */
|
|
88
|
+
interface SEORule {
|
|
89
|
+
readonly match: string;
|
|
90
|
+
readonly priority?: number;
|
|
91
|
+
readonly seo: Partial<SEOInput>;
|
|
92
|
+
}
|
|
93
|
+
type TagDescriptor = {
|
|
94
|
+
readonly kind: "meta";
|
|
95
|
+
readonly name?: string;
|
|
96
|
+
readonly property?: string;
|
|
97
|
+
readonly content: string;
|
|
98
|
+
} | {
|
|
99
|
+
readonly kind: "link";
|
|
100
|
+
readonly rel: string;
|
|
101
|
+
readonly href: string;
|
|
102
|
+
readonly hreflang?: string;
|
|
103
|
+
} | {
|
|
104
|
+
readonly kind: "script-jsonld";
|
|
105
|
+
readonly json: string;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** Stable codes for programmatic handling (enterprise / observability). */
|
|
109
|
+
type SEOErrorCode = "VALIDATION" | "ADAPTER_NOT_FOUND" | "MIGRATE_NOT_IMPLEMENTED" | "USE_SEO_NOT_AVAILABLE";
|
|
110
|
+
declare class SEOError extends Error {
|
|
111
|
+
readonly code: SEOErrorCode;
|
|
112
|
+
readonly cause?: unknown;
|
|
113
|
+
constructor(code: SEOErrorCode, message?: string, options?: {
|
|
114
|
+
cause?: unknown;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
declare function isSEOError(e: unknown): e is SEOError;
|
|
118
|
+
|
|
119
|
+
/** Normalize partial + config into a canonical `SEO` document (Wave 1 baseline). */
|
|
120
|
+
declare function createSEO(input: SEOInput, config?: SEOConfig): SEO;
|
|
121
|
+
/** Merge parent `SEO` with child input (featured as `withSEO` in docs / Next package). */
|
|
122
|
+
declare function withSEO(parent: SEO, child: SEOInput, config?: SEOConfig): SEO;
|
|
123
|
+
declare function mergeSEO(parent: SEO, child: SEOInput, config?: SEOConfig): SEO;
|
|
124
|
+
|
|
125
|
+
/** WebPage helper — ensures `@context` + `@type`. */
|
|
126
|
+
declare function webPage(parts: {
|
|
127
|
+
readonly name: string;
|
|
128
|
+
readonly description?: string;
|
|
129
|
+
readonly url: string;
|
|
130
|
+
}): JSONLD;
|
|
131
|
+
declare function article(parts: {
|
|
132
|
+
readonly headline: string;
|
|
133
|
+
readonly description?: string;
|
|
134
|
+
readonly datePublished?: string;
|
|
135
|
+
readonly url: string;
|
|
136
|
+
}): JSONLD;
|
|
137
|
+
declare function organization(parts: {
|
|
138
|
+
readonly name: string;
|
|
139
|
+
readonly url?: string;
|
|
140
|
+
readonly logo?: string;
|
|
141
|
+
}): JSONLD;
|
|
142
|
+
declare function person(parts: {
|
|
143
|
+
readonly name: string;
|
|
144
|
+
readonly url?: string;
|
|
145
|
+
}): JSONLD;
|
|
146
|
+
declare function product(parts: {
|
|
147
|
+
readonly name: string;
|
|
148
|
+
readonly description?: string;
|
|
149
|
+
readonly sku?: string;
|
|
150
|
+
readonly image?: string;
|
|
151
|
+
readonly url: string;
|
|
152
|
+
}): JSONLD;
|
|
153
|
+
declare function breadcrumbList(parts: {
|
|
154
|
+
readonly items: ReadonlyArray<{
|
|
155
|
+
readonly name: string;
|
|
156
|
+
readonly url: string;
|
|
157
|
+
}>;
|
|
158
|
+
}): JSONLD;
|
|
159
|
+
declare function faqPage(parts: {
|
|
160
|
+
readonly questions: ReadonlyArray<{
|
|
161
|
+
readonly question: string;
|
|
162
|
+
readonly answer: string;
|
|
163
|
+
}>;
|
|
164
|
+
}): JSONLD;
|
|
165
|
+
/** Technical / how-to article (docs templates, PRD §2.5). */
|
|
166
|
+
declare function techArticle(parts: {
|
|
167
|
+
readonly headline: string;
|
|
168
|
+
readonly description?: string;
|
|
169
|
+
readonly datePublished?: string;
|
|
170
|
+
readonly url: string;
|
|
171
|
+
}): JSONLD;
|
|
172
|
+
/** Escape hatch for custom `@type` graphs (caller owns validity). */
|
|
173
|
+
declare function customSchema(node: JSONLD): JSONLD;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Single JSON-LD serialization path — `JSON.stringify` on the whole graph only (ARCHITECTURE §7).
|
|
177
|
+
* Includes validation to prevent prototype pollution and XSS attacks.
|
|
178
|
+
* For multiple nodes, callers may pass an array; consumers typically emit one script tag per call.
|
|
179
|
+
*
|
|
180
|
+
* @param data - JSON-LD node or array of nodes to serialize
|
|
181
|
+
* @returns JSON string suitable for embedding in <script type="application/ld+json">
|
|
182
|
+
* @throws {SEOError} If validation fails
|
|
183
|
+
*
|
|
184
|
+
* @security Validates against prototype pollution, ensures @context is schema.org,
|
|
185
|
+
* and properly escapes all user content via JSON.stringify
|
|
186
|
+
*/
|
|
187
|
+
declare function serializeJSONLD(data: JSONLD | readonly JSONLD[]): string;
|
|
188
|
+
|
|
189
|
+
/** Vanilla tag list for snapshots and non-framework hosts (ARCHITECTURE §8). */
|
|
190
|
+
declare function renderTags(seo: SEO): TagDescriptor[];
|
|
191
|
+
|
|
192
|
+
type ValidationSeverity = "warning" | "error";
|
|
193
|
+
/** Stable machine-readable codes (PRD §3.5 / observability). */
|
|
194
|
+
type ValidationIssueCode = "TITLE_EMPTY" | "TITLE_TOO_LONG" | "DESCRIPTION_MISSING" | "DESCRIPTION_REQUIRED" | "DESCRIPTION_TOO_LONG" | "OG_IMAGE_NARROW" | "SCHEMA_MISSING_TYPE";
|
|
195
|
+
interface ValidationIssue {
|
|
196
|
+
readonly code: ValidationIssueCode;
|
|
197
|
+
readonly field: string;
|
|
198
|
+
readonly message: string;
|
|
199
|
+
readonly severity: ValidationSeverity;
|
|
200
|
+
}
|
|
201
|
+
interface ValidateSEOOptions {
|
|
202
|
+
/** When false, validation is a no-op (production / Edge bundles). */
|
|
203
|
+
readonly enabled?: boolean;
|
|
204
|
+
/** When true, missing description is `error` instead of `warning`. */
|
|
205
|
+
readonly requireDescription?: boolean;
|
|
206
|
+
/** In non-production only: when not false, log each issue to console.warn (default: true). */
|
|
207
|
+
readonly log?: boolean;
|
|
208
|
+
readonly titleMaxLength?: number;
|
|
209
|
+
readonly descriptionMaxLength?: number;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Development-oriented checks — no heavy deps (ARCHITECTURE §12).
|
|
213
|
+
* Returns structured issues; set `log: false` to consume programmatically without console noise.
|
|
214
|
+
*/
|
|
215
|
+
declare function validateSEO(seo: SEO, options?: ValidateSEOOptions): readonly ValidationIssue[];
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Register an adapter implementation.
|
|
219
|
+
*
|
|
220
|
+
* ⚠️ **SECURITY**: Only register adapters from trusted sources.
|
|
221
|
+
* Malicious adapters can intercept and modify SEO data.
|
|
222
|
+
*
|
|
223
|
+
* @param adapter - The adapter to register
|
|
224
|
+
* @throws {SEOError} If adapter ID is invalid or adapter is not an object
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```ts
|
|
228
|
+
* import { registerAdapter } from '@better-seo/core'
|
|
229
|
+
*
|
|
230
|
+
* registerAdapter({
|
|
231
|
+
* id: 'my-framework',
|
|
232
|
+
* toFramework: (seo) => { /* conversion logic *\/ }
|
|
233
|
+
* })
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
declare function registerAdapter<T>(adapter: SEOAdapter<T>): void;
|
|
237
|
+
declare function getAdapter<T = unknown>(id: string): SEOAdapter<T> | undefined;
|
|
238
|
+
declare function listAdapterIds(): string[];
|
|
239
|
+
|
|
240
|
+
declare function defineSEOPlugin(plugin: SEOPlugin): SEOPlugin;
|
|
241
|
+
|
|
242
|
+
interface SEOContext {
|
|
243
|
+
readonly config: SEOConfig;
|
|
244
|
+
/** Request-scoped `createSEO` with bound config + plugins. */
|
|
245
|
+
readonly createSEO: (input: SEOInput) => SEO;
|
|
246
|
+
readonly mergeSEO: (parent: SEO, child: SEOInput) => SEO;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Preferred production pattern for multi-tenant / Edge — explicit config, no filesystem inference (ARCHITECTURE §13).
|
|
250
|
+
*/
|
|
251
|
+
declare function createSEOContext(config: SEOConfig): SEOContext;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* ⚠️ **SECURITY WARNING**: Global state is NOT safe for multi-tenant or serverless environments.
|
|
255
|
+
*
|
|
256
|
+
* This function stores config in module-level global state, which can leak between:
|
|
257
|
+
* - Different users in serverless functions (Vercel, Netlify, Cloudflare Workers)
|
|
258
|
+
* - Concurrent requests in Node.js servers
|
|
259
|
+
* - Different tenants in multi-tenant applications
|
|
260
|
+
*
|
|
261
|
+
* **DO NOT USE** in:
|
|
262
|
+
* - Server-side rendering (SSR) with multiple users
|
|
263
|
+
* - Edge functions or Workers
|
|
264
|
+
* - Multi-tenant SaaS applications
|
|
265
|
+
* - Any environment where config must be isolated per request
|
|
266
|
+
*
|
|
267
|
+
* **SAFE ALTERNATIVE:** Use `createSEOContext()` for request-scoped configuration.
|
|
268
|
+
*
|
|
269
|
+
* @deprecated Use `createSEOContext()` for production applications.
|
|
270
|
+
* Only suitable for single-user, static sites or development.
|
|
271
|
+
*
|
|
272
|
+
* @param config - Global SEO configuration to store
|
|
273
|
+
*
|
|
274
|
+
* @see {@link createSEOContext} for request-scoped configuration
|
|
275
|
+
* @see {@link internal-docs/ARCHITECTURE.md} §10 for runtime matrix
|
|
276
|
+
* @see {@link internal-docs/USAGE.md} for security best practices
|
|
277
|
+
*/
|
|
278
|
+
declare function initSEO(config: SEOConfig): void;
|
|
279
|
+
/**
|
|
280
|
+
* Gets the global SEO config.
|
|
281
|
+
*
|
|
282
|
+
* @deprecated Use `createSEOContext()` for production applications.
|
|
283
|
+
*
|
|
284
|
+
* @returns The global SEO config, or undefined if not initialized
|
|
285
|
+
*
|
|
286
|
+
* @see {@link createSEOContext} for request-scoped configuration
|
|
287
|
+
*/
|
|
288
|
+
declare function getGlobalSEOConfig(): SEOConfig | undefined;
|
|
289
|
+
/**
|
|
290
|
+
* Resets the global SEO config.
|
|
291
|
+
*
|
|
292
|
+
* @internal Used for testing only
|
|
293
|
+
*/
|
|
294
|
+
declare function resetSEOConfigForTests(): void;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Resolve via registered adapter — prefer `@better-seo/next`’s `seo()` for the Next voilà path.
|
|
298
|
+
* @throws {SEOError} ADAPTER_NOT_FOUND when the adapter id was never registered
|
|
299
|
+
*/
|
|
300
|
+
declare function seoForFramework<T>(adapterId: string, input: SEOInput, config?: SEOConfig): T;
|
|
301
|
+
/**
|
|
302
|
+
* Stub until `@better-seo/react` (Wave 5 / V3). Calling this makes missing peer support obvious in dev.
|
|
303
|
+
* @throws {SEOError} USE_SEO_NOT_AVAILABLE
|
|
304
|
+
*/
|
|
305
|
+
declare function useSEO(): never;
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Pure rule matcher — `**` segment globs + legacy `prefix*` path match (ARCHITECTURE §11 subset).
|
|
309
|
+
*/
|
|
310
|
+
declare function applyRules(route: string, rules: readonly SEORule[]): Partial<SEOInput>;
|
|
311
|
+
/** Merge rule output into a base document (helper for adapters). */
|
|
312
|
+
declare function applyRulesToSEO(route: string, base: SEO, rules: readonly SEORule[], config?: SEOConfig): SEO;
|
|
313
|
+
/** Convenience: rules → `createSEO`. */
|
|
314
|
+
declare function createSEOForRoute(route: string, input: SEOInput, rules: readonly SEORule[], config?: SEOConfig): SEO;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Codemod-oriented helpers — stub until Wave 12 / CLI `migrate` (FEATURES C15).
|
|
318
|
+
* @throws {SEOError} MIGRATE_NOT_IMPLEMENTED
|
|
319
|
+
*/
|
|
320
|
+
declare function fromNextSeo(_nextSeoExport: unknown): never;
|
|
321
|
+
|
|
322
|
+
export { type JSONLD, type JSONLDValue, type SEO, type SEOAdapter, type SEOAlternates, type SEOConfig, type SEOContext, SEOError, type SEOErrorCode, type SEOImage, type SEOInput, type SEOMeta, type SEOPlugin, type SEORule, type TagDescriptor, type ValidateSEOOptions, type ValidationIssue, type ValidationIssueCode, type ValidationSeverity, applyRules, applyRulesToSEO, article, breadcrumbList, createSEO, createSEOContext, createSEOForRoute, customSchema, defineSEOPlugin, faqPage, fromNextSeo, getAdapter, getGlobalSEOConfig, initSEO, isSEOError, listAdapterIds, mergeSEO, organization, person, product, registerAdapter, renderTags, resetSEOConfigForTests, seoForFramework, serializeJSONLD, techArticle, useSEO, validateSEO, webPage, withSEO };
|