@happyvertical/smrt-content 0.43.3 → 0.43.5
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/AGENTS.md +9 -0
- package/agents/content-list.md +869 -0
- package/dist/content-query.d.ts +310 -0
- package/dist/content-query.d.ts.map +1 -0
- package/dist/contents.d.ts +22 -0
- package/dist/contents.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +672 -4
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +22 -2
- package/dist/smrt-knowledge.json +38 -5
- package/dist/svelte/components/ContentList.svelte +1580 -281
- package/dist/svelte/components/ContentList.svelte.d.ts +54 -2
- package/dist/svelte/components/ContentList.svelte.d.ts.map +1 -1
- package/dist/svelte/content-list-controller.d.ts +306 -0
- package/dist/svelte/content-list-controller.d.ts.map +1 -0
- package/dist/svelte/content-list-controller.js +921 -0
- package/dist/svelte/content-list-query.d.ts +498 -0
- package/dist/svelte/content-list-query.d.ts.map +1 -0
- package/dist/svelte/content-list-query.js +1294 -0
- package/dist/svelte/content-list-saved-views.d.ts +172 -0
- package/dist/svelte/content-list-saved-views.d.ts.map +1 -0
- package/dist/svelte/content-list-saved-views.js +298 -0
- package/dist/svelte/content-list-url-state.d.ts +211 -0
- package/dist/svelte/content-list-url-state.d.ts.map +1 -0
- package/dist/svelte/content-list-url-state.js +856 -0
- package/dist/svelte/i18n.contribution.d.ts +55 -0
- package/dist/svelte/i18n.contribution.d.ts.map +1 -1
- package/dist/svelte/i18n.contribution.js +57 -0
- package/dist/svelte/index.d.ts +5 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +10 -0
- package/package.json +16 -15
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["contents","content","defaultOptions","validateEntityName","DEFAULT_XML_ENTITIES","METADATA_SYMBOL","isName","SQL_STRICT","HTML_CONTEXT","XML_CONTEXT","xmlNode","result"],"sources":["../src/__smrt-register__.ts","../src/asset-associable.ts","../src/content-asset.ts","../src/content-assets.ts","../src/content-prompts.ts","../src/content-reference.ts","../src/content-references.ts","../src/database-utils.ts","../src/serialization.ts","../src/thumbnail-generator.ts","../src/content.ts","../src/content-chat-prompts.ts","../src/content-chat-session.ts","../src/content-chat-handlers.ts","../src/content-contribution-config.ts","../src/content-contribution.ts","../src/content-contribution-attachment.ts","../src/content-contribution-attachments.ts","../src/content-contribution-revision.ts","../src/content-contribution-revisions.ts","../src/content-contribution-type.ts","../src/content-contribution-types.ts","../src/content-contributor.ts","../src/content-contributors.ts","../src/content-contributions.ts","../src/content-correction.ts","../src/content-corrections.ts","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/util.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/validator.js","../../../node_modules/.pnpm/@nodable+entities@3.0.0/node_modules/@nodable/entities/src/entities.js","../../../node_modules/.pnpm/@nodable+entities@3.0.0/node_modules/@nodable/entities/src/EntityDecoder.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js","../../../node_modules/.pnpm/xml-naming@0.3.0/node_modules/xml-naming/src/index.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js","../../../node_modules/.pnpm/anynum@1.0.1/node_modules/anynum/digitTable.js","../../../node_modules/.pnpm/anynum@1.0.1/node_modules/anynum/anynum.js","../../../node_modules/.pnpm/strnum@2.4.1/node_modules/strnum/strnum.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/ignoreAttributes.js","../../../node_modules/.pnpm/path-expression-matcher@1.6.2/node_modules/path-expression-matcher/src/Expression.js","../../../node_modules/.pnpm/path-expression-matcher@1.6.2/node_modules/path-expression-matcher/src/ExpressionSet.js","../../../node_modules/.pnpm/path-expression-matcher@1.6.2/node_modules/path-expression-matcher/src/Matcher.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/html.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/xml.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/svg.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/sql.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/shell.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/redos.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/nosql.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/log.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/sql-strict.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/index.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/node2json.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js","../src/content-feed-parser.ts","../src/content-feed-source.ts","../src/content-feed-sources.ts","../src/content-types.ts","../src/safe-remote-url.ts","../src/contents.ts","../src/content-feed-sync.ts","../src/content-governance-assignment.ts","../src/content-governance-assignments.ts","../src/content-governance-policy.ts","../src/content-governance-policies.ts","../src/content-governance-profile.ts","../src/content-governance-profiles.ts","../src/content-review.ts","../src/content-reviews.ts","../src/content-version.ts","../src/content-versions.ts","../src/utils.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","import type { Asset } from '@happyvertical/smrt-assets';\n\n/**\n * Contract for objects that participate in the content/asset association\n * pattern.\n *\n * Any class that exposes asset-relationship methods (e.g. `Content` and its\n * STI subclasses) implements this interface explicitly so consumers can rely\n * on the methods existing instead of falling back to `typeof === 'function'`\n * duck-typing checks.\n *\n * @example\n * ```ts\n * import type { AssetAssociable } from '@happyvertical/smrt-content';\n *\n * async function attachThumbnail(\n * target: AssetAssociable,\n * image: Asset,\n * ): Promise<void> {\n * // No defensive runtime checks needed — the contract guarantees the method.\n * await target.addAsset(image, 'thumbnail', 0);\n * }\n * ```\n */\nexport interface AssetAssociable {\n /**\n * Get all assets associated with this object.\n *\n * @param relationship - Optional filter by relationship type\n * (e.g. `'thumbnail'`, `'attachment'`).\n * @returns Array of associated assets. Returns an empty array if the object\n * has not been persisted yet.\n */\n getAssets(relationship?: string): Promise<Asset[]>;\n\n /**\n * Associate an asset with this object via a typed relationship.\n *\n * @param asset - The asset to associate. Must be persisted (have an `id`).\n * @param relationship - Relationship type. Must match\n * `/^[a-zA-Z_][a-zA-Z0-9_]*$/`. Defaults to `'attachment'`.\n * @param sortOrder - Non-negative integer for display order.\n * @throws if either side is unsaved or the relationship/sort order is invalid.\n */\n addAsset(\n asset: Asset,\n relationship?: string,\n sortOrder?: number,\n ): Promise<void>;\n\n /**\n * Remove an associated asset.\n *\n * @param assetId - The asset ID to detach.\n * @param relationship - Optional specific relationship to remove. If omitted,\n * all relationships between this object and the asset are removed.\n */\n removeAsset(assetId: string, relationship?: string): Promise<void>;\n}\n\n/**\n * Contract for objects exposing typed access to a `metadata` JSON field.\n *\n * Use alongside an explicit interface (such as {@link AssetAssociable}) to\n * give consumers a stable contract for reading/writing the loose JSON bag,\n * without leaking the `metadata: Record<string, any>` type into call sites.\n */\nexport interface MetadataAccessor<\n TMetadata extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Get the full metadata record. Always returns an object (never `null`).\n * The returned reference is the live object — callers should treat it as\n * read-only and use {@link MetadataAccessor.setMetadata} or\n * {@link MetadataAccessor.updateMetadata} to mutate it safely.\n */\n getMetadata(): TMetadata;\n\n /**\n * Replace the entire metadata record.\n *\n * @param metadata - The new metadata object. `null`/`undefined` clears it.\n */\n setMetadata(metadata: TMetadata | null | undefined): void;\n\n /**\n * Shallow-merge the supplied patch over the existing metadata.\n *\n * @param patch - Partial metadata. Keys present in the patch overwrite the\n * existing record; keys absent from the patch are preserved.\n * @returns The merged metadata record.\n */\n updateMetadata(patch: Partial<TMetadata>): TMetadata;\n}\n\n/**\n * Runtime type guard for {@link AssetAssociable}.\n *\n * The interface exists primarily so that statically-typed consumers can drop\n * defensive `typeof === 'function'` checks. This guard is for the rare cases\n * where a value enters the system as `unknown` (deserialised payload, plugin\n * input, etc.) and the caller needs to confirm shape before delegating.\n *\n * @example\n * ```ts\n * if (isAssetAssociable(input)) {\n * await input.addAsset(asset, 'attachment');\n * }\n * ```\n */\nexport function isAssetAssociable(value: unknown): value is AssetAssociable {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<AssetAssociable>;\n return (\n typeof candidate.getAssets === 'function' &&\n typeof candidate.addAsset === 'function' &&\n typeof candidate.removeAsset === 'function'\n );\n}\n\n/**\n * Runtime type guard for {@link MetadataAccessor}.\n *\n * Mirrors {@link isAssetAssociable} for the metadata-accessor contract.\n */\nexport function isMetadataAccessor(value: unknown): value is MetadataAccessor {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<MetadataAccessor>;\n return (\n typeof candidate.getMetadata === 'function' &&\n typeof candidate.setMetadata === 'function' &&\n typeof candidate.updateMetadata === 'function'\n );\n}\n\n/**\n * Returns `true` if `value` is a plain object (not an array, not `null`,\n * not a class instance with a custom prototype). Used by `Content`'s metadata\n * accessors to enforce the \"record-shaped\" contract — arrays and other\n * non-record objects are normalised to `{}` rather than silently leaked\n * through.\n *\n * @internal\n */\nexport function isPlainMetadataRecord(\n value: unknown,\n): value is Record<string, unknown> {\n if (!value || typeof value !== 'object') return false;\n if (Array.isArray(value)) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface ContentAssetOptions extends SmrtObjectOptions {\n contentId?: string;\n assetId?: string;\n relationship?: string;\n sortOrder?: number;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_assets',\n conflictColumns: ['content_id', 'asset_id', 'relationship'],\n api: false,\n mcp: false,\n cli: false,\n})\nexport class ContentAsset extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('Content', { required: true })\n contentId = '';\n\n @crossPackageRef('@happyvertical/smrt-assets:Asset', { required: true })\n assetId = '';\n\n @field({ required: true })\n relationship = 'attachment';\n\n @field()\n sortOrder = 0;\n\n constructor(options: ContentAssetOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.assetId) this.assetId = options.assetId;\n if (options.relationship) this.relationship = options.relationship;\n if (options.sortOrder !== undefined) this.sortOrder = options.sortOrder;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n}\n","import type { SmrtCollectionOptions } from '@happyvertical/smrt-core';\nimport { SmrtJunction, smrt } from '@happyvertical/smrt-core';\nimport { ContentAsset } from './content-asset';\n\nexport interface ContentAssetCollectionOptions extends SmrtCollectionOptions {}\n\n@smrt({\n api: false,\n mcp: false,\n cli: false,\n})\nexport class ContentAssetCollection extends SmrtJunction<ContentAsset> {\n static readonly _itemClass = ContentAsset;\n protected leftField = 'contentId';\n protected rightField = 'assetId';\n}\n","import {\n definePrompt,\n type ResolvedPromptAI,\n} from '@happyvertical/smrt-prompts';\n\nexport const smrtContentReviewPrompt = definePrompt({\n key: 'smrtContent.review',\n template: `Content review request\n\nContent ID: {contentId}\nReview kind: {kind}\nPolicy key: {policyKey}\nTitle: {contentTitle}\nDescription: {contentDescription}\n\nBody:\n{contentBody}\n\n{reviewPrompt}`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport const smrtContentApplyCorrectionPrompt = definePrompt({\n key: 'smrtContent.applyCorrection',\n template: `You are revising an article draft to apply a factual correction.\n\nReturn only the fully revised body text, with no commentary.\n\nCurrent body:\n{body}\n\nCorrection summary:\n{summary}\n\nIncorrect text to fix:\n{incorrectText}\n\nCorrected text to incorporate:\n{correctedText}`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport const smrtContentThumbnailAIGeneratePrompt = definePrompt({\n key: 'smrtContent.thumbnail.aiGenerate',\n template: `Create a {style} thumbnail image for an article titled \"{title}\". {descriptionClause}Style: {styleHint}. The image should be suitable for a news article or blog post thumbnail.`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport function promptMessageOptions(ai: ResolvedPromptAI) {\n return {\n ...(ai.params || {}),\n ...(ai.model ? { model: ai.model } : {}),\n ...(typeof ai.temperature === 'number'\n ? { temperature: ai.temperature }\n : {}),\n ...(typeof ai.maxTokens === 'number' ? { maxTokens: ai.maxTokens } : {}),\n };\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface ContentReferenceOptions extends SmrtObjectOptions {\n sourceId?: string;\n targetId?: string;\n tenantId?: string | null;\n // ContentVersion.version pinned at citation time. Optional: references\n // created without a pin behave as before (they track the live target).\n // When set, callers can compare against the target's latest version to\n // surface drift between what was cited and what the target now says.\n targetVersion?: number | null;\n createdAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_references',\n conflictColumns: ['source_id', 'target_id'],\n})\nexport class ContentReference extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('Content', { required: true })\n sourceId = '';\n\n @foreignKey('Content', { required: true })\n targetId = '';\n\n @field({ type: 'integer', nullable: true })\n targetVersion: number | null = null;\n\n @field()\n createdAt = new Date();\n\n constructor(options: ContentReferenceOptions = {}) {\n super(options);\n if (options.sourceId) this.sourceId = options.sourceId;\n if (options.targetId) this.targetId = options.targetId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.targetVersion !== undefined)\n this.targetVersion = options.targetVersion;\n if (options.createdAt) this.createdAt = options.createdAt;\n }\n}\n","import type {\n JunctionAttachOptions,\n SmrtCollectionOptions,\n} from '@happyvertical/smrt-core';\nimport { SmrtJunction, smrt } from '@happyvertical/smrt-core';\nimport { ContentReference } from './content-reference';\n\nexport interface ContentReferencesOptions extends SmrtCollectionOptions {}\n\n/**\n * The `attach()` override below restores find-or-create idempotency for\n * `(sourceId, targetId)` — duplicate calls return the existing row\n * unchanged, preserving `id` and `createdAt`. This matters because\n * `ContentReference` rows are externally addressable via\n * `/api/v1/contentreferences/[id]`.\n *\n * Two REST entry points (both auto-generated by the scanner):\n * - `POST /api/v1/contentreferences` (from model CRUD) calls\n * `collection.create()` which is upsert-based — id/createdAt get\n * rewritten on conflict. Convenient for callers that don't care\n * about row id stability.\n * - `POST /api/v1/contentreferences/attach` (from the override below)\n * is idempotent. Use this for stable URLs.\n *\n * Internal callers (`Content.addReference()`) always hit the idempotent\n * path because they call the collection directly.\n *\n * The `/attach` route exists because R2 round-7 added `@smrt()` to this\n * class, which made the scanner pick up the override as a custom\n * collection method route. Pre-R2 had a `/link` route from the\n * pre-rename method name.\n */\n// Decorator with empty config — only needed so the scanner detects the\n// class. See FactContentCollection for the full rationale.\n@smrt()\nexport class ContentReferences extends SmrtJunction<ContentReference> {\n static readonly _itemClass = ContentReference;\n protected leftField = 'sourceId';\n protected rightField = 'targetId';\n // content_references has no sort_order column — preserve insertion order\n // by sorting on created_at, and disable setLinks position auto-indexing\n // so it doesn't try to write integer indices into the timestamp column.\n protected sortField: string | null = 'createdAt';\n protected positionField: string | null = null;\n\n async getForSource(sourceId: string): Promise<ContentReference[]> {\n return (await this.list({\n where: { sourceId },\n orderBy: 'created_at ASC',\n })) as ContentReference[];\n }\n\n async getForTarget(targetId: string): Promise<ContentReference[]> {\n return (await this.list({\n where: { targetId },\n orderBy: 'created_at ASC',\n })) as ContentReference[];\n }\n\n /**\n * Find-or-create idempotency: if a reference already exists for\n * (sourceId, targetId), return the existing row unchanged instead of\n * upserting a new row. This preserves the existing row's `id` and\n * `createdAt`, which is important because reference rows are\n * externally addressable via `/api/v1/contentreferences/[id]`.\n *\n * The base `SmrtJunction.attach` flow (this.create → db.upsert) would\n * overwrite both columns on every duplicate call.\n *\n * Reference pinning (main): `opts.targetVersion` pins the citation to a\n * specific `ContentVersion.version` for drift detection. Re-attaching an\n * existing edge with a different `targetVersion` updates the pin in place;\n * `undefined` leaves an existing pin untouched, while a brand-new row\n * defaults the pin to `null` (unpinned).\n */\n async attach(\n sourceId: string,\n targetId: string,\n opts: JunctionAttachOptions = {},\n ): Promise<ContentReference> {\n const targetVersion = opts.targetVersion as number | null | undefined;\n const existing = (await this.get({\n sourceId,\n targetId,\n })) as ContentReference | null;\n if (existing) {\n if (\n targetVersion !== undefined &&\n existing.targetVersion !== targetVersion\n ) {\n existing.targetVersion = targetVersion;\n await existing.save();\n }\n return existing;\n }\n return super.attach(sourceId, targetId, {\n ...opts,\n targetVersion: targetVersion ?? null,\n });\n }\n\n async unlink(sourceId: string, targetId: string): Promise<void> {\n await this.detach(sourceId, targetId);\n }\n}\n","export function isMissingTableError(\n error: unknown,\n tableName: string,\n): boolean {\n const message = String(\n (error as Error)?.message || error || '',\n ).toLowerCase();\n\n return (\n message.includes(tableName.toLowerCase()) &&\n (message.includes('no such table') ||\n message.includes('does not exist') ||\n message.includes('relation'))\n );\n}\n\nexport function getQueryRows(result: unknown): Record<string, unknown>[] {\n return Array.isArray(result)\n ? (result as Record<string, unknown>[])\n : Array.isArray((result as { rows?: Record<string, unknown>[] })?.rows)\n ? ((result as { rows: Record<string, unknown>[] }).rows ?? [])\n : [];\n}\n","/**\n * Plain JSON shape produced by serializing a SMRT model instance. Values are\n * intentionally `unknown` — callers spread these records into API responses and\n * narrow individual fields where they need a concrete type.\n */\ntype SerializedRecord = Record<string, unknown>;\n\n/**\n * Minimal structural view of the model instances passed to the serializers.\n * Every model used here exposes `toJSON()` plus optional accessor methods for\n * its JSON-backed fields; the accessors are typed loosely because each model\n * returns a different concrete shape.\n */\ninterface SerializableModel {\n toJSON?: () => unknown;\n getMetadata?: () => unknown;\n getSnapshot?: () => unknown;\n getFindings?: () => unknown;\n getAllowedChannels?: () => unknown;\n getIntakeRules?: () => unknown;\n getPromotion?: () => unknown;\n getRevisions?: () => Promise<unknown[]> | unknown[];\n getAttachments?: () => Promise<unknown[]> | unknown[];\n getContributor?: () => Promise<unknown> | unknown;\n getReferences?: () => Promise<unknown[]> | unknown[];\n getAssets?: () => Promise<unknown[]> | unknown[];\n getReferenceDrift?: () => Promise<unknown> | unknown;\n metadata?: unknown;\n}\n\nfunction asModel(value: unknown): SerializableModel {\n return value && typeof value === 'object' ? (value as SerializableModel) : {};\n}\n\nfunction toJSON(value: unknown): SerializedRecord {\n const model = asModel(value);\n if (typeof model.toJSON === 'function') {\n const serialized = model.toJSON();\n return serialized && typeof serialized === 'object'\n ? (serialized as SerializedRecord)\n : {};\n }\n\n return value && typeof value === 'object' ? (value as SerializedRecord) : {};\n}\n\nexport function serializeFact(fact: unknown) {\n const model = asModel(fact);\n const data = toJSON(fact);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeFactLink(link: unknown) {\n const model = asModel(link);\n const data = toJSON(link);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentVersion(version: unknown) {\n const model = asModel(version);\n const data = toJSON(version);\n return {\n ...data,\n snapshot:\n typeof model.getSnapshot === 'function'\n ? model.getSnapshot()\n : data.snapshot || {},\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentReview(review: unknown) {\n const model = asModel(review);\n const data = toJSON(review);\n return {\n ...data,\n findings:\n typeof model.getFindings === 'function'\n ? model.getFindings()\n : data.findings || [],\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentCorrection(correction: unknown) {\n const model = asModel(correction);\n const data = toJSON(correction);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentContributor(contributor: unknown) {\n const model = asModel(contributor);\n const data = toJSON(contributor);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentContributionType(contributionType: unknown) {\n const model = asModel(contributionType);\n const data = toJSON(contributionType);\n return {\n ...data,\n allowedChannels:\n typeof model.getAllowedChannels === 'function'\n ? model.getAllowedChannels()\n : data.allowedChannels || [],\n intakeRules:\n typeof model.getIntakeRules === 'function'\n ? model.getIntakeRules()\n : data.intakeRules || {},\n promotion:\n typeof model.getPromotion === 'function'\n ? model.getPromotion()\n : data.promotion || {},\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentContributionRevision(revision: unknown) {\n const model = asModel(revision);\n const data = toJSON(revision);\n return {\n ...data,\n sourceMessageId: data.sourceMessageId || null,\n sourceThreadKey: data.sourceThreadKey || null,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentContributionAttachment(attachment: unknown) {\n const model = asModel(attachment);\n const data = toJSON(attachment);\n return {\n ...data,\n revisionId: data.revisionId || null,\n fileKey: data.fileKey || null,\n sourceUri: data.sourceUri || null,\n promotedAssetId: data.promotedAssetId || null,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport async function serializeContentContribution(contribution: unknown) {\n const model = asModel(contribution);\n const [revisions, attachments, contributor] = await Promise.all([\n typeof model.getRevisions === 'function' ? model.getRevisions() : [],\n typeof model.getAttachments === 'function' ? model.getAttachments() : [],\n typeof model.getContributor === 'function' ? model.getContributor() : null,\n ]);\n\n return {\n ...toJSON(contribution),\n contributor: contributor ? serializeContentContributor(contributor) : null,\n revisions: revisions.map(serializeContentContributionRevision),\n attachments: attachments.map(serializeContentContributionAttachment),\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : model.metadata || {},\n };\n}\n\nexport function serializeContentReviewProfileEvaluation(profile: unknown) {\n const data = toJSON(profile);\n return {\n ...data,\n requirements: Array.isArray(data.requirements) ? data.requirements : [],\n };\n}\n\nexport function serializeContentReviewPolicy(policy: unknown) {\n return {\n ...toJSON(policy),\n };\n}\n\nexport function serializeContentGovernanceProfile(profile: unknown) {\n const model = asModel(profile);\n const data = toJSON(profile);\n return {\n ...data,\n requirements: Array.isArray(data.requirements) ? data.requirements : [],\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentGovernanceAssignment(assignment: unknown) {\n const model = asModel(assignment);\n const data = toJSON(assignment);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentGovernanceState(state: unknown) {\n const data = toJSON(state);\n return {\n ...data,\n reviewPolicies: Array.isArray(data.reviewPolicies)\n ? data.reviewPolicies.map(serializeContentReviewPolicy)\n : [],\n availableProfiles: Array.isArray(data.availableProfiles)\n ? data.availableProfiles.map(serializeContentGovernanceProfile)\n : [],\n reviewProfiles: Array.isArray(data.reviewProfiles)\n ? data.reviewProfiles.map(serializeContentReviewProfileEvaluation)\n : [],\n };\n}\n\n/**\n * Reference-drift edge keyed by target content id. Mirrors the shape returned\n * by `Content.getReferenceDrift()`.\n */\ninterface ReferenceDriftEdge {\n citedVersion: number | null;\n currentVersion: number | null;\n isDrifted: boolean;\n}\n\nexport async function serializeContent(content: unknown) {\n const model = asModel(content);\n const [references, assets] = await Promise.all([\n typeof model.getReferences === 'function' ? model.getReferences() : [],\n typeof model.getAssets === 'function' ? model.getAssets() : [],\n ]);\n\n // Only resolve drift when there are references to drift against — list\n // endpoints serializing many ref-less items shouldn't pay the version\n // lookup cost.\n const drift =\n references.length > 0 && typeof model.getReferenceDrift === 'function'\n ? await model.getReferenceDrift()\n : [];\n\n const driftByTargetId = new Map<string, ReferenceDriftEdge>(\n Array.isArray(drift)\n ? drift\n .map((entry) => asModel(entry))\n .filter(\n (entry): entry is SerializableModel & { targetId: string } =>\n typeof (entry as { targetId?: unknown }).targetId === 'string',\n )\n .map((entry) => {\n const edge = entry as {\n targetId: string;\n citedVersion?: unknown;\n currentVersion?: unknown;\n isDrifted?: unknown;\n };\n return [\n edge.targetId,\n {\n citedVersion: (edge.citedVersion as number | null) ?? null,\n currentVersion: (edge.currentVersion as number | null) ?? null,\n isDrifted: Boolean(edge.isDrifted),\n },\n ] satisfies [string, ReferenceDriftEdge];\n })\n : [],\n );\n\n return {\n ...toJSON(content),\n referenceIds: references\n .map((reference) => asModel(reference) as { id?: unknown })\n .map((reference) => reference.id)\n .filter(Boolean),\n references: references.map((reference) => {\n const base = toJSON(reference);\n const edge =\n typeof base.id === 'string' ? driftByTargetId.get(base.id) : null;\n return edge\n ? {\n ...base,\n citedVersion: edge.citedVersion,\n currentVersion: edge.currentVersion,\n isDrifted: edge.isDrifted,\n }\n : base;\n }),\n assetIds: assets\n .map((asset) => (asModel(asset) as { id?: unknown }).id)\n .filter(Boolean),\n assets: assets.map((asset) => toJSON(asset)),\n };\n}\n","/**\n * Thumbnail Generator for Content\n *\n * Generates thumbnail images for content using various strategies:\n * - headline-card: Draws article title on branded background\n * - static-map: Uses static maps API for location-based content\n * - ai-generate: Uses AI image generation for creative thumbnails\n */\n\nimport type { AIClient, AIClientOptions } from '@happyvertical/ai';\nimport { fetchStaticMap, type StaticMapProvider } from '@happyvertical/geo';\nimport {\n generateHeadlineCard,\n type HeadlineCardTemplate,\n} from '@happyvertical/images';\nimport type { DatabaseConfig } from '@happyvertical/smrt-core';\nimport type { Image } from '@happyvertical/smrt-images';\nimport { ImageCollection } from '@happyvertical/smrt-images';\nimport {\n type ResolvedPrompt,\n resolvePrompt,\n} from '@happyvertical/smrt-prompts';\nimport type { Content } from './content';\nimport {\n promptMessageOptions,\n smrtContentThumbnailAIGeneratePrompt,\n} from './content-prompts';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Available thumbnail generation strategies\n */\nexport type ThumbnailStrategy = 'headline-card' | 'static-map' | 'ai-generate';\n\n/**\n * Base options for all strategies\n */\ninterface BaseThumbnailOptions {\n /**\n * Generation strategy\n */\n strategy: ThumbnailStrategy;\n\n /**\n * Width in pixels\n * @default 1200\n */\n width?: number;\n\n /**\n * Height in pixels\n * @default 630\n */\n height?: number;\n}\n\n/**\n * Options for headline card strategy\n */\nexport interface HeadlineCardThumbnailOptions extends BaseThumbnailOptions {\n strategy: 'headline-card';\n\n /**\n * Primary brand color (hex)\n * @default '#3b82f6'\n */\n brandColor?: string;\n\n /**\n * Background color (hex)\n * @default '#ffffff'\n */\n backgroundColor?: string;\n\n /**\n * Optional subtitle/category text\n */\n subtitle?: string;\n\n /**\n * Optional logo URL\n */\n logoUrl?: string;\n\n /**\n * Template style\n * @default 'default'\n */\n template?: HeadlineCardTemplate;\n}\n\n/**\n * Options for static map strategy\n */\nexport interface StaticMapThumbnailOptions extends BaseThumbnailOptions {\n strategy: 'static-map';\n\n /**\n * Map provider\n * @default 'mapbox'\n */\n mapProvider?: StaticMapProvider;\n\n /**\n * Zoom level (1-20)\n * @default 14\n */\n zoom?: number;\n\n /**\n * Marker color\n * @default 'e74c3c'\n */\n markerColor?: string;\n\n /**\n * Mapbox style (if using mapbox provider)\n */\n mapboxStyle?: string;\n\n /**\n * Google map type (if using google provider)\n */\n googleMapType?: 'roadmap' | 'satellite' | 'terrain' | 'hybrid';\n}\n\n/**\n * Options for AI generation strategy\n */\nexport interface AIGenerateThumbnailOptions extends BaseThumbnailOptions {\n strategy: 'ai-generate';\n\n /**\n * AI provider configuration\n */\n ai?: AIClientOptions | AIClient;\n\n /**\n * Custom prompt for image generation\n * If not provided, generates based on content title/body\n */\n prompt?: string;\n\n /**\n * Style hint for image generation\n * @default 'photorealistic'\n */\n style?: 'photorealistic' | 'illustration' | 'abstract' | 'minimal';\n}\n\n/**\n * Union type for all thumbnail options\n */\nexport type ThumbnailOptions =\n | HeadlineCardThumbnailOptions\n | StaticMapThumbnailOptions\n | AIGenerateThumbnailOptions;\n\n// ============================================================================\n// Generator Class\n// ============================================================================\n\n/**\n * Options for ThumbnailGenerator\n */\nexport interface ThumbnailGeneratorOptions {\n /**\n * Database configuration for storing generated images\n */\n db?: DatabaseConfig;\n\n /**\n * Alias for `db` — mirrors the `SmrtClassOptions.persistence` alias so\n * callers can pass the same options shape they use for SmrtObject/Collection.\n *\n * @deprecated Prefer `db`. Retained for parity with `SmrtClassOptions`.\n */\n persistence?: DatabaseConfig;\n\n /**\n * AI client configuration for AI-generated thumbnails\n */\n ai?: AIClientOptions | AIClient;\n}\n\ninterface ImageGenerationClient {\n generateImage(\n prompt: string,\n options?: Record<string, unknown>,\n ): Promise<{\n images?: Array<{ data?: Buffer | string }>;\n }>;\n}\n\nfunction isImageGenerationClient(\n value: AIClientOptions | AIClient,\n): value is AIClient & ImageGenerationClient {\n return (\n !!value &&\n typeof value === 'object' &&\n typeof (value as Record<string, unknown>).generateImage === 'function'\n );\n}\n\nfunction isAIClientOptions(\n value: AIClientOptions | AIClient,\n): value is AIClientOptions {\n return (\n !!value && typeof value === 'object' && !isImageGenerationClient(value)\n );\n}\n\n/**\n * ThumbnailGenerator creates thumbnails for content using various strategies\n */\nexport class ThumbnailGenerator {\n constructor(\n private content: Content,\n private options: ThumbnailGeneratorOptions = {},\n ) {\n // Normalize the `persistence` alias to `db` once so every downstream call\n // (prompt resolution, ImageCollection.create, save sites) sees the same\n // database regardless of which option name the caller used. Without this,\n // a caller passing `persistence: ...` would have prompt resolution honor\n // the alias while image saving silently used `undefined`.\n if (!this.options.db && this.options.persistence) {\n this.options.db = this.options.persistence;\n }\n }\n\n /**\n * Generate a thumbnail using the specified strategy\n */\n async generate(options: ThumbnailOptions): Promise<Image> {\n switch (options.strategy) {\n case 'headline-card':\n return this.generateHeadlineCard(options);\n case 'static-map':\n return this.generateStaticMap(options);\n case 'ai-generate':\n return this.generateWithAI(options);\n default:\n throw new Error(\n // `options` is narrowed to `never` here (exhaustive switch); read the\n // runtime discriminant through a minimal structural view.\n `Unknown thumbnail strategy: ${(options as { strategy: string }).strategy}`,\n );\n }\n }\n\n /**\n * Generate a headline card thumbnail\n */\n private async generateHeadlineCard(\n options: HeadlineCardThumbnailOptions,\n ): Promise<Image> {\n const title = this.content.title || this.content.name || 'Untitled';\n\n const result = await generateHeadlineCard(title, {\n width: options.width ?? 1200,\n height: options.height ?? 630,\n brandColor: options.brandColor,\n backgroundColor: options.backgroundColor,\n subtitle: options.subtitle ?? this.content.category ?? undefined,\n logoUrl: options.logoUrl,\n template: options.template,\n });\n\n return this.createImageFromBuffer(result.buffer, {\n width: result.width,\n height: result.height,\n mimeType: result.mimeType,\n name: `${this.content.id}-headline.png`,\n });\n }\n\n /**\n * Generate a static map thumbnail\n */\n private async generateStaticMap(\n options: StaticMapThumbnailOptions,\n ): Promise<Image> {\n // Coordinates live in the loose `metadata` bag (typed `unknown` values);\n // read them at a documented `string | number` boundary for arithmetic.\n const coordinateMetadata = this.content.metadata as Record<\n string,\n string | number | null | undefined\n >;\n const rawLatitude = coordinateMetadata?.latitude ?? coordinateMetadata?.lat;\n const rawLongitude =\n coordinateMetadata?.longitude ??\n coordinateMetadata?.lng ??\n coordinateMetadata?.lon;\n\n if (rawLatitude == null || rawLongitude == null) {\n throw new Error(\n 'Content metadata must contain latitude and longitude for static-map strategy',\n );\n }\n\n // Parse and validate coordinates\n // Use unary + for strict parsing (rejects \"45invalid\" unlike parseFloat)\n const latitude =\n typeof rawLatitude === 'string' ? +rawLatitude : rawLatitude;\n const longitude =\n typeof rawLongitude === 'string' ? +rawLongitude : rawLongitude;\n\n if (!Number.isFinite(latitude) || latitude < -90 || latitude > 90) {\n throw new Error(\n `Invalid latitude value \"${rawLatitude}\" in content metadata; expected a number between -90 and 90.`,\n );\n }\n\n if (!Number.isFinite(longitude) || longitude < -180 || longitude > 180) {\n throw new Error(\n `Invalid longitude value \"${rawLongitude}\" in content metadata; expected a number between -180 and 180.`,\n );\n }\n\n type FetchStaticMapOptions = NonNullable<\n Parameters<typeof fetchStaticMap>[2]\n >;\n const mapboxStyle = options.mapboxStyle as\n | FetchStaticMapOptions['mapboxStyle']\n | undefined;\n\n const result = await fetchStaticMap(latitude, longitude, {\n provider: options.mapProvider ?? 'mapbox',\n width: options.width ?? 1200,\n height: options.height ?? 630,\n zoom: options.zoom ?? 14,\n markerColor: options.markerColor,\n mapboxStyle,\n googleMapType: options.googleMapType,\n });\n\n return this.createImageFromBuffer(result.buffer, {\n width: result.width,\n height: result.height,\n mimeType: result.mimeType,\n name: `${this.content.id}-map.png`,\n });\n }\n\n /**\n * Generate a thumbnail using AI image generation\n */\n private async generateWithAI(\n options: AIGenerateThumbnailOptions,\n ): Promise<Image> {\n // Dynamic import to avoid requiring AI package when not using this strategy\n const { getAI } = await import('@happyvertical/ai');\n\n const aiInput = options.ai ?? this.options.ai;\n if (!aiInput) {\n throw new Error(\n 'AI configuration required for ai-generate strategy. Provide via options.ai or constructor options.',\n );\n }\n\n const ai = isImageGenerationClient(aiInput)\n ? aiInput\n : isAIClientOptions(aiInput)\n ? await getAI(aiInput)\n : (() => {\n throw new Error(\n 'AI client does not support image generation for ai-generate thumbnails.',\n );\n })();\n\n // Generate prompt if not provided. When the caller supplies a literal\n // prompt we skip prompt resolution entirely (no tenant override path).\n // When we resolve from the registry we also forward the resolved AI\n // options (model, params) so `editable: { model, params }` actually\n // takes effect for thumbnail generation.\n const width = options.width ?? 1200;\n const height = options.height ?? 630;\n let prompt: string;\n let aiOverrideOptions: Record<string, unknown> = {};\n if (options.prompt) {\n prompt = options.prompt;\n } else {\n const built = await this.buildAIPrompt(options.style ?? 'photorealistic');\n prompt = built.text;\n aiOverrideOptions = promptMessageOptions(built.ai);\n }\n\n const result = await ai.generateImage(prompt, {\n ...aiOverrideOptions,\n size: `${width}x${height}`,\n outputFormat: 'buffer',\n });\n\n if (!result.images || result.images.length === 0) {\n throw new Error('AI image generation returned no results');\n }\n\n // Handle buffer or base64 responses\n let buffer: Buffer;\n const imageData = result.images[0].data;\n if (Buffer.isBuffer(imageData)) {\n buffer = imageData;\n } else if (typeof imageData === 'string') {\n // Could be base64 or URL - try base64 first\n if (imageData.startsWith('http')) {\n const response = await fetch(imageData);\n if (!response.ok) {\n throw new Error(\n `AI image generation URL fetch failed: ${response.status} ${response.statusText}`,\n );\n }\n buffer = Buffer.from(await response.arrayBuffer());\n } else {\n buffer = Buffer.from(imageData, 'base64');\n }\n } else {\n throw new Error('AI image generation returned unexpected format');\n }\n\n return this.createImageFromBuffer(buffer, {\n width: options.width ?? 1200,\n height: options.height ?? 630,\n mimeType: 'image/png',\n name: `${this.content.id}-ai.png`,\n });\n }\n\n /**\n * Build a prompt for AI image generation based on content.\n *\n * Resolves via `@happyvertical/smrt-prompts` so tenants can override the\n * template/profile/model/params at runtime. Only non-PII content fields\n * (title, description) and the caller-supplied style hint are passed.\n * Internal IDs and the freeform `metadata` blob are intentionally excluded.\n *\n * Returns the full ResolvedPrompt (text + ai config) so the caller can\n * forward `model`/`params` overrides to `ai.generateImage()`. Returning\n * only the text would silently drop the editable model/params overrides.\n */\n private async buildAIPrompt(style: string): Promise<ResolvedPrompt> {\n const title = this.content.title || 'Untitled';\n const description = this.content.description || '';\n\n const stylePrompts: Record<string, string> = {\n photorealistic:\n 'photorealistic, high quality, professional photography, 8k resolution',\n illustration:\n 'digital illustration, clean vector art, modern design, vibrant colors',\n abstract:\n 'abstract art, geometric shapes, modern minimalist, artistic interpretation',\n minimal:\n 'minimalist design, simple shapes, clean composition, subtle colors',\n };\n\n const styleHint = stylePrompts[style] || stylePrompts.photorealistic;\n\n return resolvePrompt(smrtContentThumbnailAIGeneratePrompt.key, {\n db: this.options.db,\n tenantId: this.content.tenantId,\n variables: {\n style,\n title,\n styleHint,\n descriptionClause: description\n ? `The article is about: ${description}. `\n : '',\n },\n });\n }\n\n /**\n * Create an Image object from a buffer\n */\n private async createImageFromBuffer(\n buffer: Buffer,\n metadata: {\n width: number;\n height: number;\n mimeType: string;\n name: string;\n },\n ): Promise<Image> {\n const images = await ImageCollection.create({\n db: this.options.db,\n });\n\n // Create the image record. `SmrtCollection.create()` already persists\n // (upsert) the row, so a follow-up `image.save()` was redundant (#1387).\n const image = await images.create({\n name: metadata.name,\n mimeType: metadata.mimeType,\n width: metadata.width,\n height: metadata.height,\n sourceUri: `data:${metadata.mimeType};base64,${buffer.toString('base64')}`,\n });\n\n return image;\n }\n}\n","import { type Asset, AssetCollection } from '@happyvertical/smrt-assets';\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport type {\n Fact,\n FactClaimSupportAssessment,\n FactClaimSupportStatus,\n FactContent,\n FactContentRelationship,\n FactEvidence,\n FactEvidenceStatus,\n FactExtractionCandidate,\n FactSource,\n} from '@happyvertical/smrt-facts';\nimport type { Image } from '@happyvertical/smrt-images';\nimport { ImageCollection } from '@happyvertical/smrt-images';\nimport { resolvePrompt } from '@happyvertical/smrt-prompts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { AssetAssociable, MetadataAccessor } from './asset-associable';\nimport { isPlainMetadataRecord } from './asset-associable';\nimport type { ContentBodyFormat } from './body-format';\nimport { isContentBodyFormat } from './body-format';\nimport { ContentAssetCollection } from './content-assets';\nimport {\n buildContentGovernanceAssignmentKey,\n buildContentReviewPrompt,\n type ContentGovernanceState,\n type ContentReviewFinding,\n type ContentReviewProfileEvaluation,\n type CreateContentVersionOptions,\n getAcceptedContentReviewStatuses,\n getContentReviewKind,\n getContentReviewPolicy,\n getContentReviewProfileKeys,\n getContentReviewRequirements,\n type IssueContentCorrectionOptions,\n parseContentReviewResponse,\n type ResolvedContentGovernance,\n type RunContentReviewOptions,\n resolveConfiguredContentGovernance,\n resolveEffectiveContentGovernance,\n} from './content-governance';\nimport {\n promptMessageOptions,\n smrtContentApplyCorrectionPrompt,\n smrtContentReviewPrompt,\n} from './content-prompts';\nimport { ContentReferences } from './content-references';\nimport type { ContentReview } from './content-review';\nimport { normalizeContentTransparency } from './content-transparency';\nimport { isMissingTableError } from './database-utils';\nimport {\n serializeContent,\n serializeContentCorrection,\n serializeContentReview,\n serializeContentVersion,\n serializeFact,\n serializeFactLink,\n} from './serialization';\nimport type { ThumbnailOptions } from './thumbnail-generator';\nimport { ThumbnailGenerator } from './thumbnail-generator';\n\nconst USED_FACT_RELATIONSHIPS = new Set<FactContentRelationship>([\n 'supports',\n 'referenced_in',\n 'contradicts',\n]);\nconst FACT_AUDIT_GENERATED_BY = 'content.factAudit';\nconst FACT_AUDIT_DOMAIN = 'content-audit';\n\ntype FactAuditSourceMaterial = {\n sourceKind: string;\n sourceId: string;\n sourceUrl: string;\n sourceTitle: string;\n locator: string;\n text: string;\n};\n\ntype FactAuditSourceSelector = {\n sourceKind: string;\n sourceId: string;\n};\n\ntype FactAuditResourceRepairOptions = {\n sources?: FactAuditSourceSelector[];\n maxFactsPerSource?: number;\n context?: string;\n};\n\ntype FactAuditClaimRecheckOptions = {\n claimFactIds?: string[];\n sourceIds?: string[];\n sources?: FactAuditSourceSelector[];\n maxCandidateEvidence?: number;\n};\n\ntype FactEvidenceStatusUpdateOptions = {\n evidenceIds?: string[];\n status?: FactEvidenceStatus;\n reason?: string;\n};\n\ntype FactAuditClaim = {\n id: string | null;\n fact: Record<string, unknown>;\n supportStatus: FactClaimSupportStatus;\n claimQuote: string | null;\n rationale: string | null;\n confidence: number | null;\n relationship: string | null;\n linkMetadata: Record<string, unknown>;\n evidence: Record<string, unknown>[];\n matchedFacts: Array<{\n fact: Record<string, unknown>;\n evidence: Record<string, unknown>[];\n }>;\n};\n\ntype FactAuditResourceClaim = {\n id: string | null;\n fact: Record<string, unknown>;\n sourceKind: string | null;\n sourceId: string | null;\n sourceUrl: string | null;\n sourceTitle: string | null;\n locator: string | null;\n quote: string | null;\n status: FactEvidenceStatus;\n confidence: number | null;\n evidence: Record<string, unknown>[];\n};\n\n/**\n * Minimal structural view of a metadata-bearing SMRT record (fact link,\n * fact, evidence, source, version, review, correction). The fact-audit and\n * transparency code paths interact with these entities loosely via their\n * accessor methods rather than importing the concrete cross-package classes\n * (which would create circular dependencies). `getMetadata`/`setMetadata`\n * are optional because some paths receive plain serialized records.\n */\ninterface MetadataBearer {\n getMetadata?: () => Record<string, unknown>;\n setMetadata?: (metadata: Record<string, unknown>) => void;\n updateMetadata?: (patch: Record<string, unknown>) => unknown;\n metadata?: unknown;\n}\n\n/**\n * Structural view of a content↔fact link as consumed by the fact-audit\n * pipeline. Backed by `FactContent` from `@happyvertical/smrt-facts`.\n */\ninterface FactAuditLinkLike extends MetadataBearer {\n factId?: string | null;\n relationship?: string | null;\n save?: () => Promise<unknown>;\n delete?: () => Promise<unknown>;\n}\n\n/**\n * Structural view of a fact-evidence record as consumed by the fact-audit\n * pipeline. Backed by `FactEvidence` from `@happyvertical/smrt-facts`.\n */\ninterface FactAuditEvidenceLike extends MetadataBearer {\n id?: string | null;\n factId?: string | null;\n status?: string | null;\n sourceKind?: string | null;\n sourceId?: string | null;\n sourceUrl?: string | null;\n sourceTitle?: string | null;\n locator?: string | null;\n quote?: string | null;\n confidence?: number | null;\n evidenceKey?: string | null;\n tenantId?: string | null;\n delete?: () => Promise<unknown>;\n}\n\n/**\n * Structural view of a fact-source record as consumed by the fact-audit\n * pipeline. Backed by `FactSource` from `@happyvertical/smrt-facts`.\n */\ninterface FactAuditSourceLike extends MetadataBearer {\n id?: string | null;\n sourceType?: string | null;\n delete?: () => Promise<unknown>;\n}\n\n/**\n * Loosely-read extra fields the fact-audit source scanner probes on an\n * {@link Asset}. These are not declared `Asset` fields (assets vary by\n * provider); they are read defensively and normalized via `normalizeAuditText`.\n */\ninterface FactAuditAssetExtraFields {\n text?: unknown;\n body?: unknown;\n title?: unknown;\n filename?: unknown;\n url?: unknown;\n sourceUrl?: unknown;\n fileKey?: unknown;\n}\n\n/**\n * Structural view of a support candidate carried through claim assessment.\n * The `evidence` array holds the serialized evidence summaries built in\n * {@link Content.getCurrentFactAuditSupportCandidates}.\n */\ninterface FactAuditSupportCandidate {\n id: string;\n statement: string;\n evidence: Array<{ id?: string | null; [key: string]: unknown }>;\n}\n\n/**\n * Loose view of a serialized record produced by the `serialize*` helpers\n * (which return index-signature records that erase named keys at the type\n * level). Adds back the few keys the transparency snapshot reads while keeping\n * the rest as `unknown`.\n */\ninterface SerializedRecord {\n id?: string | null;\n status?: unknown;\n usedInArticle?: boolean;\n metadata?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Transient, non-persisted fields synchronized into junction links during\n * `save()`. They are attached dynamically from constructor options rather\n * than declared as ORM-managed fields, so callers narrow `this` to this\n * shape instead of reaching in untyped.\n */\ninterface ContentTransientLinkIds {\n referenceIds?: string[];\n assetIds?: string[];\n}\n\ntype FactAuditState = {\n counts: Record<FactClaimSupportStatus | 'total', number>;\n claims: FactAuditClaim[];\n resourceClaims: FactAuditResourceClaim[];\n warnings: string[];\n generatedBy: string;\n latestAuditRunId: string | null;\n};\n\nfunction normalizeFingerprintValue(value: unknown): unknown {\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => normalizeFingerprintValue(entry));\n }\n\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, entryValue]) => [\n key,\n normalizeFingerprintValue(entryValue),\n ]),\n );\n }\n\n return value ?? null;\n}\n\nfunction hashFingerprint(input: string): string {\n let hash = 5381;\n\n for (let index = 0; index < input.length; index += 1) {\n hash = (hash * 33) ^ input.charCodeAt(index);\n }\n\n return `fp-${(hash >>> 0).toString(16).padStart(8, '0')}`;\n}\n\nfunction createFingerprint(value: unknown): string {\n return hashFingerprint(JSON.stringify(normalizeFingerprintValue(value)));\n}\n\nfunction normalizeAuditText(value: unknown): string {\n return String(value ?? '')\n .trim()\n .replace(/\\s+/g, ' ');\n}\n\n/**\n * Extract a human-readable message from an unknown caught value. Mirrors the\n * previous `error.message || error` template interpolation without relying on\n * an `any`-typed catch binding.\n */\nfunction errorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n typeof (error as { message?: unknown }).message === 'string'\n ) {\n return (error as { message: string }).message;\n }\n return String(error);\n}\n\nfunction createFactAuditRunId(contentId: string): string {\n return `fact-audit-${hashFingerprint(\n `${contentId}:${new Date().toISOString()}:${Math.random()}`,\n )}`;\n}\n\nfunction parseAuditMetadata(value: unknown): Record<string, unknown> {\n if (!value) return {};\n if (typeof value === 'object') return value as Record<string, unknown>;\n try {\n return JSON.parse(String(value)) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nfunction getLinkMetadata(link: MetadataBearer): Record<string, unknown> {\n return typeof link?.getMetadata === 'function' ? link.getMetadata() : {};\n}\n\nfunction getFactMetadata(fact: MetadataBearer): Record<string, unknown> {\n return typeof fact?.getMetadata === 'function'\n ? fact.getMetadata()\n : parseAuditMetadata(fact?.metadata);\n}\n\nfunction getGeneratedFactAuditMetadata(\n link: MetadataBearer,\n): Record<string, unknown> | null {\n const metadata = getLinkMetadata(link);\n if (metadata.generatedBy === FACT_AUDIT_GENERATED_BY) {\n return metadata;\n }\n\n const nested = metadata.factAudit;\n if (\n nested &&\n typeof nested === 'object' &&\n (nested as Record<string, unknown>).generatedBy === FACT_AUDIT_GENERATED_BY\n ) {\n return nested as Record<string, unknown>;\n }\n\n return null;\n}\n\nfunction getEvidenceMetadata(\n evidence: MetadataBearer,\n): Record<string, unknown> {\n return typeof evidence?.getMetadata === 'function'\n ? evidence.getMetadata()\n : parseAuditMetadata(evidence?.metadata);\n}\n\nfunction isGeneratedFactAuditEvidence(\n evidence: MetadataBearer,\n contentId: string,\n): boolean {\n const metadata = getEvidenceMetadata(evidence);\n return (\n metadata.generatedBy === FACT_AUDIT_GENERATED_BY &&\n metadata.contentId === contentId\n );\n}\n\nfunction isGeneratedArticleClaimFact(\n fact: MetadataBearer,\n contentId: string,\n): boolean {\n const metadata = getFactMetadata(fact);\n if (metadata.generatedBy !== FACT_AUDIT_GENERATED_BY) {\n return false;\n }\n\n const role = metadata.auditFactRole || metadata.factAuditRole;\n const isArticleClaim =\n role === 'article-claim' || metadata.claimOnly === true;\n\n return isArticleClaim && metadata.contentId === contentId;\n}\n\nfunction normalizeFactEvidenceStatus(\n value: unknown,\n): FactEvidenceStatus | null {\n const allowed: FactEvidenceStatus[] = [\n 'supports',\n 'contradicts',\n 'unclear',\n 'irrelevant',\n 'invalid',\n ];\n\n return allowed.includes(value as FactEvidenceStatus)\n ? (value as FactEvidenceStatus)\n : null;\n}\n\nfunction sourceMatchesSelector(\n source: FactAuditSourceMaterial,\n selector: FactAuditSourceSelector,\n): boolean {\n return (\n source.sourceKind === selector.sourceKind &&\n source.sourceId === selector.sourceId\n );\n}\n\nfunction filterAuditSources(\n sources: FactAuditSourceMaterial[],\n selectors: FactAuditSourceSelector[] | undefined,\n): FactAuditSourceMaterial[] {\n if (!selectors || selectors.length === 0) {\n return sources;\n }\n\n return sources.filter((source) =>\n selectors.some((selector) => sourceMatchesSelector(source, selector)),\n );\n}\n\nfunction getContentText(content: Content): string {\n return [content.title, content.description, content.body]\n .map(normalizeAuditText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\nfunction readNestedString(\n source: Record<string, unknown>,\n path: string[],\n): string | null {\n let current: unknown = source;\n for (const key of path) {\n if (!current || typeof current !== 'object') {\n return null;\n }\n current = (current as Record<string, unknown>)[key];\n }\n return typeof current === 'string' && current ? current : null;\n}\n\n/**\n * Coerce an unknown value into a plain record. Objects pass through; JSON\n * strings are parsed (falling back to `{}` on failure); anything else yields\n * an empty record. Used to read loosely-typed `metadata` fields that may be\n * stored either as parsed objects or JSON strings.\n */\nfunction asRecord(value: unknown): Record<string, unknown> {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n if (typeof value === 'string') {\n try {\n const parsed = JSON.parse(value);\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n return {};\n}\n\n/**\n * Walk a nested record path, returning the record at the end of the path or\n * an empty record if any segment is missing/non-record-shaped.\n */\nfunction readNestedRecord(\n source: Record<string, unknown>,\n path: string[],\n): Record<string, unknown> {\n let current: unknown = source;\n for (const key of path) {\n if (!current || typeof current !== 'object') {\n return {};\n }\n current = (current as Record<string, unknown>)[key];\n }\n return asRecord(current);\n}\n\nfunction getPublicPrompt(metadata: Record<string, unknown>): string | null {\n return (\n readNestedString(metadata, [\n 'transparency',\n 'generation',\n 'publicPrompt',\n ]) ||\n readNestedString(metadata, ['generation', 'publicPrompt']) ||\n readNestedString(metadata, ['publicPrompt']) ||\n null\n );\n}\n\n/**\n * Options for Content initialization\n */\nexport interface ContentOptions extends SmrtObjectOptions {\n /**\n * Content type classification\n */\n type?: string | null;\n\n /**\n * Content variant for namespaced classification within types\n * Format: generator:domain:specific-type\n * Example: \"praeco:meeting:upcoming\"\n */\n variant?: string | null;\n\n /**\n * Reference to file storage key\n */\n fileKey?: string | null;\n\n /**\n * Author of the content\n */\n author?: string | null;\n\n /**\n * Content title\n */\n title?: string | null;\n\n /**\n * Short description or summary\n */\n description?: string | null;\n\n /**\n * Main content body text\n */\n body?: string | null;\n\n /**\n * Stored body format.\n */\n bodyFormat?: ContentBodyFormat | null;\n\n /**\n * Date when content was published\n */\n publish_date?: Date | null;\n\n /**\n * URL source of the content\n */\n url?: string | null;\n\n /**\n * Original source identifier\n */\n source?: string | null;\n\n /**\n * Publication status\n */\n status?: 'published' | 'draft' | 'review' | 'archived' | 'deleted' | null;\n\n /**\n * Content state flag\n */\n state?: 'deprecated' | 'active' | 'highlighted' | null;\n\n /**\n * Original URL of the content\n */\n original_url?: string | null;\n\n /**\n * Content language\n */\n language?: string | null;\n\n /**\n * Content tags\n */\n tags?: string[];\n\n /**\n * Hierarchical category path for URL routing\n * Format: 'parent/child' (e.g., 'politics/local')\n * Each content belongs to exactly ONE category\n */\n category?: string | null;\n\n /**\n * Additional metadata\n */\n metadata?: Record<string, unknown>;\n\n /**\n * ID of the thumbnail asset for this content\n */\n thumbnailAssetId?: string | null;\n\n /**\n * Transient reference IDs used by editors and API payloads.\n * These are synchronized into ContentReference links during save.\n */\n referenceIds?: string[];\n\n /**\n * Transient asset IDs used by editors and API payloads.\n */\n assetIds?: string[];\n\n /**\n * Tenant ID for multi-tenant isolation\n */\n tenantId?: string | null;\n}\n\n/**\n * Structured content object with metadata and body text\n *\n * Content represents any text-based content with metadata such as\n * title, author, description, and publishing information. It supports\n * referencing related content objects.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: {\n include: [\n 'list',\n 'get',\n 'create',\n 'update',\n 'delete',\n 'getFactsState',\n 'syncFactsState',\n 'getFactAuditStateAction',\n 'repairFactAuditAction',\n 'repairFactEvidenceAction',\n 'recheckFactClaimsAction',\n 'updateFactEvidenceStatusAction',\n 'getGovernanceStateAction',\n 'listReviews',\n 'runReviewAction',\n 'listReviewProfilesAction',\n 'evaluateReviewProfileAction',\n 'getPublishedTransparencyAction',\n 'previewTransparencyAction',\n 'listCorrections',\n 'issueCorrectionAction',\n 'listVersions',\n 'mutateVersionAction',\n ],\n routes: {\n getFactsState: { method: 'GET', path: 'facts' },\n syncFactsState: { method: 'PUT', path: 'facts' },\n getFactAuditStateAction: { method: 'GET', path: 'fact-audit' },\n repairFactAuditAction: { method: 'POST', path: 'fact-audit/repair' },\n repairFactEvidenceAction: {\n method: 'POST',\n path: 'fact-audit/evidence/repair',\n },\n recheckFactClaimsAction: {\n method: 'POST',\n path: 'fact-audit/claims/recheck',\n },\n updateFactEvidenceStatusAction: {\n method: 'PUT',\n path: 'fact-audit/evidence/status',\n },\n getGovernanceStateAction: { method: 'GET', path: 'governance' },\n listReviews: { method: 'GET', path: 'reviews' },\n runReviewAction: { method: 'POST', path: 'reviews' },\n listReviewProfilesAction: { method: 'GET', path: 'review-profiles' },\n evaluateReviewProfileAction: {\n method: 'GET',\n path: 'review-profiles/[profileKey]',\n },\n getPublishedTransparencyAction: {\n method: 'GET',\n path: 'transparency',\n },\n previewTransparencyAction: {\n method: 'GET',\n path: 'transparency/preview',\n },\n listCorrections: { method: 'GET', path: 'corrections' },\n issueCorrectionAction: { method: 'POST', path: 'corrections' },\n listVersions: { method: 'GET', path: 'versions' },\n mutateVersionAction: { method: 'POST', path: 'versions' },\n },\n serializers: {\n item: {\n importPath: '$lib/server/content-api-serializers',\n exportName: 'serializeContent',\n },\n },\n },\n mcp: {\n include: ['list', 'get', 'create', 'update'], // AI tools for content management\n },\n cli: true, // Enable CLI commands for content management\n // Content's own list pages sort by publish date inside a tenant, not by\n // `created_at`, so the generated `(tenant_id, created_at)` ordering index\n // (#2363) does not serve them — this is the second access path on the same\n // table and it has to be declared (#2357, measured in #2340). Declared\n // indexes are appended before the automatic passes, so this one also stands\n // in for the standalone `contents_tenant_id_idx` (#2359): a btree serves\n // every prefix of its column list.\n indexes: [\n {\n name: 'contents_tenant_id_publish_date_idx',\n columns: ['tenantId', 'publish_date'],\n },\n ],\n})\nexport class Content\n extends SmrtObject\n implements AssetAssociable, MetadataAccessor<Record<string, unknown>>\n{\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global content\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Array of referenced content objects\n */\n protected references: Content[] = [];\n\n /**\n * Content type classification\n */\n public type: string | null = null;\n\n /**\n * Content variant for namespaced classification within types\n * Format: generator:domain:specific-type\n * Example: \"praeco:meeting:upcoming\"\n */\n public variant: string | null = null;\n\n /**\n * Reference to file storage key\n */\n public fileKey: string | null = null;\n\n /**\n * Author of the content\n */\n public author: string | null = null;\n\n /**\n * Human-readable name for SMRT framework compatibility\n */\n @field({ required: true })\n public name: string = '';\n\n /**\n * Content title\n */\n public title = '';\n\n /**\n * Short description or summary\n */\n public description: string | null = null;\n\n /**\n * Main content body text\n */\n public body = '';\n\n /**\n * Format used to persist the body field.\n */\n public bodyFormat: ContentBodyFormat | null = null;\n\n /**\n * Date when content was published\n */\n public publish_date: Date | null = null;\n\n /**\n * URL source of the content\n */\n public url: string | null = null;\n\n /**\n * Original source identifier\n */\n public source: string | null = null;\n\n /**\n * Original URL of the content\n */\n public original_url: string | null = null;\n\n /**\n * Content language\n */\n public language: string | null = null;\n\n /**\n * Content tags\n */\n public tags: string[] = [];\n\n /**\n * Hierarchical category path for URL routing\n * Format: 'parent/child' (e.g., 'politics/local')\n * Each content belongs to exactly ONE category\n */\n public category: string | null = null;\n\n /**\n * Publication status\n */\n public status: 'published' | 'draft' | 'review' | 'archived' | 'deleted' =\n 'draft';\n\n /**\n * Content state flag\n */\n public state: 'deprecated' | 'active' | 'highlighted' = 'active';\n\n /**\n * Additional JSON metadata for flexible schema extension\n */\n public metadata: Record<string, unknown> = {};\n\n /**\n * ID of the thumbnail asset for this content\n */\n @crossPackageRef('@happyvertical/smrt-assets:Asset')\n public thumbnailAssetId: string | null = null;\n\n /**\n * Creates a new Content instance\n */\n constructor(options: ContentOptions = {}) {\n super(options);\n this.type = options.type || null;\n this.variant = options.variant || null;\n this.fileKey = options.fileKey || null;\n this.author = options.author || null;\n if (options.name) this.name = options.name;\n this.title = options.title || '';\n this.description = options.description || null;\n this.body = options.body || '';\n this.bodyFormat = isContentBodyFormat(options.bodyFormat)\n ? options.bodyFormat\n : null;\n this.publish_date = options.publish_date || null;\n this.source = options.source || null;\n this.original_url = options.original_url || null;\n this.language = options.language || null;\n this.status = options.status || 'draft';\n this.tags = options.tags || [];\n this.category = options.category || null;\n this.state = options.state || 'active';\n this.metadata = options.metadata || {};\n this.thumbnailAssetId = options.thumbnailAssetId ?? null;\n const transient = this as Content & ContentTransientLinkIds;\n if (Array.isArray(options.referenceIds)) {\n transient.referenceIds = [...options.referenceIds];\n }\n if (Array.isArray(options.assetIds)) {\n transient.assetIds = [...options.assetIds];\n }\n }\n\n /**\n * Initializes this content object\n *\n * @returns Promise that resolves to this instance\n */\n async initialize(): Promise<this> {\n await super.initialize();\n return this;\n }\n\n protected override async validateBeforeSave(): Promise<void> {\n if (!this.name && this.title) {\n this.name = this.title;\n }\n\n if (!this.title && this.name) {\n this.title = this.name;\n }\n\n await super.validateBeforeSave();\n\n if (this.status !== 'published') {\n return;\n }\n\n const governance = await this.resolvePublicationGovernance();\n const profileKey = governance?.publicationProfileKey;\n\n if (\n !governance?.isGoverned ||\n !governance.enforcePublishReadiness ||\n !profileKey\n ) {\n return;\n }\n\n const evaluation = await this.evaluateReviewProfile(profileKey);\n const blockingRequirements = evaluation.requirements.filter(\n (requirement) => requirement.blocking && !requirement.satisfied,\n );\n\n if (blockingRequirements.length === 0) {\n return;\n }\n\n const details = blockingRequirements.map((requirement) => {\n if (requirement.missing) {\n return `${requirement.label} has not been run yet`;\n }\n\n if (requirement.stale) {\n return `${requirement.label} is stale and must be rerun`;\n }\n\n if (requirement.latestStatus) {\n return `${requirement.label} returned ${requirement.latestStatus}`;\n }\n\n return `${requirement.label} is not satisfied`;\n });\n\n throw new ValidationError(\n `Cannot publish content until the \"${profileKey}\" review profile is satisfied. ${details.join('; ')}`,\n 'VALIDATION_PUBLISH_READINESS',\n {\n profileKey,\n blockingRequirements: blockingRequirements.map((requirement) => ({\n policyKey: requirement.policyKey,\n label: requirement.label,\n missing: requirement.missing,\n stale: requirement.stale,\n latestStatus: requirement.latestStatus,\n })),\n },\n );\n }\n\n override async save() {\n const shouldConsiderPublicationSnapshot = this.status === 'published';\n\n let governance: ResolvedContentGovernance | null = null;\n let previous: Content | null = null;\n let previousPublicationFingerprint: string | null = null;\n\n if (shouldConsiderPublicationSnapshot) {\n governance = await this.resolvePublicationGovernance();\n\n if (governance?.isGoverned && governance.transparencyEnabled) {\n previous = await this.getPersistedContent();\n previousPublicationFingerprint =\n await this.getLatestPublicationSnapshotFingerprint();\n }\n }\n\n await super.save();\n await this.syncPendingReferenceIds();\n await this.syncPendingAssetIds();\n\n if (\n !shouldConsiderPublicationSnapshot ||\n !governance?.isGoverned ||\n !governance.transparencyEnabled\n ) {\n return this;\n }\n\n const nextPublicationFingerprint =\n await this.buildPublicationSnapshotFingerprint(governance);\n\n if (\n nextPublicationFingerprint &&\n nextPublicationFingerprint !== previousPublicationFingerprint\n ) {\n await this.createVersion({\n kind: 'publication',\n summary:\n previous?.status === 'published'\n ? 'Published content updated.'\n : 'Content published.',\n metadata: {\n publicationSnapshotFingerprint: nextPublicationFingerprint,\n publicationProfileKey: governance.publicationProfileKey,\n transparency: await this.buildTransparencySnapshot({\n snapshotKind: 'published',\n governance,\n }),\n },\n });\n }\n\n return this;\n }\n\n private async getReferenceCollection() {\n return ContentReferences.create({ db: this.db });\n }\n\n private async getFactCollection() {\n const { FactCollection } = await import('@happyvertical/smrt-facts');\n return FactCollection.create(this.options);\n }\n\n private async getFactContentCollection() {\n const { FactContentCollection } = await import('@happyvertical/smrt-facts');\n return FactContentCollection.create(this.options);\n }\n\n private async getFactSourceCollection() {\n const { FactSourceCollection } = await import('@happyvertical/smrt-facts');\n return FactSourceCollection.create(this.options);\n }\n\n private async getFactEvidenceCollection() {\n const { FactEvidenceCollection } = await import(\n '@happyvertical/smrt-facts'\n );\n return FactEvidenceCollection.create(this.options);\n }\n\n private async getContentVersionCollection() {\n const { ContentVersionCollection } = await import('./content-versions');\n return ContentVersionCollection.create(this.options);\n }\n\n private async getContentReviewCollection() {\n const { ContentReviewCollection } = await import('./content-reviews');\n return ContentReviewCollection.create(this.options);\n }\n\n private async getContentCorrectionCollection() {\n const { ContentCorrectionCollection } = await import(\n './content-corrections'\n );\n return ContentCorrectionCollection.create(this.options);\n }\n\n private async getContentsCollection() {\n const { Contents } = await import('./contents');\n return Contents.create({ db: this.db });\n }\n\n private getConfiguredGovernance(): ResolvedContentGovernance {\n return resolveConfiguredContentGovernance({\n contentType: this.type,\n contentVariant: this.variant,\n });\n }\n\n public async resolveGovernance(): Promise<ResolvedContentGovernance> {\n return resolveEffectiveContentGovernance({\n contentType: this.type,\n contentVariant: this.variant,\n db: this.db,\n tenantId: this.tenantId ?? null,\n });\n }\n\n private async hasPersistedGovernanceAssignments(): Promise<boolean> {\n if (!this.db || typeof this.db.query !== 'function') {\n return false;\n }\n\n try {\n const exactKey = buildContentGovernanceAssignmentKey(\n this.type || '',\n this.variant || '',\n );\n const typeOnlyKey = buildContentGovernanceAssignmentKey(this.type || '');\n const keys =\n exactKey === typeOnlyKey ? [exactKey] : [exactKey, typeOnlyKey];\n const placeholders = keys.map(() => '?').join(', ');\n const result = await this.db.query(\n `SELECT 1 AS matched FROM content_governance_assignments WHERE key IN (${placeholders}) LIMIT 1`,\n keys,\n );\n const rows = Array.isArray(result) ? result : (result?.rows ?? []);\n return rows.length > 0;\n } catch {\n return false;\n }\n }\n\n private async resolvePublicationGovernance(): Promise<ResolvedContentGovernance | null> {\n const configuredGovernance = this.getConfiguredGovernance();\n\n if (configuredGovernance.isGoverned) {\n return this.resolveGovernance();\n }\n\n if (!(await this.hasPersistedGovernanceAssignments())) {\n return null;\n }\n\n const governance = await this.resolveGovernance();\n return governance.isGoverned ? governance : null;\n }\n\n private async requireGovernance(\n feature = 'governance workflow',\n ): Promise<ResolvedContentGovernance> {\n const governance = await this.resolveGovernance();\n\n if (!governance.isGoverned) {\n throw new Error(\n `Governance is not enabled for content type \"${this.type || 'content'}\"${this.variant ? ` variant \"${this.variant}\"` : ''}, so ${feature} is unavailable.`,\n );\n }\n\n return governance;\n }\n\n private async requireFactLinking(\n feature = 'fact linking',\n ): Promise<ResolvedContentGovernance> {\n const governance = await this.requireGovernance(feature);\n\n if (!governance.factLinkingEnabled) {\n throw new Error(\n `Fact linking is not enabled for content type \"${this.type || 'content'}\"${this.variant ? ` variant \"${this.variant}\"` : ''}.`,\n );\n }\n\n return governance;\n }\n\n private async getPersistedContent(): Promise<Content | null> {\n if (!this.id) {\n return null;\n }\n\n const contents = await this.getContentsCollection();\n return (await contents.get({ id: this.id as string })) as Content | null;\n }\n\n private async buildReviewFingerprint(policyKey: string): Promise<string> {\n const governance = await this.resolveGovernance();\n const kind = getContentReviewKind(policyKey, governance.reviewPolicies);\n const policy = getContentReviewPolicy(policyKey, governance.reviewPolicies);\n const [references, facts, factLinks] = await Promise.all([\n this.getReferences(),\n kind === 'facts' && governance.factLinkingEnabled\n ? this.getFacts({\n latestOnly: true,\n includeSuperseded: false,\n })\n : Promise.resolve([]),\n kind === 'facts' && governance.factLinkingEnabled\n ? this.getFactLinks()\n : Promise.resolve([]),\n ]);\n\n return createFingerprint({\n scope: 'content-review',\n policyKey,\n kind,\n policyInstructions: policy?.instructions || '',\n content: {\n id: this.id || null,\n type: this.type,\n variant: this.variant,\n title: this.title,\n description: this.description,\n body: this.body,\n author: this.author,\n state: this.state,\n publishDate: this.publish_date,\n language: this.language,\n category: this.category,\n tags: this.tags,\n metadata: this.metadata,\n },\n referenceIds: references\n .map((reference) => reference.id)\n .filter(Boolean)\n .sort(),\n facts: facts.map((fact) => ({\n id: fact.id || null,\n // Pre-R3-C this was `parentId`; renamed to `previousFactId` in\n // smrt-facts. Existing cached fingerprints will invalidate, which\n // is the correct behaviour — the review surface (a key in the\n // hash) changed.\n previousFactId: fact.previousFactId || null,\n status: fact.status || null,\n textRefined: fact.textRefined || '',\n sourceCount: fact.sourceCount ?? 0,\n confidence: fact.confidence ?? null,\n metadata:\n typeof fact?.getMetadata === 'function' ? fact.getMetadata() : {},\n })),\n factLinks: factLinks.map((link) => ({\n factId: link.factId || null,\n relationship: link.relationship || null,\n metadata:\n typeof link?.getMetadata === 'function' ? link.getMetadata() : {},\n })),\n });\n }\n\n private async buildTransparencySnapshot(\n options: {\n snapshotKind?: 'preview' | 'published';\n governance?: ResolvedContentGovernance;\n } = {},\n ) {\n const snapshotKind = options.snapshotKind || 'preview';\n const governance = options.governance || (await this.resolveGovernance());\n\n if (!governance.isGoverned || !governance.transparencyEnabled) {\n return null;\n }\n\n const [\n references,\n facts,\n factLinks,\n reviews,\n corrections,\n versions,\n reviewProfiles,\n ] = await Promise.all([\n this.getReferences(),\n governance.factLinkingEnabled\n ? this.getFacts({\n latestOnly: true,\n includeSuperseded: false,\n })\n : Promise.resolve([]),\n governance.factLinkingEnabled ? this.getFactLinks() : Promise.resolve([]),\n this.listReviews(),\n this.listCorrections(),\n this.listVersions(),\n this.listReviewProfilesAction(),\n ]);\n\n const factSources = await this.getFactSourceCollection();\n const factSourcesByFactId = new Map<string, FactSource[]>();\n\n for (const fact of facts) {\n const factId = fact.id as string | undefined;\n if (!factId) {\n continue;\n }\n\n const sources = await factSources.getForFact(factId);\n factSourcesByFactId.set(factId, sources);\n }\n\n const usedFactIds = new Set(\n factLinks\n .filter((link) =>\n USED_FACT_RELATIONSHIPS.has(\n (link.relationship || 'related') as FactContentRelationship,\n ),\n )\n .map((link) => link.factId)\n .filter(Boolean),\n );\n\n const linkedFacts = facts.map((fact) => {\n const factId = fact.id as string | undefined;\n const link = factLinks.find((entry) => entry.factId === factId);\n const sources = (factId ? factSourcesByFactId.get(factId) : []) || [];\n\n return {\n ...serializeFact(fact),\n relationship: link?.relationship || null,\n linkMetadata:\n typeof link?.getMetadata === 'function' ? link.getMetadata() : {},\n usedInArticle: factId ? usedFactIds.has(factId) : false,\n sources: sources.map((source) => ({\n id: source.id || null,\n sourceType: source.sourceType || null,\n sourceUrl: source.sourceUrl || null,\n sourceTitle: source.sourceTitle || null,\n credibility: source.credibility ?? null,\n extractedAt: source.extractedAt || null,\n metadata:\n typeof source?.getMetadata === 'function'\n ? source.getMetadata()\n : {},\n })),\n };\n });\n\n const referenceGroups = await Promise.all(\n references.map(async (reference) => {\n const sourceUrls = [\n reference.url,\n reference.original_url,\n reference.source,\n ].filter(Boolean) as string[];\n\n const extractedFacts = new Map<string, Fact>();\n for (const sourceUrl of sourceUrls) {\n const matches = await factSources.list({\n where: { sourceUrl },\n orderBy: 'created_at ASC',\n });\n\n for (const match of matches) {\n if (!match.factId || extractedFacts.has(match.factId)) {\n continue;\n }\n\n const fact = await match.getFact();\n if (fact?.id) {\n extractedFacts.set(fact.id as string, fact);\n }\n }\n }\n\n const extractedFactRecords: SerializedRecord[] = [\n ...extractedFacts.values(),\n ].map((fact) => {\n const factId = fact.id as string | undefined;\n return {\n ...serializeFact(fact),\n usedInArticle: factId ? usedFactIds.has(factId) : false,\n };\n });\n\n return {\n id: reference.id || null,\n title: reference.title || reference.name || reference.url || null,\n url: reference.url || null,\n originalUrl: reference.original_url || null,\n type: reference.type || null,\n source: reference.source || null,\n usedFactIds: extractedFactRecords\n .filter((fact) => fact.id && usedFactIds.has(fact.id))\n .map((fact) => fact.id),\n extractedFacts: extractedFactRecords,\n };\n }),\n );\n\n const publicGeneration = readNestedRecord(this.metadata, [\n 'transparency',\n 'generation',\n ]);\n const generationMetadata = readNestedRecord(this.metadata, ['generation']);\n const serializedCorrections = (corrections as SerializedRecord[])\n .filter((correction) => correction.status === 'published')\n .map((correction) => {\n // `corrections` is already serialized to plain records, so the\n // metadata is the parsed object on `correction.metadata`.\n const correctionMetadata = asRecord(correction.metadata);\n\n return {\n ...serializeContentCorrection(correction),\n provenance: {\n autoGeneratedDraft: Boolean(correctionMetadata.autoGeneratedDraft),\n draftVersionId: correctionMetadata.draftVersionId || null,\n draftVersionNumber: correctionMetadata.draftVersionNumber || null,\n sourceCorrectionVersionId:\n correctionMetadata.sourceCorrectionVersionId || null,\n sourceCorrectionVersionNumber:\n correctionMetadata.sourceCorrectionVersionNumber || null,\n },\n };\n });\n const serializedVersionHistory = (versions as SerializedRecord[]).map(\n (version) => {\n // `versions` is already serialized to plain records.\n const versionMetadata = asRecord(version.metadata);\n\n return {\n id: version.id || null,\n version: version.version ?? null,\n kind: version.kind || null,\n summary: version.summary || '',\n createdAt: version.createdAt || null,\n provenance: {\n policyKey: versionMetadata.policyKey || null,\n reviewFingerprint:\n versionMetadata.reviewFingerprint ||\n versionMetadata.contentFingerprint ||\n null,\n factId: versionMetadata.factId || null,\n replacementFactId: versionMetadata.replacementFactId || null,\n sourceCorrectionVersionId:\n versionMetadata.sourceCorrectionVersionId || null,\n sourceCorrectionVersionNumber:\n versionMetadata.sourceCorrectionVersionNumber || null,\n correctionDraft: versionMetadata.correctionDraft || null,\n publicationSnapshotFingerprint:\n versionMetadata.publicationSnapshotFingerprint || null,\n },\n };\n },\n );\n\n return normalizeContentTransparency(\n {\n generatedAt: new Date().toISOString(),\n snapshotKind,\n contentId: (this.id as string) || null,\n currentContentStatus: this.status || null,\n publicationProfileKey: governance.publicationProfileKey || undefined,\n generation: {\n aiAssisted:\n publicGeneration.aiAssisted ??\n generationMetadata.aiAssisted ??\n Boolean(getPublicPrompt(this.metadata)),\n publicPrompt: getPublicPrompt(this.metadata),\n model: publicGeneration.model || generationMetadata.model || null,\n },\n factsUsed: linkedFacts.filter((fact) => fact.usedInArticle),\n linkedFacts,\n otherExtractedFacts: referenceGroups.flatMap((reference) =>\n reference.extractedFacts.filter((fact) => !fact.usedInArticle),\n ),\n references: referenceGroups,\n reviews,\n reviewProfiles,\n corrections: serializedCorrections,\n versionHistory: serializedVersionHistory,\n },\n {\n snapshotKind,\n contentId: (this.id as string) || null,\n currentContentStatus: this.status || null,\n publicationProfileKey: governance.publicationProfileKey || undefined,\n },\n );\n }\n\n /**\n * Fingerprint of the *content-bearing* publication surface only.\n *\n * This must converge: two byte-identical `save()`s of published content\n * have to produce the same fingerprint so the publication-version writer\n * (`save()`) does not append a redundant `ContentVersion` on every save.\n *\n * It therefore deliberately excludes everything that grows or carries a\n * timestamp/ordering with each save — `versionHistory`, `reviews`,\n * `corrections`, and any `generatedAt`/`createdAt`/`id` fields. The earlier\n * implementation fingerprinted the full transparency snapshot (which embeds\n * the growing `versionHistory`), so the stored fingerprint of vN predated vN\n * and the next save's recomputed fingerprint always differed → unbounded\n * redundant publication versions (#1387 blocker).\n *\n * The surface mirrors `buildReviewFingerprint`'s content block, plus the\n * pinned reference edges (`{ targetId, targetVersion }`) — a pin change is a\n * meaningful republication — and the publication profile key.\n */\n private async buildPublicationSnapshotFingerprint(\n governance: ResolvedContentGovernance,\n ): Promise<string | null> {\n if (!governance.isGoverned || !governance.transparencyEnabled) {\n return null;\n }\n\n const referenceCollection = await this.getReferenceCollection();\n const [referenceEdges, facts, factLinks] = await Promise.all([\n this.id ? referenceCollection.getForSource(this.id) : Promise.resolve([]),\n governance.factLinkingEnabled\n ? this.getFacts({ latestOnly: true, includeSuperseded: false })\n : Promise.resolve([]),\n governance.factLinkingEnabled ? this.getFactLinks() : Promise.resolve([]),\n ]);\n\n return createFingerprint({\n scope: 'content-publication',\n publicationProfileKey: governance.publicationProfileKey || null,\n content: {\n id: this.id || null,\n type: this.type,\n variant: this.variant,\n title: this.title,\n description: this.description,\n body: this.body,\n author: this.author,\n state: this.state,\n publishDate: this.publish_date,\n language: this.language,\n category: this.category,\n tags: this.tags,\n metadata: this.metadata,\n },\n // Pinned reference edges only — ordering-independent so it converges.\n references: referenceEdges\n .map((edge) => ({\n targetId: edge.targetId || null,\n targetVersion: edge.targetVersion ?? null,\n }))\n .sort((a, b) => String(a.targetId).localeCompare(String(b.targetId))),\n facts: facts\n .map((fact) => ({\n id: fact.id || null,\n previousFactId: fact.previousFactId || null,\n status: fact.status || null,\n textRefined: fact.textRefined || '',\n sourceCount: fact.sourceCount ?? 0,\n confidence: fact.confidence ?? null,\n metadata:\n typeof fact?.getMetadata === 'function' ? fact.getMetadata() : {},\n }))\n .sort((a, b) => String(a.id).localeCompare(String(b.id))),\n factLinks: factLinks\n .map((link) => ({\n factId: link.factId || null,\n relationship: link.relationship || null,\n metadata:\n typeof link?.getMetadata === 'function' ? link.getMetadata() : {},\n }))\n .sort((a, b) => String(a.factId).localeCompare(String(b.factId))),\n });\n }\n\n private async getLatestPublicationSnapshotFingerprint(): Promise<\n string | null\n > {\n const versions = await this.getVersions();\n const latestPublicationVersion = [...versions]\n .reverse()\n .find((version) => version.kind === 'publication');\n\n if (!latestPublicationVersion) {\n return null;\n }\n\n return (\n latestPublicationVersion.getMetadata().publicationSnapshotFingerprint ||\n null\n );\n }\n\n private async buildCorrectionDraftSnapshot(\n options: IssueContentCorrectionOptions,\n replacementFactId: string,\n ): Promise<{\n snapshot: Record<string, unknown>;\n metadata: Record<string, unknown>;\n }> {\n const correctedText =\n options.correctedText || options.correctedFactText || '';\n const incorrectText = options.incorrectText || '';\n let body = this.body;\n let generationMethod = 'metadata';\n\n if (incorrectText && correctedText && body.includes(incorrectText)) {\n body = body.replace(incorrectText, correctedText);\n generationMethod = 'replace';\n } else if (correctedText) {\n const ai = this.ai as {\n message?: (\n prompt: string,\n options?: Record<string, unknown>,\n ) => Promise<string>;\n };\n if (ai?.message) {\n const resolvedPrompt = await resolvePrompt(\n smrtContentApplyCorrectionPrompt.key,\n {\n db: this.options.db,\n tenantId: this.tenantId,\n variables: {\n body: this.body,\n correctedText,\n incorrectText: incorrectText || 'Not provided',\n summary: options.summary || '',\n },\n },\n );\n\n try {\n const proposedBody = (\n await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n )\n ).trim();\n if (proposedBody) {\n body = proposedBody;\n generationMethod = 'ai';\n }\n } catch {\n generationMethod = 'metadata';\n }\n }\n }\n\n return {\n snapshot: {\n title: this.title,\n description: this.description,\n body,\n status: 'draft',\n metadata: {\n ...(this.metadata || {}),\n governance: {\n ...asRecord(this.metadata.governance),\n correctionDraft: {\n summary: options.summary,\n incorrectText,\n correctedText,\n factId: options.factId || null,\n replacementFactId: replacementFactId || null,\n autoGenerated: true,\n generationMethod,\n },\n },\n },\n },\n metadata: {\n summary: options.summary,\n incorrectText,\n correctedText,\n factId: options.factId || null,\n replacementFactId: replacementFactId || null,\n autoGenerated: true,\n generationMethod,\n },\n };\n }\n\n private async getAssetCollection() {\n return AssetCollection.create({ db: this.db });\n }\n\n private async getContentAssetCollection() {\n return ContentAssetCollection.create({ db: this.db });\n }\n\n private async getContentAssetLinks(\n relationship?: string,\n ): Promise<Array<{ assetId: string; sortOrder: number }>> {\n if (!this.id) {\n return [];\n }\n\n try {\n const contentAssets = await this.getContentAssetCollection();\n const links = await contentAssets.byLeft(\n this.id,\n relationship ? { relationship } : {},\n );\n\n return links\n .filter((link) => link.assetId)\n .map((link) => ({\n assetId: link.assetId,\n sortOrder: link.sortOrder ?? 0,\n }));\n } catch (error) {\n if (isMissingTableError(error, 'content_assets')) {\n return [];\n }\n\n throw error;\n }\n }\n\n private async resolveAssetsForLinks(\n links: Array<{ assetId: string; sortOrder: number }>,\n ): Promise<Asset[]> {\n if (links.length === 0) {\n return [];\n }\n\n const assetIds = [...new Set(links.map((link) => link.assetId))];\n const assets = await this.getAssetCollection();\n const resolved = await assets.listByIds(assetIds);\n const assetsById = new Map(\n resolved\n .filter((asset) => asset.id)\n .map((asset) => [asset.id as string, asset]),\n );\n\n return links\n .map((link) => assetsById.get(link.assetId))\n .filter(Boolean) as Asset[];\n }\n\n private async resolveReferenceTarget(content: Content | string) {\n if (typeof content !== 'string') {\n return content;\n }\n\n const contents = await this.getContentsCollection();\n\n return (await contents.getOrUpsert(\n {\n url: content,\n tenantId: this.tenantId,\n },\n {\n name: content,\n title: content,\n type: 'reference',\n tenantId: this.tenantId,\n },\n )) as Content;\n }\n\n /**\n * Loads referenced content objects\n *\n * @returns Promise that resolves when references are loaded\n */\n public async loadReferences() {\n this.references = await this.getReferences();\n }\n\n private getPendingReferenceIds(): string[] | null {\n const pendingReferenceIds = (this as Content & ContentTransientLinkIds)\n .referenceIds;\n\n if (!Array.isArray(pendingReferenceIds)) {\n return null;\n }\n\n return [\n ...new Set(\n pendingReferenceIds.filter(\n (referenceId): referenceId is string =>\n typeof referenceId === 'string' &&\n referenceId.length > 0 &&\n referenceId !== this.id,\n ),\n ),\n ];\n }\n\n private getPendingAssetIds(): string[] | null {\n const pendingAssetIds = (this as Content & ContentTransientLinkIds)\n .assetIds;\n\n if (!Array.isArray(pendingAssetIds)) {\n return null;\n }\n\n return [\n ...new Set(\n pendingAssetIds.filter(\n (assetId): assetId is string =>\n typeof assetId === 'string' && assetId.length > 0,\n ),\n ),\n ];\n }\n\n private async syncPendingReferenceIds(): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const pendingReferenceIds = this.getPendingReferenceIds();\n if (pendingReferenceIds === null) {\n return;\n }\n\n const currentReferences = await this.getReferences();\n const currentReferenceIds = currentReferences\n .map((reference) => reference.id)\n .filter((referenceId): referenceId is string => Boolean(referenceId));\n const currentReferenceIdSet = new Set(currentReferenceIds);\n const pendingReferenceIdSet = new Set(pendingReferenceIds);\n\n for (const referenceId of currentReferenceIds) {\n if (!pendingReferenceIdSet.has(referenceId)) {\n await this.removeReference(referenceId);\n }\n }\n\n const referenceIdsToAdd = pendingReferenceIds.filter(\n (referenceId) => !currentReferenceIdSet.has(referenceId),\n );\n\n if (referenceIdsToAdd.length === 0) {\n this.references = await this.getReferences();\n return;\n }\n\n const contents = await this.getContentsCollection();\n const resolvedReferences = await contents.listByIds(referenceIdsToAdd);\n const referencesById = new Map(\n resolvedReferences\n .filter((reference) => reference.id)\n .map((reference) => [reference.id as string, reference]),\n );\n\n for (const referenceId of referenceIdsToAdd) {\n const reference = referencesById.get(referenceId);\n if (reference) {\n await this.addReference(reference);\n }\n }\n\n this.references = await this.getReferences();\n }\n\n private async syncPendingAssetIds(): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const pendingAssetIds = this.getPendingAssetIds();\n if (pendingAssetIds === null) {\n return;\n }\n\n const currentAssets = await this.getAssets();\n const currentAssetIds = currentAssets\n .map((asset) => asset.id)\n .filter((assetId): assetId is string => Boolean(assetId));\n const currentAssetIdSet = new Set(currentAssetIds);\n const pendingAssetIdSet = new Set(pendingAssetIds);\n\n for (const assetId of currentAssetIds) {\n if (!pendingAssetIdSet.has(assetId)) {\n await this.removeAsset(assetId);\n }\n }\n\n const assetIdsToAdd = pendingAssetIds.filter(\n (assetId) => !currentAssetIdSet.has(assetId),\n );\n\n if (assetIdsToAdd.length === 0) {\n return;\n }\n\n const assets = await this.getAssetCollection();\n const resolvedAssets = await assets.listByIds(assetIdsToAdd);\n const assetsById = new Map(\n resolvedAssets\n .filter((asset) => asset.id)\n .map((asset) => [asset.id as string, asset]),\n );\n\n for (const assetId of assetIdsToAdd) {\n const asset = assetsById.get(assetId);\n if (asset) {\n await this.addAsset(asset);\n }\n }\n }\n\n /**\n * Adds a reference to another content object.\n *\n * @param content - Content object or URL to reference\n * @param options.targetVersion - Optional ContentVersion.version to pin the\n * citation to. Pass the target's current version (typically the latest\n * publication) to enable drift detection later. Pass `null` or omit to\n * leave the reference untracked.\n * @returns Promise that resolves when the reference is added\n */\n public async addReference(\n content: Content | string,\n options: { targetVersion?: number | null } = {},\n ) {\n if (!this.id) {\n throw new Error('Cannot add reference to unsaved content');\n }\n\n const target = await this.resolveReferenceTarget(content);\n\n if (!target.id) {\n throw new Error('Cannot add reference to unsaved content');\n }\n if (this.id === target.id) {\n return;\n }\n\n const references = await this.getReferenceCollection();\n // R2 junction `attach` carries extra row fields via its opts bag, so the\n // tenant scope and main's citation pin (targetVersion) ride along together.\n await references.attach(this.id, target.id, {\n tenantId: this.tenantId,\n targetVersion: options.targetVersion,\n });\n this.references = await this.getReferences();\n }\n\n /**\n * Removes a reference to another content object\n *\n * @param targetId - ID of the referenced content to remove\n */\n public async removeReference(targetId: string) {\n if (!this.id) {\n return;\n }\n\n const references = await this.getReferenceCollection();\n await references.detach(this.id, targetId);\n this.references = this.references.filter(\n (reference) => reference.id !== targetId,\n );\n }\n\n /**\n * Gets all referenced content objects\n *\n * @returns Promise resolving to an array of referenced Content objects\n */\n public async getReferences() {\n if (!this.id) {\n return [];\n }\n\n const references = await this.getReferenceCollection();\n const linkedReferences = await references.byLeft(this.id);\n const targetIds = linkedReferences.map((reference) => reference.targetId);\n\n if (targetIds.length === 0) {\n this.references = [];\n return this.references;\n }\n\n const contents = await this.getContentsCollection();\n const resolved = await contents.listByIds(targetIds);\n const referencesById = new Map(\n resolved\n .filter((content) => content.id)\n .map((content) => [content.id as string, content]),\n );\n\n this.references = targetIds\n .map((targetId) => referencesById.get(targetId))\n .filter(Boolean) as Content[];\n return this.references;\n }\n\n /**\n * Returns the raw reference edges with their citation pins\n * (`{ targetId, targetVersion }`). Unlike `getReferences()` (which resolves\n * to `Content` objects and loses the per-edge `targetVersion`), this keeps\n * the pin so callers — notably version snapshots — can reconstruct pinned\n * citations on restore. See `ContentVersionCollection.restoreIntoContent`.\n */\n public async getReferenceEdges(): Promise<\n Array<{ targetId: string; targetVersion: number | null }>\n > {\n if (!this.id) {\n return [];\n }\n\n const references = await this.getReferenceCollection();\n const linkedReferences = await references.getForSource(this.id);\n return linkedReferences\n .filter((edge) => Boolean(edge.targetId))\n .map((edge) => ({\n targetId: edge.targetId as string,\n targetVersion: edge.targetVersion ?? null,\n }));\n }\n\n /**\n * Returns one entry per reference edge with the pinned `targetVersion` and\n * the target's latest version. Drift exists when both are present and\n * differ — callers can use this to surface \"the source you cited has been\n * updated\" affordances in editors or review tools.\n *\n * `currentVersion` is the target's latest **publication** `ContentVersion`,\n * because pins are taken against the latest publication (see `addReference`).\n * Auto-created `correction`/`draft`/`manual` versions bump the shared\n * `(content_id, version)` counter but do NOT republish, so comparing against\n * the max version of *any* kind produced false drift positives (#1387 #4).\n *\n * Unpinned references (`citedVersion === null`) are included with\n * `currentVersion` populated when available so callers can choose to\n * surface them as \"pinnable\" suggestions.\n */\n public async getReferenceDrift(): Promise<\n Array<{\n targetId: string;\n citedVersion: number | null;\n currentVersion: number | null;\n isDrifted: boolean;\n }>\n > {\n if (!this.id) {\n return [];\n }\n\n const references = await this.getReferenceCollection();\n const linkedReferences = await references.getForSource(this.id);\n if (linkedReferences.length === 0) {\n return [];\n }\n\n const versions = await this.getContentVersionCollection();\n const targetIds = linkedReferences.map((reference) => reference.targetId);\n\n // Single query for all target *publication* versions; pick the max per\n // contentId. Pins are taken against the latest publication, so only\n // publication versions count as drift (#1387 #4). Filtering by kind here\n // also avoids the N+1 of loading each version to inspect its kind.\n const allVersions = await versions.list({\n where: { contentId: targetIds, kind: 'publication' },\n orderBy: 'version DESC',\n });\n const latestByContentId = new Map<string, number>();\n for (const version of allVersions) {\n if (!latestByContentId.has(version.contentId)) {\n latestByContentId.set(version.contentId, version.version);\n }\n }\n\n return linkedReferences.map((reference) => {\n const currentVersion = latestByContentId.get(reference.targetId) ?? null;\n const citedVersion = reference.targetVersion ?? null;\n return {\n targetId: reference.targetId,\n citedVersion,\n currentVersion,\n isDrifted:\n citedVersion !== null &&\n currentVersion !== null &&\n citedVersion !== currentVersion,\n };\n });\n }\n\n public isGoverned(): boolean {\n return this.getConfiguredGovernance().isGoverned;\n }\n\n public async getFactLinks(\n options: { relationship?: FactContentRelationship } = {},\n ) {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.factLinkingEnabled || !this.id) {\n return [];\n }\n\n if (!this.id) {\n return [];\n }\n\n const links = await this.getFactContentCollection();\n return options.relationship\n ? links.byRight(this.id as string, { relationship: options.relationship })\n : links.byRight(this.id as string);\n }\n\n public async getFacts(\n options: {\n relationship?: FactContentRelationship;\n includeSuperseded?: boolean;\n latestOnly?: boolean;\n } = {},\n ): Promise<Fact[]> {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.factLinkingEnabled || !this.id) {\n return [];\n }\n\n if (!this.id) {\n return [];\n }\n\n const facts = await this.getFactCollection();\n return facts.getForContent(this.id as string, options);\n }\n\n public async addFact(\n fact: Fact | string,\n relationship?: FactContentRelationship,\n metadata?: Record<string, unknown>,\n ) {\n const governance = await this.requireFactLinking('fact association');\n\n if (!this.id) {\n throw new Error('Cannot associate an unsaved content item with a fact');\n }\n\n const factId = typeof fact === 'string' ? fact : (fact.id as string);\n if (!factId) {\n throw new Error('Fact ID is required to create a content-fact link');\n }\n\n const links = await this.getFactContentCollection();\n return links.attach(factId, this.id as string, {\n relationship: relationship || governance.defaultFactRelationship,\n metadata,\n });\n }\n\n public async removeFact(\n factId: string,\n relationship?: FactContentRelationship,\n ): Promise<void> {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.factLinkingEnabled) {\n return;\n }\n\n if (!this.id) {\n return;\n }\n\n const links = await this.getFactContentCollection();\n if (relationship) {\n await links.detach(factId, this.id as string, { relationship });\n return;\n }\n\n await links.detach(factId, this.id as string);\n }\n\n public async syncFacts(\n factIds: string[],\n relationship?: FactContentRelationship,\n ): Promise<{ added: string[]; kept: string[]; removed: string[] }> {\n const governance = await this.requireFactLinking('fact sync');\n\n if (!this.id) {\n throw new Error('Cannot sync facts for unsaved content');\n }\n\n const uniqueFactIds = [...new Set(factIds.filter(Boolean))];\n const links = await this.getFactContentCollection();\n const resolvedRelationship =\n relationship || governance.defaultFactRelationship;\n const existing = await links.byRight(this.id as string, {\n relationship: resolvedRelationship,\n });\n\n const existingIds = new Set(existing.map((link) => link.factId));\n const desiredIds = new Set(uniqueFactIds);\n\n const kept = uniqueFactIds.filter((factId) => existingIds.has(factId));\n const added = uniqueFactIds.filter((factId) => !existingIds.has(factId));\n const removed = existing\n .map((link) => link.factId)\n .filter((factId) => !desiredIds.has(factId));\n\n for (const factId of added) {\n await links.attach(factId, this.id as string, {\n relationship: resolvedRelationship,\n });\n }\n\n for (const factId of removed) {\n await links.detach(factId, this.id as string, {\n relationship: resolvedRelationship,\n });\n }\n\n return { added, kept, removed };\n }\n\n public async browseFacts(\n query = '',\n options: {\n limit?: number;\n offset?: number;\n minSimilarity?: number;\n includeSuperseded?: boolean;\n latestOnly?: boolean;\n } = {},\n ): Promise<Fact[]> {\n await this.requireFactLinking('fact catalog browsing');\n const facts = await this.getFactCollection();\n return facts.browseCatalog(query, {\n ...options,\n tenantId: this.tenantId,\n });\n }\n\n private async getFactAuditSourceMaterials(): Promise<{\n sources: FactAuditSourceMaterial[];\n warnings: string[];\n }> {\n const warnings: string[] = [];\n const sources: FactAuditSourceMaterial[] = [];\n const references = await this.getReferences();\n const assets = await this.getAssets();\n\n for (const reference of references) {\n const referenceId = (reference.id as string | undefined) || '';\n const text = getContentText(reference);\n // `sourceUrl` is not a declared Content field; read it structurally.\n const referenceSourceUrl = (reference as { sourceUrl?: unknown })\n .sourceUrl;\n const sourceUrl =\n normalizeAuditText(reference.url) ||\n normalizeAuditText(referenceSourceUrl) ||\n normalizeAuditText(reference.fileKey);\n const sourceTitle =\n normalizeAuditText(reference.title) ||\n normalizeAuditText(reference.name) ||\n sourceUrl ||\n referenceId;\n\n if (!text) {\n warnings.push(\n `Reference ${sourceTitle || referenceId} has no extracted text.`,\n );\n continue;\n }\n\n sources.push({\n sourceKind: 'content-reference',\n sourceId: referenceId,\n sourceUrl,\n sourceTitle,\n locator: sourceTitle,\n text,\n });\n }\n\n for (const asset of assets as Array<Asset & FactAuditAssetExtraFields>) {\n const metadata =\n typeof asset?.getMetadata === 'function'\n ? asset.getMetadata()\n : parseAuditMetadata(asset?.metadata);\n const text = [\n metadata.extractedText,\n metadata.text,\n metadata.ocrText,\n asset?.text,\n asset?.body,\n asset?.description,\n ]\n .map(normalizeAuditText)\n .filter(Boolean)\n .join('\\n\\n');\n const assetId = normalizeAuditText(asset?.id);\n const sourceTitle =\n normalizeAuditText(asset?.title) ||\n normalizeAuditText(asset?.name) ||\n normalizeAuditText(asset?.filename) ||\n assetId;\n const sourceUrl =\n normalizeAuditText(asset?.url) ||\n normalizeAuditText(asset?.sourceUrl) ||\n normalizeAuditText(asset?.fileKey);\n\n if (!text) {\n warnings.push(`Asset ${sourceTitle || assetId} has no extracted text.`);\n continue;\n }\n\n sources.push({\n sourceKind: 'asset',\n sourceId: assetId,\n sourceUrl,\n sourceTitle,\n locator: sourceTitle,\n text,\n });\n }\n\n return { sources, warnings };\n }\n\n private factMatchesTenant(fact: {\n tenantId?: string | null;\n tenant_id?: string | null;\n }): boolean {\n return (\n fact.tenantId === this.tenantId ||\n fact.tenant_id === this.tenantId ||\n (!fact.tenantId && !fact.tenant_id && !this.tenantId)\n );\n }\n\n private async findExactArticleClaimFact(\n statement: string,\n ): Promise<Fact | null> {\n const normalizedStatement = normalizeAuditText(statement);\n const facts = await this.getFactCollection();\n const linkedClaimFacts = await Promise.all(\n (await this.getFactLinks({ relationship: 'referenced_in' })).map((link) =>\n facts.get({ id: link.factId }),\n ),\n );\n const linkedMatch = linkedClaimFacts.find(\n (fact): fact is Fact =>\n Boolean(fact) &&\n this.factMatchesTenant(fact as Fact) &&\n normalizeAuditText((fact as Fact).textRefined) === normalizedStatement,\n );\n if (linkedMatch) {\n return linkedMatch;\n }\n\n const matches = await facts.list({\n where: { textRefined: normalizedStatement },\n orderBy: 'updated_at DESC',\n });\n\n return (\n matches.find(\n (fact) =>\n this.factMatchesTenant(fact) &&\n this.id &&\n isGeneratedArticleClaimFact(fact, this.id as string),\n ) || null\n );\n }\n\n private async safeAuditLink(\n factId: string,\n relationship: FactContentRelationship,\n metadata: Record<string, unknown>,\n ) {\n const links = await this.getFactContentCollection();\n const existing = (\n await links.byRight(this.id as string, { relationship })\n ).find((link) => link.factId === factId);\n\n if (existing) {\n const existingMetadata = getLinkMetadata(existing);\n if (existingMetadata.generatedBy === FACT_AUDIT_GENERATED_BY) {\n existing.setMetadata?.({\n ...existingMetadata,\n ...metadata,\n });\n } else {\n existing.setMetadata?.({\n ...existingMetadata,\n factAudit: {\n ...asRecord(existingMetadata.factAudit),\n ...metadata,\n },\n });\n }\n await existing.save();\n return existing;\n }\n\n return links.attach(factId, this.id as string, { relationship, metadata });\n }\n\n private async clearGeneratedFactAudit(): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const [links, evidences] = await Promise.all([\n this.getFactLinks(),\n this.getFactEvidenceCollection(),\n ]);\n\n for (const link of links) {\n const metadata = getLinkMetadata(link);\n const nestedFactAudit = asRecord(metadata.factAudit);\n if (metadata.generatedBy === FACT_AUDIT_GENERATED_BY) {\n await link.delete();\n } else if (\n metadata.factAudit &&\n typeof metadata.factAudit === 'object' &&\n nestedFactAudit.generatedBy === FACT_AUDIT_GENERATED_BY\n ) {\n const { factAudit: _removed, ...preservedMetadata } = metadata;\n link.setMetadata?.(preservedMetadata);\n await link.save();\n }\n }\n\n const generatedEvidence = await evidences.list({\n where: { tenantId: this.tenantId ?? null },\n });\n for (const evidence of generatedEvidence) {\n const metadata =\n typeof evidence.getMetadata === 'function'\n ? evidence.getMetadata()\n : {};\n if (\n metadata.generatedBy === FACT_AUDIT_GENERATED_BY &&\n metadata.contentId === this.id\n ) {\n await evidence.delete();\n }\n }\n }\n\n private async clearGeneratedFactSourcesForSources(\n sources: FactAuditSourceMaterial[],\n ): Promise<string[]> {\n if (!this.id || sources.length === 0) {\n return [];\n }\n\n const sourceKeys = new Set(\n sources.map((source) => `${source.sourceKind}:${source.sourceId}`),\n );\n const factSources = await this.getFactSourceCollection();\n const generatedSources = await factSources.list({\n where: { tenantId: this.tenantId ?? null },\n });\n const deletedSourceIds: string[] = [];\n\n for (const source of generatedSources) {\n const metadata =\n typeof source.getMetadata === 'function' ? source.getMetadata() : {};\n const sourceKey = `${source.sourceType || ''}:${metadata.sourceId || ''}`;\n if (\n sourceKeys.has(sourceKey) &&\n metadata.generatedBy === FACT_AUDIT_GENERATED_BY &&\n metadata.contentId === this.id\n ) {\n if (typeof source.id === 'string') {\n deletedSourceIds.push(source.id);\n }\n await source.delete();\n }\n }\n\n return deletedSourceIds;\n }\n\n private async extractReferenceFactsForAudit(\n sources: FactAuditSourceMaterial[],\n options: {\n auditRunId: string;\n maxFactsPerSource?: number;\n context?: string;\n replaceGenerated?: boolean;\n },\n ) {\n const facts = await this.getFactCollection();\n const evidences = await this.getFactEvidenceCollection();\n const warnings: string[] = [];\n const referenceFacts = new Map<string, Fact>();\n let deletedEvidenceIds: string[] = [];\n let deletedSourceIds: string[] = [];\n\n if (options.replaceGenerated && sources.length > 0) {\n const replacement = await evidences.replaceGeneratedForSources(\n sources.map((source) => ({\n sourceKind: source.sourceKind,\n sourceId: source.sourceId,\n })),\n {\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id as string,\n tenantId: this.tenantId ?? null,\n },\n );\n deletedEvidenceIds = replacement.deletedEvidenceIds;\n deletedSourceIds =\n await this.clearGeneratedFactSourcesForSources(sources);\n }\n\n for (const source of sources) {\n let candidates: FactExtractionCandidate[] = [];\n try {\n candidates = await facts.extractCandidatesFromText(source.text, {\n domain: FACT_AUDIT_DOMAIN,\n sourceType: source.sourceKind,\n context: options.context || source.sourceTitle,\n maxFacts: options.maxFactsPerSource ?? 24,\n tenantId: this.tenantId,\n });\n } catch (error) {\n warnings.push(\n `Failed to extract facts from ${source.sourceTitle}: ${errorMessage(error)}`,\n );\n continue;\n }\n\n for (const candidate of candidates) {\n const result = await facts.reconcile({\n rawInput: candidate.statement,\n type: candidate.type || 'assertion',\n domain: FACT_AUDIT_DOMAIN,\n tenantId: this.tenantId,\n source: {\n sourceType: source.sourceKind,\n sourceUrl: source.sourceUrl,\n sourceTitle: source.sourceTitle,\n credibility: candidate.confidence ?? 0.75,\n metadata: {\n auditRunId: options.auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n sourceId: source.sourceId,\n quote: candidate.sourceExcerpt || null,\n locator: source.locator || null,\n },\n },\n });\n referenceFacts.set(result.fact.id as string, result.fact);\n\n await evidences.upsertEvidence({\n factId: result.fact.id as string,\n status: 'supports',\n sourceKind: source.sourceKind,\n sourceId: source.sourceId,\n sourceUrl: source.sourceUrl,\n sourceTitle: source.sourceTitle,\n quote: candidate.sourceExcerpt || candidate.statement,\n locator: source.locator,\n extractionMethod: 'ai-reference-fact',\n confidence: candidate.confidence ?? 0.75,\n tenantId: this.tenantId,\n metadata: {\n auditRunId: options.auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n candidateMetadata: candidate.metadata || {},\n },\n });\n }\n }\n\n return {\n referenceFacts,\n warnings,\n referenceFactsExtracted: referenceFacts.size,\n deletedEvidenceIds,\n deletedSourceIds,\n repairedSources: sources.map((source) => ({\n sourceKind: source.sourceKind,\n sourceId: source.sourceId,\n sourceTitle: source.sourceTitle,\n })),\n };\n }\n\n private async getCurrentFactAuditSupportCandidates(\n options: {\n referenceFacts?: Map<string, Fact>;\n sources?: FactAuditSourceSelector[];\n sourceIds?: string[];\n maxCandidateEvidence?: number;\n } = {},\n ) {\n const evidences = await this.getFactEvidenceCollection();\n const allFacts = await this.getFactCollection();\n const candidateFacts = new Map<string, FactAuditSupportCandidate>();\n const candidateEvidence = new Map<string, FactEvidence>();\n const sourceKeys = new Set(\n (options.sources || []).map(\n (source) => `${source.sourceKind}:${source.sourceId}`,\n ),\n );\n const sourceIds = new Set(options.sourceIds || []);\n const maxCandidateEvidence = Math.max(\n 1,\n options.maxCandidateEvidence ?? 120,\n );\n\n const evidenceEntries = options.referenceFacts\n ? (\n await Promise.all(\n [...options.referenceFacts.keys()].map((factId) =>\n evidences.getForFact(factId),\n ),\n )\n ).flat()\n : await evidences.list({\n where: { tenantId: this.tenantId ?? null },\n });\n\n for (const entry of evidenceEntries) {\n if (!isGeneratedFactAuditEvidence(entry, this.id as string)) {\n continue;\n }\n if (entry.sourceKind === 'content') {\n continue;\n }\n if (entry.status === 'irrelevant' || entry.status === 'invalid') {\n continue;\n }\n if (\n sourceKeys.size > 0 &&\n !sourceKeys.has(`${entry.sourceKind}:${entry.sourceId}`)\n ) {\n continue;\n }\n if (sourceIds.size > 0 && !sourceIds.has(entry.sourceId)) {\n continue;\n }\n if (candidateEvidence.size >= maxCandidateEvidence) {\n break;\n }\n\n const fact =\n options.referenceFacts?.get(entry.factId) ||\n (await allFacts.get({ id: entry.factId }));\n if (!fact) {\n continue;\n }\n\n const factId = fact.id as string;\n const existing = candidateFacts.get(factId) || {\n id: factId,\n statement: fact.textRefined || fact.textRaw || '',\n evidence: [],\n };\n const serializedEvidence = {\n id: entry.id || null,\n status: entry.status || 'supports',\n quote: entry.quote || null,\n sourceTitle: entry.sourceTitle || null,\n sourceUrl: entry.sourceUrl || null,\n locator: entry.locator || null,\n };\n existing.evidence.push(serializedEvidence);\n candidateFacts.set(factId, existing);\n if (typeof entry.id === 'string') {\n candidateEvidence.set(entry.id, entry);\n }\n }\n\n return {\n supportCandidates: [...candidateFacts.values()],\n candidateFactIds: new Set(candidateFacts.keys()),\n candidateEvidence,\n };\n }\n\n public async repairFactAudit(\n options: {\n maxReferenceFactsPerSource?: number;\n maxArticleClaims?: number;\n context?: string;\n } = {},\n ) {\n await this.requireFactLinking('fact audit repair');\n if (!this.id) {\n throw new Error('Cannot repair fact audit for unsaved content');\n }\n\n const auditRunId = createFactAuditRunId(this.id as string);\n const facts = await this.getFactCollection();\n const evidences = await this.getFactEvidenceCollection();\n const warnings: string[] = [];\n const articleText = getContentText(this);\n\n const sourceMaterials = await this.getFactAuditSourceMaterials();\n warnings.push(...sourceMaterials.warnings);\n await this.clearGeneratedFactAudit();\n const referenceRepair = await this.extractReferenceFactsForAudit(\n sourceMaterials.sources,\n {\n auditRunId,\n maxFactsPerSource: options.maxReferenceFactsPerSource,\n context: options.context,\n },\n );\n warnings.push(...referenceRepair.warnings);\n const referenceFacts = referenceRepair.referenceFacts;\n\n let claims: FactExtractionCandidate[] = [];\n if (!articleText) {\n warnings.push('Article has no text to audit.');\n } else {\n try {\n claims = await facts.extractArticleClaims(articleText, {\n domain: FACT_AUDIT_DOMAIN,\n sourceType: 'article',\n context: options.context || this.title || this.slug || '',\n maxFacts: options.maxArticleClaims ?? 32,\n tenantId: this.tenantId,\n });\n } catch (error) {\n warnings.push(\n `Failed to extract article claims: ${errorMessage(error)}`,\n );\n }\n }\n\n const { supportCandidates, candidateFactIds, candidateEvidence } =\n await this.getCurrentFactAuditSupportCandidates({\n referenceFacts,\n });\n\n const findings: ContentReviewFinding[] = [];\n\n for (const claim of claims) {\n let assessment: FactClaimSupportAssessment;\n try {\n assessment = await facts.assessClaimSupport(\n claim.statement,\n supportCandidates,\n { tenantId: this.tenantId },\n );\n } catch (error) {\n warnings.push(\n `Failed to assess claim \"${claim.statement}\": ${errorMessage(error)}`,\n );\n assessment = {\n status: 'needs_review' as FactClaimSupportStatus,\n matchedFactIds: [],\n matchedEvidenceIds: [],\n rationale: 'Support assessment failed.',\n confidence: undefined,\n };\n }\n\n const matchedFactIds = assessment.matchedFactIds.filter((factId) =>\n candidateFactIds.has(factId),\n );\n let claimFact = await this.findExactArticleClaimFact(claim.statement);\n\n if (!claimFact) {\n claimFact = await facts.create({\n textRefined: claim.statement,\n textRaw: claim.statement,\n type: claim.type || 'assertion',\n domain: FACT_AUDIT_DOMAIN,\n status:\n assessment.status === 'unsupported' ||\n assessment.status === 'needs_review'\n ? 'pending'\n : 'active',\n sourceCount: 0,\n confidence: claim.confidence ?? assessment.confidence ?? 0.5,\n tenantId: this.tenantId,\n metadata: JSON.stringify({\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n auditFactRole: 'article-claim',\n claimOnly: matchedFactIds.length === 0,\n }),\n });\n } else if (\n this.id &&\n isGeneratedArticleClaimFact(claimFact, this.id as string)\n ) {\n claimFact.status =\n assessment.status === 'unsupported' ||\n assessment.status === 'needs_review'\n ? 'pending'\n : 'active';\n claimFact.confidence =\n claim.confidence ?? assessment.confidence ?? claimFact.confidence;\n claimFact.updateMetadata?.({\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n auditFactRole: 'article-claim',\n claimOnly: matchedFactIds.length === 0,\n });\n await claimFact.save();\n }\n\n const articleEvidence = await evidences.upsertEvidence({\n factId: claimFact.id as string,\n status: 'supports',\n sourceKind: 'content',\n sourceId: this.id as string,\n sourceTitle: this.title || this.slug || (this.id as string),\n quote: claim.sourceExcerpt || claim.statement,\n locator: this.title || this.slug || '',\n extractionMethod: 'ai-article-claim',\n confidence: claim.confidence ?? assessment.confidence ?? 0.5,\n tenantId: this.tenantId,\n metadata: {\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n supportStatus: assessment.status,\n },\n });\n\n let supportingEvidenceIds = assessment.matchedEvidenceIds.filter(\n (evidenceId) => candidateEvidence.has(evidenceId),\n );\n for (const matchedFactId of matchedFactIds) {\n if (supportingEvidenceIds.length > 0) {\n continue;\n }\n const candidate = supportCandidates.find(\n (entry) => entry.id === matchedFactId,\n );\n supportingEvidenceIds = [\n ...supportingEvidenceIds,\n ...(candidate?.evidence || [])\n .map((entry) => entry.id)\n .filter((id: unknown): id is string => typeof id === 'string'),\n ];\n }\n supportingEvidenceIds = [...new Set(supportingEvidenceIds)];\n\n const linkMetadata = {\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n supportStatus: assessment.status,\n claimQuote: claim.sourceExcerpt || claim.statement,\n claimFactId: claimFact.id as string,\n articleEvidenceId: articleEvidence.id || null,\n supportingFactIds: matchedFactIds,\n supportingEvidenceIds,\n rationale: assessment.rationale,\n confidence: assessment.confidence ?? claim.confidence ?? null,\n };\n\n await this.safeAuditLink(\n claimFact.id as string,\n 'referenced_in',\n linkMetadata,\n );\n\n for (const matchedFactId of matchedFactIds) {\n await this.safeAuditLink(\n matchedFactId,\n assessment.status === 'contradicted' ? 'contradicts' : 'supports',\n {\n ...linkMetadata,\n supportingEvidenceIds,\n },\n );\n }\n\n if (assessment.status !== 'supported') {\n findings.push({\n severity: assessment.status === 'contradicted' ? 'error' : 'warning',\n title:\n assessment.status === 'contradicted'\n ? 'Contradicted article claim'\n : 'Unsupported article claim',\n detail: assessment.rationale || 'The claim needs editorial review.',\n factId: claimFact.id as string,\n quote: claim.sourceExcerpt || claim.statement,\n ruleId: 'fact-audit',\n });\n }\n }\n\n const reviews = await this.getContentReviewCollection();\n await reviews.createFromResult({\n contentId: this.id as string,\n kind: 'facts',\n policyKey: 'facts',\n reviewer: 'system',\n result: {\n status: findings.length > 0 ? 'flagged' : 'passed',\n summary:\n findings.length > 0\n ? `${findings.length} article claim(s) need review.`\n : 'Article claims are supported by available evidence.',\n findings,\n },\n metadata: {\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n warnings,\n },\n tenantId: this.tenantId,\n });\n\n const state = await this.getFactAuditState();\n return {\n ...state,\n repair: {\n auditRunId,\n claimsExtracted: claims.length,\n referenceFactsExtracted: referenceRepair.referenceFactsExtracted,\n warnings,\n },\n };\n }\n\n public async repairFactAuditAction(\n options: {\n maxReferenceFactsPerSource?: number;\n maxArticleClaims?: number;\n context?: string;\n } = {},\n ) {\n return this.repairFactAudit(options);\n }\n\n public async repairFactEvidence(\n options: FactAuditResourceRepairOptions = {},\n ) {\n await this.requireFactLinking('fact evidence repair');\n if (!this.id) {\n throw new Error('Cannot repair fact evidence for unsaved content');\n }\n\n const auditRunId = createFactAuditRunId(this.id as string);\n const sourceMaterials = await this.getFactAuditSourceMaterials();\n const sources = filterAuditSources(\n sourceMaterials.sources,\n options.sources,\n );\n const warnings: string[] = [];\n\n if (options.sources?.length && sources.length === 0) {\n warnings.push('No matching resource text was available for repair.');\n }\n\n const repair = await this.extractReferenceFactsForAudit(sources, {\n auditRunId,\n maxFactsPerSource: options.maxFactsPerSource,\n context: options.context,\n replaceGenerated: true,\n });\n warnings.push(...repair.warnings);\n\n const state = await this.getFactAuditState();\n return {\n ...state,\n evidenceRepair: {\n auditRunId,\n referenceFactsExtracted: repair.referenceFactsExtracted,\n repairedSources: repair.repairedSources,\n deletedEvidenceIds: repair.deletedEvidenceIds,\n deletedSourceIds: repair.deletedSourceIds,\n warnings,\n },\n };\n }\n\n public async repairFactEvidenceAction(\n options: FactAuditResourceRepairOptions = {},\n ) {\n return this.repairFactEvidence(options);\n }\n\n private async clearGeneratedSupportLinksForClaim(\n claimFactId: string,\n articleEvidenceId: string | null,\n ): Promise<void> {\n const links = await this.getFactLinks();\n\n for (const link of links) {\n if (\n link.relationship !== 'supports' &&\n link.relationship !== 'contradicts'\n ) {\n continue;\n }\n\n const metadata = getGeneratedFactAuditMetadata(link);\n if (!metadata) {\n continue;\n }\n\n const matchesEvidence =\n articleEvidenceId &&\n typeof metadata.articleEvidenceId === 'string' &&\n metadata.articleEvidenceId === articleEvidenceId;\n const matchesClaim =\n typeof metadata.claimFactId === 'string' &&\n metadata.claimFactId === claimFactId;\n\n if (matchesEvidence || matchesClaim) {\n await link.delete();\n }\n }\n }\n\n public async recheckFactClaims(options: FactAuditClaimRecheckOptions = {}) {\n await this.requireFactLinking('claim support recheck');\n if (!this.id) {\n throw new Error('Cannot recheck fact claims for unsaved content');\n }\n\n const auditRunId = createFactAuditRunId(this.id as string);\n const facts = await this.getFactCollection();\n const evidences = await this.getFactEvidenceCollection();\n const links = await this.getFactContentCollection();\n const claimIdFilter = new Set(options.claimFactIds || []);\n const { supportCandidates, candidateFactIds, candidateEvidence } =\n await this.getCurrentFactAuditSupportCandidates({\n sources: options.sources,\n sourceIds: options.sourceIds,\n maxCandidateEvidence: options.maxCandidateEvidence,\n });\n const claimLinks = (\n await links.byRight(this.id as string, { relationship: 'referenced_in' })\n )\n .map((link) => ({\n link,\n metadata: getGeneratedFactAuditMetadata(link),\n }))\n .filter(\n (\n entry,\n ): entry is { link: FactContent; metadata: Record<string, unknown> } =>\n entry.metadata !== null &&\n (claimIdFilter.size === 0 || claimIdFilter.has(entry.link.factId)),\n );\n const warnings: string[] = [];\n let recheckedClaims = 0;\n\n for (const { link, metadata } of claimLinks) {\n const claimFact = await facts.get({ id: link.factId });\n if (!claimFact) {\n continue;\n }\n\n const claimText =\n normalizeAuditText(metadata.claimQuote) ||\n normalizeAuditText(claimFact.textRefined) ||\n normalizeAuditText(claimFact.textRaw);\n if (!claimText) {\n continue;\n }\n\n let assessment: FactClaimSupportAssessment;\n try {\n assessment = await facts.assessClaimSupport(\n claimText,\n supportCandidates,\n { tenantId: this.tenantId },\n );\n } catch (error) {\n warnings.push(\n `Failed to recheck claim \"${claimText}\": ${errorMessage(error)}`,\n );\n assessment = {\n status: 'needs_review',\n matchedFactIds: [],\n matchedEvidenceIds: [],\n rationale: 'Support assessment failed.',\n confidence: undefined,\n };\n }\n\n const matchedFactIds = assessment.matchedFactIds.filter((factId) =>\n candidateFactIds.has(factId),\n );\n let supportStatus = assessment.status;\n if (\n (supportStatus === 'supported' || supportStatus === 'contradicted') &&\n matchedFactIds.length === 0\n ) {\n supportStatus = 'needs_review';\n }\n let supportingEvidenceIds = assessment.matchedEvidenceIds.filter(\n (evidenceId) => candidateEvidence.has(evidenceId),\n );\n if (supportingEvidenceIds.length === 0) {\n for (const matchedFactId of matchedFactIds) {\n const candidate = supportCandidates.find(\n (entry) => entry.id === matchedFactId,\n );\n supportingEvidenceIds.push(\n ...(candidate?.evidence || [])\n .map((entry) => entry.id)\n .filter((id: unknown): id is string => typeof id === 'string'),\n );\n }\n }\n supportingEvidenceIds = [...new Set(supportingEvidenceIds)];\n\n const articleEvidenceId =\n typeof metadata.articleEvidenceId === 'string'\n ? metadata.articleEvidenceId\n : null;\n const nextMetadata = {\n ...metadata,\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n supportStatus,\n supportingFactIds: matchedFactIds,\n supportingEvidenceIds,\n rationale: assessment.rationale,\n confidence: assessment.confidence ?? metadata.confidence ?? null,\n claimFactId: link.factId,\n };\n const existingMetadata = getLinkMetadata(link);\n if (existingMetadata.generatedBy === FACT_AUDIT_GENERATED_BY) {\n link.setMetadata?.(nextMetadata);\n } else {\n link.setMetadata?.({\n ...existingMetadata,\n factAudit: nextMetadata,\n });\n }\n await link.save();\n\n if (articleEvidenceId) {\n const articleEvidence = await evidences.get({ id: articleEvidenceId });\n if (articleEvidence) {\n articleEvidence.updateMetadata({\n auditRunId,\n supportStatus,\n });\n await articleEvidence.save();\n }\n }\n\n await this.clearGeneratedSupportLinksForClaim(\n link.factId,\n articleEvidenceId,\n );\n for (const matchedFactId of matchedFactIds) {\n await this.safeAuditLink(\n matchedFactId,\n supportStatus === 'contradicted' ? 'contradicts' : 'supports',\n {\n ...nextMetadata,\n articleEvidenceId,\n },\n );\n }\n\n recheckedClaims += 1;\n }\n\n const state = await this.getFactAuditState();\n return {\n ...state,\n claimRecheck: {\n auditRunId,\n recheckedClaims,\n candidateFacts: supportCandidates.length,\n candidateEvidence: candidateEvidence.size,\n warnings,\n },\n };\n }\n\n public async recheckFactClaimsAction(\n options: FactAuditClaimRecheckOptions = {},\n ) {\n return this.recheckFactClaims(options);\n }\n\n public async updateFactEvidenceStatus(\n options: FactEvidenceStatusUpdateOptions = {},\n ) {\n await this.requireFactLinking('evidence status update');\n if (!this.id) {\n throw new Error('Cannot update fact evidence for unsaved content');\n }\n\n const status = normalizeFactEvidenceStatus(options.status);\n if (!status) {\n throw new Error('A valid evidence status is required');\n }\n\n const requestedEvidenceIds = [\n ...new Set(\n (options.evidenceIds || []).filter(\n (id): id is string => typeof id === 'string' && id.length > 0,\n ),\n ),\n ];\n const evidences = await this.getFactEvidenceCollection();\n const sourceMaterials = await this.getFactAuditSourceMaterials();\n const allowedSourceKeys = new Set(\n sourceMaterials.sources.map(\n (source: FactAuditSourceMaterial) =>\n `${source.sourceKind}:${source.sourceId}`,\n ),\n );\n const allowedEvidenceIds: string[] = [];\n\n for (const evidenceId of requestedEvidenceIds) {\n const evidence = await evidences.get({ id: evidenceId });\n if (!evidence) {\n continue;\n }\n\n if (\n this.tenantId &&\n evidence.tenantId &&\n evidence.tenantId !== this.tenantId\n ) {\n continue;\n }\n\n const metadata = getEvidenceMetadata(evidence);\n const sourceKey = `${evidence.sourceKind || ''}:${\n evidence.sourceId || ''\n }`;\n if (\n metadata.contentId === this.id ||\n (evidence.sourceKind === 'content' && evidence.sourceId === this.id) ||\n allowedSourceKeys.has(sourceKey)\n ) {\n allowedEvidenceIds.push(evidenceId);\n }\n }\n\n const updated = await evidences.bulkUpdateStatus(\n allowedEvidenceIds,\n status,\n {\n reason: options.reason,\n },\n );\n const state = await this.getFactAuditState();\n\n return {\n ...state,\n evidenceStatusUpdate: {\n status,\n requestedEvidenceIds,\n updatedEvidenceIds: updated\n .map((entry) => entry.id)\n .filter((id: unknown): id is string => typeof id === 'string'),\n skippedEvidenceIds: requestedEvidenceIds.filter(\n (id) => !allowedEvidenceIds.includes(id),\n ),\n },\n };\n }\n\n public async updateFactEvidenceStatusAction(\n options: FactEvidenceStatusUpdateOptions = {},\n ) {\n return this.updateFactEvidenceStatus(options);\n }\n\n public async getFactAuditState(): Promise<FactAuditState> {\n if (!this.id) {\n return {\n counts: {\n total: 0,\n supported: 0,\n unsupported: 0,\n contradicted: 0,\n needs_review: 0,\n },\n claims: [],\n resourceClaims: [],\n warnings: [],\n generatedBy: FACT_AUDIT_GENERATED_BY,\n latestAuditRunId: null,\n };\n }\n\n const [facts, factLinks] = await Promise.all([\n this.getFacts({\n relationship: 'referenced_in',\n latestOnly: false,\n includeSuperseded: false,\n }),\n this.getFactLinks({ relationship: 'referenced_in' }),\n ]);\n const factMap = new Map(\n facts\n .filter((fact) => fact.id)\n .map((fact) => [fact.id as string, fact] as const),\n );\n const evidences = await this.getFactEvidenceCollection();\n const allFacts = await this.getFactCollection();\n const generatedLinks = factLinks\n .map((link) => ({\n link,\n metadata: getGeneratedFactAuditMetadata(link),\n }))\n .filter(\n (\n entry,\n ): entry is { link: FactContent; metadata: Record<string, unknown> } =>\n entry.metadata !== null,\n );\n const claims: FactAuditClaim[] = [];\n const resourceClaimsByKey = new Map<string, FactAuditResourceClaim>();\n const warnings: string[] = [];\n let latestAuditRunId: string | null = null;\n\n for (const { link, metadata } of generatedLinks) {\n const fact = factMap.get(link.factId);\n if (!fact) {\n continue;\n }\n\n // Opaque audit-link metadata values; cast at the read boundary.\n latestAuditRunId =\n (metadata.auditRunId as string | null) || latestAuditRunId;\n const status = (metadata.supportStatus ||\n 'needs_review') as FactClaimSupportStatus;\n const matchedFactIds = Array.isArray(metadata.supportingFactIds)\n ? metadata.supportingFactIds\n : [];\n const articleEvidenceId =\n typeof metadata.articleEvidenceId === 'string'\n ? metadata.articleEvidenceId\n : null;\n const supportingEvidenceIds = new Set(\n Array.isArray(metadata.supportingEvidenceIds)\n ? metadata.supportingEvidenceIds.filter(\n (id: unknown): id is string => typeof id === 'string',\n )\n : [],\n );\n const [allClaimEvidence, matchedFacts] = await Promise.all([\n evidences.getForFact(fact.id as string),\n Promise.all(\n matchedFactIds.map(async (factId: string) => {\n const matchedFact = await allFacts.get({ id: factId });\n const matchedEvidence = (await evidences.getForFact(factId)).filter(\n (entry) =>\n supportingEvidenceIds.size === 0\n ? entry.sourceKind !== 'content' || entry.sourceId !== this.id\n : supportingEvidenceIds.has(entry.id as string),\n );\n return matchedFact\n ? {\n fact: serializeFact(matchedFact),\n evidence: matchedEvidence.map((entry) => ({\n ...serializeFact(entry),\n metadata:\n typeof entry.getMetadata === 'function'\n ? entry.getMetadata()\n : {},\n })),\n }\n : null;\n }),\n ),\n ]);\n const claimEvidence = allClaimEvidence.filter((entry) =>\n articleEvidenceId\n ? entry.id === articleEvidenceId\n : entry.sourceKind === 'content' && entry.sourceId === this.id,\n );\n\n claims.push({\n id: fact.id as string,\n fact: serializeFact(fact),\n supportStatus: status,\n // Opaque audit-link metadata values; cast at the read boundary.\n claimQuote: (metadata.claimQuote as string | null) || null,\n rationale: (metadata.rationale as string | null) || null,\n confidence: (metadata.confidence as number | null) ?? null,\n relationship: link.relationship || null,\n linkMetadata: metadata,\n evidence: claimEvidence.map((entry) => ({\n ...serializeFact(entry),\n metadata:\n typeof entry.getMetadata === 'function' ? entry.getMetadata() : {},\n })),\n matchedFacts: matchedFacts.filter(\n Boolean,\n ) as FactAuditClaim['matchedFacts'],\n });\n }\n\n const generatedEvidence = await evidences.list({\n where: { tenantId: this.tenantId ?? null },\n });\n for (const evidence of generatedEvidence) {\n const metadata =\n typeof evidence.getMetadata === 'function'\n ? evidence.getMetadata()\n : {};\n if (\n metadata.generatedBy !== FACT_AUDIT_GENERATED_BY ||\n metadata.contentId !== this.id ||\n evidence.sourceKind === 'content'\n ) {\n continue;\n }\n\n const fact = await allFacts.get({ id: evidence.factId });\n if (!fact) {\n continue;\n }\n\n const key = [\n evidence.factId,\n evidence.sourceKind,\n evidence.sourceId,\n evidence.evidenceKey,\n ].join(':');\n const serializedEvidence = {\n ...serializeFact(evidence),\n metadata,\n };\n const existing = resourceClaimsByKey.get(key);\n if (existing) {\n existing.evidence.push(serializedEvidence);\n continue;\n }\n\n resourceClaimsByKey.set(key, {\n id: fact.id as string,\n fact: serializeFact(fact),\n sourceKind: evidence.sourceKind || null,\n sourceId: evidence.sourceId || null,\n sourceUrl: evidence.sourceUrl || null,\n sourceTitle: evidence.sourceTitle || null,\n locator: evidence.locator || null,\n quote: evidence.quote || null,\n status: evidence.status || 'supports',\n confidence: evidence.confidence ?? null,\n evidence: [serializedEvidence],\n });\n }\n\n const latestReview = (\n await this.getContentReviewCollection()\n ).getLatestForPolicyKey(this.id as string, 'facts');\n const review = await latestReview;\n const reviewMetadata =\n review && typeof review.getMetadata === 'function'\n ? review.getMetadata()\n : {};\n if (\n reviewMetadata.generatedBy === FACT_AUDIT_GENERATED_BY &&\n Array.isArray(reviewMetadata.warnings)\n ) {\n warnings.push(...reviewMetadata.warnings);\n }\n\n const counts = {\n total: claims.length,\n supported: 0,\n unsupported: 0,\n contradicted: 0,\n needs_review: 0,\n };\n for (const claim of claims) {\n counts[claim.supportStatus] += 1;\n }\n\n return {\n counts,\n claims,\n resourceClaims: [...resourceClaimsByKey.values()],\n warnings,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n latestAuditRunId,\n };\n }\n\n public async getFactAuditStateAction() {\n return this.getFactAuditState();\n }\n\n public async getFactsState(\n options: { relationship?: FactContentRelationship } = {},\n ) {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.factLinkingEnabled) {\n return {\n factIds: [],\n facts: [],\n factLinks: [],\n };\n }\n\n const relationship = options.relationship;\n const [facts, factLinks] = await Promise.all([\n this.getFacts({\n relationship,\n latestOnly: true,\n includeSuperseded: false,\n }),\n this.getFactLinks(relationship ? { relationship } : {}),\n ]);\n\n return {\n factIds: facts.map((fact) => fact.id).filter(Boolean),\n facts: facts.map(serializeFact),\n factLinks: factLinks.map(serializeFactLink),\n };\n }\n\n public async syncFactsState(\n options: {\n factIds?: string[];\n relationship?: FactContentRelationship;\n } = {},\n ) {\n const governance = await this.requireFactLinking('fact sync');\n const relationship =\n options.relationship || governance.defaultFactRelationship;\n const sync = await this.syncFacts(options.factIds || [], relationship);\n const state = await this.getFactsState({ relationship });\n return {\n ...state,\n sync,\n };\n }\n\n public async createVersion(options: CreateContentVersionOptions = {}) {\n const versions = await this.getContentVersionCollection();\n return versions.createSnapshot(this, options);\n }\n\n public async getVersions() {\n if (!this.id) {\n return [];\n }\n\n const versions = await this.getContentVersionCollection();\n return versions.listForContent(this.id as string);\n }\n\n public async restoreFromVersion(versionNumber: number) {\n const versions = await this.getContentVersionCollection();\n return versions.restoreIntoContent(this, versionNumber);\n }\n\n public async getReviews(kind?: RunContentReviewOptions['kind']) {\n if (!this.id) {\n return [];\n }\n\n const reviews = await this.getContentReviewCollection();\n return reviews.listForContent(this.id as string, kind);\n }\n\n public async listReviews(\n options: { kind?: RunContentReviewOptions['kind'] } = {},\n ) {\n const reviews = await this.getReviews(options.kind);\n return reviews.map(serializeContentReview);\n }\n\n public async getReviewRequirements(\n profileKey: string,\n governance?: ResolvedContentGovernance,\n ) {\n const resolvedGovernance = governance || (await this.resolveGovernance());\n return getContentReviewRequirements(\n profileKey,\n resolvedGovernance.availableProfiles,\n );\n }\n\n public async getGovernanceState(): Promise<ContentGovernanceState> {\n const governance = await this.resolveGovernance();\n\n if (!governance.isGoverned) {\n return {\n ...governance,\n reviewProfiles: [],\n };\n }\n\n return {\n ...governance,\n reviewProfiles: await this.listReviewProfilesAction(),\n };\n }\n\n public async getGovernanceStateAction() {\n return this.getGovernanceState();\n }\n\n public async listReviewProfilesAction() {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned) {\n return [];\n }\n\n return Promise.all(\n getContentReviewProfileKeys(governance.availableProfiles).map(\n (profileKey) => this.evaluateReviewProfile(profileKey),\n ),\n );\n }\n\n public async evaluateReviewProfile(\n profileKey: string,\n ): Promise<ContentReviewProfileEvaluation> {\n const governance = await this.resolveGovernance();\n const requirements = await this.getReviewRequirements(\n profileKey,\n governance,\n );\n\n if (requirements.length === 0) {\n return {\n profileKey,\n ready: true,\n complete: true,\n requirements: [],\n };\n }\n\n const reviews = await this.getContentReviewCollection();\n const reviewFingerprintCache = new Map<string, string>();\n const evaluatedRequirements = await Promise.all(\n requirements.map(async (requirement) => {\n if (!reviewFingerprintCache.has(requirement.policyKey)) {\n reviewFingerprintCache.set(\n requirement.policyKey,\n await this.buildReviewFingerprint(requirement.policyKey),\n );\n }\n\n const latestReview =\n this.id && requirement.policyKey\n ? await reviews.getLatestForPolicyKey(\n this.id as string,\n requirement.policyKey,\n )\n : null;\n const acceptedStatuses = getAcceptedContentReviewStatuses(requirement);\n const latestStatus = latestReview?.status ?? null;\n const latestMetadata =\n typeof latestReview?.getMetadata === 'function'\n ? latestReview.getMetadata()\n : {};\n const currentFingerprint =\n reviewFingerprintCache.get(requirement.policyKey) || null;\n const reviewedFingerprint =\n latestMetadata?.reviewFingerprint ||\n latestMetadata?.contentFingerprint ||\n null;\n const missing = !latestReview;\n const stale =\n !missing &&\n !!reviewedFingerprint &&\n reviewedFingerprint !== currentFingerprint;\n const executed =\n latestStatus !== null && latestStatus !== 'pending' && !stale;\n const satisfied =\n !stale &&\n latestStatus !== null &&\n acceptedStatuses.includes(latestStatus);\n\n return {\n kind: getContentReviewKind(\n requirement.policyKey,\n governance.reviewPolicies,\n ),\n policyKey: requirement.policyKey,\n label:\n requirement.label ||\n getContentReviewPolicy(\n requirement.policyKey,\n governance.reviewPolicies,\n )?.label ||\n requirement.policyKey,\n blocking: requirement.blocking === true,\n acceptedStatuses,\n missing,\n stale,\n executed,\n satisfied,\n latestReviewId: (latestReview?.id as string) || null,\n latestStatus,\n latestSummary: latestReview?.summary || null,\n };\n }),\n );\n\n return {\n profileKey,\n ready: evaluatedRequirements\n .filter((requirement) => requirement.blocking)\n .every((requirement) => requirement.satisfied),\n complete: evaluatedRequirements.every(\n (requirement) => requirement.executed,\n ),\n requirements: evaluatedRequirements,\n };\n }\n\n public async evaluateReviewProfileAction(\n options: { profileKey?: string } = {},\n ) {\n if (!options.profileKey) {\n throw new Error('profileKey is required');\n }\n\n return this.evaluateReviewProfile(options.profileKey);\n }\n\n public async isReadyForReviewProfile(profileKey: string): Promise<boolean> {\n const evaluation = await this.evaluateReviewProfile(profileKey);\n return evaluation.ready;\n }\n\n public async getPublishedTransparency() {\n if (!this.id) {\n return null;\n }\n\n const versions = await this.getContentVersionCollection();\n const latestPublicationVersion =\n await versions.getLatestPublishedForContent(this.id as string);\n\n return latestPublicationVersion?.getTransparency() || null;\n }\n\n public async getPublishedTransparencyAction() {\n return this.getPublishedTransparency();\n }\n\n public async previewTransparency() {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.transparencyEnabled) {\n return null;\n }\n\n return this.buildTransparencySnapshot({\n snapshotKind: 'preview',\n governance,\n });\n }\n\n public async previewTransparencyAction() {\n return this.previewTransparency();\n }\n\n public async runReview(options: RunContentReviewOptions = {}) {\n const governance = await this.requireGovernance('review execution');\n\n if (!this.id) {\n throw new Error('Cannot review unsaved content');\n }\n\n const policyKey = options.policyKey || options.kind || 'custom';\n const policy = getContentReviewPolicy(policyKey, governance.reviewPolicies);\n const kind =\n options.kind ||\n getContentReviewKind(policyKey, governance.reviewPolicies);\n const facts =\n options.facts !== undefined\n ? options.facts\n : governance.factLinkingEnabled &&\n (kind === 'facts' || Boolean(options.factIds?.length))\n ? await this.getFacts({\n latestOnly: true,\n includeSuperseded: false,\n })\n : [];\n const filteredFacts =\n options.factIds && options.factIds.length > 0\n ? facts.filter((fact) => options.factIds?.includes(fact.id as string))\n : facts;\n const reviewPrompt = buildContentReviewPrompt({\n kind,\n content: this,\n facts: filteredFacts,\n policy,\n customInstructions: options.instructions,\n });\n const resolvedPrompt = await resolvePrompt(smrtContentReviewPrompt.key, {\n db: this.options.db,\n tenantId: this.tenantId,\n variables: {\n contentBody: this.body,\n contentDescription: this.description ?? '',\n contentId: this.id ?? '',\n contentTitle: this.title,\n kind,\n policyKey: policy?.key || kind,\n reviewPrompt,\n },\n });\n const reviewFingerprint = await this.buildReviewFingerprint(policyKey);\n const ai = this.ai as {\n message?: (\n prompt: string,\n options?: Record<string, unknown>,\n ) => Promise<string>;\n };\n if (!ai?.message) {\n throw new Error('AI client is not configured for content reviews');\n }\n\n const rawResponse = await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n );\n const result = parseContentReviewResponse(rawResponse);\n const version =\n options.createVersion === false\n ? null\n : await this.createVersion({\n kind: 'review',\n summary: result.summary,\n metadata: {\n kind,\n policyKey,\n reviewFingerprint,\n },\n });\n\n const reviews = await this.getContentReviewCollection();\n return reviews.createFromResult({\n contentId: this.id as string,\n contentVersionId: version?.id as string | undefined,\n kind,\n policyKey,\n reviewer: options.reviewer || 'system',\n result,\n metadata: {\n ...(options.metadata || {}),\n prompt: resolvedPrompt.text,\n rawResponse,\n reviewFingerprint,\n factIds: filteredFacts.map((fact) => fact.id),\n },\n tenantId: this.tenantId,\n });\n }\n\n public async runReviewAction(options: RunContentReviewOptions = {}) {\n let review: ContentReview;\n\n if (options.kind === 'facts') {\n review = await this.reviewFacts(options);\n } else if (options.kind === 'safety') {\n review = await this.reviewSafety(options);\n } else {\n review = await this.runReview(options);\n }\n\n return serializeContentReview(review);\n }\n\n public async reviewFacts(\n options: Omit<RunContentReviewOptions, 'kind'> = {},\n ) {\n return this.runReview({\n ...options,\n kind: 'facts',\n policyKey: options.policyKey || 'facts',\n });\n }\n\n public async reviewSafety(\n options: Omit<RunContentReviewOptions, 'kind'> = {},\n ) {\n const governance = await this.requireGovernance('safety review');\n const safetyPolicy = getContentReviewPolicy(\n options.policyKey || 'safety',\n governance.reviewPolicies,\n );\n const baseInstructions = safetyPolicy?.instructions || '';\n\n return this.runReview({\n ...options,\n kind: 'safety',\n policyKey: options.policyKey || 'safety',\n instructions:\n options.instructions && baseInstructions\n ? `${baseInstructions}\\n\\nAdditional app-level guidance:\\n${options.instructions}`\n : options.instructions || baseInstructions,\n });\n }\n\n public async getCorrections() {\n if (!this.id) {\n return [];\n }\n\n const corrections = await this.getContentCorrectionCollection();\n return corrections.listForContent(this.id as string);\n }\n\n public async listCorrections() {\n const corrections = await this.getCorrections();\n return corrections.map(serializeContentCorrection);\n }\n\n public async issueCorrection(options: IssueContentCorrectionOptions) {\n const governance = await this.requireGovernance('corrections');\n\n if (!this.id) {\n throw new Error('Cannot issue a correction for unsaved content');\n }\n\n let replacementFactId = '';\n if (\n governance.factLinkingEnabled &&\n options.factId &&\n options.correctedFactText\n ) {\n const facts = await this.getFactCollection();\n const existing = await facts.get({ id: options.factId });\n if (!existing) {\n throw new Error(`Fact not found for correction: ${options.factId}`);\n }\n\n // Create the correction branch directly so editorial corrections do not\n // block on synchronous embedding generation inside facts.branch().\n const replacement = await facts.create({\n textRefined: options.correctedFactText,\n textRaw: options.correctedFactText,\n type: existing.getType(),\n domain: existing.domain,\n status: 'active',\n tenantId: existing.tenantId ?? this.tenantId ?? null,\n previousFactId: options.factId,\n evolutionType: 'correction',\n });\n existing.status = 'superseded';\n await existing.save();\n replacementFactId = replacement.id as string;\n await this.addFact(replacementFactId);\n }\n\n const version =\n options.createVersion === false\n ? null\n : await this.createVersion({\n kind: 'correction',\n summary: options.summary,\n metadata: {\n factId: options.factId || null,\n replacementFactId: replacementFactId || null,\n },\n });\n const correctionDraft =\n options.createVersion === false\n ? null\n : await this.buildCorrectionDraftSnapshot(options, replacementFactId);\n const draftVersion =\n options.createVersion === false || !correctionDraft\n ? null\n : await this.createVersion({\n kind: 'draft',\n summary: `Auto-created correction draft: ${options.summary}`,\n snapshot: correctionDraft.snapshot,\n metadata: {\n ...correctionDraft.metadata,\n sourceCorrectionVersionId: (version?.id as string) || null,\n sourceCorrectionVersionNumber: version?.version ?? null,\n },\n });\n\n const corrections = await this.getContentCorrectionCollection();\n const shouldPublish = options.publish ?? this.status === 'published';\n return corrections.issue({\n contentId: this.id as string,\n contentVersionId: (version?.id as string) || '',\n factId: options.factId || '',\n replacementFactId,\n correctionType: options.correctionType || 'fact',\n status: shouldPublish ? 'published' : 'draft',\n summary: options.summary,\n incorrectText: options.incorrectText || '',\n correctedText: options.correctedText || options.correctedFactText || '',\n publicNote: options.publicNote || '',\n metadata: {\n ...(options.metadata || {}),\n autoGeneratedDraft: Boolean(draftVersion),\n draftVersionId: (draftVersion?.id as string) || null,\n draftVersionNumber: draftVersion?.version ?? null,\n sourceCorrectionVersionId: (version?.id as string) || null,\n sourceCorrectionVersionNumber: version?.version ?? null,\n correctionProfileKey: governance.correctionProfileKey || null,\n },\n tenantId: this.tenantId,\n publishedAt: shouldPublish ? new Date() : null,\n });\n }\n\n public async issueCorrectionAction(options: IssueContentCorrectionOptions) {\n const correction = await this.issueCorrection(options);\n return serializeContentCorrection(correction);\n }\n\n public async listVersions() {\n const versions = await this.getVersions();\n return versions.map(serializeContentVersion);\n }\n\n public async mutateVersionAction(\n options: CreateContentVersionOptions & {\n action?: string;\n versionNumber?: number | string;\n } = {},\n ) {\n if (options.action === 'restore') {\n const versionNumber = Number(options.versionNumber);\n if (!Number.isFinite(versionNumber)) {\n throw new Error('versionNumber is required to restore a version');\n }\n\n const restored = await this.restoreFromVersion(versionNumber);\n return serializeContent(restored);\n }\n\n const version = await this.createVersion(options);\n return serializeContentVersion(version);\n }\n\n /**\n * Note: toJSON() is inherited from SmrtObject\n *\n * The parent implementation handles:\n * - STI discriminator (_meta_type) for polymorphic queries\n * - Meta field extraction (_meta_data) for child-specific fields\n * - Automatic serialization of all fields from manifest\n *\n * DO NOT override toJSON() unless you call super.toJSON() first.\n * See issue #377 for details on why this override was removed.\n */\n\n // ============================================\n // Category Helper Methods\n // ============================================\n\n /**\n * Get category segments as array\n * @example 'politics/local' -> ['politics', 'local']\n */\n getCategorySegments(): string[] {\n if (!this.category) return [];\n return this.category.split('/').filter(Boolean);\n }\n\n /**\n * Get parent category path\n * @example 'politics/local/town' -> 'politics/local'\n * @example 'politics' -> null\n */\n getParentCategory(): string | null {\n const segments = this.getCategorySegments();\n if (segments.length <= 1) return null;\n return segments.slice(0, -1).join('/');\n }\n\n /**\n * Get root (top-level) category\n * @example 'politics/local/town' -> 'politics'\n */\n getRootCategory(): string | null {\n const segments = this.getCategorySegments();\n return segments[0] || null;\n }\n\n /**\n * Get all ancestor category paths (for breadcrumbs)\n * @example 'politics/local' -> ['politics', 'politics/local']\n */\n getAncestorPaths(): string[] {\n const segments = this.getCategorySegments();\n return segments.map((_, i) => segments.slice(0, i + 1).join('/'));\n }\n\n /**\n * Check if content belongs to a category (optionally including subcategories)\n * @param categoryPath - Category to check\n * @param includeChildren - If true, matches 'politics' for content in 'politics/local'\n */\n isInCategory(categoryPath: string, includeChildren = true): boolean {\n if (!this.category) return false;\n if (includeChildren) {\n return (\n this.category === categoryPath ||\n this.category.startsWith(`${categoryPath}/`)\n );\n }\n return this.category === categoryPath;\n }\n\n // ============================================\n // Asset Relationship Methods\n // ============================================\n\n /**\n * Get all assets associated with this content\n * @param relationship - Optional filter by relationship type (e.g., 'thumbnail', 'attachment')\n * @returns Promise resolving to array of assets\n */\n async getAssets(relationship?: string): Promise<Asset[]> {\n if (!this.id) {\n return [];\n }\n\n return this.resolveAssetsForLinks(\n await this.getContentAssetLinks(relationship),\n );\n }\n\n /**\n * Add an asset to this content with a relationship type\n * @param asset - The asset to associate\n * @param relationship - Relationship type (e.g., 'thumbnail', 'attachment', 'inline')\n * @param sortOrder - Optional sort order for display\n */\n async addAsset(\n asset: Asset,\n relationship = 'attachment',\n sortOrder = 0,\n ): Promise<void> {\n if (!this.id || !asset.id) {\n throw new Error('Cannot associate unsaved content or asset');\n }\n\n // Validate relationship - must start with letter/underscore, contain only alphanumeric and underscores\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(relationship)) {\n throw new Error(\n `Invalid relationship type \"${relationship}\"; must start with a letter or underscore and contain only letters, digits, and underscores`,\n );\n }\n\n // Validate sortOrder is a reasonable integer\n if (\n !Number.isInteger(sortOrder) ||\n sortOrder < 0 ||\n sortOrder > 2147483647\n ) {\n throw new Error(\n `Invalid sortOrder \"${sortOrder}\"; must be a non-negative integer`,\n );\n }\n\n const contentAssets = await this.getContentAssetCollection();\n await contentAssets.attach(this.id, asset.id, {\n relationship,\n sortOrder,\n tenantId: this.tenantId,\n });\n }\n\n /**\n * Remove an asset from this content\n * @param assetId - ID of the asset to remove\n * @param relationship - Optional specific relationship to remove (removes all if not specified)\n */\n async removeAsset(assetId: string, relationship?: string): Promise<void> {\n if (!this.id) {\n return;\n }\n\n try {\n const contentAssets = await this.getContentAssetCollection();\n await contentAssets.detach(\n this.id,\n assetId,\n relationship ? { relationship } : {},\n );\n } catch (error) {\n if (!isMissingTableError(error, 'content_assets')) {\n throw error;\n }\n }\n }\n\n // ============================================\n // Metadata Accessors (MetadataAccessor contract)\n // ============================================\n\n /**\n * Get the full metadata record. Always returns a plain object — never\n * `null`, never an array — so callers can safely read nested keys without\n * defensive checks.\n *\n * Pure read with no side-effect on `this.metadata`: if the field is\n * currently `null` (e.g. fresh from the DB) or non-record-shaped, an\n * empty object is returned but the field is **not** mutated. This avoids\n * accidentally marking the object dirty during a read, which would\n * otherwise cause SmrtObject's save lifecycle to write `{}` back over a\n * NULL column on the next save. Callers that want to normalise the\n * stored field should use {@link Content.setMetadata}.\n */\n getMetadata(): Record<string, unknown> {\n return isPlainMetadataRecord(this.metadata) ? this.metadata : {};\n }\n\n /**\n * Replace the full metadata record. Passing `null`/`undefined` (or any\n * non-record value such as an array) clears it to an empty object so\n * downstream readers can rely on the field always being a plain object.\n */\n setMetadata(metadata: Record<string, unknown> | null | undefined): void {\n this.metadata = isPlainMetadataRecord(metadata) ? { ...metadata } : {};\n }\n\n /**\n * Shallow-merge a patch over the current metadata. Returns the resulting\n * record so callers can chain reads without re-reading the field. Unlike\n * {@link Content.getMetadata}, this method does intentionally write back\n * to `this.metadata` because the merge is a write.\n */\n updateMetadata(\n patch: Partial<Record<string, unknown>>,\n ): Record<string, unknown> {\n const next = { ...this.getMetadata(), ...(patch ?? {}) };\n this.metadata = next;\n return next;\n }\n\n // ============================================\n // Thumbnail Convenience Methods\n // ============================================\n\n /**\n * Get the thumbnail image for this content\n * @returns Promise resolving to the thumbnail Image or null\n */\n async getThumbnail(): Promise<Image | null> {\n if (!this.thumbnailAssetId) {\n return null;\n }\n\n const images = await ImageCollection.create({\n db: this.options?.db,\n });\n\n return images.get({ id: this.thumbnailAssetId });\n }\n\n /**\n * Set the thumbnail image for this content\n * @param image - The image to set as thumbnail\n */\n async setThumbnail(image: Image): Promise<void> {\n // Add as asset with 'thumbnail' relationship\n await this.addAsset(image, 'thumbnail', 0);\n\n // Update thumbnailAssetId\n this.thumbnailAssetId = image.id ?? null;\n await this.save();\n }\n\n /**\n * Generate a thumbnail for this content using the specified strategy\n *\n * @param options - Thumbnail generation options including strategy\n * @returns Promise resolving to the generated Image\n *\n * @example Headline card thumbnail\n * ```typescript\n * const thumbnail = await content.generateThumbnail({\n * strategy: 'headline-card',\n * brandColor: '#1a56db',\n * logoUrl: 'https://example.com/logo.png'\n * });\n * ```\n *\n * @example Static map thumbnail (requires metadata.latitude/longitude)\n * ```typescript\n * const thumbnail = await content.generateThumbnail({\n * strategy: 'static-map',\n * mapProvider: 'mapbox'\n * });\n * ```\n *\n * @example AI-generated thumbnail\n * ```typescript\n * const thumbnail = await content.generateThumbnail({\n * strategy: 'ai-generate'\n * });\n * ```\n */\n async generateThumbnail(options: ThumbnailOptions): Promise<Image> {\n const generator = new ThumbnailGenerator(this, this.options);\n const image = await generator.generate(options);\n await this.setThumbnail(image);\n return image;\n }\n}\n","import { definePrompt } from '@happyvertical/smrt-prompts';\n\nexport const contentEditorSessionPrompt = definePrompt({\n key: 'content.chat.contentEditorSession',\n template:\n 'You are an AI assistant collaborating with the user to edit and improve a specific piece of content.',\n ai: {\n temperature: 0.7,\n maxTokens: 2000,\n },\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport const contentEditorInteractionPrompt = definePrompt({\n key: 'content.chat.contentEditorInteraction',\n template: `CURRENT CONTENT FORM STATE:\n- title: {title}\n- description: {description}\n- type: {type}\n- status: {status}\n- state: {state}\n- body:\n{body}\n{references}\n\nINSTRUCTIONS FOR CO-AUTHORING:\nYou are a content co-author. You can update any of the above fields.\n\nWhen you want to make changes to the content, output a JSON code block with a top-level \"fields\" object containing only the fields you want to change.\n\nValid field names: title, description, type, status, state, body.\nValid status values: draft, published, archived.\nValid state values: active, highlighted, deprecated.\nValid type values: article, document, mirror.\n\nRULES:\n- Only include fields you are actually changing.\n- If the user asks to write or rewrite content, update the \"body\" field.\n- If the user asks to change the title, update the \"title\" field.\n- You can change multiple fields at once.\n- Always respond conversationally before the JSON block.\n- The JSON block will be automatically applied to the editor.`,\n ai: {\n temperature: 0.7,\n maxTokens: 2000,\n },\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n","import type { ResolvedPrompt } from '@happyvertical/smrt-prompts';\n\ntype SessionContext = Record<string, unknown>;\n\nexport interface ContentChatAISelection {\n profile: string | null;\n provider: string | null;\n model: string | null;\n allowedModels: string[] | null;\n temperature: number | null;\n maxTokens: number | null;\n}\n\nexport interface ContentEditorInteractionInput {\n fields?: Record<string, unknown> | null;\n currentEditorState?: unknown;\n references?: string | null;\n}\n\nconst CONTENT_PROMPT_DELIMITER = 'SMRT_CONTENT_FIELD';\n\nfunction readString(\n context: SessionContext,\n key: keyof ContentChatAISelection,\n): string | null {\n const value = context[key];\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nfunction readNumber(\n context: SessionContext,\n key: keyof ContentChatAISelection,\n): number | null {\n const value = context[key];\n return typeof value === 'number' ? value : null;\n}\n\nexport function inferProviderFromModel(model: string): string {\n const normalized = model.toLowerCase();\n const providerPrefix = normalized.match(/^([a-z0-9_-]+)\\//)?.[1];\n if (providerPrefix === 'openrouter') {\n return 'openrouter';\n }\n if (providerPrefix === 'anthropic') {\n return 'anthropic';\n }\n if (providerPrefix === 'gemini' || providerPrefix === 'google') {\n return 'gemini';\n }\n if (providerPrefix === 'openai') {\n return 'openai';\n }\n\n const modelTokens = normalized.split(/[^a-z0-9]+/).filter(Boolean);\n if (\n modelTokens.includes('claude') ||\n modelTokens.includes('sonnet') ||\n modelTokens.includes('haiku') ||\n modelTokens.includes('opus')\n ) {\n return 'anthropic';\n }\n\n if (modelTokens.includes('gemini')) {\n return 'gemini';\n }\n\n return 'openai';\n}\n\nexport function getContentChatAISelection(\n context: SessionContext,\n): ContentChatAISelection {\n const allowedModels = context.allowedModels;\n return {\n profile: readString(context, 'profile'),\n provider: readString(context, 'provider'),\n model: readString(context, 'model'),\n allowedModels:\n Array.isArray(allowedModels) &&\n allowedModels.every((value) => typeof value === 'string')\n ? allowedModels\n : null,\n temperature: readNumber(context, 'temperature'),\n maxTokens: readNumber(context, 'maxTokens'),\n };\n}\n\nexport function resolveContentChatModelSelection(\n context: SessionContext,\n requestedModel: string | null | undefined,\n fallbackModel: string,\n): { model: string; provider: string } {\n const stored = getContentChatAISelection(context);\n const model = requestedModel || stored.model || fallbackModel;\n const provider =\n stored.provider && stored.model && model === stored.model\n ? stored.provider\n : inferProviderFromModel(model);\n\n return { model, provider };\n}\n\nexport function buildContentChatModelUpdates(\n context: SessionContext,\n requestedModel: string | null | undefined,\n fallbackModel: string,\n): Record<string, string | null> {\n const stored = getContentChatAISelection(context);\n const { model, provider } = resolveContentChatModelSelection(\n context,\n requestedModel,\n fallbackModel,\n );\n\n return {\n model,\n provider,\n ...(stored.profile && stored.model && model !== stored.model\n ? { profile: null }\n : {}),\n };\n}\n\nexport function buildContentEditorSessionContext(\n contentId: string,\n prompt: ResolvedPrompt,\n): SessionContext {\n return {\n contentId,\n ...(prompt.ai.profile ? { profile: prompt.ai.profile } : {}),\n ...(prompt.ai.provider ? { provider: prompt.ai.provider } : {}),\n ...(prompt.ai.model ? { model: prompt.ai.model } : {}),\n ...(prompt.ai.allowedModels\n ? { allowedModels: prompt.ai.allowedModels }\n : {}),\n ...(prompt.ai.temperature !== undefined\n ? { temperature: prompt.ai.temperature }\n : {}),\n ...(prompt.ai.maxTokens !== undefined\n ? { maxTokens: prompt.ai.maxTokens }\n : {}),\n };\n}\n\nfunction displayValue(value: unknown, fallback = '(empty)'): string {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : fallback;\n }\n\n if (value === undefined || value === null) {\n return fallback;\n }\n\n return String(value);\n}\n\nfunction delimitPromptValue(name: string, value: string): string {\n // Strip any forged SMRT_ delimiter markers from the user value so it can't\n // close the wrapper early or open a fake field. The previous `[^>]*` stopped\n // at the first `>` (so `<<<SMRT_X>injected>>>` slipped through) and was\n // case-sensitive (so `<<<smrt_...>>>` slipped through). `[\\s\\S]*?` is\n // non-greedy and `>`-tolerant, and the `i` flag closes the case bypass\n // (#1387 minor).\n const sanitizedValue = value.replace(/<<<\\s*(?:END_)?SMRT_[\\s\\S]*?>>>/gi, '');\n return `<<<${CONTENT_PROMPT_DELIMITER} name=${name}>>>\\n${sanitizedValue}\\n<<<END_${CONTENT_PROMPT_DELIMITER}>>>`;\n}\n\nexport function buildContentEditorInteractionVariables(\n input: ContentEditorInteractionInput,\n): Record<string, string> {\n const fields = input.fields ?? {};\n\n return {\n title: delimitPromptValue('title', displayValue(fields.title)),\n description: delimitPromptValue(\n 'description',\n displayValue(fields.description),\n ),\n type: displayValue(fields.type, 'article'),\n status: displayValue(fields.status, 'draft'),\n state: displayValue(fields.state, 'active'),\n body: delimitPromptValue(\n 'body',\n displayValue(fields.body ?? input.currentEditorState),\n ),\n references: input.references ?? '',\n };\n}\n","import { type AIClientOptions, getAI } from '@happyvertical/ai';\nimport { ChatService } from '@happyvertical/smrt-chat';\nimport { sendAgentReply } from '@happyvertical/smrt-chat/internal/agent-runtime';\nimport {\n type ResolvedPrompt,\n resolvePrompt,\n} from '@happyvertical/smrt-prompts';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { sanitizeHtml, stripHtml } from './body-format';\nimport {\n contentEditorInteractionPrompt,\n contentEditorSessionPrompt,\n} from './content-chat-prompts';\nimport {\n buildContentChatModelUpdates,\n buildContentEditorInteractionVariables,\n buildContentEditorSessionContext,\n getContentChatAISelection,\n resolveContentChatModelSelection,\n} from './content-chat-session';\n\nconst CONTENT_EDITOR_AGENT_ID = 'content_editor';\nconst REFERENCE_TITLE_MAX_LENGTH = 200;\nconst REFERENCE_BODY_MAX_LENGTH = 2000;\nconst MAX_REFERENCE_IDS = 20;\n\nexport type ContentChatAIConfig = AIClientOptions & {\n apiKey?: string;\n allowedModels?: string[];\n model?: string;\n defaultModel?: string;\n provider?: string;\n type?: string;\n};\n\nexport interface ContentChatCollectionLike {\n db: DatabaseInterface;\n // Accepts either a raw id or a bound `{ id, tenantId }` filter so the content\n // lookup can be tenant-bound at the query level (S5 #1392) rather than doing a\n // cross-tenant `get(id)` followed only by a post-filter. A real SmrtCollection\n // satisfies this — its `get` takes `string | Record<string, any>`.\n get(filter: string | Record<string, unknown>): Promise<unknown>;\n}\n\nexport interface ContentChatSessionInput {\n db: DatabaseInterface;\n contents?: ContentChatCollectionLike;\n tenantId?: string | null;\n profileId?: string | null;\n contentId: string;\n resolvePromptFn?: typeof resolvePrompt;\n}\n\nexport interface CreateContentChatThreadInput extends ContentChatSessionInput {\n title: string;\n sessionId: string;\n model?: string | null;\n initializeChatCollections?: () => Promise<void>;\n}\n\nexport interface ListContentChatThreadMessagesInput {\n db: DatabaseInterface;\n contents?: ContentChatCollectionLike;\n tenantId?: string | null;\n profileId?: string | null;\n contentId: string;\n threadId: string;\n}\n\nexport interface SendContentChatThreadMessageInput {\n contents: ContentChatCollectionLike;\n tenantId?: string | null;\n profileId?: string | null;\n contentId: string;\n threadId: string;\n content: string;\n sessionId: string;\n model?: string | null;\n currentEditorState?: unknown;\n referenceIds?: unknown;\n formFields?: Record<string, unknown> | null;\n aiConfig?: ContentChatAIConfig | null;\n env?: Record<string, string | undefined>;\n resolvePromptFn?: typeof resolvePrompt;\n getAIClient?: typeof getAI;\n resolveAI?: (\n input: ResolveContentChatAIInput,\n ) => Promise<ResolvedContentChatAI | null> | ResolvedContentChatAI | null;\n}\n\nexport interface ResolveContentChatAIInput {\n aiConfig: ContentChatAIConfig;\n env: Record<string, string | undefined>;\n sessionContext: Record<string, unknown>;\n requestedModel?: string | null;\n interactionPrompt: ResolvedPrompt;\n}\n\nexport interface ResolvedContentChatAI {\n clientOptions: ContentChatAIConfig;\n model: string;\n provider?: string | null;\n profile?: string | null;\n temperature?: number | null;\n maxTokens?: number | null;\n}\n\nfunction resolveTenantScope(value: string | null | undefined): string | null {\n return asNonEmptyString(value);\n}\n\nfunction resolveChatTenantId(value: string | null | undefined): string {\n return resolveTenantScope(value) ?? 'global';\n}\n\nfunction resolveProfileId(value: string | null | undefined): string {\n return value || 'system';\n}\n\nfunction asNonEmptyString(value: unknown): string | null {\n return typeof value === 'string' && value.trim().length > 0\n ? value.trim()\n : null;\n}\n\nfunction parseJsonObject(value: unknown): Record<string, unknown> {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n if (typeof value !== 'string' || value.length === 0) {\n return {};\n }\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction getSessionContext(session: unknown): Record<string, unknown> {\n const candidate = session as {\n getSessionContext?: () => Record<string, unknown>;\n sessionContext?: unknown;\n };\n return typeof candidate.getSessionContext === 'function'\n ? candidate.getSessionContext()\n : parseJsonObject(candidate.sessionContext);\n}\n\nfunction isActiveSession(session: unknown): boolean {\n const candidate = session as { isActive?: () => boolean; status?: string };\n return typeof candidate?.isActive === 'function'\n ? candidate.isActive()\n : candidate?.status === 'active';\n}\n\nfunction isSchemaMissingError(error: unknown): boolean {\n const message = String((error as { message?: unknown })?.message ?? '');\n return (\n (error as { code?: string })?.code === 'DB_SCHEMA_MISSING' ||\n /^no such table:\\s+(?:agent_sessions|chat_(?:rooms|threads|messages|participants|reactions))\\b/i.test(\n message,\n ) ||\n /relation [\"']?(?:agent_sessions|chat_(?:rooms|threads|messages|participants|reactions))[\"']? does not exist/i.test(\n message,\n )\n );\n}\n\nexport function contentChatSessionMatchesContent(\n session: unknown,\n contentId: string,\n): boolean {\n const context = getSessionContext(session);\n return asNonEmptyString(context.contentId) === contentId;\n}\n\n/**\n * Stable agent-session key scoping a content-editor session to its content\n * (S5 #1392).\n *\n * `createAgentSession` reuses ANY active session for the same\n * agent/profile/tenant unless a `sessionKey` is supplied. The content-editor\n * agent (`content_editor`) opens a distinct conversation per content, so a\n * keyless create would reuse — and the handler would then REWRITE — a session\n * created for a different content, returning the other content's room/threads.\n * Keying the session on the content id makes a new content always create a\n * distinct session/room.\n */\nexport function contentChatSessionKey(contentId: string): string {\n return `content:${contentId}`;\n}\n\n/**\n * Tenant-bound content lookup (S5 #1392).\n *\n * Binds `tenantId` (including the `null` \"global\" scope) into the lookup itself\n * so a content row from another tenant can never be RESOLVED first and only\n * rejected after the fact. The `referenceBelongsToTenant` post-filter at every\n * call site remains as defense-in-depth.\n */\nfunction getContentForTenant(\n contents: ContentChatCollectionLike,\n tenantScope: string | null,\n id: string,\n): Promise<unknown> {\n return contents.get({ id, tenantId: tenantScope });\n}\n\nasync function assertContentExistsForTenant(\n contents: ContentChatCollectionLike | undefined,\n tenantScope: string | null,\n contentId: string,\n): Promise<void> {\n if (!contents) {\n return;\n }\n\n const content = await getContentForTenant(contents, tenantScope, contentId);\n if (!content || !referenceBelongsToTenant(content, tenantScope)) {\n throw new Error('Content not found');\n }\n}\n\nexport function contentChatSessionIsAuthorized(\n session: unknown,\n input: {\n profileId: string;\n contentId: string;\n },\n): boolean {\n const candidate = session as {\n agentId?: string;\n participantProfileId?: string;\n };\n return (\n Boolean(session) &&\n isActiveSession(session) &&\n candidate.agentId === CONTENT_EDITOR_AGENT_ID &&\n candidate.participantProfileId === input.profileId &&\n contentChatSessionMatchesContent(session, input.contentId)\n );\n}\n\nexport function contentChatMessageToJSON(\n message: unknown,\n): Record<string, unknown> {\n return typeof (message as { toJSON?: () => unknown })?.toJSON === 'function'\n ? ((message as { toJSON: () => unknown }).toJSON() as Record<\n string,\n unknown\n >)\n : (message as Record<string, unknown>);\n}\n\nexport function serializeContentChatMessageForUI(\n message: unknown,\n): Record<string, unknown> {\n const json = contentChatMessageToJSON(message);\n return {\n ...json,\n senderName: json.role === 'user' ? 'You' : 'AI Assistant',\n senderAvatarUrl: json.senderAvatarUrl || '',\n };\n}\n\nexport async function getOrCreateContentEditorChatSession(\n input: ContentChatSessionInput,\n) {\n const tenantScope = resolveTenantScope(input.tenantId);\n const tenantId = resolveChatTenantId(input.tenantId);\n const profileId = resolveProfileId(input.profileId);\n const chatService = await ChatService.create({ tenantId, db: input.db });\n await chatService.initialize();\n await assertContentExistsForTenant(\n input.contents,\n tenantScope,\n input.contentId,\n );\n\n let activeSession = null;\n try {\n // Tenant-bound session list via the ChatService facade (S5 #1392): the raw\n // `agentSessions.list({ where })` reach-in was tenant-UNBOUND and could\n // surface a session from another tenant before authorization.\n const agentSessions = await chatService.findActiveAgentSessions({\n tenantId,\n agentId: CONTENT_EDITOR_AGENT_ID,\n participantProfileId: profileId,\n });\n activeSession =\n agentSessions.find((session: unknown) =>\n contentChatSessionMatchesContent(session, input.contentId),\n ) ?? null;\n } catch (error) {\n if (!isSchemaMissingError(error)) {\n throw error;\n }\n // Chat tables may not exist yet. createAgentSession will surface the\n // concrete schema error if the runtime cannot create/use them.\n }\n\n if (!activeSession) {\n const resolvedPrompt = await (input.resolvePromptFn ?? resolvePrompt)(\n contentEditorSessionPrompt.key,\n {\n db: input.db,\n tenantId,\n },\n );\n // Scope the session to this content (S5 #1392) so a new contentId can never\n // reuse — and have its context rewritten over — a session/room created for a\n // different content. The handler-side `contentChatSessionMatchesContent`\n // filter above already narrows reads; this binds the create/reuse path too.\n const { session } = await chatService.createAgentSession({\n tenantId,\n agentId: CONTENT_EDITOR_AGENT_ID,\n actorProfileId: profileId,\n systemPrompt: resolvedPrompt.text,\n sessionKey: contentChatSessionKey(input.contentId),\n });\n await session.updateSessionContext(\n buildContentEditorSessionContext(input.contentId, resolvedPrompt),\n );\n activeSession = session;\n }\n\n let threads: unknown[] = [];\n try {\n const chatRoomId = (activeSession as { chatRoomId?: string | null })\n .chatRoomId;\n if (!chatRoomId) {\n throw new Error('Agent session is missing a chat room');\n }\n // Membership- and tenant-gated thread list via the facade (S5 #1392); the\n // editor (profileId) is the room owner, so the membership check passes.\n threads = await chatService.listRoomThreads({\n roomId: chatRoomId,\n actorProfileId: profileId,\n tenantId,\n });\n } catch {\n // Threads table may not exist yet.\n }\n\n return {\n chatService,\n session: activeSession,\n threads,\n };\n}\n\nexport async function createContentEditorChatThread(\n input: CreateContentChatThreadInput,\n) {\n await input.initializeChatCollections?.();\n\n const tenantScope = resolveTenantScope(input.tenantId);\n const tenantId = resolveChatTenantId(input.tenantId);\n const profileId = resolveProfileId(input.profileId);\n const chatService = await ChatService.create({ tenantId, db: input.db });\n await assertContentExistsForTenant(\n input.contents,\n tenantScope,\n input.contentId,\n );\n // Tenant-bound session lookup via the facade (S5 #1392): the raw\n // `agentSessions.get(id)` was tenant-UNBOUND and could select a session from\n // another tenant before the ownership/context check below.\n const session = await chatService.getAgentSession({\n agentSessionId: input.sessionId,\n tenantId,\n });\n\n if (\n !contentChatSessionIsAuthorized(session, {\n profileId,\n contentId: input.contentId,\n })\n ) {\n throw new Error('Active session not found');\n }\n\n const chatRoomId = (session as { chatRoomId?: string | null }).chatRoomId;\n if (!chatRoomId) {\n throw new Error('Active session is missing a chat room');\n }\n\n let updates: Record<string, string | null> | null = null;\n if (input.model) {\n const ctx = getSessionContext(session);\n const allowedModels = Array.isArray(ctx.allowedModels)\n ? ctx.allowedModels\n : [];\n if (allowedModels.length > 0 && !allowedModels.includes(input.model)) {\n throw new Error('AI model is not allowed for content chat');\n }\n updates = buildContentChatModelUpdates(ctx, input.model, input.model);\n }\n\n // Member-checked thread creation via the facade (S5 #1392): the editor owns\n // the agent room, so the membership check passes. Replaces the raw\n // `threads.create(...)` that skipped the facade entirely.\n const thread = await chatService.startThread({\n tenantId,\n roomId: chatRoomId,\n actorProfileId: profileId,\n title: input.title,\n });\n\n if (updates) {\n const ctx = getSessionContext(session);\n if (Object.entries(updates).some(([key, value]) => ctx[key] !== value)) {\n await (\n session as { updateSessionContext: (value: unknown) => Promise<void> }\n ).updateSessionContext(updates);\n }\n }\n\n return { chatService, session, thread };\n}\n\nexport async function listContentEditorChatThreadMessages(\n input: ListContentChatThreadMessagesInput,\n) {\n const tenantScope = resolveTenantScope(input.tenantId);\n const tenantId = resolveChatTenantId(input.tenantId);\n const profileId = resolveProfileId(input.profileId);\n const chatService = await ChatService.create({ tenantId, db: input.db });\n await assertContentExistsForTenant(\n input.contents,\n tenantScope,\n input.contentId,\n );\n // Tenant-bound thread + session lookups via the facade (S5 #1392): the raw\n // `threads.get(id)` / `agentSessions.list({ where })` reach-ins were\n // tenant-UNBOUND and could select cross-tenant chat state before authZ.\n const thread = await chatService.getThread({\n threadId: input.threadId,\n tenantId,\n });\n if (!thread) {\n throw new Error('Thread not found');\n }\n\n const roomId = (thread as { roomId?: string }).roomId;\n const activeSessions = await chatService.findActiveAgentSessions({\n tenantId,\n agentId: CONTENT_EDITOR_AGENT_ID,\n participantProfileId: profileId,\n });\n const authorizedSession = activeSessions.find(\n (session: unknown) =>\n (session as { chatRoomId?: string }).chatRoomId === roomId &&\n contentChatSessionMatchesContent(session, input.contentId),\n );\n if (!authorizedSession) {\n throw new Error('Thread not found');\n }\n\n // Membership- + tenant-gated thread message read via the facade (S5 #1392);\n // the editor owns the room, so the membership check passes. Returned\n // chronological (oldest-first) by the facade.\n const messages = await chatService.getThreadMessages({\n threadId: input.threadId,\n actorProfileId: profileId,\n tenantId,\n limit: 100,\n });\n\n return {\n chatService,\n thread,\n messages,\n };\n}\n\nfunction getEnvApiKey(\n provider: string,\n env: Record<string, string | undefined>,\n): string | undefined {\n return provider === 'anthropic'\n ? env.ANTHROPIC_API_KEY\n : provider === 'gemini'\n ? env.GEMINI_API_KEY\n : env.OPENAI_API_KEY;\n}\n\nfunction defaultEnv(): Record<string, string | undefined> {\n return typeof process !== 'undefined' ? process.env : {};\n}\n\nexport function resolveDefaultContentChatAI(\n input: ResolveContentChatAIInput,\n): ResolvedContentChatAI {\n const storedSelection = getContentChatAISelection(input.sessionContext);\n const fallbackModel =\n storedSelection.model ??\n input.aiConfig.model ??\n input.aiConfig.defaultModel ??\n input.requestedModel ??\n null;\n if (!fallbackModel) {\n throw new Error('AI model is not configured for content chat');\n }\n\n const { model, provider } = resolveContentChatModelSelection(\n input.sessionContext,\n input.requestedModel,\n fallbackModel,\n );\n const allowedModels =\n input.aiConfig.allowedModels ??\n input.interactionPrompt.ai.allowedModels ??\n storedSelection.allowedModels;\n if (\n Array.isArray(allowedModels) &&\n allowedModels.length > 0 &&\n !allowedModels.includes(model)\n ) {\n throw new Error('AI model is not allowed for content chat');\n }\n const apiKey = input.aiConfig.apiKey || getEnvApiKey(provider, input.env);\n\n return {\n clientOptions: {\n ...input.aiConfig,\n provider,\n apiKey,\n model,\n },\n model,\n provider,\n temperature: storedSelection.temperature ?? 0.7,\n maxTokens: storedSelection.maxTokens ?? 2000,\n };\n}\n\nfunction sanitizeReferenceText(value: unknown, maxLength: number): string {\n const text = stripHtml(sanitizeHtml(String(value ?? '')))\n .replace(/<<<(?:END_)?SMRT_[^>]*>>>/g, '')\n .replace(/<{2,}/g, '< <')\n .replace(/>{2,}/g, '> >')\n .replace(/\\s+/g, ' ')\n .trim();\n\n return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text;\n}\n\nfunction sanitizeReferenceId(value: string): string {\n return value.replace(/[^\\w:.-]/g, '_').slice(0, 128);\n}\n\nfunction getReferenceTenantId(value: unknown): string | null {\n const candidate = value as {\n tenantId?: unknown;\n tenant_id?: unknown;\n } | null;\n return (\n asNonEmptyString(candidate?.tenantId) ??\n asNonEmptyString(candidate?.tenant_id)\n );\n}\n\nfunction referenceBelongsToTenant(\n ref: unknown,\n tenantScope: string | null,\n): boolean {\n const refTenantId = getReferenceTenantId(ref);\n if (tenantScope === null) {\n return !refTenantId;\n }\n return refTenantId === tenantScope;\n}\n\nasync function buildReferenceContext(\n contents: ContentChatCollectionLike,\n tenantScope: string | null,\n referenceIds: unknown,\n): Promise<string> {\n if (!Array.isArray(referenceIds) || referenceIds.length === 0) {\n return '';\n }\n\n const refTexts: string[] = [];\n const ids = [\n ...new Set(\n referenceIds\n .map(asNonEmptyString)\n .filter((id): id is string => Boolean(id)),\n ),\n ].slice(0, MAX_REFERENCE_IDS);\n for (const id of ids) {\n // Tenant-bound lookup (S5 #1392): bind tenantId into the query rather than\n // a cross-tenant get(id) + post-filter. The post-filter below stays as\n // defense-in-depth.\n const ref = (await getContentForTenant(contents, tenantScope, id)) as {\n title?: string;\n body?: string;\n tenantId?: string;\n tenant_id?: string;\n } | null;\n if (ref && referenceBelongsToTenant(ref, tenantScope)) {\n const safeTitle = sanitizeReferenceText(\n ref.title,\n REFERENCE_TITLE_MAX_LENGTH,\n );\n const safeBody = sanitizeReferenceText(\n ref.body,\n REFERENCE_BODY_MAX_LENGTH,\n );\n refTexts.push(\n `<<<SMRT_REFERENCE id=${sanitizeReferenceId(id)}>>>\\nTitle: ${safeTitle}\\nBody: ${safeBody}\\n<<<END_SMRT_REFERENCE>>>`,\n );\n }\n }\n\n return refTexts.length > 0\n ? `\\n\\nATTACHED REFERENCE MATERIALS:\\n${refTexts.join('\\n\\n')}\\n`\n : '';\n}\n\nexport async function sendContentEditorChatThreadMessage(\n input: SendContentChatThreadMessageInput,\n) {\n const tenantScope = resolveTenantScope(input.tenantId);\n const tenantId = resolveChatTenantId(input.tenantId);\n const profileId = resolveProfileId(input.profileId);\n const chatService = await ChatService.create({\n tenantId,\n db: input.contents.db,\n });\n await assertContentExistsForTenant(\n input.contents,\n tenantScope,\n input.contentId,\n );\n\n // Tenant-bound session + thread lookups via the facade (S5 #1392): the raw\n // `agentSessions.get(id)` / `threads.get(id)` reach-ins were tenant-UNBOUND\n // and could select cross-tenant chat state before the authorization below.\n const session = await chatService.getAgentSession({\n agentSessionId: input.sessionId,\n tenantId,\n });\n if (\n !contentChatSessionIsAuthorized(session, {\n profileId,\n contentId: input.contentId,\n })\n ) {\n throw new Error('Active session not found');\n }\n\n const thread = await chatService.getThread({\n threadId: input.threadId,\n tenantId,\n });\n if (\n !thread ||\n (thread as { roomId?: string }).roomId !==\n (session as { chatRoomId?: string | null }).chatRoomId\n ) {\n throw new Error('Thread not found');\n }\n\n const chatRoomId = (session as { chatRoomId?: string | null }).chatRoomId;\n if (!chatRoomId) {\n throw new Error('Active session is missing a chat room');\n }\n\n const references = await buildReferenceContext(\n input.contents,\n tenantScope,\n input.referenceIds,\n );\n const interactionPrompt = await (input.resolvePromptFn ?? resolvePrompt)(\n contentEditorInteractionPrompt.key,\n {\n db: input.contents.db,\n tenantId,\n variables: buildContentEditorInteractionVariables({\n fields: input.formFields,\n currentEditorState: input.currentEditorState,\n references,\n }),\n },\n );\n\n const sessionContext = getSessionContext(session);\n const aiConfig = input.aiConfig ?? {};\n const resolvedAI =\n (await input.resolveAI?.({\n aiConfig,\n env: input.env ?? defaultEnv(),\n sessionContext,\n requestedModel: input.model,\n interactionPrompt,\n })) ??\n resolveDefaultContentChatAI({\n aiConfig,\n env: input.env ?? defaultEnv(),\n sessionContext,\n requestedModel: input.model,\n interactionPrompt,\n });\n\n const userMessage = await chatService.sendMessage({\n tenantId,\n roomId: chatRoomId,\n threadId: (thread as { id: string }).id,\n actorProfileId: profileId,\n content: input.content,\n agentSessionId: (session as { id: string }).id,\n });\n\n // Membership- + tenant-gated thread history via the facade (S5 #1392); the\n // editor owns the room. Returned chronological (oldest-first) by the facade.\n const history = await chatService.getThreadMessages({\n threadId: (thread as { id: string }).id,\n actorProfileId: profileId,\n tenantId,\n limit: 10,\n });\n\n const conversation = history.map((message: unknown) => {\n const json = contentChatMessageToJSON(message);\n return {\n role: json.role as 'user' | 'assistant' | 'system',\n content: String(json.content ?? ''),\n };\n });\n\n const ai = await (input.getAIClient ?? getAI)(resolvedAI.clientOptions);\n\n const fullSystemPrompt =\n typeof (session as { systemPrompt?: unknown }).systemPrompt === 'string'\n ? (session as { systemPrompt: string }).systemPrompt\n : '';\n conversation.unshift({\n role: 'system',\n content: `${fullSystemPrompt}\\n\\n${interactionPrompt.text}`,\n });\n\n const response = await ai.chat(conversation, {\n model: resolvedAI.model,\n temperature: resolvedAI.temperature ?? 0.7,\n maxTokens: resolvedAI.maxTokens ?? 2000,\n });\n\n // Author the assistant reply AS the session's agent via the trusted internal\n // agent-runtime surface — never via the public sendMessage (which would force\n // the message to the caller's identity with role 'user') (S5 #1392).\n const agentMessage = await sendAgentReply(chatService, {\n tenantId,\n agentSessionId: (session as { id: string }).id,\n threadId: (thread as { id: string }).id,\n content: response.content,\n kind: 'assistant',\n });\n\n const updates = buildContentChatModelUpdates(\n sessionContext,\n input.model,\n resolvedAI.model,\n );\n updates.model = resolvedAI.model;\n if (resolvedAI.provider !== undefined) {\n updates.provider = resolvedAI.provider;\n }\n if (resolvedAI.profile !== undefined) {\n updates.profile = resolvedAI.profile;\n }\n if (\n Object.entries(updates).some(\n ([key, value]) => sessionContext[key] !== value,\n )\n ) {\n await (\n session as { updateSessionContext: (value: unknown) => Promise<void> }\n ).updateSessionContext(updates);\n }\n\n return {\n chatService,\n session,\n thread,\n userMessage,\n agentMessage,\n };\n}\n","import {\n getCurrentTenant,\n isSuperAdminBypass,\n isSystemContext,\n isTenancyEnabled,\n} from '@happyvertical/smrt-tenancy';\nimport type { DatabaseInterface } from '@happyvertical/sql';\n\nexport type ContentContributionChannel = 'web' | 'email';\n\nexport type ContentContributionStatus =\n | 'submitted'\n | 'quarantined'\n | 'needs_changes'\n | 'approved'\n | 'promoted'\n | 'rejected'\n | 'withdrawn';\n\nexport type ContentContributionIntakeDecision =\n | 'accepted'\n | 'quarantined'\n | 'rejected';\n\nexport type ContentContributorTrustLevel = 'standard' | 'trusted' | 'blocked';\n\nexport interface ContentContributionIntakeRules {\n maxFiles?: number | null;\n maxTotalBytes?: number | null;\n allowedMimePatterns?: string[];\n blockedMimePatterns?: string[];\n quarantineMimePatterns?: string[];\n blockedTextPatterns?: string[];\n quarantineTextPatterns?: string[];\n trustedOnly?: boolean;\n}\n\nexport interface ContentContributionPromotionMapping {\n targetContentType?: string | null;\n targetContentVariant?: string | null;\n targetContentStatus?: 'draft' | 'review';\n autoPromoteTrusted?: boolean;\n createAssets?: boolean;\n assetRelationship?: string;\n}\n\nexport interface ContentContributionTypeDefinition {\n key: string;\n label: string;\n enabled?: boolean;\n allowedChannels?: ContentContributionChannel[];\n allowText?: boolean;\n allowFiles?: boolean;\n allowEmptyText?: boolean;\n intakeRules?: ContentContributionIntakeRules;\n promotion?: ContentContributionPromotionMapping;\n metadata?: Record<string, unknown>;\n}\n\nexport interface PersistedContentContributionTypeRecord\n extends ContentContributionTypeDefinition {\n id?: string;\n tenantId?: string | null;\n createdAt?: string | null;\n updatedAt?: string | null;\n}\n\nexport interface ContentContributionConfig {\n types: ContentContributionTypeDefinition[];\n}\n\nexport interface ResolvedContentContributionType\n extends ContentContributionTypeDefinition {}\n\nexport interface ContentContributionTypeConfigState {\n effective: ContentContributionTypeDefinition[];\n persisted: PersistedContentContributionTypeRecord[];\n}\n\nexport interface ContentContributionConfigReadOptions {\n db?: DatabaseInterface | null;\n tenantId?: string | null;\n}\n\nexport interface EvaluateContributionIntakeOptions {\n contributionType: ContentContributionTypeDefinition;\n trustLevel?: ContentContributorTrustLevel;\n channel: ContentContributionChannel;\n title?: string | null;\n description?: string | null;\n body?: string | null;\n attachments?: Array<{\n mimeType?: string | null;\n size?: number | null;\n }>;\n}\n\nexport interface EvaluateContributionIntakeResult {\n decision: ContentContributionIntakeDecision;\n reasons: string[];\n}\n\nconst DEFAULT_CONTENT_CONTRIBUTION_CONFIG: ContentContributionConfig = {\n types: [],\n};\n\nlet contributionConfig = cloneContributionConfig(\n DEFAULT_CONTENT_CONTRIBUTION_CONFIG,\n);\n\nfunction cloneJSONArray<T>(items: T[] | null | undefined): T[] {\n return Array.isArray(items) ? items.map((item) => structuredClone(item)) : [];\n}\n\nfunction safeParseJSONObject(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction safeParseJSONArray<T>(raw: unknown): T[] {\n if (!raw) {\n return [];\n }\n\n if (Array.isArray(raw)) {\n return raw.map((item) => structuredClone(item)) as T[];\n }\n\n if (typeof raw !== 'string') {\n return [];\n }\n\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? (parsed as T[]) : [];\n } catch {\n return [];\n }\n}\n\nfunction cloneIntakeRules(\n rules: ContentContributionIntakeRules | null | undefined,\n): ContentContributionIntakeRules {\n return {\n maxFiles:\n typeof rules?.maxFiles === 'number' ? Math.max(0, rules.maxFiles) : null,\n maxTotalBytes:\n typeof rules?.maxTotalBytes === 'number'\n ? Math.max(0, rules.maxTotalBytes)\n : null,\n allowedMimePatterns: cloneJSONArray(rules?.allowedMimePatterns),\n blockedMimePatterns: cloneJSONArray(rules?.blockedMimePatterns),\n quarantineMimePatterns: cloneJSONArray(rules?.quarantineMimePatterns),\n blockedTextPatterns: cloneJSONArray(rules?.blockedTextPatterns),\n quarantineTextPatterns: cloneJSONArray(rules?.quarantineTextPatterns),\n trustedOnly: rules?.trustedOnly === true,\n };\n}\n\nfunction clonePromotionMapping(\n promotion: ContentContributionPromotionMapping | null | undefined,\n): ContentContributionPromotionMapping {\n return {\n targetContentType: promotion?.targetContentType || null,\n targetContentVariant: promotion?.targetContentVariant || null,\n targetContentStatus:\n promotion?.targetContentStatus === 'review' ? 'review' : 'draft',\n autoPromoteTrusted: promotion?.autoPromoteTrusted === true,\n createAssets: promotion?.createAssets !== false,\n assetRelationship: promotion?.assetRelationship || 'attachment',\n };\n}\n\nfunction cloneTypeDefinition(\n type: ContentContributionTypeDefinition,\n): ContentContributionTypeDefinition {\n return {\n key: type.key,\n label: type.label,\n enabled: type.enabled !== false,\n allowedChannels:\n type.allowedChannels && type.allowedChannels.length > 0\n ? [...type.allowedChannels]\n : ['web'],\n allowText: type.allowText !== false,\n allowFiles: type.allowFiles === true,\n allowEmptyText: type.allowEmptyText === true,\n intakeRules: cloneIntakeRules(type.intakeRules),\n promotion: clonePromotionMapping(type.promotion),\n metadata: safeParseJSONObject(type.metadata),\n };\n}\n\nfunction clonePersistedTypeRecord(\n type: PersistedContentContributionTypeRecord,\n): PersistedContentContributionTypeRecord {\n return {\n ...cloneTypeDefinition(type),\n id: type.id,\n tenantId: type.tenantId ?? null,\n createdAt: type.createdAt ?? null,\n updatedAt: type.updatedAt ?? null,\n };\n}\n\nfunction cloneContributionConfig(\n config: ContentContributionConfig,\n): ContentContributionConfig {\n return {\n types: config.types.map(cloneTypeDefinition),\n };\n}\n\nfunction mergeByKey<T extends { key: string }>(\n baseItems: T[],\n overrides: T[],\n normalize: (item: T) => T,\n): T[] {\n const merged = new Map<string, T>();\n\n for (const item of baseItems.map(normalize)) {\n merged.set(item.key, item);\n }\n\n for (const item of overrides.map(normalize)) {\n merged.set(item.key, item);\n }\n\n return [...merged.values()];\n}\n\nfunction isMissingContributionTypesTableError(error: unknown): boolean {\n const message = String(\n (error as Error)?.message || error || '',\n ).toLowerCase();\n return (\n message.includes('content_contribution_types') &&\n (message.includes('no such table') ||\n message.includes('does not exist') ||\n message.includes('relation'))\n );\n}\n\nfunction resolveContributionConfigTenantFilter(\n tenantId: string | null | undefined,\n): string | null | undefined {\n if (tenantId !== undefined) {\n return tenantId;\n }\n\n if (isSystemContext() || isSuperAdminBypass()) {\n return undefined;\n }\n\n const currentTenant = getCurrentTenant();\n if (currentTenant?.tenantId) {\n return currentTenant.tenantId;\n }\n\n return isTenancyEnabled() ? null : undefined;\n}\n\nasync function listPersistedContributionTypeRows(\n db: DatabaseInterface,\n tenantId: string | null | undefined,\n): Promise<Record<string, unknown>[]> {\n const byCreatedAt = (\n a: Record<string, unknown>,\n b: Record<string, unknown>,\n ): number =>\n String(a.created_at || a.createdAt || '').localeCompare(\n String(b.created_at || b.createdAt || ''),\n );\n const sortRows = (rows: Record<string, unknown>[]) => rows.sort(byCreatedAt);\n\n if (tenantId === undefined) {\n return sortRows(\n (await db.list('content_contribution_types', {})) as Record<\n string,\n unknown\n >[],\n );\n }\n\n if (tenantId === null) {\n return sortRows(\n (await db.list('content_contribution_types', {\n tenant_id: null,\n })) as Record<string, unknown>[],\n );\n }\n\n const [globalRows, tenantRows] = await Promise.all([\n db.list('content_contribution_types', { tenant_id: null }) as Promise<\n Record<string, unknown>[]\n >,\n db.list('content_contribution_types', { tenant_id: tenantId }) as Promise<\n Record<string, unknown>[]\n >,\n ]);\n\n return [...sortRows(globalRows), ...sortRows(tenantRows)];\n}\n\nfunction mapPersistedTypeRow(\n row: Record<string, unknown>,\n): PersistedContentContributionTypeRecord {\n return {\n // `id` is an optional string column; coerce non-string/empty driver\n // values to undefined so a numeric id can't leak past the `id?: string`\n // shape (mirrors the getRowString pattern used elsewhere).\n id: typeof row.id === 'string' && row.id ? row.id : undefined,\n key: String(row.key || ''),\n label: String(row.label || row.key || ''),\n enabled: row.enabled !== false && row.enabled !== 0,\n allowedChannels: safeParseJSONArray<ContentContributionChannel>(\n row.allowedChannels || row.allowed_channels,\n ),\n allowText:\n row.allowText === undefined && row.allow_text === undefined\n ? true\n : row.allowText !== false && row.allow_text !== 0,\n allowFiles:\n row.allowFiles === true ||\n row.allow_files === true ||\n row.allow_files === 1,\n allowEmptyText:\n row.allowEmptyText === true ||\n row.allow_empty_text === true ||\n row.allow_empty_text === 1,\n // Persisted JSON blobs deserialize to the documented intake/promotion\n // shapes; they are re-normalized structurally by clone* helpers downstream.\n intakeRules: safeParseJSONObject(\n row.intakeRules || row.intake_rules,\n ) as ContentContributionIntakeRules,\n promotion: safeParseJSONObject(\n row.promotion || row.promotion_mapping,\n ) as ContentContributionPromotionMapping,\n metadata: safeParseJSONObject(row.metadata),\n tenantId: (row.tenantId ?? row.tenant_id ?? null) as string | null,\n createdAt: (row.createdAt || row.created_at || null) as string | null,\n updatedAt: (row.updatedAt || row.updated_at || null) as string | null,\n };\n}\n\nexport function getContentContributionConfig(): ContentContributionConfig {\n return cloneContributionConfig(contributionConfig);\n}\n\nexport function configureContentContributions(\n config: Partial<ContentContributionConfig>,\n): ContentContributionConfig {\n contributionConfig = {\n types: config.types\n ? mergeByKey(contributionConfig.types, config.types, cloneTypeDefinition)\n : contributionConfig.types.map(cloneTypeDefinition),\n };\n\n return getContentContributionConfig();\n}\n\nexport function resetContentContributionConfig(): ContentContributionConfig {\n contributionConfig = cloneContributionConfig(\n DEFAULT_CONTENT_CONTRIBUTION_CONFIG,\n );\n return getContentContributionConfig();\n}\n\nexport function hasStaticContentContributionType(key: string): boolean {\n return contributionConfig.types.some((type) => type.key === key);\n}\n\nexport async function loadPersistedContentContributionTypes(\n options: ContentContributionConfigReadOptions = {},\n): Promise<PersistedContentContributionTypeRecord[]> {\n if (!options.db) {\n return [];\n }\n\n try {\n const tenantId = resolveContributionConfigTenantFilter(options.tenantId);\n const rows = await listPersistedContributionTypeRows(options.db, tenantId);\n return rows.map((row) => mapPersistedTypeRow(row));\n } catch (error) {\n if (isMissingContributionTypesTableError(error)) {\n return [];\n }\n\n throw error;\n }\n}\n\nexport async function getEffectiveContentContributionConfig(\n options: ContentContributionConfigReadOptions = {},\n): Promise<ContentContributionConfig> {\n const persisted = await loadPersistedContentContributionTypes({\n db: options.db,\n tenantId: options.tenantId,\n });\n\n return {\n types: mergeByKey(\n contributionConfig.types,\n persisted,\n cloneTypeDefinition as (\n item: ContentContributionTypeDefinition,\n ) => ContentContributionTypeDefinition,\n ),\n };\n}\n\nexport async function getContentContributionTypeConfigState(\n options: ContentContributionConfigReadOptions = {},\n): Promise<ContentContributionTypeConfigState> {\n const [effective, persisted] = await Promise.all([\n getEffectiveContentContributionConfig(options),\n loadPersistedContentContributionTypes(options),\n ]);\n\n return {\n effective: effective.types,\n persisted: persisted.map(clonePersistedTypeRecord),\n };\n}\n\nexport async function resolveEffectiveContentContributionType(\n key: string,\n options: ContentContributionConfigReadOptions = {},\n): Promise<ResolvedContentContributionType | null> {\n const effective = await getEffectiveContentContributionConfig(options);\n const type = effective.types.find((entry) => entry.key === key);\n return type ? cloneTypeDefinition(type) : null;\n}\n\nfunction matchesMimePattern(mimeType: string, pattern: string): boolean {\n if (!pattern) {\n return false;\n }\n\n if (pattern.endsWith('/*')) {\n return mimeType.startsWith(pattern.slice(0, -1));\n }\n\n return mimeType === pattern;\n}\n\nfunction matchesAnyMimePattern(mimeType: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchesMimePattern(mimeType, pattern));\n}\n\nfunction containsPattern(text: string, patterns: string[]): boolean {\n const lowerText = text.toLowerCase();\n return patterns.some((pattern) => lowerText.includes(pattern.toLowerCase()));\n}\n\nexport function evaluateContributionIntake(\n options: EvaluateContributionIntakeOptions,\n): EvaluateContributionIntakeResult {\n const type = cloneTypeDefinition(options.contributionType);\n const rules = cloneIntakeRules(type.intakeRules);\n const trustLevel = options.trustLevel || 'standard';\n const attachments = options.attachments || [];\n const text = [options.title, options.description, options.body]\n .filter(Boolean)\n .join('\\n')\n .trim();\n const hasText = text.length > 0;\n const hasFiles = attachments.length > 0;\n const totalBytes = attachments.reduce(\n (sum, attachment) => sum + Math.max(0, attachment.size || 0),\n 0,\n );\n const reasons: string[] = [];\n\n if (type.enabled === false) {\n return {\n decision: 'rejected',\n reasons: ['This contribution type is currently disabled.'],\n };\n }\n\n if (\n Array.isArray(type.allowedChannels) &&\n type.allowedChannels.length > 0 &&\n !type.allowedChannels.includes(options.channel)\n ) {\n return {\n decision: 'rejected',\n reasons: [\n `${options.channel} submissions are not allowed for this type.`,\n ],\n };\n }\n\n if (trustLevel === 'blocked') {\n return {\n decision: 'rejected',\n reasons: ['This contributor is blocked from submitting new material.'],\n };\n }\n\n if (rules.trustedOnly && trustLevel !== 'trusted') {\n return {\n decision: 'rejected',\n reasons: ['This contribution type is limited to trusted contributors.'],\n };\n }\n\n if (type.allowText === false && hasText) {\n return {\n decision: 'rejected',\n reasons: ['This contribution type does not accept text submissions.'],\n };\n }\n\n if (type.allowFiles !== true && hasFiles) {\n return {\n decision: 'rejected',\n reasons: ['This contribution type does not accept file attachments.'],\n };\n }\n\n if (\n type.allowText === true &&\n type.allowEmptyText !== true &&\n !hasText &&\n !hasFiles\n ) {\n return {\n decision: 'rejected',\n reasons: ['A text body or at least one attachment is required.'],\n };\n }\n\n if (\n typeof rules.maxFiles === 'number' &&\n attachments.length > rules.maxFiles\n ) {\n return {\n decision: 'rejected',\n reasons: [\n `This contribution type accepts at most ${rules.maxFiles} files.`,\n ],\n };\n }\n\n if (\n typeof rules.maxTotalBytes === 'number' &&\n totalBytes > rules.maxTotalBytes\n ) {\n return {\n decision: 'rejected',\n reasons: [\n 'The attached files exceed the size limit for this contribution type.',\n ],\n };\n }\n\n const mimeTypes = attachments\n .map((attachment) => attachment.mimeType || '')\n .filter(Boolean);\n\n if (\n rules.allowedMimePatterns &&\n rules.allowedMimePatterns.length > 0 &&\n mimeTypes.some(\n (mimeType) =>\n !matchesAnyMimePattern(mimeType, rules.allowedMimePatterns || []),\n )\n ) {\n return {\n decision: 'rejected',\n reasons: ['One or more attachments use a file type that is not allowed.'],\n };\n }\n\n if (\n mimeTypes.some((mimeType) =>\n matchesAnyMimePattern(mimeType, rules.blockedMimePatterns || []),\n )\n ) {\n return {\n decision: 'rejected',\n reasons: ['One or more attachments match blocked file-type rules.'],\n };\n }\n\n if (containsPattern(text, rules.blockedTextPatterns || [])) {\n return {\n decision: 'rejected',\n reasons: ['The submission body matches blocked intake rules.'],\n };\n }\n\n if (\n mimeTypes.some((mimeType) =>\n matchesAnyMimePattern(mimeType, rules.quarantineMimePatterns || []),\n )\n ) {\n reasons.push('One or more attachments triggered quarantine file rules.');\n }\n\n if (containsPattern(text, rules.quarantineTextPatterns || [])) {\n reasons.push('The submission body triggered quarantine text rules.');\n }\n\n if (reasons.length > 0) {\n return {\n decision: 'quarantined',\n reasons,\n };\n }\n\n return {\n decision: 'accepted',\n reasons: [],\n };\n}\n","import {\n AssetCollection,\n AssetStatusCollection,\n AssetTypeCollection,\n} from '@happyvertical/smrt-assets';\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentContributionAttachment } from './content-contribution-attachment';\nimport {\n type ContentContributionChannel,\n type ContentContributionIntakeDecision,\n type ContentContributionStatus,\n type ContentContributionTypeDefinition,\n resolveEffectiveContentContributionType,\n} from './content-contribution-config';\nimport type { ContentContributionRevision } from './content-contribution-revision';\nimport type { ContentContributor } from './content-contributor';\n\nexport interface ContentContributionAttachmentInput {\n filename: string;\n mimeType?: string | null;\n size?: number | null;\n fileKey?: string | null;\n sourceUri?: string | null;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AppendContentContributionRevisionOptions {\n title?: string | null;\n description?: string | null;\n body?: string | null;\n channel?: ContentContributionChannel;\n sourceMessageId?: string | null;\n sourceThreadKey?: string | null;\n attachments?: ContentContributionAttachmentInput[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface ApproveContentContributionOptions {\n editorNote?: string | null;\n promote?: boolean;\n targetStatus?: 'draft' | 'review';\n}\n\nexport interface RejectContentContributionOptions {\n editorNote?: string | null;\n}\n\nexport interface RequestChangesContentContributionOptions {\n editorNote?: string | null;\n}\n\nexport interface WithdrawContentContributionOptions {\n reason?: string | null;\n}\n\nexport interface PromoteContentContributionOptions {\n editorNote?: string | null;\n targetStatus?: 'draft' | 'review';\n}\n\nexport interface ContentContributionOptions extends SmrtObjectOptions {\n contributorId?: string;\n contributionTypeKey?: string;\n status?: ContentContributionStatus;\n intakeDecision?: ContentContributionIntakeDecision;\n channel?: ContentContributionChannel;\n title?: string | null;\n description?: string | null;\n body?: string | null;\n contributorEmail?: string | null;\n contributorName?: string | null;\n threadKey?: string | null;\n sourceMessageId?: string | null;\n editorNotes?: string | null;\n promotedContentId?: string | null;\n revisionCount?: number;\n approvedAt?: Date | null;\n promotedAt?: Date | null;\n rejectedAt?: Date | null;\n withdrawnAt?: Date | null;\n requestedChangesAt?: Date | null;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseMetadata(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object'\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction inferAssetTypeSlug(mimeType: string): string {\n if (mimeType.startsWith('image/')) {\n return 'image';\n }\n if (mimeType.startsWith('video/')) {\n return 'video';\n }\n if (mimeType.startsWith('audio/')) {\n return 'audio';\n }\n return 'document';\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contributions',\n conflictColumns: ['id'],\n api: {\n include: [\n 'list',\n 'get',\n 'create',\n 'update',\n 'delete',\n 'appendRevisionAction',\n 'requestChangesAction',\n 'approveAction',\n 'rejectAction',\n 'withdrawAction',\n 'promoteAction',\n ],\n routes: {\n appendRevisionAction: { method: 'POST', path: 'revisions' },\n requestChangesAction: { method: 'POST', path: 'request-changes' },\n approveAction: { method: 'POST', path: 'approve' },\n rejectAction: { method: 'POST', path: 'reject' },\n withdrawAction: { method: 'POST', path: 'withdraw' },\n promoteAction: { method: 'POST', path: 'promote' },\n },\n },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class ContentContribution extends SmrtObject {\n @foreignKey('ContentContributor', { required: true })\n contributorId = '';\n\n @field({ required: true })\n contributionTypeKey = '';\n\n status: ContentContributionStatus = 'submitted';\n intakeDecision: ContentContributionIntakeDecision = 'accepted';\n channel: ContentContributionChannel = 'web';\n title = '';\n description = '';\n body = '';\n contributorEmail = '';\n contributorName = '';\n threadKey = '';\n\n @crossPackageRef('@happyvertical/smrt-messages:Message')\n sourceMessageId = '';\n\n editorNotes = '';\n\n @foreignKey('Content')\n promotedContentId = '';\n revisionCount = 0;\n approvedAt: Date | null = null;\n promotedAt: Date | null = null;\n rejectedAt: Date | null = null;\n withdrawnAt: Date | null = null;\n requestedChangesAt: Date | null = null;\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributionOptions = {}) {\n super(options);\n if (options.contributorId !== undefined)\n this.contributorId = options.contributorId;\n if (options.contributionTypeKey !== undefined)\n this.contributionTypeKey = options.contributionTypeKey;\n if (options.status !== undefined) this.status = options.status;\n if (options.intakeDecision !== undefined)\n this.intakeDecision = options.intakeDecision;\n if (options.channel !== undefined) this.channel = options.channel;\n if (options.title !== undefined) this.title = options.title || '';\n if (options.description !== undefined)\n this.description = options.description || '';\n if (options.body !== undefined) this.body = options.body || '';\n if (options.contributorEmail !== undefined)\n this.contributorEmail = options.contributorEmail || '';\n if (options.contributorName !== undefined)\n this.contributorName = options.contributorName || '';\n if (options.threadKey !== undefined)\n this.threadKey = options.threadKey || '';\n if (options.sourceMessageId !== undefined)\n this.sourceMessageId = options.sourceMessageId || '';\n if (options.editorNotes !== undefined)\n this.editorNotes = options.editorNotes || '';\n if (options.promotedContentId !== undefined)\n this.promotedContentId = options.promotedContentId || '';\n if (options.revisionCount !== undefined)\n this.revisionCount = options.revisionCount;\n if (options.approvedAt !== undefined) this.approvedAt = options.approvedAt;\n if (options.promotedAt !== undefined) this.promotedAt = options.promotedAt;\n if (options.rejectedAt !== undefined) this.rejectedAt = options.rejectedAt;\n if (options.withdrawnAt !== undefined)\n this.withdrawnAt = options.withdrawnAt;\n if (options.requestedChangesAt !== undefined)\n this.requestedChangesAt = options.requestedChangesAt;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n return parseMetadata(this.metadata);\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = JSON.stringify(metadata || {});\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n description: this.description || null,\n body: this.body || null,\n contributorEmail: this.contributorEmail || null,\n contributorName: this.contributorName || null,\n threadKey: this.threadKey || null,\n sourceMessageId: this.sourceMessageId || null,\n editorNotes: this.editorNotes || null,\n promotedContentId: this.promotedContentId || null,\n metadata: this.getMetadata(),\n };\n }\n\n private async getRevisionCollection() {\n const { ContentContributionRevisionCollection } = await import(\n './content-contribution-revisions'\n );\n return ContentContributionRevisionCollection.create(this.options);\n }\n\n private async getAttachmentCollection() {\n const { ContentContributionAttachmentCollection } = await import(\n './content-contribution-attachments'\n );\n return ContentContributionAttachmentCollection.create(this.options);\n }\n\n private async getContributorCollection() {\n const { ContentContributorCollection } = await import(\n './content-contributors'\n );\n return ContentContributorCollection.create(this.options);\n }\n\n private async getContentsCollection() {\n const { Contents } = await import('./contents');\n return Contents.create({ db: this.db });\n }\n\n private async getAssetCollection() {\n return AssetCollection.create(this.options);\n }\n\n private async getAssetStatusCollection() {\n return AssetStatusCollection.create(this.options);\n }\n\n private async getAssetTypeCollection() {\n return AssetTypeCollection.create(this.options);\n }\n\n async getContributor(): Promise<ContentContributor | null> {\n if (!this.contributorId) {\n return null;\n }\n\n const contributors = await this.getContributorCollection();\n return contributors.get({ id: this.contributorId });\n }\n\n async getRevisions(): Promise<ContentContributionRevision[]> {\n if (!this.id) {\n return [];\n }\n\n const revisions = await this.getRevisionCollection();\n return revisions.listForContribution(this.id);\n }\n\n async getLatestRevision(): Promise<ContentContributionRevision | null> {\n if (!this.id) {\n return null;\n }\n\n const revisions = await this.getRevisionCollection();\n return revisions.getLatestForContribution(this.id);\n }\n\n async getAttachments(): Promise<ContentContributionAttachment[]> {\n if (!this.id) {\n return [];\n }\n\n const attachments = await this.getAttachmentCollection();\n return attachments.listForContribution(this.id);\n }\n\n async resolveContributionType(): Promise<ContentContributionTypeDefinition | null> {\n return resolveEffectiveContentContributionType(this.contributionTypeKey, {\n db: this.db,\n tenantId: this.tenantId ?? null,\n });\n }\n\n private ensureEditable(): void {\n if (\n this.revisionCount > 0 &&\n ['rejected', 'withdrawn', 'promoted'].includes(this.status)\n ) {\n throw new Error(\n `Contribution \"${this.id || 'new'}\" cannot be edited once it is ${this.status}.`,\n );\n }\n }\n\n private async createRevision(\n options: AppendContentContributionRevisionOptions,\n ): Promise<ContentContributionRevision> {\n if (!this.id) {\n throw new Error('Contribution must be saved before creating revisions.');\n }\n\n const revisions = await this.getRevisionCollection();\n const nextRevisionNumber = Math.max(1, this.revisionCount + 1);\n const revision = await revisions.create({\n contributionId: this.id,\n revisionNumber: nextRevisionNumber,\n channel: options.channel || this.channel,\n title: options.title ?? this.title,\n description: options.description ?? this.description,\n body: options.body ?? this.body,\n sourceMessageId: options.sourceMessageId || '',\n sourceThreadKey: options.sourceThreadKey || this.threadKey || '',\n metadata: JSON.stringify(options.metadata || {}),\n tenantId: this.tenantId,\n });\n\n return revision;\n }\n\n private async createHeldAttachments(\n revisionId: string | null,\n attachments: ContentContributionAttachmentInput[],\n channel: ContentContributionChannel,\n ): Promise<ContentContributionAttachment[]> {\n if (!this.id || attachments.length === 0) {\n return [];\n }\n\n const records = await this.getAttachmentCollection();\n const created: ContentContributionAttachment[] = [];\n for (const attachment of attachments) {\n const item = await records.create({\n contributionId: this.id,\n revisionId: revisionId || '',\n filename: attachment.filename,\n mimeType: attachment.mimeType || 'application/octet-stream',\n size: Math.max(0, attachment.size || 0),\n fileKey: attachment.fileKey || '',\n sourceUri: attachment.sourceUri || '',\n channel,\n tenantId: this.tenantId,\n metadata: JSON.stringify(attachment.metadata || {}),\n });\n created.push(item);\n }\n\n return created;\n }\n\n async appendRevisionAction(\n options: AppendContentContributionRevisionOptions = {},\n ) {\n this.ensureEditable();\n\n const revision = await this.createRevision(options);\n await this.createHeldAttachments(\n revision.id || null,\n options.attachments || [],\n options.channel || this.channel,\n );\n\n this.title = options.title ?? this.title;\n this.description = options.description ?? this.description;\n this.body = options.body ?? this.body;\n this.channel = options.channel || this.channel;\n this.sourceMessageId = options.sourceMessageId || this.sourceMessageId;\n this.threadKey = options.sourceThreadKey || this.threadKey;\n this.revisionCount = revision.revisionNumber;\n\n if (this.status === 'needs_changes') {\n this.status = 'submitted';\n this.requestedChangesAt = null;\n }\n\n const metadata = this.getMetadata();\n metadata.lastRevisionId = revision.id || null;\n metadata.lastRevisionAt = revision.createdAt.toISOString();\n this.setMetadata(metadata);\n await this.save();\n\n return {\n contribution: this.toJSON(),\n revision: revision.toJSON(),\n attachments: (await this.getAttachments()).map((item) => item.toJSON()),\n };\n }\n\n async requestChangesAction(\n options: RequestChangesContentContributionOptions = {},\n ) {\n if (['rejected', 'withdrawn', 'promoted'].includes(this.status)) {\n throw new Error(\n `Contribution \"${this.id || 'new'}\" cannot request changes after ${this.status}.`,\n );\n }\n\n this.status = 'needs_changes';\n this.requestedChangesAt = new Date();\n this.editorNotes = options.editorNote || this.editorNotes;\n\n const metadata = this.getMetadata();\n const requestHistory = Array.isArray(metadata.requestChangesHistory)\n ? metadata.requestChangesHistory\n : [];\n requestHistory.push({\n note: options.editorNote || '',\n at: this.requestedChangesAt.toISOString(),\n });\n metadata.requestChangesHistory = requestHistory;\n this.setMetadata(metadata);\n\n await this.save();\n return this.toJSON();\n }\n\n async rejectAction(options: RejectContentContributionOptions = {}) {\n if (this.status === 'promoted') {\n throw new Error('Promoted contributions cannot be rejected.');\n }\n\n this.status = 'rejected';\n this.rejectedAt = new Date();\n this.editorNotes = options.editorNote || this.editorNotes;\n\n const metadata = this.getMetadata();\n metadata.rejection = {\n note: options.editorNote || '',\n at: this.rejectedAt.toISOString(),\n };\n this.setMetadata(metadata);\n\n await this.save();\n return this.toJSON();\n }\n\n async withdrawAction(options: WithdrawContentContributionOptions = {}) {\n if (this.status === 'promoted') {\n throw new Error('Promoted contributions cannot be withdrawn.');\n }\n\n this.status = 'withdrawn';\n this.withdrawnAt = new Date();\n\n const metadata = this.getMetadata();\n metadata.withdrawal = {\n reason: options.reason || '',\n at: this.withdrawnAt.toISOString(),\n };\n this.setMetadata(metadata);\n\n await this.save();\n return this.toJSON();\n }\n\n async approveAction(options: ApproveContentContributionOptions = {}) {\n if (['rejected', 'withdrawn', 'promoted'].includes(this.status)) {\n throw new Error(\n `Contribution \"${this.id || 'new'}\" cannot be approved after ${this.status}.`,\n );\n }\n\n this.status = 'approved';\n this.approvedAt = new Date();\n this.editorNotes = options.editorNote || this.editorNotes;\n await this.save();\n\n if (options.promote === false) {\n return {\n contribution: this.toJSON(),\n content: null,\n assets: [],\n };\n }\n\n return this.promoteAction({\n editorNote: options.editorNote,\n targetStatus: options.targetStatus,\n });\n }\n\n async promoteAction(options: PromoteContentContributionOptions = {}) {\n if (this.promotedContentId) {\n const contents = await this.getContentsCollection();\n const content = await contents.get({ id: this.promotedContentId });\n return {\n contribution: this.toJSON(),\n content: content ? content.toJSON() : null,\n assets: await this.getAttachments().then((items) =>\n items\n .filter((item) => item.promotedAssetId)\n .map((item) => ({\n attachmentId: item.id,\n assetId: item.promotedAssetId,\n })),\n ),\n };\n }\n\n const type = await this.resolveContributionType();\n if (!type) {\n throw new Error(\n `Contribution type \"${this.contributionTypeKey}\" is not configured.`,\n );\n }\n\n const promotion = type.promotion || {};\n if (!promotion.targetContentType) {\n throw new Error(\n `Contribution type \"${type.key}\" is missing promotion.targetContentType.`,\n );\n }\n\n const contributor = await this.getContributor();\n const latestRevision = await this.getLatestRevision();\n const attachments = await this.getAttachments();\n\n const contents = await this.getContentsCollection();\n const content = await contents.create({\n tenantId: this.tenantId,\n type: promotion.targetContentType,\n variant: promotion.targetContentVariant || null,\n source: 'contribution',\n status: options.targetStatus || promotion.targetContentStatus || 'draft',\n title: latestRevision?.title || this.title,\n description: latestRevision?.description || this.description || null,\n body: latestRevision?.body || this.body || '',\n author:\n contributor?.name ||\n this.contributorName ||\n this.contributorEmail ||\n null,\n name:\n latestRevision?.title ||\n this.title ||\n `${type.label || type.key} contribution`,\n metadata: {\n contribution: {\n contributionId: this.id || null,\n contributorId: this.contributorId || null,\n contributorEmail: this.contributorEmail || null,\n contributorName: this.contributorName || null,\n contributionTypeKey: this.contributionTypeKey,\n revisionCount: this.revisionCount,\n sourceMessageId: this.sourceMessageId || null,\n threadKey: this.threadKey || null,\n },\n },\n });\n await content.save();\n\n const promotedAssets: Array<{\n attachmentId: string | null;\n assetId: string | null;\n }> = [];\n\n if (promotion.createAssets !== false && attachments.length > 0) {\n const [assets, statuses, typesCollection] = await Promise.all([\n this.getAssetCollection(),\n this.getAssetStatusCollection(),\n this.getAssetTypeCollection(),\n ]);\n\n await Promise.all([\n statuses.initializeCommonStatuses(),\n typesCollection.initializeCommonTypes(),\n ]);\n\n for (const [index, attachment] of attachments.entries()) {\n const asset = await assets.create({\n tenantId: this.tenantId,\n ownerProfileId: contributor?.profileId || null,\n name: attachment.filename || `Contribution attachment ${index + 1}`,\n description: `Promoted from contribution ${this.id || ''}`.trim(),\n mimeType: attachment.mimeType || 'application/octet-stream',\n sourceUri:\n attachment.sourceUri ||\n attachment.fileKey ||\n `held://content-contribution/${attachment.id || index}`,\n typeSlug: inferAssetTypeSlug(attachment.mimeType || ''),\n statusSlug: 'draft',\n sourceType: 'content-contribution',\n externalId: attachment.id || '',\n });\n await asset.save();\n\n await content.addAsset(\n asset,\n promotion.assetRelationship || 'attachment',\n index,\n );\n\n attachment.promotedAssetId = asset.id || '';\n await attachment.save();\n promotedAssets.push({\n attachmentId: attachment.id || null,\n assetId: asset.id || null,\n });\n }\n }\n\n this.promotedContentId = content.id || '';\n this.promotedAt = new Date();\n this.status = 'promoted';\n this.editorNotes = options.editorNote || this.editorNotes;\n\n const metadata = this.getMetadata();\n metadata.promotion = {\n contentId: content.id || null,\n contentStatus: content.status || null,\n promotedAt: this.promotedAt.toISOString(),\n assetIds: promotedAssets.map((item) => item.assetId).filter(Boolean),\n };\n this.setMetadata(metadata);\n await this.save();\n\n return {\n contribution: this.toJSON(),\n content: content.toJSON(),\n assets: promotedAssets,\n };\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentContributionChannel } from './content-contribution-config';\n\nexport interface ContentContributionAttachmentOptions\n extends SmrtObjectOptions {\n contributionId?: string;\n revisionId?: string | null;\n filename?: string;\n mimeType?: string;\n size?: number;\n fileKey?: string | null;\n sourceUri?: string | null;\n channel?: ContentContributionChannel;\n promotedAssetId?: string | null;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseMetadata(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object'\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contribution_attachments',\n conflictColumns: ['id'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentContributionAttachment extends SmrtObject {\n @foreignKey('ContentContribution', { required: true })\n contributionId = '';\n\n @foreignKey('ContentContributionRevision')\n revisionId = '';\n\n filename = '';\n mimeType = '';\n size = 0;\n fileKey = '';\n sourceUri = '';\n channel: ContentContributionChannel = 'web';\n\n @crossPackageRef('@happyvertical/smrt-assets:Asset')\n promotedAssetId = '';\n\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributionAttachmentOptions = {}) {\n super(options);\n if (options.contributionId !== undefined)\n this.contributionId = options.contributionId;\n if (options.revisionId !== undefined)\n this.revisionId = options.revisionId || '';\n if (options.filename !== undefined) this.filename = options.filename;\n if (options.mimeType !== undefined) this.mimeType = options.mimeType;\n if (options.size !== undefined) this.size = options.size;\n if (options.fileKey !== undefined) this.fileKey = options.fileKey || '';\n if (options.sourceUri !== undefined)\n this.sourceUri = options.sourceUri || '';\n if (options.channel !== undefined) this.channel = options.channel;\n if (options.promotedAssetId !== undefined)\n this.promotedAssetId = options.promotedAssetId || '';\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n return parseMetadata(this.metadata);\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n revisionId: this.revisionId || null,\n fileKey: this.fileKey || null,\n sourceUri: this.sourceUri || null,\n promotedAssetId: this.promotedAssetId || null,\n metadata: this.getMetadata(),\n };\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentContributionAttachment } from './content-contribution-attachment';\n\nexport class ContentContributionAttachmentCollection extends SmrtCollection<ContentContributionAttachment> {\n static readonly _itemClass = ContentContributionAttachment;\n\n async listForContribution(\n contributionId: string,\n ): Promise<ContentContributionAttachment[]> {\n return this.list({\n where: { contributionId },\n orderBy: 'created_at ASC',\n });\n }\n\n async listForRevision(\n revisionId: string,\n ): Promise<ContentContributionAttachment[]> {\n return this.list({\n where: { revisionId },\n orderBy: 'created_at ASC',\n });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentContributionChannel } from './content-contribution-config';\n\nexport interface ContentContributionRevisionOptions extends SmrtObjectOptions {\n contributionId?: string;\n revisionNumber?: number;\n channel?: ContentContributionChannel;\n title?: string;\n description?: string | null;\n body?: string | null;\n sourceMessageId?: string | null;\n sourceThreadKey?: string | null;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseMetadata(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object'\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contribution_revisions',\n conflictColumns: ['contribution_id', 'revision_number'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentContributionRevision extends SmrtObject {\n @foreignKey('ContentContribution', { required: true })\n contributionId = '';\n\n revisionNumber = 1;\n channel: ContentContributionChannel = 'web';\n title = '';\n description = '';\n body = '';\n\n @crossPackageRef('@happyvertical/smrt-messages:Message')\n sourceMessageId = '';\n\n sourceThreadKey = '';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributionRevisionOptions = {}) {\n super(options);\n if (options.contributionId !== undefined)\n this.contributionId = options.contributionId;\n if (options.revisionNumber !== undefined)\n this.revisionNumber = options.revisionNumber;\n if (options.channel !== undefined) this.channel = options.channel;\n if (options.title !== undefined) this.title = options.title;\n if (options.description !== undefined)\n this.description = options.description || '';\n if (options.body !== undefined) this.body = options.body || '';\n if (options.sourceMessageId !== undefined)\n this.sourceMessageId = options.sourceMessageId || '';\n if (options.sourceThreadKey !== undefined)\n this.sourceThreadKey = options.sourceThreadKey || '';\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n return parseMetadata(this.metadata);\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n sourceMessageId: this.sourceMessageId || null,\n sourceThreadKey: this.sourceThreadKey || null,\n metadata: this.getMetadata(),\n };\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentContributionRevision } from './content-contribution-revision';\n\nexport class ContentContributionRevisionCollection extends SmrtCollection<ContentContributionRevision> {\n static readonly _itemClass = ContentContributionRevision;\n\n async listForContribution(\n contributionId: string,\n ): Promise<ContentContributionRevision[]> {\n return this.list({\n where: { contributionId },\n orderBy: 'revision_number ASC',\n });\n }\n\n async getLatestForContribution(\n contributionId: string,\n ): Promise<ContentContributionRevision | null> {\n const revisions = await this.list({\n where: { contributionId },\n orderBy: 'revision_number DESC',\n limit: 1,\n });\n\n return revisions[0] || null;\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type ContentContributionChannel,\n type ContentContributionIntakeRules,\n type ContentContributionPromotionMapping,\n type ContentContributionTypeDefinition,\n hasStaticContentContributionType,\n} from './content-contribution-config';\nimport { getQueryRows, isMissingTableError } from './database-utils';\n\nexport interface ContentContributionTypeOptions extends SmrtObjectOptions {\n key?: string;\n label?: string;\n enabled?: boolean;\n allowedChannels?: ContentContributionChannel[] | string;\n allowText?: boolean;\n allowFiles?: boolean;\n allowEmptyText?: boolean;\n intakeRules?: ContentContributionIntakeRules | string;\n promotion?: ContentContributionPromotionMapping | string;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseJSON<T>(raw: unknown, fallback: T): T {\n if (!raw) {\n return fallback;\n }\n\n if (typeof raw === 'object') {\n return structuredClone(raw as T);\n }\n\n if (typeof raw !== 'string') {\n return fallback;\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? (parsed as T) : fallback;\n } catch {\n return fallback;\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contribution_types',\n conflictColumns: ['key'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentContributionType extends SmrtObject {\n @field({ required: true })\n key = '';\n\n label = '';\n enabled = true;\n allowedChannels = '';\n allowText = true;\n allowFiles = false;\n allowEmptyText = false;\n intakeRules = '';\n promotion = '';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributionTypeOptions = {}) {\n super(options);\n if (options.key !== undefined) this.key = options.key;\n if (options.label !== undefined) this.label = options.label;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.allowText !== undefined) this.allowText = options.allowText;\n if (options.allowFiles !== undefined) this.allowFiles = options.allowFiles;\n if (options.allowEmptyText !== undefined)\n this.allowEmptyText = options.allowEmptyText;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.allowedChannels !== undefined) {\n this.allowedChannels =\n typeof options.allowedChannels === 'string'\n ? options.allowedChannels\n : JSON.stringify(options.allowedChannels);\n }\n\n if (options.intakeRules !== undefined) {\n this.intakeRules =\n typeof options.intakeRules === 'string'\n ? options.intakeRules\n : JSON.stringify(options.intakeRules);\n }\n\n if (options.promotion !== undefined) {\n this.promotion =\n typeof options.promotion === 'string'\n ? options.promotion\n : JSON.stringify(options.promotion);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getAllowedChannels(): ContentContributionChannel[] {\n const channels = parseJSON<ContentContributionChannel[]>(\n this.allowedChannels,\n [],\n );\n return channels.length > 0 ? channels : ['web'];\n }\n\n setAllowedChannels(channels: ContentContributionChannel[]): void {\n this.allowedChannels = JSON.stringify(channels);\n }\n\n getIntakeRules(): ContentContributionIntakeRules {\n return parseJSON<ContentContributionIntakeRules>(this.intakeRules, {});\n }\n\n setIntakeRules(rules: ContentContributionIntakeRules): void {\n this.intakeRules = JSON.stringify(rules || {});\n }\n\n getPromotion(): ContentContributionPromotionMapping {\n return parseJSON<ContentContributionPromotionMapping>(this.promotion, {});\n }\n\n setPromotion(promotion: ContentContributionPromotionMapping): void {\n this.promotion = JSON.stringify(promotion || {});\n }\n\n getMetadata(): Record<string, unknown> {\n return parseJSON<Record<string, unknown>>(this.metadata, {});\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = JSON.stringify(metadata || {});\n }\n\n toDefinition(): ContentContributionTypeDefinition {\n return {\n key: this.key,\n label: this.label || this.key,\n enabled: this.enabled !== false,\n allowedChannels: this.getAllowedChannels(),\n allowText: this.allowText !== false,\n allowFiles: this.allowFiles === true,\n allowEmptyText: this.allowEmptyText === true,\n intakeRules: this.getIntakeRules(),\n promotion: this.getPromotion(),\n metadata: this.getMetadata(),\n };\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n allowedChannels: this.getAllowedChannels(),\n intakeRules: this.getIntakeRules(),\n promotion: this.getPromotion(),\n metadata: this.getMetadata(),\n };\n }\n\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n\n if (!this.key) {\n throw new Error('Contribution type key is required.');\n }\n\n const promotion = this.getPromotion();\n if (!promotion.targetContentType) {\n throw new Error(\n `Contribution type \"${this.key}\" must define promotion.targetContentType.`,\n );\n }\n }\n\n override async delete(): Promise<void> {\n if (!this.db) {\n return super.delete();\n }\n\n const hasFallback = hasStaticContentContributionType(this.key);\n\n if (!hasFallback) {\n try {\n const existing = await this.db.query(\n 'SELECT id FROM content_contributions WHERE contribution_type_key = ? LIMIT 1',\n this.key,\n );\n const rows = getQueryRows(existing);\n\n if (rows.length > 0) {\n throw new Error(\n `Cannot delete contribution type \"${this.key}\" because contributions already reference it.`,\n );\n }\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('already reference it')\n ) {\n throw error;\n }\n if (!isMissingTableError(error, 'content_contributions')) {\n throw error;\n }\n }\n }\n\n return super.delete();\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentContributionType } from './content-contribution-type';\n\nexport class ContentContributionTypeCollection extends SmrtCollection<ContentContributionType> {\n static readonly _itemClass = ContentContributionType;\n\n async getByKey(key: string): Promise<ContentContributionType | null> {\n return this.get({ key });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentContributorTrustLevel } from './content-contribution-config';\n\nexport interface ContentContributorOptions extends SmrtObjectOptions {\n profileId?: string | null;\n email?: string;\n name?: string;\n trustLevel?: ContentContributorTrustLevel;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseMetadata(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object'\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contributors',\n conflictColumns: ['email'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentContributor extends SmrtObject {\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n profileId = '';\n\n @field({ required: true })\n email = '';\n\n name = '';\n trustLevel: ContentContributorTrustLevel = 'standard';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributorOptions = {}) {\n super(options);\n if (options.profileId !== undefined)\n this.profileId = options.profileId || '';\n if (options.email !== undefined)\n this.email = options.email.toLowerCase().trim();\n if (options.name !== undefined) this.name = options.name;\n if (options.trustLevel !== undefined) this.trustLevel = options.trustLevel;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n return parseMetadata(this.metadata);\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = JSON.stringify(metadata || {});\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n profileId: this.profileId || null,\n metadata: this.getMetadata(),\n };\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n ProfileCollection,\n ProfileTypeCollection,\n} from '@happyvertical/smrt-profiles';\nimport { ContentContributor } from './content-contributor';\n\nexport class ContentContributorCollection extends SmrtCollection<ContentContributor> {\n static readonly _itemClass = ContentContributor;\n\n async getByEmail(email: string): Promise<ContentContributor | null> {\n return this.get({ email: email.toLowerCase().trim() });\n }\n\n async getByProfileId(profileId: string): Promise<ContentContributor | null> {\n return this.get({ profileId });\n }\n\n async findOrCreateByEmail(options: {\n email: string;\n name?: string | null;\n tenantId?: string | null;\n }): Promise<ContentContributor> {\n const normalizedEmail = options.email.toLowerCase().trim();\n const existing = await this.getByEmail(normalizedEmail);\n if (existing) {\n if (options.name && !existing.name) {\n existing.name = options.name;\n await existing.save();\n }\n return existing;\n }\n\n const profileCollection = await ProfileCollection.create(this.options);\n let profile = await profileCollection.findByEmail(normalizedEmail);\n\n if (!profile) {\n const profileTypes = await ProfileTypeCollection.create(this.options);\n const personType = await profileTypes.getOrCreateBySlug('person', {\n name: 'Person',\n description: 'Individual person',\n });\n\n profile = await profileCollection.create({\n typeId: personType.id || undefined,\n email: normalizedEmail,\n name: options.name || normalizedEmail.split('@')[0],\n tenantId: options.tenantId || undefined,\n });\n await profile.save();\n }\n\n const contributor = await this.create({\n email: normalizedEmail,\n name: options.name || profile.name || normalizedEmail.split('@')[0],\n profileId: profile.id || undefined,\n tenantId: options.tenantId || undefined,\n trustLevel: 'standard',\n });\n await contributor.save();\n return contributor;\n }\n}\n","import { SmrtCollection, smrt } from '@happyvertical/smrt-core';\nimport {\n AttachmentCollection,\n type Email,\n EmailCollection,\n} from '@happyvertical/smrt-messages';\nimport type { ContentContributionAttachmentInput } from './content-contribution';\nimport { ContentContribution } from './content-contribution';\nimport type { ContentContributionTypeDefinition } from './content-contribution-config';\nimport {\n evaluateContributionIntake,\n getContentContributionTypeConfigState,\n getEffectiveContentContributionConfig,\n} from './content-contribution-config';\nimport { ContentContributorCollection } from './content-contributors';\n\nexport interface SubmitWebContentContributionOptions {\n typeKey: string;\n contributorEmail: string;\n contributorName?: string | null;\n title?: string | null;\n description?: string | null;\n body?: string | null;\n attachments?: ContentContributionAttachmentInput[];\n metadata?: Record<string, unknown>;\n tenantId?: string | null;\n}\n\nexport interface IngestEmailContentContributionOptions {\n emailId: string;\n typeKey?: string | null;\n tenantId?: string | null;\n}\n\nfunction toContributionStatus(\n decision: ReturnType<typeof evaluateContributionIntake>['decision'],\n) {\n if (decision === 'quarantined') {\n return 'quarantined' as const;\n }\n\n if (decision === 'rejected') {\n return 'rejected' as const;\n }\n\n return 'submitted' as const;\n}\n\nfunction pickDefaultEmailType(\n types: ContentContributionTypeDefinition[],\n): ContentContributionTypeDefinition | null {\n const emailTypes = types.filter(\n (type) =>\n type.enabled !== false && (type.allowedChannels || []).includes('email'),\n );\n\n return emailTypes.length === 1 ? emailTypes[0] : null;\n}\n\n@smrt({\n api: {\n include: [\n 'submitWebContribution',\n 'ingestEmailContribution',\n 'listForContributor',\n 'listInboxAction',\n 'getContributionTypesAction',\n ],\n routes: {\n submitWebContribution: {\n scope: 'collection',\n method: 'POST',\n path: 'submit',\n },\n ingestEmailContribution: {\n scope: 'collection',\n method: 'POST',\n path: 'ingest-email',\n },\n listForContributor: {\n scope: 'collection',\n method: 'GET',\n path: 'by-contributor',\n },\n listInboxAction: {\n scope: 'collection',\n method: 'GET',\n path: 'inbox',\n },\n getContributionTypesAction: {\n scope: 'collection',\n method: 'GET',\n path: 'types',\n },\n },\n },\n mcp: false,\n cli: false,\n})\nexport class ContentContributions extends SmrtCollection<ContentContribution> {\n static readonly _itemClass = ContentContribution;\n\n private async getContributorCollection() {\n return ContentContributorCollection.create(this.options);\n }\n\n private async getEmailCollection() {\n return EmailCollection.create(this.options);\n }\n\n private async getAttachmentCollection() {\n return AttachmentCollection.create(this.options);\n }\n\n private async resolveSubmissionType(\n typeKey: string | null | undefined,\n tenantId?: string | null,\n ): Promise<ContentContributionTypeDefinition> {\n const effective = await getEffectiveContentContributionConfig({\n db: this.db,\n tenantId,\n });\n\n if (typeKey) {\n const match = effective.types.find((type) => type.key === typeKey);\n if (match) {\n return match;\n }\n throw new Error(`Unknown contribution type \"${typeKey}\".`);\n }\n\n const defaultType = pickDefaultEmailType(effective.types);\n if (!defaultType) {\n throw new Error(\n 'A contribution type key is required unless there is exactly one email-enabled type.',\n );\n }\n\n return defaultType;\n }\n\n async getContributionTypesAction(options: { tenantId?: string | null } = {}) {\n return getContentContributionTypeConfigState({\n db: this.db,\n tenantId: options.tenantId,\n });\n }\n\n async listForContributor(\n options: {\n contributorId?: string;\n contributorEmail?: string;\n tenantId?: string | null;\n } = {},\n ) {\n let contributorId = options.contributorId;\n\n if (!contributorId && options.contributorEmail) {\n const contributors = await this.getContributorCollection();\n const contributor = await contributors.getByEmail(\n options.contributorEmail,\n );\n contributorId = contributor?.id || undefined;\n }\n\n if (!contributorId) {\n return [];\n }\n\n const where: { contributorId: string; tenantId?: string | null } = {\n contributorId,\n };\n if (options.tenantId !== undefined) {\n where.tenantId = options.tenantId;\n }\n\n return this.list({\n where,\n orderBy: 'updated_at DESC',\n });\n }\n\n async listInboxAction(\n options: { statuses?: string | string[]; tenantId?: string | null } = {},\n ): Promise<ContentContribution[]> {\n const requested = Array.isArray(options.statuses)\n ? options.statuses\n : typeof options.statuses === 'string'\n ? options.statuses.split(',').map((item) => item.trim())\n : ['submitted', 'quarantined', 'needs_changes', 'approved'];\n\n const contributions = await this.list({\n where:\n options.tenantId !== undefined\n ? { tenantId: options.tenantId }\n : undefined,\n orderBy: 'updated_at DESC',\n });\n\n return contributions.filter((contribution) =>\n requested.includes(contribution.status),\n );\n }\n\n async submitWebContribution(options: SubmitWebContentContributionOptions) {\n const type = await this.resolveSubmissionType(\n options.typeKey,\n options.tenantId,\n );\n const contributors = await this.getContributorCollection();\n const contributor = await contributors.findOrCreateByEmail({\n email: options.contributorEmail,\n name: options.contributorName || null,\n tenantId: options.tenantId ?? null,\n });\n\n const intake = evaluateContributionIntake({\n contributionType: type,\n trustLevel: contributor.trustLevel,\n channel: 'web',\n title: options.title || null,\n description: options.description || null,\n body: options.body || null,\n attachments: options.attachments || [],\n });\n\n const status = toContributionStatus(intake.decision);\n\n const contribution = await this.create({\n contributorId: contributor.id || '',\n contributionTypeKey: type.key,\n status,\n intakeDecision: intake.decision,\n channel: 'web',\n title: options.title || '',\n description: options.description || '',\n body: options.body || '',\n contributorEmail: contributor.email,\n contributorName: contributor.name,\n editorNotes: intake.reasons.join('\\n'),\n tenantId: options.tenantId ?? null,\n metadata: JSON.stringify({\n intake: {\n decision: intake.decision,\n reasons: intake.reasons,\n },\n submission: options.metadata || {},\n }),\n });\n await contribution.save();\n\n await contribution.appendRevisionAction({\n title: options.title,\n description: options.description,\n body: options.body,\n attachments: options.attachments || [],\n channel: 'web',\n metadata: {\n source: 'web',\n },\n });\n\n if (\n contributor.trustLevel === 'trusted' &&\n intake.decision === 'accepted' &&\n type.promotion?.autoPromoteTrusted === true\n ) {\n return contribution.approveAction({\n editorNote: 'Auto-promoted trusted contributor submission.',\n targetStatus: type.promotion?.targetContentStatus || 'draft',\n });\n }\n\n return {\n contribution: contribution.toJSON(),\n intake,\n };\n }\n\n async ingestEmailContribution(\n options: IngestEmailContentContributionOptions,\n ) {\n const emailCollection = await this.getEmailCollection();\n // EmailCollection.get() is typed as the Message base; the rows it returns\n // are Emails, so narrow to Email to read the email-specific fields below.\n const email = (await emailCollection.get({\n id: options.emailId,\n })) as Email | null;\n if (!email) {\n throw new Error(`Email \"${options.emailId}\" not found.`);\n }\n\n const type = await this.resolveSubmissionType(\n options.typeKey,\n options.tenantId,\n );\n const contributors = await this.getContributorCollection();\n const contributor = await contributors.findOrCreateByEmail({\n email: email.fromAddress,\n name: email.fromName || null,\n tenantId: options.tenantId ?? null,\n });\n\n const attachmentCollection = await this.getAttachmentCollection();\n const inboundAttachments = await attachmentCollection.getByMessage(\n email.id || '',\n );\n const normalizedAttachments: ContentContributionAttachmentInput[] =\n inboundAttachments\n .filter((attachment) => !attachment.isInline())\n .map((attachment) => ({\n filename: attachment.filename || 'attachment',\n mimeType: attachment.contentType || 'application/octet-stream',\n size: attachment.size || 0,\n sourceUri: attachment.filePath || attachment.sourceUrl || null,\n metadata: {\n messageId: email.id || null,\n attachmentId: attachment.id || null,\n },\n }));\n\n const threadKey =\n email.threadId || email.messageId || email.inReplyTo || email.id || '';\n\n const intake = evaluateContributionIntake({\n contributionType: type,\n trustLevel: contributor.trustLevel,\n channel: 'email',\n title: email.subject || null,\n body: email.textBody || email.body || null,\n attachments: normalizedAttachments,\n });\n\n const existing =\n threadKey &&\n (await this.get({\n threadKey,\n contributorId: contributor.id || '',\n }));\n\n if (existing) {\n existing.intakeDecision = intake.decision;\n existing.editorNotes = intake.reasons.join('\\n');\n\n const metadata = existing.getMetadata();\n metadata.lastEmailIntake = {\n decision: intake.decision,\n reasons: intake.reasons,\n emailId: email.id || null,\n };\n existing.setMetadata(metadata);\n\n if (intake.decision === 'rejected') {\n existing.status = 'rejected';\n existing.rejectedAt = new Date();\n await existing.save();\n\n return {\n contribution: existing.toJSON(),\n intake,\n revision: null,\n attachments: (await existing.getAttachments()).map((item) =>\n item.toJSON(),\n ),\n };\n }\n\n existing.status = toContributionStatus(intake.decision);\n await existing.save();\n\n const appended = await existing.appendRevisionAction({\n title: email.subject || existing.title,\n body: email.textBody || email.body || '',\n channel: 'email',\n sourceMessageId: email.id || '',\n sourceThreadKey: threadKey,\n attachments: normalizedAttachments,\n metadata: {\n source: 'email-reply',\n emailId: email.id || null,\n },\n });\n\n return {\n ...appended,\n intake,\n };\n }\n\n const status = toContributionStatus(intake.decision);\n\n const contribution = await this.create({\n contributorId: contributor.id || '',\n contributionTypeKey: type.key,\n status,\n intakeDecision: intake.decision,\n channel: 'email',\n title: email.subject || '',\n description: '',\n body: email.textBody || email.body || '',\n contributorEmail: contributor.email,\n contributorName: contributor.name,\n threadKey,\n sourceMessageId: email.id || '',\n editorNotes: intake.reasons.join('\\n'),\n tenantId: options.tenantId ?? null,\n metadata: JSON.stringify({\n intake: {\n decision: intake.decision,\n reasons: intake.reasons,\n },\n email: {\n emailId: email.id || null,\n messageId: email.messageId || null,\n inReplyTo: email.inReplyTo || null,\n threadId: email.threadId || null,\n },\n }),\n });\n await contribution.save();\n\n await contribution.appendRevisionAction({\n title: email.subject || '',\n body: email.textBody || email.body || '',\n channel: 'email',\n sourceMessageId: email.id || null,\n sourceThreadKey: threadKey,\n attachments: normalizedAttachments,\n metadata: {\n source: 'email',\n emailId: email.id || null,\n },\n });\n\n if (\n contributor.trustLevel === 'trusted' &&\n intake.decision === 'accepted' &&\n type.promotion?.autoPromoteTrusted === true\n ) {\n return contribution.approveAction({\n editorNote: 'Auto-promoted trusted contributor email submission.',\n targetStatus: type.promotion?.targetContentStatus || 'draft',\n });\n }\n\n return {\n contribution: contribution.toJSON(),\n intake,\n };\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n ContentCorrectionStatus,\n ContentCorrectionType,\n} from './content-governance';\n\nexport interface ContentCorrectionOptions extends SmrtObjectOptions {\n contentId?: string;\n contentVersionId?: string;\n factId?: string;\n replacementFactId?: string;\n correctionType?: ContentCorrectionType;\n status?: ContentCorrectionStatus;\n summary?: string;\n incorrectText?: string;\n correctedText?: string;\n publicNote?: string;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n publishedAt?: Date | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_corrections',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class ContentCorrection extends SmrtObject {\n @foreignKey('Content', { required: true })\n contentId = '';\n\n @foreignKey('ContentVersion')\n contentVersionId = '';\n\n @crossPackageRef('@happyvertical/smrt-facts:Fact')\n factId = '';\n\n @crossPackageRef('@happyvertical/smrt-facts:Fact')\n replacementFactId = '';\n correctionType: ContentCorrectionType = 'fact';\n status: ContentCorrectionStatus = 'draft';\n summary = '';\n incorrectText = '';\n correctedText = '';\n publicNote = '';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n publishedAt: Date | null = null;\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentCorrectionOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.contentVersionId !== undefined)\n this.contentVersionId = options.contentVersionId;\n if (options.factId !== undefined) this.factId = options.factId;\n if (options.replacementFactId !== undefined)\n this.replacementFactId = options.replacementFactId;\n if (options.correctionType !== undefined)\n this.correctionType = options.correctionType;\n if (options.status !== undefined) this.status = options.status;\n if (options.summary !== undefined) this.summary = options.summary;\n if (options.incorrectText !== undefined)\n this.incorrectText = options.incorrectText;\n if (options.correctedText !== undefined)\n this.correctedText = options.correctedText;\n if (options.publicNote !== undefined) this.publicNote = options.publicNote;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.publishedAt !== undefined)\n this.publishedAt = options.publishedAt;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentCorrection } from './content-correction';\n\nexport class ContentCorrectionCollection extends SmrtCollection<ContentCorrection> {\n static readonly _itemClass = ContentCorrection;\n\n async listForContent(contentId: string): Promise<ContentCorrection[]> {\n return this.list({\n where: { contentId },\n orderBy: 'created_at DESC',\n });\n }\n\n async getPublishedForContent(\n contentId: string,\n ): Promise<ContentCorrection[]> {\n return this.list({\n where: { contentId, status: 'published' },\n orderBy: 'published_at DESC',\n });\n }\n\n async issue(\n options: ConstructorParameters<typeof ContentCorrection>[0],\n ): Promise<ContentCorrection> {\n if (!options) {\n return this.create({});\n }\n\n return this.create({\n ...options,\n metadata:\n typeof options.metadata === 'string'\n ? options.metadata\n : options.metadata\n ? JSON.stringify(options.metadata)\n : undefined,\n });\n }\n}\n","'use strict';\n\nconst nameStartChar = ':A-Za-z_\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD';\nconst nameChar = nameStartChar + '\\\\-.\\\\d\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040';\nexport const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*';\nconst regexName = new RegExp('^' + nameRegexp + '$');\n\nexport function getAllMatches(string, regex) {\n const matches = [];\n let match = regex.exec(string);\n while (match) {\n const allmatches = [];\n allmatches.startIndex = regex.lastIndex - match[0].length;\n const len = match.length;\n for (let index = 0; index < len; index++) {\n allmatches.push(match[index]);\n }\n matches.push(allmatches);\n match = regex.exec(string);\n }\n return matches;\n}\n\nexport const isName = function (string) {\n const match = regexName.exec(string);\n return !(match === null || typeof match === 'undefined');\n}\n\nexport function isExist(v) {\n return typeof v !== 'undefined';\n}\n\nexport function isEmptyObject(obj) {\n return Object.keys(obj).length === 0;\n}\n\nexport function getValue(v) {\n if (exports.isExist(v)) {\n return v;\n } else {\n return '';\n }\n}\n\n/**\n * Dangerous property names that could lead to prototype pollution or security issues\n */\nexport const DANGEROUS_PROPERTY_NAMES = [\n // '__proto__',\n // 'constructor',\n // 'prototype',\n 'hasOwnProperty',\n 'toString',\n 'valueOf',\n '__defineGetter__',\n '__defineSetter__',\n '__lookupGetter__',\n '__lookupSetter__'\n];\n\nexport const criticalProperties = [\"__proto__\", \"constructor\", \"prototype\"];","'use strict';\n\nimport { getAllMatches, isName } from './util.js';\n\nconst defaultOptions = {\n allowBooleanAttributes: false, //A tag can have attributes without any value\n unpairedTags: []\n};\n\n//const tagsPattern = new RegExp(\"<\\\\/?([\\\\w:\\\\-_\\.]+)\\\\s*\\/?>\",\"g\");\nexport function validate(xmlData, options) {\n options = Object.assign({}, defaultOptions, options);\n\n //xmlData = xmlData.replace(/(\\r\\n|\\n|\\r)/gm,\"\");//make it single line\n //xmlData = xmlData.replace(/(^\\s*<\\?xml.*?\\?>)/g,\"\");//Remove XML starting tag\n //xmlData = xmlData.replace(/(<!DOCTYPE[\\s\\w\\\"\\.\\/\\-\\:]+(\\[.*\\])*\\s*>)/g,\"\");//Remove DOCTYPE\n const tags = [];\n let tagFound = false;\n\n //indicates that the root tag has been closed (aka. depth 0 has been reached)\n let reachedRoot = false;\n\n if (xmlData[0] === '\\ufeff') {\n // check for byte order mark (BOM)\n xmlData = xmlData.substr(1);\n }\n\n for (let i = 0; i < xmlData.length; i++) {\n\n if (xmlData[i] === '<' && xmlData[i + 1] === '?') {\n i += 2;\n i = readPI(xmlData, i);\n if (i.err) return i;\n } else if (xmlData[i] === '<') {\n //starting of tag\n //read until you reach to '>' avoiding any '>' in attribute value\n let tagStartPos = i;\n i++;\n\n if (xmlData[i] === '!') {\n i = readCommentAndCDATA(xmlData, i);\n continue;\n } else {\n let closingTag = false;\n if (xmlData[i] === '/') {\n //closing tag\n closingTag = true;\n i++;\n }\n //read tagname\n let tagName = '';\n for (; i < xmlData.length &&\n xmlData[i] !== '>' &&\n xmlData[i] !== ' ' &&\n xmlData[i] !== '\\t' &&\n xmlData[i] !== '\\n' &&\n xmlData[i] !== '\\r'; i++\n ) {\n tagName += xmlData[i];\n }\n tagName = tagName.trim();\n //console.log(tagName);\n\n if (tagName[tagName.length - 1] === '/') {\n //self closing tag without attributes\n tagName = tagName.substring(0, tagName.length - 1);\n //continue;\n i--;\n }\n if (!validateTagName(tagName)) {\n let msg;\n if (tagName.trim().length === 0) {\n msg = \"Invalid space after '<'.\";\n } else {\n msg = \"Tag '\" + tagName + \"' is an invalid name.\";\n }\n return getErrorObject('InvalidTag', msg, getLineNumberForPosition(xmlData, i));\n }\n\n const result = readAttributeStr(xmlData, i);\n if (result === false) {\n return getErrorObject('InvalidAttr', \"Attributes for '\" + tagName + \"' have open quote.\", getLineNumberForPosition(xmlData, i));\n }\n let attrStr = result.value;\n i = result.index;\n\n if (attrStr[attrStr.length - 1] === '/') {\n //self closing tag\n const attrStrStart = i - attrStr.length;\n attrStr = attrStr.substring(0, attrStr.length - 1);\n const isValid = validateAttributeString(attrStr, options);\n if (isValid === true) {\n tagFound = true;\n //continue; //text may presents after self closing tag\n } else {\n //the result from the nested function returns the position of the error within the attribute\n //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute\n //this gives us the absolute index in the entire xml, which we can use to find the line at last\n return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));\n }\n } else if (closingTag) {\n if (!result.tagClosed) {\n return getErrorObject('InvalidTag', \"Closing tag '\" + tagName + \"' doesn't have proper closing.\", getLineNumberForPosition(xmlData, i));\n } else if (attrStr.trim().length > 0) {\n return getErrorObject('InvalidTag', \"Closing tag '\" + tagName + \"' can't have attributes or invalid starting.\", getLineNumberForPosition(xmlData, tagStartPos));\n } else if (tags.length === 0) {\n return getErrorObject('InvalidTag', \"Closing tag '\" + tagName + \"' has not been opened.\", getLineNumberForPosition(xmlData, tagStartPos));\n } else {\n const otg = tags.pop();\n if (tagName !== otg.tagName) {\n let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);\n return getErrorObject('InvalidTag',\n \"Expected closing tag '\" + otg.tagName + \"' (opened in line \" + openPos.line + \", col \" + openPos.col + \") instead of closing tag '\" + tagName + \"'.\",\n getLineNumberForPosition(xmlData, tagStartPos));\n }\n\n //when there are no more tags, we reached the root level.\n if (tags.length == 0) {\n reachedRoot = true;\n }\n }\n } else {\n const isValid = validateAttributeString(attrStr, options);\n if (isValid !== true) {\n //the result from the nested function returns the position of the error within the attribute\n //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute\n //this gives us the absolute index in the entire xml, which we can use to find the line at last\n return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));\n }\n\n //if the root level has been reached before ...\n if (reachedRoot === true) {\n return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));\n } else if (options.unpairedTags.indexOf(tagName) !== -1) {\n //don't push into stack\n } else {\n tags.push({ tagName, tagStartPos });\n }\n tagFound = true;\n }\n\n //skip tag text value\n //It may include comments and CDATA value\n for (i++; i < xmlData.length; i++) {\n if (xmlData[i] === '<') {\n if (xmlData[i + 1] === '!') {\n //comment or CADATA\n i++;\n i = readCommentAndCDATA(xmlData, i);\n continue;\n } else if (xmlData[i + 1] === '?') {\n i = readPI(xmlData, ++i);\n if (i.err) return i;\n } else {\n break;\n }\n } else if (xmlData[i] === '&') {\n const afterAmp = validateAmpersand(xmlData, i);\n if (afterAmp == -1)\n return getErrorObject('InvalidChar', \"char '&' is not expected.\", getLineNumberForPosition(xmlData, i));\n i = afterAmp;\n } else {\n if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {\n return getErrorObject('InvalidXml', \"Extra text at the end\", getLineNumberForPosition(xmlData, i));\n }\n }\n } //end of reading tag text value\n if (xmlData[i] === '<') {\n i--;\n }\n }\n } else {\n if (isWhiteSpace(xmlData[i])) {\n continue;\n }\n return getErrorObject('InvalidChar', \"char '\" + xmlData[i] + \"' is not expected.\", getLineNumberForPosition(xmlData, i));\n }\n }\n\n if (!tagFound) {\n return getErrorObject('InvalidXml', 'Start tag expected.', 1);\n } else if (tags.length == 1) {\n return getErrorObject('InvalidTag', \"Unclosed tag '\" + tags[0].tagName + \"'.\", getLineNumberForPosition(xmlData, tags[0].tagStartPos));\n } else if (tags.length > 0) {\n return getErrorObject('InvalidXml', \"Invalid '\" +\n JSON.stringify(tags.map(t => t.tagName), null, 4).replace(/\\r?\\n/g, '') +\n \"' found.\", { line: 1, col: 1 });\n }\n\n return true;\n};\n\nfunction isWhiteSpace(char) {\n return char === ' ' || char === '\\t' || char === '\\n' || char === '\\r';\n}\n/**\n * Read Processing insstructions and skip\n * @param {*} xmlData\n * @param {*} i\n */\nfunction readPI(xmlData, i) {\n const start = i;\n for (; i < xmlData.length; i++) {\n if (xmlData[i] == '?' || xmlData[i] == ' ') {\n //tagname\n const tagname = xmlData.substr(start, i - start);\n if (i > 5 && tagname === 'xml') {\n return getErrorObject('InvalidXml', 'XML declaration allowed only at the start of the document.', getLineNumberForPosition(xmlData, i));\n } else if (xmlData[i] == '?' && xmlData[i + 1] == '>') {\n //check if valid attribut string\n i++;\n break;\n } else {\n continue;\n }\n }\n }\n return i;\n}\n\nfunction readCommentAndCDATA(xmlData, i) {\n if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {\n //comment\n for (i += 3; i < xmlData.length; i++) {\n if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {\n i += 2;\n break;\n }\n }\n } else if (\n xmlData.length > i + 8 &&\n xmlData[i + 1] === 'D' &&\n xmlData[i + 2] === 'O' &&\n xmlData[i + 3] === 'C' &&\n xmlData[i + 4] === 'T' &&\n xmlData[i + 5] === 'Y' &&\n xmlData[i + 6] === 'P' &&\n xmlData[i + 7] === 'E'\n ) {\n let angleBracketsCount = 1;\n for (i += 8; i < xmlData.length; i++) {\n if (xmlData[i] === '<') {\n angleBracketsCount++;\n } else if (xmlData[i] === '>') {\n angleBracketsCount--;\n if (angleBracketsCount === 0) {\n break;\n }\n }\n }\n } else if (\n xmlData.length > i + 9 &&\n xmlData[i + 1] === '[' &&\n xmlData[i + 2] === 'C' &&\n xmlData[i + 3] === 'D' &&\n xmlData[i + 4] === 'A' &&\n xmlData[i + 5] === 'T' &&\n xmlData[i + 6] === 'A' &&\n xmlData[i + 7] === '['\n ) {\n for (i += 8; i < xmlData.length; i++) {\n if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {\n i += 2;\n break;\n }\n }\n }\n\n return i;\n}\n\nconst doubleQuote = '\"';\nconst singleQuote = \"'\";\n\n/**\n * Keep reading xmlData until '<' is found outside the attribute value.\n * @param {string} xmlData\n * @param {number} i\n */\nfunction readAttributeStr(xmlData, i) {\n let attrStr = '';\n let startChar = '';\n let tagClosed = false;\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {\n if (startChar === '') {\n startChar = xmlData[i];\n } else if (startChar !== xmlData[i]) {\n //if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa\n } else {\n startChar = '';\n }\n } else if (xmlData[i] === '>') {\n if (startChar === '') {\n tagClosed = true;\n break;\n }\n }\n attrStr += xmlData[i];\n }\n if (startChar !== '') {\n return false;\n }\n\n return {\n value: attrStr,\n index: i,\n tagClosed: tagClosed\n };\n}\n\n/**\n * Select all the attributes whether valid or invalid.\n */\nconst validAttrStrRegxp = new RegExp('(\\\\s*)([^\\\\s=]+)(\\\\s*=)?(\\\\s*([\\'\"])(([\\\\s\\\\S])*?)\\\\5)?', 'g');\n\n//attr, =\"sd\", a=\"amit's\", a=\"sd\"b=\"saf\", ab cd=\"\"\n\nfunction validateAttributeString(attrStr, options) {\n //console.log(\"start:\"+attrStr+\":end\");\n\n //if(attrStr.trim().length === 0) return true; //empty string\n\n const matches = getAllMatches(attrStr, validAttrStrRegxp);\n const attrNames = {};\n\n for (let i = 0; i < matches.length; i++) {\n if (matches[i][1].length === 0) {\n //nospace before attribute name: a=\"sd\"b=\"saf\"\n return getErrorObject('InvalidAttr', \"Attribute '\" + matches[i][2] + \"' has no space in starting.\", getPositionFromMatch(matches[i]))\n } else if (matches[i][3] !== undefined && matches[i][4] === undefined) {\n return getErrorObject('InvalidAttr', \"Attribute '\" + matches[i][2] + \"' is without value.\", getPositionFromMatch(matches[i]));\n } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {\n //independent attribute: ab\n return getErrorObject('InvalidAttr', \"boolean attribute '\" + matches[i][2] + \"' is not allowed.\", getPositionFromMatch(matches[i]));\n }\n /* else if(matches[i][6] === undefined){//attribute without value: ab=\n return { err: { code:\"InvalidAttr\",msg:\"attribute \" + matches[i][2] + \" has no value assigned.\"}};\n } */\n const attrName = matches[i][2];\n if (!validateAttrName(attrName)) {\n return getErrorObject('InvalidAttr', \"Attribute '\" + attrName + \"' is an invalid name.\", getPositionFromMatch(matches[i]));\n }\n if (!Object.prototype.hasOwnProperty.call(attrNames, attrName)) {\n //check for duplicate attribute.\n attrNames[attrName] = 1;\n } else {\n return getErrorObject('InvalidAttr', \"Attribute '\" + attrName + \"' is repeated.\", getPositionFromMatch(matches[i]));\n }\n }\n\n return true;\n}\n\nfunction validateNumberAmpersand(xmlData, i) {\n let re = /\\d/;\n if (xmlData[i] === 'x') {\n i++;\n re = /[\\da-fA-F]/;\n }\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === ';')\n return i;\n if (!xmlData[i].match(re))\n break;\n }\n return -1;\n}\n\nfunction validateAmpersand(xmlData, i) {\n // https://www.w3.org/TR/xml/#dt-charref\n i++;\n if (xmlData[i] === ';')\n return -1;\n if (xmlData[i] === '#') {\n i++;\n return validateNumberAmpersand(xmlData, i);\n }\n let count = 0;\n for (; i < xmlData.length; i++, count++) {\n if (xmlData[i].match(/\\w/) && count < 20)\n continue;\n if (xmlData[i] === ';')\n break;\n return -1;\n }\n return i;\n}\n\nfunction getErrorObject(code, message, lineNumber) {\n return {\n err: {\n code: code,\n msg: message,\n line: lineNumber.line || lineNumber,\n col: lineNumber.col,\n },\n };\n}\n\nfunction validateAttrName(attrName) {\n return isName(attrName);\n}\n\n// const startsWithXML = /^xml/i;\n\nfunction validateTagName(tagname) {\n return isName(tagname) /* && !tagname.match(startsWithXML) */;\n}\n\n//this function returns the line number for the character at the given index\nfunction getLineNumberForPosition(xmlData, index) {\n const lines = xmlData.substring(0, index).split(/\\r?\\n/);\n return {\n line: lines.length,\n\n // column number is last line's length + 1, because column numbering starts at 1:\n col: lines[lines.length - 1].length + 1\n };\n}\n\n//this function returns the position of the first character of match within attrStr\nfunction getPositionFromMatch(match) {\n return match.startIndex + match[1].length;\n}\n","// ---------------------------------------------------------------------------\n// Complete HTML5 named entity reference\n// Organized by logical categories for easy maintenance and selective importing\n// ---------------------------------------------------------------------------\n\n/**\n * Basic Latin & Special Characters\n * @type {Record<string, string>}\n */\nexport const BASIC_LATIN = {\n amp: '&',\n AMP: '&',\n lt: '<',\n LT: '<',\n gt: '>',\n GT: '>',\n quot: '\"',\n QUOT: '\"',\n apos: \"'\",\n lsquo: '‘',\n rsquo: '’',\n ldquo: '“',\n rdquo: '”',\n lsquor: '‚',\n rsquor: '’',\n ldquor: '„',\n bdquo: '„',\n comma: ',',\n period: '.',\n colon: ':',\n semi: ';',\n excl: '!',\n quest: '?',\n num: '#',\n dollar: '$',\n percent: '%',\n ast: '*',\n commat: '@',\n lowbar: '_',\n verbar: '|',\n vert: '|',\n sol: '/',\n bsol: '\\\\',\n lbrace: '{',\n rbrace: '}',\n lbrack: '[',\n rbrack: ']',\n lpar: '(',\n rpar: ')',\n nbsp: '\\u00a0',\n iexcl: '¡',\n cent: '¢',\n pound: '£',\n curren: '¤',\n yen: '¥',\n brvbar: '¦',\n sect: '§',\n uml: '¨',\n copy: '©',\n COPY: '©',\n ordf: 'ª',\n laquo: '«',\n not: '¬',\n shy: '\\u00ad',\n reg: '®',\n REG: '®',\n macr: '¯',\n deg: '°',\n plusmn: '±',\n sup2: '²',\n sup3: '³',\n acute: '´',\n micro: 'µ',\n para: '¶',\n middot: '·',\n cedil: '¸',\n sup1: '¹',\n ordm: 'º',\n raquo: '»',\n frac14: '¼',\n frac12: '½',\n half: '½',\n frac34: '¾',\n iquest: '¿',\n times: '×',\n div: '÷',\n divide: '÷',\n};\n\n/**\n * Latin Extended & Accented Letters (A-Z)\n * @type {Record<string, string>}\n */\nexport const LATIN_ACCENTS = {\n Agrave: 'À',\n agrave: 'à',\n Aacute: 'Á',\n aacute: 'á',\n Acirc: 'Â',\n acirc: 'â',\n Atilde: 'Ã',\n atilde: 'ã',\n Auml: 'Ä',\n auml: 'ä',\n Aring: 'Å',\n aring: 'å',\n AElig: 'Æ',\n aelig: 'æ',\n Ccedil: 'Ç',\n ccedil: 'ç',\n Egrave: 'È',\n egrave: 'è',\n Eacute: 'É',\n eacute: 'é',\n Ecirc: 'Ê',\n ecirc: 'ê',\n Euml: 'Ë',\n euml: 'ë',\n Igrave: 'Ì',\n igrave: 'ì',\n Iacute: 'Í',\n iacute: 'í',\n Icirc: 'Î',\n icirc: 'î',\n Iuml: 'Ï',\n iuml: 'ï',\n ETH: 'Ð',\n eth: 'ð',\n Ntilde: 'Ñ',\n ntilde: 'ñ',\n Ograve: 'Ò',\n ograve: 'ò',\n Oacute: 'Ó',\n oacute: 'ó',\n Ocirc: 'Ô',\n ocirc: 'ô',\n Otilde: 'Õ',\n otilde: 'õ',\n Ouml: 'Ö',\n ouml: 'ö',\n Oslash: 'Ø',\n oslash: 'ø',\n Ugrave: 'Ù',\n ugrave: 'ù',\n Uacute: 'Ú',\n uacute: 'ú',\n Ucirc: 'Û',\n ucirc: 'û',\n Uuml: 'Ü',\n uuml: 'ü',\n Yacute: 'Ý',\n yacute: 'ý',\n THORN: 'Þ',\n thorn: 'þ',\n szlig: 'ß',\n yuml: 'ÿ',\n Yuml: 'Ÿ',\n};\n\n/**\n * Latin Extended (Letters with diacritics)\n * @type {Record<string, string>}\n */\nexport const LATIN_EXTENDED = {\n Amacr: 'Ā',\n amacr: 'ā',\n Abreve: 'Ă',\n abreve: 'ă',\n Aogon: 'Ą',\n aogon: 'ą',\n Cacute: 'Ć',\n cacute: 'ć',\n Ccirc: 'Ĉ',\n ccirc: 'ĉ',\n Cdot: 'Ċ',\n cdot: 'ċ',\n Ccaron: 'Č',\n ccaron: 'č',\n Dcaron: 'Ď',\n dcaron: 'ď',\n Dstrok: 'Đ',\n dstrok: 'đ',\n Emacr: 'Ē',\n emacr: 'ē',\n Ecaron: 'Ě',\n ecaron: 'ě',\n Edot: 'Ė',\n edot: 'ė',\n Eogon: 'Ę',\n eogon: 'ę',\n Gcirc: 'Ĝ',\n gcirc: 'ĝ',\n Gbreve: 'Ğ',\n gbreve: 'ğ',\n Gdot: 'Ġ',\n gdot: 'ġ',\n Gcedil: 'Ģ',\n Hcirc: 'Ĥ',\n hcirc: 'ĥ',\n Hstrok: 'Ħ',\n hstrok: 'ħ',\n Itilde: 'Ĩ',\n itilde: 'ĩ',\n Imacr: 'Ī',\n imacr: 'ī',\n Iogon: 'Į',\n iogon: 'į',\n Idot: 'İ',\n IJlig: 'IJ',\n ijlig: 'ij',\n Jcirc: 'Ĵ',\n jcirc: 'ĵ',\n Kcedil: 'Ķ',\n kcedil: 'ķ',\n kgreen: 'ĸ',\n Lacute: 'Ĺ',\n lacute: 'ĺ',\n Lcedil: 'Ļ',\n lcedil: 'ļ',\n Lcaron: 'Ľ',\n lcaron: 'ľ',\n Lmidot: 'Ŀ',\n lmidot: 'ŀ',\n Lstrok: 'Ł',\n lstrok: 'ł',\n Nacute: 'Ń',\n nacute: 'ń',\n Ncaron: 'Ň',\n ncaron: 'ň',\n Ncedil: 'Ņ',\n ncedil: 'ņ',\n ENG: 'Ŋ',\n eng: 'ŋ',\n Omacr: 'Ō',\n omacr: 'ō',\n Odblac: 'Ő',\n odblac: 'ő',\n OElig: 'Œ',\n oelig: 'œ',\n Racute: 'Ŕ',\n racute: 'ŕ',\n Rcaron: 'Ř',\n rcaron: 'ř',\n Rcedil: 'Ŗ',\n rcedil: 'ŗ',\n Sacute: 'Ś',\n sacute: 'ś',\n Scirc: 'Ŝ',\n scirc: 'ŝ',\n Scedil: 'Ş',\n scedil: 'ş',\n Scaron: 'Š',\n scaron: 'š',\n Tcedil: 'Ţ',\n tcedil: 'ţ',\n Tcaron: 'Ť',\n tcaron: 'ť',\n Tstrok: 'Ŧ',\n tstrok: 'ŧ',\n Utilde: 'Ũ',\n utilde: 'ũ',\n Umacr: 'Ū',\n umacr: 'ū',\n Ubreve: 'Ŭ',\n ubreve: 'ŭ',\n Uring: 'Ů',\n uring: 'ů',\n Udblac: 'Ű',\n udblac: 'ű',\n Uogon: 'Ų',\n uogon: 'ų',\n Wcirc: 'Ŵ',\n wcirc: 'ŵ',\n Ycirc: 'Ŷ',\n ycirc: 'ŷ',\n Zacute: 'Ź',\n zacute: 'ź',\n Zdot: 'Ż',\n zdot: 'ż',\n Zcaron: 'Ž',\n zcaron: 'ž',\n};\n\n/**\n * Greek Letters\n * @type {Record<string, string>}\n */\nexport const GREEK = {\n Alpha: 'Α',\n alpha: 'α',\n Beta: 'Β',\n beta: 'β',\n Gamma: 'Γ',\n gamma: 'γ',\n Delta: 'Δ',\n delta: 'δ',\n Epsilon: 'Ε',\n epsilon: 'ε',\n epsiv: 'ϵ',\n varepsilon: 'ϵ',\n Zeta: 'Ζ',\n zeta: 'ζ',\n Eta: 'Η',\n eta: 'η',\n Theta: 'Θ',\n theta: 'θ',\n thetasym: 'ϑ',\n vartheta: 'ϑ',\n Iota: 'Ι',\n iota: 'ι',\n Kappa: 'Κ',\n kappa: 'κ',\n kappav: 'ϰ',\n varkappa: 'ϰ',\n Lambda: 'Λ',\n lambda: 'λ',\n Mu: 'Μ',\n mu: 'μ',\n Nu: 'Ν',\n nu: 'ν',\n Xi: 'Ξ',\n xi: 'ξ',\n Omicron: 'Ο',\n omicron: 'ο',\n Pi: 'Π',\n pi: 'π',\n piv: 'ϖ',\n varpi: 'ϖ',\n Rho: 'Ρ',\n rho: 'ρ',\n rhov: 'ϱ',\n varrho: 'ϱ',\n Sigma: 'Σ',\n sigma: 'σ',\n sigmaf: 'ς',\n sigmav: 'ς',\n varsigma: 'ς',\n Tau: 'Τ',\n tau: 'τ',\n Upsilon: 'Υ',\n upsilon: 'υ',\n upsi: 'υ',\n Upsi: 'ϒ',\n upsih: 'ϒ',\n Phi: 'Φ',\n phi: 'φ',\n phiv: 'ϕ',\n varphi: 'ϕ',\n Chi: 'Χ',\n chi: 'χ',\n Psi: 'Ψ',\n psi: 'ψ',\n Omega: 'Ω',\n omega: 'ω',\n ohm: 'Ω',\n Gammad: 'Ϝ',\n gammad: 'ϝ',\n digamma: 'ϝ',\n};\n\n/**\n * Cyrillic Letters\n * @type {Record<string, string>}\n */\nexport const CYRILLIC = {\n Afr: '𝔄',\n afr: '𝔞',\n Acy: 'А',\n acy: 'а',\n Bcy: 'Б',\n bcy: 'б',\n Vcy: 'В',\n vcy: 'в',\n Gcy: 'Г',\n gcy: 'г',\n Dcy: 'Д',\n dcy: 'д',\n IEcy: 'Е',\n iecy: 'е',\n IOcy: 'Ё',\n iocy: 'ё',\n ZHcy: 'Ж',\n zhcy: 'ж',\n Zcy: 'З',\n zcy: 'з',\n Icy: 'И',\n icy: 'и',\n Jcy: 'Й',\n jcy: 'й',\n Kcy: 'К',\n kcy: 'к',\n Lcy: 'Л',\n lcy: 'л',\n Mcy: 'М',\n mcy: 'м',\n Ncy: 'Н',\n ncy: 'н',\n Ocy: 'О',\n ocy: 'о',\n Pcy: 'П',\n pcy: 'п',\n Rcy: 'Р',\n rcy: 'р',\n Scy: 'С',\n scy: 'с',\n Tcy: 'Т',\n tcy: 'т',\n Ucy: 'У',\n ucy: 'у',\n Fcy: 'Ф',\n fcy: 'ф',\n KHcy: 'Х',\n khcy: 'х',\n TScy: 'Ц',\n tscy: 'ц',\n CHcy: 'Ч',\n chcy: 'ч',\n SHcy: 'Ш',\n shcy: 'ш',\n SHCHcy: 'Щ',\n shchcy: 'щ',\n HARDcy: 'Ъ',\n hardcy: 'ъ',\n Ycy: 'Ы',\n ycy: 'ы',\n SOFTcy: 'Ь',\n softcy: 'ь',\n Ecy: 'Э',\n ecy: 'э',\n YUcy: 'Ю',\n yucy: 'ю',\n YAcy: 'Я',\n yacy: 'я',\n DJcy: 'Ђ',\n djcy: 'ђ',\n GJcy: 'Ѓ',\n gjcy: 'ѓ',\n Jukcy: 'Є',\n jukcy: 'є',\n DScy: 'Ѕ',\n dscy: 'ѕ',\n Iukcy: 'І',\n iukcy: 'і',\n YIcy: 'Ї',\n yicy: 'ї',\n Jsercy: 'Ј',\n jsercy: 'ј',\n LJcy: 'Љ',\n ljcy: 'љ',\n NJcy: 'Њ',\n njcy: 'њ',\n TSHcy: 'Ћ',\n tshcy: 'ћ',\n KJcy: 'Ќ',\n kjcy: 'ќ',\n Ubrcy: 'Ў',\n ubrcy: 'ў',\n DZcy: 'Џ',\n dzcy: 'џ',\n};\n\n/**\n * Mathematical Operators & Relations\n * @type {Record<string, string>}\n */\nexport const MATH = {\n plus: '+',\n pm: '±',\n times: '×',\n div: '÷',\n divide: '÷',\n sdot: '⋅',\n star: '☆',\n starf: '★',\n bigstar: '★',\n lowast: '∗',\n ast: '*',\n midast: '*',\n compfn: '∘',\n smallcircle: '∘',\n bullet: '•',\n bull: '•',\n nbsp: '\\u00a0',\n hellip: '…',\n mldr: '…',\n prime: '′',\n Prime: '″',\n tprime: '‴',\n bprime: '‵',\n backprime: '‵',\n minus: '−',\n minusd: '∸',\n dotminus: '∸',\n plusdo: '∔',\n dotplus: '∔',\n plusmn: '±',\n minusplus: '∓',\n mnplus: '∓',\n mp: '∓',\n setminus: '∖',\n smallsetminus: '∖',\n Backslash: '∖',\n setmn: '∖',\n ssetmn: '∖',\n lowbar: '_',\n verbar: '|',\n vert: '|',\n VerticalLine: '|',\n colon: ':',\n Colon: '∷',\n Proportion: '∷',\n ratio: '∶',\n equals: '=',\n ne: '≠',\n nequiv: '≢',\n equiv: '≡',\n Congruent: '≡',\n sim: '∼',\n thicksim: '∼',\n thksim: '∼',\n sime: '≃',\n simeq: '≃',\n TildeEqual: '≃',\n asymp: '≈',\n approx: '≈',\n thickapprox: '≈',\n thkap: '≈',\n TildeTilde: '≈',\n ncong: '≇',\n cong: '≅',\n TildeFullEqual: '≅',\n asympeq: '≍',\n CupCap: '≍',\n bump: '≎',\n Bumpeq: '≎',\n HumpDownHump: '≎',\n bumpe: '≏',\n bumpeq: '≏',\n HumpEqual: '≏',\n le: '≤',\n LessEqual: '≤',\n ge: '≥',\n GreaterEqual: '≥',\n lesseqgtr: '⋚',\n lesseqqgtr: '⪋',\n greater: '>',\n less: '<',\n};\n\n/**\n * Mathematical Operators (Advanced)\n * @type {Record<string, string>}\n */\nexport const MATH_ADVANCED = {\n alefsym: 'ℵ',\n aleph: 'ℵ',\n beth: 'ℶ',\n gimel: 'ℷ',\n daleth: 'ℸ',\n forall: '∀',\n ForAll: '∀',\n part: '∂',\n PartialD: '∂',\n exist: '∃',\n Exists: '∃',\n nexist: '∄',\n nexists: '∄',\n empty: '∅',\n emptyset: '∅',\n emptyv: '∅',\n varnothing: '∅',\n nabla: '∇',\n Del: '∇',\n isin: '∈',\n isinv: '∈',\n in: '∈',\n Element: '∈',\n notin: '∉',\n notinva: '∉',\n ni: '∋',\n niv: '∋',\n SuchThat: '∋',\n ReverseElement: '∋',\n notni: '∌',\n notniva: '∌',\n prod: '∏',\n Product: '∏',\n coprod: '∐',\n Coproduct: '∐',\n sum: '∑',\n Sum: '∑',\n minus: '−',\n mp: '∓',\n plusdo: '∔',\n dotplus: '∔',\n setminus: '∖',\n lowast: '∗',\n radic: '√',\n Sqrt: '√',\n prop: '∝',\n propto: '∝',\n Proportional: '∝',\n varpropto: '∝',\n infin: '∞',\n infintie: '⧝',\n ang: '∠',\n angle: '∠',\n angmsd: '∡',\n measuredangle: '∡',\n angsph: '∢',\n mid: '∣',\n VerticalBar: '∣',\n nmid: '∤',\n nsmid: '∤',\n npar: '∦',\n parallel: '∥',\n spar: '∥',\n nparallel: '∦',\n nspar: '∦',\n and: '∧',\n wedge: '∧',\n or: '∨',\n vee: '∨',\n cap: '∩',\n cup: '∪',\n int: '∫',\n Integral: '∫',\n conint: '∮',\n ContourIntegral: '∮',\n Conint: '∯',\n DoubleContourIntegral: '∯',\n Cconint: '∰',\n there4: '∴',\n therefore: '∴',\n Therefore: '∴',\n becaus: '∵',\n because: '∵',\n Because: '∵',\n ratio: '∶',\n Proportion: '∷',\n minusd: '∸',\n dotminus: '∸',\n mDDot: '∺',\n homtht: '∻',\n sim: '∼',\n bsimg: '∽',\n backsim: '∽',\n ac: '∾',\n mstpos: '∾',\n acd: '∿',\n VerticalTilde: '≀',\n wr: '≀',\n wreath: '≀',\n nsime: '≄',\n nsimeq: '≄',\n ncong: '≇',\n simne: '≆',\n ncongdot: '⩭̸',\n ngsim: '≵',\n nsim: '≁',\n napprox: '≉',\n nap: '≉',\n ngeq: '≱',\n nge: '≱',\n nleq: '≰',\n nle: '≰',\n ngtr: '≯',\n ngt: '≯',\n nless: '≮',\n nlt: '≮',\n nprec: '⊀',\n npr: '⊀',\n nsucc: '⊁',\n nsc: '⊁',\n};\n\n/**\n * Arrows\n * @type {Record<string, string>}\n */\nexport const ARROWS = {\n larr: '←',\n leftarrow: '←',\n LeftArrow: '←',\n uarr: '↑',\n uparrow: '↑',\n UpArrow: '↑',\n rarr: '→',\n rightarrow: '→',\n RightArrow: '→',\n darr: '↓',\n downarrow: '↓',\n DownArrow: '↓',\n harr: '↔',\n leftrightarrow: '↔',\n LeftRightArrow: '↔',\n varr: '↕',\n updownarrow: '↕',\n UpDownArrow: '↕',\n nwarr: '↖',\n nwarrow: '↖',\n UpperLeftArrow: '↖',\n nearr: '↗',\n nearrow: '↗',\n UpperRightArrow: '↗',\n searr: '↘',\n searrow: '↘',\n LowerRightArrow: '↘',\n swarr: '↙',\n swarrow: '↙',\n LowerLeftArrow: '↙',\n lArr: '⇐',\n Leftarrow: '⇐',\n uArr: '⇑',\n Uparrow: '⇑',\n rArr: '⇒',\n Rightarrow: '⇒',\n dArr: '⇓',\n Downarrow: '⇓',\n hArr: '⇔',\n Leftrightarrow: '⇔',\n iff: '⇔',\n vArr: '⇕',\n Updownarrow: '⇕',\n lAarr: '⇚',\n Lleftarrow: '⇚',\n rAarr: '⇛',\n Rrightarrow: '⇛',\n lrarr: '⇆',\n leftrightarrows: '⇆',\n rlarr: '⇄',\n rightleftarrows: '⇄',\n lrhar: '⇋',\n leftrightharpoons: '⇋',\n ReverseEquilibrium: '⇋',\n rlhar: '⇌',\n rightleftharpoons: '⇌',\n Equilibrium: '⇌',\n udarr: '⇅',\n UpArrowDownArrow: '⇅',\n duarr: '⇵',\n DownArrowUpArrow: '⇵',\n llarr: '⇇',\n leftleftarrows: '⇇',\n rrarr: '⇉',\n rightrightarrows: '⇉',\n ddarr: '⇊',\n downdownarrows: '⇊',\n har: '↽',\n lhard: '↽',\n leftharpoondown: '↽',\n lharu: '↼',\n leftharpoonup: '↼',\n rhard: '⇁',\n rightharpoondown: '⇁',\n rharu: '⇀',\n rightharpoonup: '⇀',\n lsh: '↰',\n Lsh: '↰',\n rsh: '↱',\n Rsh: '↱',\n ldsh: '↲',\n rdsh: '↳',\n hookleftarrow: '↩',\n hookrightarrow: '↪',\n mapstoleft: '↤',\n mapstoup: '↥',\n map: '↦',\n mapsto: '↦',\n mapstodown: '↧',\n crarr: '↵',\n nleftarrow: '↚',\n nleftrightarrow: '↮',\n nrightarrow: '↛',\n nrarr: '↛',\n larrtl: '↢',\n rarrtl: '↣',\n leftarrowtail: '↢',\n rightarrowtail: '↣',\n twoheadleftarrow: '↞',\n twoheadrightarrow: '↠',\n Larr: '↞',\n Rarr: '↠',\n larrhk: '↩',\n rarrhk: '↪',\n larrlp: '↫',\n looparrowleft: '↫',\n rarrlp: '↬',\n looparrowright: '↬',\n harrw: '↭',\n leftrightsquigarrow: '↭',\n nrarrw: '↝̸',\n rarrw: '↝',\n rightsquigarrow: '↝',\n larrbfs: '⤟',\n rarrbfs: '⤠',\n nvHarr: '⤄',\n nvlArr: '⤂',\n nvrArr: '⤃',\n larrfs: '⤝',\n rarrfs: '⤞',\n Map: '⤅',\n larrsim: '⥳',\n rarrsim: '⥴',\n harrcir: '⥈',\n Uarrocir: '⥉',\n lurdshar: '⥊',\n ldrdhar: '⥧',\n ldrushar: '⥋',\n rdldhar: '⥩',\n lrhard: '⥭',\n uharr: '↾',\n uharl: '↿',\n dharr: '⇂',\n dharl: '⇃',\n Uarr: '↟',\n Darr: '↡',\n zigrarr: '⇝',\n nwArr: '⇖',\n neArr: '⇗',\n seArr: '⇘',\n swArr: '⇙',\n nharr: '↮',\n nhArr: '⇎',\n nlarr: '↚',\n nlArr: '⇍',\n nrArr: '⇏',\n larrb: '⇤',\n LeftArrowBar: '⇤',\n rarrb: '⇥',\n RightArrowBar: '⇥',\n};\n\n/**\n * Geometric Shapes\n * @type {Record<string, string>}\n */\nexport const SHAPES = {\n square: '□',\n Square: '□',\n squ: '□',\n squf: '▪',\n squarf: '▪',\n blacksquar: '▪',\n blacksquare: '▪',\n FilledVerySmallSquare: '▪',\n blk34: '▓',\n blk12: '▒',\n blk14: '░',\n block: '█',\n srect: '▭',\n rect: '▭',\n sdot: '⋅',\n sdotb: '⊡',\n dotsquare: '⊡',\n triangle: '▵',\n tri: '▵',\n trine: '▵',\n utri: '▵',\n triangledown: '▿',\n dtri: '▿',\n tridown: '▿',\n triangleleft: '◃',\n ltri: '◃',\n triangleright: '▹',\n rtri: '▹',\n blacktriangle: '▴',\n utrif: '▴',\n blacktriangledown: '▾',\n dtrif: '▾',\n blacktriangleleft: '◂',\n ltrif: '◂',\n blacktriangleright: '▸',\n rtrif: '▸',\n loz: '◊',\n lozenge: '◊',\n blacklozenge: '⧫',\n lozf: '⧫',\n bigcirc: '◯',\n xcirc: '◯',\n circ: 'ˆ',\n Circle: '○',\n cir: '○',\n o: '○',\n bullet: '•',\n bull: '•',\n hellip: '…',\n mldr: '…',\n nldr: '‥',\n boxh: '─',\n HorizontalLine: '─',\n boxv: '│',\n boxdr: '┌',\n boxdl: '┐',\n boxur: '└',\n boxul: '┘',\n boxvr: '├',\n boxvl: '┤',\n boxhd: '┬',\n boxhu: '┴',\n boxvh: '┼',\n boxH: '═',\n boxV: '║',\n boxdR: '╒',\n boxDr: '╓',\n boxDR: '╔',\n boxDl: '╕',\n boxdL: '╖',\n boxDL: '╗',\n boxuR: '╘',\n boxUr: '╙',\n boxUR: '╚',\n boxUl: '╜',\n boxuL: '╛',\n boxUL: '╝',\n boxvR: '╞',\n boxVr: '╟',\n boxVR: '╠',\n boxVl: '╢',\n boxvL: '╡',\n boxVL: '╣',\n boxHd: '╤',\n boxhD: '╥',\n boxHD: '╦',\n boxHu: '╧',\n boxhU: '╨',\n boxHU: '╩',\n boxvH: '╪',\n boxVh: '╫',\n boxVH: '╬',\n};\n\n/**\n * Punctuation & Diacritics\n * @type {Record<string, string>}\n */\nexport const PUNCTUATION = {\n excl: '!',\n iexcl: '¡',\n brvbar: '¦',\n sect: '§',\n uml: '¨',\n copy: '©',\n ordf: 'ª',\n laquo: '«',\n not: '¬',\n shy: '\\u00ad',\n reg: '®',\n macr: '¯',\n deg: '°',\n plusmn: '±',\n sup2: '²',\n sup3: '³',\n acute: '´',\n micro: 'µ',\n para: '¶',\n middot: '·',\n cedil: '¸',\n sup1: '¹',\n ordm: 'º',\n raquo: '»',\n frac14: '¼',\n frac12: '½',\n frac34: '¾',\n iquest: '¿',\n nbsp: '\\u00a0',\n comma: ',',\n period: '.',\n colon: ':',\n semi: ';',\n vert: '|',\n Verbar: '‖',\n verbar: '|',\n dblac: '˝',\n circ: 'ˆ',\n caron: 'ˇ',\n breve: '˘',\n dot: '˙',\n ring: '˚',\n ogon: '˛',\n tilde: '˜',\n DiacriticalGrave: '`',\n DiacriticalAcute: '´',\n DiacriticalTilde: '˜',\n DiacriticalDot: '˙',\n DiacriticalDoubleAcute: '˝',\n grave: '`',\n};\n\n/**\n * Currency Symbols\n * @type {Record<string, string>}\n */\nexport const CURRENCY = {\n cent: '¢',\n pound: '£',\n curren: '¤',\n yen: '¥',\n euro: '€',\n dollar: '$',\n fnof: 'ƒ',\n inr: '₹',\n af: '؋',\n birr: 'ብር',\n peso: '₱',\n rub: '₽',\n won: '₩',\n yuan: '¥',\n cedil: '¸',\n};\n\n/**\n * Fractions\n * @type {Record<string, string>}\n */\nexport const FRACTIONS = {\n frac12: '½',\n half: '½',\n frac13: '⅓',\n frac14: '¼',\n frac15: '⅕',\n frac16: '⅙',\n frac18: '⅛',\n frac23: '⅔',\n frac25: '⅖',\n frac34: '¾',\n frac35: '⅗',\n frac38: '⅜',\n frac45: '⅘',\n frac56: '⅚',\n frac58: '⅝',\n frac78: '⅞',\n frasl: '⁄',\n};\n\n/**\n * Miscellaneous Symbols\n * @type {Record<string, string>}\n */\nexport const MISC_SYMBOLS = {\n trade: '™',\n TRADE: '™',\n telrec: '⌕',\n target: '⌖',\n ulcorn: '⌜',\n ulcorner: '⌜',\n urcorn: '⌝',\n urcorner: '⌝',\n dlcorn: '⌞',\n llcorner: '⌞',\n drcorn: '⌟',\n lrcorner: '⌟',\n intercal: '⊺',\n intcal: '⊺',\n oplus: '⊕',\n CirclePlus: '⊕',\n ominus: '⊖',\n CircleMinus: '⊖',\n otimes: '⊗',\n CircleTimes: '⊗',\n osol: '⊘',\n odot: '⊙',\n CircleDot: '⊙',\n oast: '⊛',\n circledast: '⊛',\n odash: '⊝',\n circleddash: '⊝',\n ocirc: '⊚',\n circledcirc: '⊚',\n boxplus: '⊞',\n plusb: '⊞',\n boxminus: '⊟',\n minusb: '⊟',\n boxtimes: '⊠',\n timesb: '⊠',\n boxdot: '⊡',\n sdotb: '⊡',\n veebar: '⊻',\n vee: '∨',\n barvee: '⊽',\n and: '∧',\n wedge: '∧',\n Cap: '⋒',\n Cup: '⋓',\n Fork: '⋔',\n pitchfork: '⋔',\n epar: '⋕',\n ltlarr: '⥶',\n nvap: '≍⃒',\n nvsim: '∼⃒',\n nvge: '≥⃒',\n nvle: '≤⃒',\n nvlt: '<⃒',\n nvgt: '>⃒',\n nvltrie: '⊴⃒',\n nvrtrie: '⊵⃒',\n Vdash: '⊩',\n dashv: '⊣',\n vDash: '⊨',\n Vvdash: '⊪',\n nvdash: '⊬',\n nvDash: '⊭',\n nVdash: '⊮',\n nVDash: '⊯',\n};\n\nexport const XML = {\n amp: \"&\",\n apos: \"'\",\n gt: \">\",\n lt: \"<\",\n quot: \"\\\"\"\n}\nexport const COMMON_HTML = {\n nbsp: '\\u00a0',\n copy: '\\u00a9',\n reg: '\\u00ae',\n trade: '\\u2122',\n mdash: '\\u2014',\n ndash: '\\u2013',\n hellip: '\\u2026',\n laquo: '\\u00ab',\n raquo: '\\u00bb',\n lsquo: '\\u2018',\n rsquo: '\\u2019',\n ldquo: '\\u201c',\n rdquo: '\\u201d',\n bull: '\\u2022',\n para: '\\u00b6',\n sect: '\\u00a7',\n deg: '\\u00b0',\n frac12: '\\u00bd',\n frac14: '\\u00bc',\n frac34: '\\u00be',\n}\n// ---------------------------------------------------------------------------\n// Note: NUMERIC_ENTITIES (&#NNN; / &#xHH;) are handled by the scanner directly\n// via String.fromCodePoint() without any map lookup.\n// ---------------------------------------------------------------------------","// ---------------------------------------------------------------------------\n// Built-in named entity map (name → replacement string)\n// No regex, no {regex,val} objects — just flat key/value pairs.\n// ---------------------------------------------------------------------------\n\nimport { XML as DEFAULT_XML_ENTITIES } from \"./entities.js\"\n\n// ---------------------------------------------------------------------------\n// Entity hook action constants\n// ---------------------------------------------------------------------------\n\n/**\n * Action constants for `onExternalEntity` and `onInputEntity` hooks.\n *\n * Use these instead of raw strings to avoid typos:\n *\n * @example\n * import EntityDecoder, { ENTITY_ACTION } from './EntityDecoder.js';\n * const dec = new EntityDecoder({\n * onInputEntity: (name, value) => ENTITY_ACTION.BLOCK,\n * });\n */\nexport const ENTITY_ACTION = Object.freeze({\n /** Resolve and expand the entity normally. */\n ALLOW: 'allow',\n /** Silently skip this entity — it will not be registered. */\n BLOCK: 'block',\n /** Throw an error, aborting entity registration entirely. */\n THROW: 'throw',\n});\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst SPECIAL_CHARS = new Set('!?\\\\\\\\/[]$%{}^&*()<>|+');\n\n/**\n * Validate that an entity name contains no dangerous characters.\n * @param {string} name\n * @returns {string} the name, unchanged\n * @throws {Error} on invalid characters\n */\nfunction validateEntityName(name) {\n if (name[0] === '#') {\n throw new Error(`[EntityReplacer] Invalid character '#' in entity name: \"${name}\"`);\n }\n for (const ch of name) {\n if (SPECIAL_CHARS.has(ch)) {\n throw new Error(`[EntityReplacer] Invalid character '${ch}' in entity name: \"${name}\"`);\n }\n }\n return name;\n}\n\n/**\n * Merge one or more entity maps into a flat name→string map.\n * Accepts either:\n * - plain string values: { amp: '&' }\n * - legacy {regex,val} / {regx,val}: { lt: { regex: /.../, val: '<' } }\n *\n * Values containing '&' are skipped (recursive expansion risk).\n *\n * @param {...object} maps\n * @returns {Record<string, string>}\n */\nfunction mergeEntityMaps(...maps) {\n const out = Object.create(null);\n for (const map of maps) {\n if (!map) continue;\n for (const key of Object.keys(map)) {\n const raw = map[key];\n if (typeof raw === 'string') {\n out[key] = raw;\n } else if (raw && typeof raw === 'object' && raw.val !== undefined) {\n // Legacy {regex,val} or {regx,val} — extract the string val only\n const val = raw.val;\n if (typeof val === 'string') {\n out[key] = val;\n }\n // function vals are not supported in the scanner — skip\n }\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// applyLimitsTo helpers\n// ---------------------------------------------------------------------------\n\nconst LIMIT_TIER_EXTERNAL = 'external'; // input/runtime + persistent external maps\nconst LIMIT_TIER_BASE = 'base'; // DEFAULT_XML_ENTITIES + namedEntities (system) maps\nconst LIMIT_TIER_ALL = 'all'; // every entity regardless of tier\n\n/**\n * Resolve `applyLimitsTo` option into a normalised Set of tier strings.\n * Accepted values: 'external' | 'base' | 'all' | string[]\n * Default: 'external' (only untrusted injected entities are counted).\n * @param {string|string[]|undefined} raw\n * @returns {Set<string>}\n */\nfunction parseLimitTiers(raw) {\n if (!raw || raw === LIMIT_TIER_EXTERNAL) return new Set([LIMIT_TIER_EXTERNAL]);\n if (raw === LIMIT_TIER_ALL) return new Set([LIMIT_TIER_ALL]);\n if (raw === LIMIT_TIER_BASE) return new Set([LIMIT_TIER_BASE]);\n if (Array.isArray(raw)) return new Set(raw);\n return new Set([LIMIT_TIER_EXTERNAL]); // safe default for unrecognised values\n}\n\n// ---------------------------------------------------------------------------\n// NCR (Numeric Character Reference) classification\n// ---------------------------------------------------------------------------\n\n// Severity order — higher number = stricter action.\n// Used to enforce minimum action levels for specific codepoint ranges.\nconst NCR_LEVEL = Object.freeze({ allow: 0, leave: 1, remove: 2, throw: 3 });\n\n// XML 1.0 §2.2: allowed chars are #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]\n// Restricted C0: U+0001–U+001F excluding U+0009, U+000A, U+000D\nconst XML10_ALLOWED_C0 = new Set([0x09, 0x0A, 0x0D]);\n\n/**\n * Parse the `ncr` constructor option into flat, hot-path-friendly fields.\n * @param {object|undefined} ncr\n * @returns {{ xmlVersion: number, onLevel: number, nullLevel: number }}\n */\nfunction parseNCRConfig(ncr) {\n if (!ncr) {\n return { xmlVersion: 1.0, onLevel: NCR_LEVEL.allow, nullLevel: NCR_LEVEL.remove };\n }\n const xmlVersion = ncr.xmlVersion === 1.1 ? 1.1 : 1.0;\n const onLevel = NCR_LEVEL[ncr.onNCR] ?? NCR_LEVEL.allow;\n const nullLevel = NCR_LEVEL[ncr.nullNCR] ?? NCR_LEVEL.remove;\n // 'allow' is not meaningful for null — clamp to at least 'remove'\n const clampedNull = Math.max(nullLevel, NCR_LEVEL.remove);\n return { xmlVersion, onLevel, nullLevel: clampedNull };\n}\n\n// ---------------------------------------------------------------------------\n// EntityReplacer\n// ---------------------------------------------------------------------------\n\n/**\n * Single-pass, zero-regex entity replacer for XML/HTML content.\n *\n * Algorithm: scan the string once for '&', read to ';', resolve via map\n * or direct codepoint conversion, build output chunks, join once at the end.\n *\n * Entity lookup priority (highest → lowest):\n * 1. input / runtime (DOCTYPE entities for current document)\n * 2. persistent external (survive across documents)\n * 3. base named map (DEFAULT_XML_ENTITIES + user-supplied namedEntities)\n *\n * Both input and external resolve as the 'external' tier for limit purposes.\n * Base map entities resolve as the 'base' tier.\n *\n * Numeric / hex references (&#NNN; / &#xHH;) are resolved directly via\n * String.fromCodePoint() — no map needed. They count as 'base' tier.\n *\n * @example\n * const replacer = new EntityReplacer({ namedEntities: COMMON_HTML });\n * replacer.setExternalEntities({ brand: 'Acme' });\n *\n * const instance = replacer.reset();\n * instance.addInputEntities({ version: '1.0' });\n * instance.encode('&brand; v&version; <'); // 'Acme v1.0 <'\n */\nexport default class EntityDecoder {\n /**\n * @param {object} [options]\n * @param {object|null} [options.namedEntities] — extra named entities merged into base map\n * @param {object} [options.limit] — security limits\n * @param {number} [options.limit.maxTotalExpansions=0] — 0 = unlimited\n * @param {number} [options.limit.maxExpandedLength=0] — 0 = unlimited\n * @param {'external'|'base'|'all'|string[]} [options.limit.applyLimitsTo='external']\n * Which entity tiers count against the security limits:\n * - 'external' (default) — only input/runtime + persistent external entities\n * - 'base' — only DEFAULT_XML_ENTITIES + namedEntities\n * - 'all' — every entity regardless of tier\n * - string[] — explicit combination, e.g. ['external', 'base']\n * @param {((resolved: string, original: string) => string)|null} [options.postCheck=null]\n * @param {string[]} [options.remove=[]] — entity names (e.g. ['nbsp', '#13']) to delete (replace with empty string)\n * @param {string[]} [options.leave=[]] — entity names to keep as literal (unchanged in output)\n * @param {object} [options.ncr] — Numeric Character Reference controls\n * @param {1.0|1.1} [options.ncr.xmlVersion=1.0]\n * XML version governing which codepoint ranges are restricted:\n * - 1.0 — C0 controls U+0001–U+001F (except U+0009/000A/000D) are prohibited\n * - 1.1 — C0 controls are allowed when written as NCRs; C1 (U+007F–U+009F) decoded as-is\n * @param {'allow'|'leave'|'remove'|'throw'} [options.ncr.onNCR='allow']\n * Base action for numeric references. Severity order: allow < leave < remove < throw.\n * For codepoint ranges that carry a minimum level (surrogates → remove, XML 1.0 C0 → remove),\n * the effective action is max(onNCR, rangeMinimum).\n * @param {'remove'|'throw'} [options.ncr.nullNCR='remove']\n * Action for U+0000 (null). 'allow' and 'leave' are clamped to 'remove' since null is never safe.\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onExternalEntity=null]\n * Hook called when an external entity is registered via `setExternalEntities()` or\n * `addExternalEntity()`. Return `ENTITY_ACTION.ALLOW` to accept the entity,\n * `ENTITY_ACTION.BLOCK` to silently skip it, or `ENTITY_ACTION.THROW` to abort with an error.\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onInputEntity=null]\n * Hook called when an input entity is registered via `addInputEntities()`. Return\n * `ENTITY_ACTION.ALLOW` to accept, `ENTITY_ACTION.BLOCK` to silently skip, or\n * `ENTITY_ACTION.THROW` to abort with an error.\n */\n constructor(options = {}) {\n this._limit = options.limit || {};\n this._maxTotalExpansions = this._limit.maxTotalExpansions || 0;\n this._maxExpandedLength = this._limit.maxExpandedLength || 0;\n this._postCheck = typeof options.postCheck === 'function' ? options.postCheck : r => r;\n this._limitTiers = parseLimitTiers(this._limit.applyLimitsTo ?? LIMIT_TIER_EXTERNAL);\n this._numericAllowed = options.numericAllowed ?? true;\n // Base map: DEFAULT_XML_ENTITIES + user-supplied extras. Immutable after construction.\n this._baseMap = mergeEntityMaps(DEFAULT_XML_ENTITIES, options.namedEntities || null);\n\n // Persistent external entities — survive across documents.\n // Stored as a separate map so reset() never touches them.\n /** @type {Record<string, string>} */\n this._externalMap = Object.create(null);\n\n // Input / runtime entities — current document only, wiped on reset().\n /** @type {Record<string, string>} */\n this._inputMap = Object.create(null);\n\n // Per-document counters\n this._totalExpansions = 0;\n this._expandedLength = 0;\n\n // --- New: remove / leave sets ---\n /** @type {Set<string>} */\n this._removeSet = new Set(options.remove && Array.isArray(options.remove) ? options.remove : []);\n /** @type {Set<string>} */\n this._leaveSet = new Set(options.leave && Array.isArray(options.leave) ? options.leave : []);\n\n // --- NCR config (parsed into flat fields for hot-path speed) ---\n const ncrCfg = parseNCRConfig(options.ncr);\n this._ncrXmlVersion = ncrCfg.xmlVersion;\n this._ncrOnLevel = ncrCfg.onLevel;\n this._ncrNullLevel = ncrCfg.nullLevel;\n\n // --- Registration hooks ---\n /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */\n this._onExternalEntity = typeof options.onExternalEntity === 'function'\n ? options.onExternalEntity\n : null;\n /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */\n this._onInputEntity = typeof options.onInputEntity === 'function'\n ? options.onInputEntity\n : null;\n }\n\n // -------------------------------------------------------------------------\n // Private: registration hook dispatch\n // -------------------------------------------------------------------------\n\n /**\n * Invoke a registration hook for a single entity name/value pair.\n * Returns true when the entity should be accepted, false when it should be\n * silently skipped (BLOCK), and throws when the hook returns THROW.\n *\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} hook\n * @param {string} name\n * @param {string} value\n * @param {string} context — used in error messages ('external' | 'input')\n * @returns {boolean} true = accept, false = skip\n */\n _applyRegistrationHook(hook, name, value, context) {\n if (!hook) return true; // no hook → always accept\n const action = hook(name, value);\n if (action === ENTITY_ACTION.BLOCK) return false;\n if (action === ENTITY_ACTION.THROW) {\n throw new Error(\n `[EntityDecoder] Registration of ${context} entity \"&${name};\" was rejected by hook`\n );\n }\n return true; // ALLOW or any unknown return value → accept\n }\n\n // -------------------------------------------------------------------------\n // Persistent external entity registration\n // -------------------------------------------------------------------------\n\n /**\n * Replace the full set of persistent external entities.\n * All keys are validated — throws on invalid characters.\n * If `onExternalEntity` is set, it is called once per entry; entries that\n * return `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW`\n * aborts the whole call.\n * @param {Record<string, string | { regex?: RegExp, val: string }>} map\n */\n setExternalEntities(map) {\n if (map) {\n for (const key of Object.keys(map)) {\n validateEntityName(key);\n }\n }\n if (!this._onExternalEntity) {\n this._externalMap = mergeEntityMaps(map);\n return;\n }\n // Hook present — resolve values first, then filter\n const flat = mergeEntityMaps(map);\n const filtered = Object.create(null);\n for (const [name, value] of Object.entries(flat)) {\n if (this._applyRegistrationHook(this._onExternalEntity, name, value, 'external')) {\n filtered[name] = value;\n }\n }\n this._externalMap = filtered;\n }\n\n /**\n * Add a single persistent external entity.\n * If `onExternalEntity` is set it is called before the entity is stored;\n * `ENTITY_ACTION.BLOCK` silently skips storage, `ENTITY_ACTION.THROW` raises.\n * @param {string} key\n * @param {string} value\n */\n addExternalEntity(key, value) {\n validateEntityName(key);\n if (typeof value === 'string' && value.indexOf('&') === -1) {\n if (this._applyRegistrationHook(this._onExternalEntity, key, value, 'external')) {\n this._externalMap[key] = value;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Input / runtime entity registration (per document)\n // -------------------------------------------------------------------------\n\n /**\n * Inject DOCTYPE entities for the current document.\n * Also resets per-document expansion counters.\n * If `onInputEntity` is set it is called once per entry; entries returning\n * `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW` aborts.\n * @param {Record<string, string | { regx?: RegExp, regex?: RegExp, val: string }>} map\n */\n addInputEntities(map) {\n this._totalExpansions = 0;\n this._expandedLength = 0;\n if (!this._onInputEntity) {\n this._inputMap = mergeEntityMaps(map);\n return;\n }\n const flat = mergeEntityMaps(map);\n const filtered = Object.create(null);\n for (const [name, value] of Object.entries(flat)) {\n if (this._applyRegistrationHook(this._onInputEntity, name, value, 'input')) {\n filtered[name] = value;\n }\n }\n this._inputMap = filtered;\n }\n\n // -------------------------------------------------------------------------\n // Per-document reset\n // -------------------------------------------------------------------------\n\n /**\n * Wipe input/runtime entities and reset counters.\n * Call this before processing each new document.\n * @returns {this}\n */\n reset() {\n this._inputMap = Object.create(null);\n this._totalExpansions = 0;\n this._expandedLength = 0;\n return this;\n }\n\n // -------------------------------------------------------------------------\n // XML version (can be set after construction, e.g. once parser reads <?xml?>)\n // -------------------------------------------------------------------------\n\n /**\n * Update the XML version used for NCR classification.\n * Call this as soon as the document's `<?xml version=\"...\">` declaration is parsed.\n * @param {1.0|1.1|number} version\n */\n setXmlVersion(version) {\n this._ncrXmlVersion = version === 1.1 ? 1.1 : 1.0;\n }\n\n // -------------------------------------------------------------------------\n // Primary API\n // -------------------------------------------------------------------------\n\n /**\n * Replace all entity references in `str` in a single pass.\n *\n * @param {string} str\n * @returns {string}\n */\n decode(str) {\n if (typeof str !== 'string' || str.length === 0) return str;\n //TODO: check if needed\n if (str.indexOf('&') === -1) return str; // fast path — no entities at all\n\n const original = str;\n const chunks = [];\n const len = str.length;\n let last = 0; // start of next unprocessed literal chunk\n let i = 0;\n\n const limitExpansions = this._maxTotalExpansions > 0;\n const limitLength = this._maxExpandedLength > 0;\n const checkLimits = limitExpansions || limitLength;\n\n while (i < len) {\n // Scan forward to next '&'\n if (str.charCodeAt(i) !== 38 /* '&' */) { i++; continue; }\n\n // --- Found '&' at position i ---\n\n // Scan forward to ';'\n let j = i + 1;\n while (j < len && str.charCodeAt(j) !== 59 /* ';' */ && (j - i) <= 32) j++;\n\n if (j >= len || str.charCodeAt(j) !== 59) {\n // No closing ';' within window — treat '&' as literal\n i++;\n continue;\n }\n\n // Raw token between '&' and ';' (exclusive)\n const token = str.slice(i + 1, j);\n if (token.length === 0) { i++; continue; }\n\n let replacement;\n let tier; // which limit tier this entity belongs to\n\n if (this._removeSet.has(token)) {\n // Remove entity: replace with empty string\n replacement = '';\n // If entity was unknown (replacement undefined), we still need a tier for limits.\n // Treat as external tier because it's user-directed removal of an unknown reference.\n if (tier === undefined) {\n tier = LIMIT_TIER_EXTERNAL;\n }\n } else if (this._leaveSet.has(token)) {\n // Do not replace — keep original &token; as literal\n i++;\n continue;\n } else if (token.charCodeAt(0) === 35 /* '#' */) {\n // ---- Numeric / NCR reference ----\n // NCR classification always runs first — prohibited codepoints must be\n // caught regardless of numericAllowed.\n const ncrResult = this._resolveNCR(token);\n if (ncrResult === undefined) {\n // 'leave' action — keep original &token; as-is\n i++;\n continue;\n }\n replacement = ncrResult; // '' for remove, char string for allow\n tier = LIMIT_TIER_BASE;\n } else {\n // ---- Named reference ----\n const resolved = this._resolveName(token);\n replacement = resolved?.value;\n tier = resolved?.tier;\n }\n\n if (replacement === undefined) {\n // Unknown entity — leave as-is, advance past '&' only\n i++;\n continue;\n }\n\n // Flush literal chunk before this entity\n if (i > last) chunks.push(str.slice(last, i));\n chunks.push(replacement);\n last = j + 1; // skip past ';'\n i = last;\n\n // Apply expansion limits only if this tier is being tracked\n if (checkLimits && this._tierCounts(tier)) {\n if (limitExpansions) {\n this._totalExpansions++;\n if (this._totalExpansions > this._maxTotalExpansions) {\n throw new Error(\n `[EntityReplacer] Entity expansion count limit exceeded: ` +\n `${this._totalExpansions} > ${this._maxTotalExpansions}`\n );\n }\n }\n if (limitLength) {\n // delta: replacement.length minus the raw &token; length (token.length + 2 for '&' and ';')\n const delta = replacement.length - (token.length + 2);\n if (delta > 0) {\n this._expandedLength += delta;\n if (this._expandedLength > this._maxExpandedLength) {\n throw new Error(\n `[EntityReplacer] Expanded content length limit exceeded: ` +\n `${this._expandedLength} > ${this._maxExpandedLength}`\n );\n }\n }\n }\n }\n }\n\n // Flush trailing literal\n if (last < len) chunks.push(str.slice(last));\n\n // If nothing was replaced, chunks is empty — return original\n const result = chunks.length === 0 ? str : chunks.join('');\n\n return this._postCheck(result, original);\n }\n\n // -------------------------------------------------------------------------\n // Private: limit tier check\n // -------------------------------------------------------------------------\n\n /**\n * Returns true if a resolved entity of the given tier should count\n * against the expansion/length limits.\n * @param {string} tier — LIMIT_TIER_EXTERNAL | LIMIT_TIER_BASE\n * @returns {boolean}\n */\n _tierCounts(tier) {\n if (this._limitTiers.has(LIMIT_TIER_ALL)) return true;\n return this._limitTiers.has(tier);\n }\n\n // -------------------------------------------------------------------------\n // Private: entity resolution\n // -------------------------------------------------------------------------\n\n /**\n * Resolve a named entity token (without & and ;).\n * Priority: inputMap > externalMap > baseMap\n * Returns the resolved value tagged with its limit tier.\n *\n * @param {string} name\n * @returns {{ value: string, tier: string }|undefined}\n */\n _resolveName(name) {\n // input and external both count as 'external' tier for limit purposes —\n // they are injected at runtime and are the untrusted surface.\n if (name in this._inputMap) return { value: this._inputMap[name], tier: LIMIT_TIER_EXTERNAL };\n if (name in this._externalMap) return { value: this._externalMap[name], tier: LIMIT_TIER_EXTERNAL };\n if (name in this._baseMap) return { value: this._baseMap[name], tier: LIMIT_TIER_BASE };\n return undefined;\n }\n\n /**\n * Classify a codepoint and return the minimum action level that must be applied.\n * Returns -1 when no minimum is imposed (normal allow path).\n *\n * Ranges checked (in priority order):\n * 1. U+0000 — null, governed by nullNCR (always ≥ remove)\n * 2. U+D800–U+DFFF — surrogates, always prohibited (min: remove)\n * 3. U+0001–U+001F \\ {0x09,0x0A,0x0D} — XML 1.0 restricted C0 (min: remove)\n * (skipped in XML 1.1 — C0 controls are allowed when written as NCRs)\n *\n * @param {number} cp — codepoint\n * @returns {number} — minimum NCR_LEVEL value, or -1 for no restriction\n */\n _classifyNCR(cp) {\n // 1. Null\n if (cp === 0) return this._ncrNullLevel;\n\n // 2. Surrogates — always prohibited, minimum 'remove'\n if (cp >= 0xD800 && cp <= 0xDFFF) return NCR_LEVEL.remove;\n\n // 3. XML 1.0 restricted C0 controls\n if (this._ncrXmlVersion === 1.0) {\n if (cp >= 0x01 && cp <= 0x1F && !XML10_ALLOWED_C0.has(cp)) return NCR_LEVEL.remove;\n }\n\n return -1; // no restriction\n }\n\n /**\n * Execute a resolved NCR action.\n *\n * @param {number} action — NCR_LEVEL value\n * @param {string} token — raw token (e.g. '#38') for error messages\n * @param {number} cp — codepoint, used only for error messages\n * @returns {string|undefined}\n * - decoded character string → 'allow'\n * - '' → 'remove'\n * - undefined → 'leave' (caller must skip past '&' only)\n * - throws Error → 'throw'\n */\n _applyNCRAction(action, token, cp) {\n switch (action) {\n case NCR_LEVEL.allow: return String.fromCodePoint(cp);\n case NCR_LEVEL.remove: return '';\n case NCR_LEVEL.leave: return undefined; // signal: keep literal\n case NCR_LEVEL.throw:\n throw new Error(\n `[EntityDecoder] Prohibited numeric character reference ` +\n `&${token}; (U+${cp.toString(16).toUpperCase().padStart(4, '0')})`\n );\n default: return String.fromCodePoint(cp);\n }\n }\n\n /**\n * Full NCR resolution pipeline for a numeric token.\n *\n * Steps:\n * 1. Parse the codepoint (decimal or hex).\n * 2. Validate the raw codepoint range (NaN, <0, >0x10FFFF).\n * 3. If numericAllowed is false and no minimum restriction applies → leave as-is.\n * 4. Classify the codepoint to find the minimum required action level.\n * 5. Resolve effective action = max(onNCR, minimum).\n * 6. Apply and return.\n *\n * @param {string} token — e.g. '#38', '#x26', '#X26'\n * @returns {string|undefined}\n * - string (incl. '') — replacement ('' = remove)\n * - undefined — leave original &token; as-is\n */\n _resolveNCR(token) {\n // Step 1: parse codepoint\n const second = token.charCodeAt(1);\n let cp;\n if (second === 120 /* x */ || second === 88 /* X */) {\n cp = parseInt(token.slice(2), 16);\n } else {\n cp = parseInt(token.slice(1), 10);\n }\n\n // Step 2: out-of-range → leave as-is unconditionally\n if (Number.isNaN(cp) || cp < 0 || cp > 0x10FFFF) return undefined;\n\n // Step 3: classify to get minimum action level\n const minimum = this._classifyNCR(cp);\n\n // Step 4: if numericAllowed is false and no hard minimum → leave\n if (!this._numericAllowed && minimum < NCR_LEVEL.remove) return undefined;\n\n // Step 5: effective action = max(configured onNCR, range minimum)\n const effective = minimum === -1\n ? this._ncrOnLevel\n : Math.max(this._ncrOnLevel, minimum);\n\n // Step 6: apply\n return this._applyNCRAction(effective, token, cp);\n }\n}","import { DANGEROUS_PROPERTY_NAMES, criticalProperties } from \"../util.js\";\nimport { COMMON_HTML, CURRENCY } from '@nodable/entities';\n\nconst defaultOnDangerousProperty = (name) => {\n if (DANGEROUS_PROPERTY_NAMES.includes(name)) {\n return \"__\" + name;\n }\n return name;\n};\n\n\nexport const defaultOptions = {\n preserveOrder: false,\n attributeNamePrefix: '@_',\n attributesGroupName: false,\n textNodeName: '#text',\n ignoreAttributes: true,\n removeNSPrefix: false, // remove NS from tag name or attribute name if true\n allowBooleanAttributes: false, //a tag can have attributes without any value\n //ignoreRootElement : false,\n parseTagValue: true,\n parseAttributeValue: false,\n trimValues: true, //Trim string values of tag and attributes\n cdataPropName: false,\n numberParseOptions: {\n hex: true,\n leadingZeros: true,\n eNotation: true,\n unicode: false\n },\n tagValueProcessor: function (tagName, val) {\n return val;\n },\n attributeValueProcessor: function (attrName, val) {\n return val;\n },\n stopNodes: [], //nested tags will not be parsed even for errors\n alwaysCreateTextNode: false,\n isArray: () => false,\n commentPropName: false,\n unpairedTags: [],\n processEntities: true,\n htmlEntities: false,\n entityDecoder: null,\n ignoreDeclaration: false,\n ignorePiTags: false,\n transformTagName: false,\n transformAttributeName: false,\n updateTag: function (tagName, jPath, attrs) {\n return tagName\n },\n // skipEmptyListItem: false\n captureMetaData: false,\n maxNestedTags: 100,\n strictReservedNames: true,\n jPath: true, // if true, pass jPath string to callbacks; if false, pass matcher instance\n onDangerousProperty: defaultOnDangerousProperty\n};\n\n\n/**\n * Validates that a property name is safe to use\n * @param {string} propertyName - The property name to validate\n * @param {string} optionName - The option field name (for error message)\n * @throws {Error} If property name is dangerous\n */\nfunction validatePropertyName(propertyName, optionName) {\n if (typeof propertyName !== 'string') {\n return; // Only validate string property names\n }\n\n const normalized = propertyName.toLowerCase();\n if (DANGEROUS_PROPERTY_NAMES.some(dangerous => normalized === dangerous.toLowerCase())) {\n throw new Error(\n `[SECURITY] Invalid ${optionName}: \"${propertyName}\" is a reserved JavaScript keyword that could cause prototype pollution`\n );\n }\n\n if (criticalProperties.some(dangerous => normalized === dangerous.toLowerCase())) {\n throw new Error(\n `[SECURITY] Invalid ${optionName}: \"${propertyName}\" is a reserved JavaScript keyword that could cause prototype pollution`\n );\n }\n}\n\n/**\n * Normalizes processEntities option for backward compatibility\n * @param {boolean|object} value \n * @returns {object} Always returns normalized object\n */\nfunction normalizeProcessEntities(value, htmlEntities) {\n // Boolean backward compatibility\n if (typeof value === 'boolean') {\n return {\n enabled: value, // true or false\n maxEntitySize: 10000,\n maxExpansionDepth: 10000,\n maxTotalExpansions: Infinity,\n maxExpandedLength: 100000,\n maxEntityCount: 1000,\n allowedTags: null,\n tagFilter: null,\n appliesTo: \"all\",\n };\n }\n\n // Object config - merge with defaults\n if (typeof value === 'object' && value !== null) {\n return {\n enabled: value.enabled !== false,\n maxEntitySize: Math.max(1, value.maxEntitySize ?? 10000),\n maxExpansionDepth: Math.max(1, value.maxExpansionDepth ?? 10000),\n maxTotalExpansions: Math.max(1, value.maxTotalExpansions ?? Infinity),\n maxExpandedLength: Math.max(1, value.maxExpandedLength ?? 100000),\n maxEntityCount: Math.max(1, value.maxEntityCount ?? 1000),\n allowedTags: value.allowedTags ?? null,\n tagFilter: value.tagFilter ?? null,\n appliesTo: value.appliesTo ?? \"all\",\n };\n }\n\n // Default to enabled with limits\n return normalizeProcessEntities(true);\n}\n\nexport const buildOptions = function (options) {\n const built = Object.assign({}, defaultOptions, options);\n\n // Validate property names to prevent prototype pollution\n const propertyNameOptions = [\n { value: built.attributeNamePrefix, name: 'attributeNamePrefix' },\n { value: built.attributesGroupName, name: 'attributesGroupName' },\n { value: built.textNodeName, name: 'textNodeName' },\n { value: built.cdataPropName, name: 'cdataPropName' },\n { value: built.commentPropName, name: 'commentPropName' }\n ];\n\n for (const { value, name } of propertyNameOptions) {\n if (value) {\n validatePropertyName(value, name);\n }\n }\n\n if (built.onDangerousProperty === null) {\n built.onDangerousProperty = defaultOnDangerousProperty;\n }\n\n // Always normalize processEntities for backward compatibility and validation\n built.processEntities = normalizeProcessEntities(built.processEntities, built.htmlEntities);\n built.unpairedTagsSet = new Set(built.unpairedTags);\n // Convert old-style stopNodes for backward compatibility\n if (built.stopNodes && Array.isArray(built.stopNodes)) {\n built.stopNodes = built.stopNodes.map(node => {\n if (typeof node === 'string' && node.startsWith('*.')) {\n // Old syntax: *.tagname meant \"tagname anywhere\"\n // Convert to new syntax: ..tagname\n return '..' + node.substring(2);\n }\n return node;\n });\n }\n //console.debug(built.processEntities)\n return built;\n};","'use strict';\n\nlet METADATA_SYMBOL;\n\nif (typeof Symbol !== \"function\") {\n METADATA_SYMBOL = \"@@xmlMetadata\";\n} else {\n METADATA_SYMBOL = Symbol(\"XML Node Metadata\");\n}\n\nexport default class XmlNode {\n constructor(tagname) {\n this.tagname = tagname;\n this.child = []; //nested tags, text, cdata, comments in order\n this[\":@\"] = Object.create(null); //attributes map\n }\n add(key, val) {\n // this.child.push( {name : key, val: val, isCdata: isCdata });\n if (key === \"__proto__\") key = \"#__proto__\";\n this.child.push({ [key]: val });\n }\n addChild(node, startIndex) {\n if (node.tagname === \"__proto__\") node.tagname = \"#__proto__\";\n if (node[\":@\"] && Object.keys(node[\":@\"]).length > 0) {\n this.child.push({ [node.tagname]: node.child, [\":@\"]: node[\":@\"] });\n } else {\n this.child.push({ [node.tagname]: node.child });\n }\n // if requested, add the startIndex\n if (startIndex !== undefined) {\n // Note: for now we just overwrite the metadata. If we had more complex metadata,\n // we might need to do an object append here: metadata = { ...metadata, startIndex }\n this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex };\n }\n }\n /** symbol used for metadata */\n static getMetaDataSymbol() {\n return METADATA_SYMBOL;\n }\n}\n","/**\n * xml-naming\n * Validates XML Name productions as defined in the XML 1.0 and 1.1 specifications.\n * Covers: Name, NCName, QName, NMToken, NMTokens\n *\n * XML 1.0 spec: https://www.w3.org/TR/xml/#NT-Name\n * XML 1.1 spec: https://www.w3.org/TR/xml11/#NT-NameStartChar\n * XML NS spec: https://www.w3.org/TR/xml-names/#NT-NCName\n */\n\n// ---------------------------------------------------------------------------\n// Character class strings — XML 1.0\n//\n// NameStartChar ::= \":\" | [A-Z] | \"_\" | [a-z]\n// | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF]\n// | [#x370-#x37D] | [#x37F-#x1FFF] <- split to exclude #x0487\n// | [#x200C-#x200D]\n// | [#x2070-#x218F] | [#x2C00-#x2FEF]\n// | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD]\n//\n// NameChar ::= NameStartChar | \"-\" | \".\" | [0-9]\n// | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]\n//\n// Note: \\u0487 (Combining Cyrillic Millions Sign) was added in Unicode 4.0,\n// after XML 1.0 was defined against Unicode 2.0. It falls inside the range\n// \\u037F-\\u1FFF but must be excluded. We split that range into\n// \\u037F-\\u0486 and \\u0488-\\u1FFF to exclude it explicitly.\n// ---------------------------------------------------------------------------\n\nconst nameStartChar10 =\n ':A-Za-z_' +\n '\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF' +\n '\\u0370-\\u037D' +\n '\\u037F-\\u0486\\u0488-\\u1FFF' + // split to exclude \\u0487\n '\\u200C-\\u200D' +\n '\\u2070-\\u218F' +\n '\\u2C00-\\u2FEF' +\n '\\u3001-\\uD7FF' +\n '\\uF900-\\uFDCF' +\n '\\uFDF0-\\uFFFD';\n\nconst nameChar10 =\n nameStartChar10 +\n '\\\\-\\\\.\\\\d' +\n '\\u00B7' +\n '\\u0300-\\u036F' +\n '\\u203F-\\u2040';\n\n// ---------------------------------------------------------------------------\n// Character class strings — XML 1.1\n//\n// Differences from XML 1.0:\n//\n// NameStartChar:\n// 1.0 has split ranges: \\u00C0-\\u00D6, \\u00D8-\\u00F6, \\u00F8-\\u02FF\n// 1.1 merges them into: \\u00C0-\\u02FF\n// (\\u00D7 x and \\u00F7 / are division symbols, excluded in both versions)\n//\n// 1.0 tops out at \\uFFFD (BMP only)\n// 1.1 adds \\u{10000}-\\u{EFFFF} (supplementary planes)\n// These require the /u flag on the RegExp — see buildRegexes below.\n//\n// NameChar:\n// 1.1 adds \\u0487 (Combining Cyrillic Millions Sign, added in Unicode 4.0)\n// ---------------------------------------------------------------------------\n\nconst nameStartChar11 =\n ':A-Za-z_' +\n '\\u00C0-\\u02FF' + // merged — 1.0 had three split ranges here\n '\\u0370-\\u037D' +\n '\\u037F-\\u0486\\u0488-\\u1FFF' + // split to exclude \\u0487 (combining mark, never a NameStartChar)\n '\\u200C-\\u200D' +\n '\\u2070-\\u218F' +\n '\\u2C00-\\u2FEF' +\n '\\u3001-\\uD7FF' +\n '\\uF900-\\uFDCF' +\n '\\uFDF0-\\uFFFD' +\n '\\u{10000}-\\u{EFFFF}'; // supplementary planes — REQUIRES /u flag on RegExp\n\nconst nameChar11 =\n nameStartChar11 +\n '\\\\-\\\\.\\\\d' +\n '\\u00B7' +\n '\\u0300-\\u036F' +\n '\\u0487' + // Combining Cyrillic Millions Sign — valid in 1.1, not 1.0\n '\\u203F-\\u2040';\n\n// ---------------------------------------------------------------------------\n// Regex builders\n//\n// XML 1.0 regexes: no flags — BMP only, standard JS regex behaviour.\n// XML 1.1 regexes: /u flag — required for \\u{10000}-\\u{EFFFF} to match actual\n// supplementary code points rather than lone surrogates (which are illegal XML).\n// ---------------------------------------------------------------------------\n\nconst buildRegexes = (startChar, char, flags = '') => {\n const ncStart = startChar.replace(':', '');\n const ncChar = char.replace(':', '');\n const ncNamePat = `[${ncStart}][${ncChar}]*`;\n\n return {\n name: new RegExp(`^[${startChar}][${char}]*$`, flags),\n ncName: new RegExp(`^${ncNamePat}$`, flags),\n qName: new RegExp(`^${ncNamePat}(?::${ncNamePat})?$`, flags),\n nmToken: new RegExp(`^[${char}]+$`, flags),\n nmTokens: new RegExp(`^[${char}]+(?:\\\\s+[${char}]+)*$`, flags),\n };\n};\n\nconst regexes10 = buildRegexes(nameStartChar10, nameChar10); // no /u — BMP only\nconst regexes11 = buildRegexes(nameStartChar11, nameChar11, 'u'); // /u — enables \\u{10000}-\\u{EFFFF}\n\n// ---------------------------------------------------------------------------\n// ASCII-only fast path (opt-in, off by default)\n//\n// The XML 1.0 vs 1.1 NameStartChar/NameChar productions differ *only* in\n// their non-ASCII ranges (merged vs split Latin-1 ranges, \\u0487, and\n// supplementary planes). Restricted to ASCII, both versions collapse to the\n// same character classes, so a single regex pair covers both xmlVersion\n// values — no /u flag needed.\n//\n// Rationale: unicode-aware regexes (the /u flag, required for XML 1.1's\n// supplementary-plane range) are measurably slower in V8 than plain\n// non-unicode regexes on the same input, even when the input is pure ASCII.\n// For the common case — HTML/SVG ids, XML tags — names are ASCII, so callers\n// who know this can opt in to skip the unicode-aware matching path entirely.\n// This is a real but *conditional* win: mainly for XML 1.1 input (avoids /u),\n// or at scale where the larger unicode character classes add engine\n// overhead. It also changes behaviour (rejects legitimate non-ASCII XML\n// 1.0/1.1 names), so it must never be silently enabled — hence off by\n// default.\n// ---------------------------------------------------------------------------\n\nconst nameStartCharAscii = ':A-Za-z_';\nconst nameCharAscii = nameStartCharAscii + '\\\\-\\\\.\\\\d';\n\nconst regexesAscii = buildRegexes(nameStartCharAscii, nameCharAscii); // no /u — ASCII only\n\nconst getRegexes = (xmlVersion = '1.0', asciiOnly = false) => {\n if (asciiOnly) return regexesAscii;\n return xmlVersion === '1.1' ? regexes11 : regexes10;\n};\n\n// ---------------------------------------------------------------------------\n// Boolean validators\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true if the string is a valid XML Name.\n * Colons are allowed anywhere (Name production).\n * Used for: DOCTYPE entity names, notation names, DTD element declarations.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const name = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).name.test(str);\n\n/**\n * Returns true if the string is a valid NCName (Non-Colonized Name).\n * Colons are not permitted.\n * Used for: namespace prefixes, local names, SVG id attributes.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const ncName = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).ncName.test(str);\n\n/**\n * Returns true if the string is a valid QName (Qualified Name).\n * Allows exactly one colon as a prefix separator: prefix:localName.\n * Used for: element and attribute names in namespace-aware XML/SVG.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const qName = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).qName.test(str);\n\n/**\n * Returns true if the string is a valid NMToken.\n * Like Name but no restriction on the first character.\n * Used for: DTD NMTOKEN attribute values.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const nmToken = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).nmToken.test(str);\n\n/**\n * Returns true if the string is a valid NMTokens value.\n * A whitespace-separated list of NMToken values.\n * Used for: DTD NMTOKENS attribute values.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const nmTokens = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).nmTokens.test(str);\n\n// ---------------------------------------------------------------------------\n// Memoized validator factory\n//\n// Real documents reuse a small vocabulary of tag/attribute names across many\n// siblings (e.g. `id`, `class`, `href` repeated across hundreds of elements).\n// The plain boolean validators above re-run the regex on every call\n// regardless of repeats. `createValidator` returns a closure with a private\n// string -> boolean cache, so repeated names after the first become O(1)\n// lookups instead of regex tests.\n//\n// - opts (xmlVersion, asciiOnly) are fixed at creation time, so the regex is\n// resolved once, not on every call.\n// - The cache is private to the returned closure — no shared/global state,\n// no cross-caller pollution.\n// - `maxCacheSize` bounds memory: once the cache reaches this many entries,\n// it stops accepting new ones (existing entries keep serving hits; new\n// misses just fall through to the regex, uncached). This avoids unbounded\n// growth against adversarial/high-cardinality input (e.g. validating\n// attacker-supplied names with no repeats) without the cost/complexity of\n// a full LRU, and without the perf cliff of reset-and-refill thrashing.\n// - Call `.reset()` on the returned function to clear the cache manually\n// (e.g. between unrelated parse calls).\n// ---------------------------------------------------------------------------\n\nconst PRODUCTIONS = ['name', 'ncName', 'qName', 'nmToken', 'nmTokens'];\n\n/**\n * Returns a memoized boolean validator function for a single production,\n * with opts fixed at creation time.\n *\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean, maxCacheSize?: number }} [opts]\n * maxCacheSize: max number of distinct strings to cache (default 2048).\n * Once reached, new strings are validated but not cached; existing cached\n * entries keep being served.\n * @returns {((str: string) => boolean) & { reset: () => void }}\n */\nexport const createValidator = (production, { xmlVersion = '1.0', asciiOnly = false, maxCacheSize = 2048 } = {}) => {\n if (!PRODUCTIONS.includes(production)) {\n throw new TypeError(\n `Unknown production \"${production}\". Must be one of: ${PRODUCTIONS.join(', ')}`\n );\n }\n\n const regex = getRegexes(xmlVersion, asciiOnly)[production];\n let cache = new Map();\n\n const validator = (str) => {\n const cached = cache.get(str);\n if (cached !== undefined) return cached;\n\n const result = regex.test(str);\n if (cache.size < maxCacheSize) cache.set(str, result);\n return result;\n };\n\n validator.reset = () => { cache = new Map(); };\n\n return validator;\n};\n\n// ---------------------------------------------------------------------------\n// Diagnostic validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validates a string against a named production and returns a detailed result.\n *\n * @param {string} str\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * @returns {{ valid: boolean, production: string, input: string, reason?: string, position?: number }}\n */\nexport const validate = (str, production, { xmlVersion = '1.0', asciiOnly = false } = {}) => {\n if (!PRODUCTIONS.includes(production)) {\n throw new TypeError(\n `Unknown production \"${production}\". Must be one of: ${PRODUCTIONS.join(', ')}`\n );\n }\n\n const validators = { name, ncName, qName, nmToken, nmTokens };\n const isValid = validators[production](str, { xmlVersion, asciiOnly });\n\n if (isValid) return { valid: true, production, input: str };\n\n let reason = 'Does not match the production rules';\n let position;\n\n // Diagnostic fallback char checks must mirror the same character set the\n // boolean validator above used, or the reported reason/position could\n // contradict the `valid: false` result (e.g. flagging a char as illegal\n // that the unicode-aware check would have accepted).\n const startCharPattern = asciiOnly ? /^[:A-Za-z_]/ : /^[:A-Za-z_\\u00C0-\\uFFFD]/;\n const namePattern = asciiOnly ? /[\\w\\-\\\\.:]/ : /[\\w\\-\\\\.:\\u00B7\\u00C0-\\uFFFD]/;\n\n if (str.length === 0) {\n reason = 'Input is empty';\n } else if (production === 'ncName' && str.includes(':')) {\n position = str.indexOf(':');\n reason = 'Colon is not allowed in NCName';\n } else if (production === 'qName' && str.startsWith(':')) {\n reason = 'QName cannot start with a colon';\n position = 0;\n } else if (production === 'qName' && str.endsWith(':')) {\n reason = 'QName cannot end with a colon';\n position = str.length - 1;\n } else if (production === 'qName' && (str.match(/:/g) || []).length > 1) {\n reason = 'QName can have at most one colon';\n position = str.lastIndexOf(':');\n } else if (\n ['name', 'ncName', 'qName'].includes(production) &&\n !startCharPattern.test(str[0])\n ) {\n reason = `First character \"${str[0]}\" is not a valid NameStartChar`;\n position = 0;\n } else {\n for (let i = 0; i < str.length; i++) {\n if (!namePattern.test(str[i])) {\n reason = `Character \"${str[i]}\" at position ${i} is not a valid NameChar`;\n position = i;\n break;\n }\n }\n }\n\n return { valid: false, production, input: str, reason, position };\n};\n\n// ---------------------------------------------------------------------------\n// Batch validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validates an array of strings against a named production.\n *\n * @param {string[]} strings\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * @returns {Array<{ valid: boolean, production: string, input: string, reason?: string, position?: number }>}\n */\nexport const validateAll = (strings, production, opts = {}) =>\n strings.map(str => validate(str, production, opts));\n\n// ---------------------------------------------------------------------------\n// Sanitizer\n// ---------------------------------------------------------------------------\n\n/**\n * Transforms an invalid string into the nearest valid XML name for the given production.\n *\n * @param {string} str\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ replacement?: string, asciiOnly?: boolean }} [opts]\n * asciiOnly: also replace any non-ASCII character, not just XML-illegal\n * ones (default false).\n * @returns {string}\n */\nexport const sanitize = (str, production = 'name', { replacement = '_', asciiOnly = false } = {}) => {\n if (!str) return replacement;\n\n let result = str;\n\n // Strip colons for NCName\n if (production === 'ncName') {\n result = result.replace(/:/g, '');\n }\n\n // Replace illegal characters\n const allowedCharPattern = asciiOnly ? /[^\\w\\-\\.:]/g : /[^\\w\\-\\.:\\u00B7\\u00C0-\\uFFFD]/g;\n result = result.replace(allowedCharPattern, replacement);\n\n // Fix invalid start character for Name / NCName / QName\n if (production !== 'nmToken' && production !== 'nmTokens') {\n if (/^[\\-\\.\\d]/.test(result)) {\n result = replacement + result;\n }\n }\n\n return result || replacement;\n};","import { qName as isName } from 'xml-naming';\n\nexport default class DocTypeReader {\n constructor(options, xmlVersion) {\n this.suppressValidationErr = !options;\n this.options = options;\n this.xmlVersion = xmlVersion || 1.0;\n }\n\n setXmlVersion(xmlVersion = 1.0) {\n this.xmlVersion = xmlVersion;\n }\n readDocType(xmlData, i) {\n const entities = Object.create(null);\n let entityCount = 0;\n\n if (xmlData[i + 3] === 'O' &&\n xmlData[i + 4] === 'C' &&\n xmlData[i + 5] === 'T' &&\n xmlData[i + 6] === 'Y' &&\n xmlData[i + 7] === 'P' &&\n xmlData[i + 8] === 'E') {\n i = i + 9;\n let angleBracketsCount = 1;\n let hasBody = false, comment = false;\n let exp = \"\";\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === '<' && !comment) { //Determine the tag type\n if (hasBody && hasSeq(xmlData, \"!ENTITY\", i)) {\n i += 7;\n let entityName, val;\n [entityName, val, i] = this.readEntityExp(xmlData, i + 1, this.suppressValidationErr);\n if (val.indexOf(\"&\") === -1) { //Parameter entities are not supported\n if (this.options.enabled !== false &&\n this.options.maxEntityCount != null &&\n entityCount >= this.options.maxEntityCount) {\n throw new Error(\n `Entity count (${entityCount + 1}) exceeds maximum allowed (${this.options.maxEntityCount})`\n );\n }\n //const escaped = entityName.replace(/[.\\-+*:]/g, '\\\\.');\n //const escaped = entityName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n entities[entityName] = val;\n entityCount++;\n }\n }\n else if (hasBody && hasSeq(xmlData, \"!ELEMENT\", i)) {\n i += 8;//Not supported\n const { index } = this.readElementExp(xmlData, i + 1);\n i = index;\n } else if (hasBody && hasSeq(xmlData, \"!ATTLIST\", i)) {\n i += 8;//Not supported\n // const {index} = this.readAttlistExp(xmlData,i+1);\n // i = index;\n } else if (hasBody && hasSeq(xmlData, \"!NOTATION\", i)) {\n i += 9;//Not supported\n const { index } = this.readNotationExp(xmlData, i + 1, this.suppressValidationErr);\n i = index;\n } else if (hasSeq(xmlData, \"!--\", i)) comment = true;\n else throw new Error(`Invalid DOCTYPE`);\n\n angleBracketsCount++;\n exp = \"\";\n } else if (xmlData[i] === '>') { //Read tag content\n if (comment) {\n if (xmlData[i - 1] === \"-\" && xmlData[i - 2] === \"-\") {\n comment = false;\n angleBracketsCount--;\n }\n } else {\n angleBracketsCount--;\n }\n if (angleBracketsCount === 0) {\n break;\n }\n } else if (xmlData[i] === '[') {\n hasBody = true;\n } else {\n exp += xmlData[i];\n }\n }\n if (angleBracketsCount !== 0) {\n throw new Error(`Unclosed DOCTYPE`);\n }\n } else {\n throw new Error(`Invalid Tag instead of DOCTYPE`);\n }\n return { entities, i };\n }\n readEntityExp(xmlData, i) {\n //External entities are not supported\n // <!ENTITY ext SYSTEM \"http://normal-website.com\" >\n\n //Parameter entities are not supported\n // <!ENTITY entityname \"&anotherElement;\">\n\n //Internal entities are supported\n // <!ENTITY entityname \"replacement text\">\n\n // Skip leading whitespace after <!ENTITY\n i = skipWhitespace(xmlData, i);\n\n // Read entity name\n const startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i]) && xmlData[i] !== '\"' && xmlData[i] !== \"'\") {\n i++;\n }\n let entityName = xmlData.substring(startIndex, i);\n\n validateEntityName(entityName, { xmlVersion: this.xmlVersion });\n\n // Skip whitespace after entity name\n i = skipWhitespace(xmlData, i);\n\n // Check for unsupported constructs (external entities or parameter entities)\n if (!this.suppressValidationErr) {\n if (xmlData.substring(i, i + 6).toUpperCase() === \"SYSTEM\") {\n throw new Error(\"External entities are not supported\");\n } else if (xmlData[i] === \"%\") {\n throw new Error(\"Parameter entities are not supported\");\n }\n }\n\n // Read entity value (internal entity)\n let entityValue = \"\";\n [i, entityValue] = this.readIdentifierVal(xmlData, i, \"entity\");\n\n // Validate entity size\n if (this.options.enabled !== false &&\n this.options.maxEntitySize != null &&\n entityValue.length > this.options.maxEntitySize) {\n throw new Error(\n `Entity \"${entityName}\" size (${entityValue.length}) exceeds maximum allowed size (${this.options.maxEntitySize})`\n );\n }\n\n i--;\n return [entityName, entityValue, i];\n }\n\n readNotationExp(xmlData, i) {\n // Skip leading whitespace after <!NOTATION\n i = skipWhitespace(xmlData, i);\n\n // Read notation name\n\n const startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n let notationName = xmlData.substring(startIndex, i);\n\n !this.suppressValidationErr && validateEntityName(notationName, { xmlVersion: this.xmlVersion });\n\n // Skip whitespace after notation name\n i = skipWhitespace(xmlData, i);\n\n // Check identifier type (SYSTEM or PUBLIC)\n const identifierType = xmlData.substring(i, i + 6).toUpperCase();\n if (!this.suppressValidationErr && identifierType !== \"SYSTEM\" && identifierType !== \"PUBLIC\") {\n throw new Error(`Expected SYSTEM or PUBLIC, found \"${identifierType}\"`);\n }\n i += identifierType.length;\n\n // Skip whitespace after identifier type\n i = skipWhitespace(xmlData, i);\n\n // Read public identifier (if PUBLIC)\n let publicIdentifier = null;\n let systemIdentifier = null;\n\n if (identifierType === \"PUBLIC\") {\n [i, publicIdentifier] = this.readIdentifierVal(xmlData, i, \"publicIdentifier\");\n\n // Skip whitespace after public identifier\n i = skipWhitespace(xmlData, i);\n\n // Optionally read system identifier\n if (xmlData[i] === '\"' || xmlData[i] === \"'\") {\n [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, \"systemIdentifier\");\n }\n } else if (identifierType === \"SYSTEM\") {\n // Read system identifier (mandatory for SYSTEM)\n [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, \"systemIdentifier\");\n\n if (!this.suppressValidationErr && !systemIdentifier) {\n throw new Error(\"Missing mandatory system identifier for SYSTEM notation\");\n }\n }\n\n return { notationName, publicIdentifier, systemIdentifier, index: --i };\n }\n\n readIdentifierVal(xmlData, i, type) {\n let identifierVal = \"\";\n const startChar = xmlData[i];\n if (startChar !== '\"' && startChar !== \"'\") {\n throw new Error(`Expected quoted string, found \"${startChar}\"`);\n }\n i++;\n\n const startIndex = i;\n while (i < xmlData.length && xmlData[i] !== startChar) {\n i++;\n }\n identifierVal = xmlData.substring(startIndex, i);\n\n if (xmlData[i] !== startChar) {\n throw new Error(`Unterminated ${type} value`);\n }\n i++;\n return [i, identifierVal];\n }\n\n readElementExp(xmlData, i) {\n // <!ELEMENT br EMPTY>\n // <!ELEMENT div ANY>\n // <!ELEMENT title (#PCDATA)>\n // <!ELEMENT book (title, author+)>\n // <!ELEMENT name (content-model)>\n\n // Skip leading whitespace after <!ELEMENT\n i = skipWhitespace(xmlData, i);\n\n // Read element name\n const startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n let elementName = xmlData.substring(startIndex, i);\n\n // Validate element name\n if (!this.suppressValidationErr && !isName(elementName, { xmlVersion: this.xmlVersion })) {\n throw new Error(`Invalid element name: \"${elementName}\"`);\n }\n\n // Skip whitespace after element name\n i = skipWhitespace(xmlData, i);\n let contentModel = \"\";\n // Expect '(' to start content model\n if (xmlData[i] === \"E\" && hasSeq(xmlData, \"MPTY\", i)) i += 4;\n else if (xmlData[i] === \"A\" && hasSeq(xmlData, \"NY\", i)) i += 2;\n else if (xmlData[i] === \"(\") {\n i++; // Move past '('\n\n // Read content model\n const startIndex = i;\n while (i < xmlData.length && xmlData[i] !== \")\") {\n i++;\n }\n contentModel = xmlData.substring(startIndex, i);\n\n if (xmlData[i] !== \")\") {\n throw new Error(\"Unterminated content model\");\n }\n\n } else if (!this.suppressValidationErr) {\n throw new Error(`Invalid Element Expression, found \"${xmlData[i]}\"`);\n }\n\n return {\n elementName,\n contentModel: contentModel.trim(),\n index: i\n };\n }\n\n readAttlistExp(xmlData, i) {\n // Skip leading whitespace after <!ATTLIST\n i = skipWhitespace(xmlData, i);\n\n // Read element name\n let startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n let elementName = xmlData.substring(startIndex, i);\n\n // Validate element name\n validateEntityName(elementName, { xmlVersion: this.xmlVersion })\n\n // Skip whitespace after element name\n i = skipWhitespace(xmlData, i);\n\n // Read attribute name\n startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n let attributeName = xmlData.substring(startIndex, i);\n\n // Validate attribute name\n if (!validateEntityName(attributeName, { xmlVersion: this.xmlVersion })) {\n throw new Error(`Invalid attribute name: \"${attributeName}\"`);\n }\n\n // Skip whitespace after attribute name\n i = skipWhitespace(xmlData, i);\n\n // Read attribute type\n let attributeType = \"\";\n if (xmlData.substring(i, i + 8).toUpperCase() === \"NOTATION\") {\n attributeType = \"NOTATION\";\n i += 8; // Move past \"NOTATION\"\n\n // Skip whitespace after \"NOTATION\"\n i = skipWhitespace(xmlData, i);\n\n // Expect '(' to start the list of notations\n if (xmlData[i] !== \"(\") {\n throw new Error(`Expected '(', found \"${xmlData[i]}\"`);\n }\n i++; // Move past '('\n\n // Read the list of allowed notations\n let allowedNotations = [];\n while (i < xmlData.length && xmlData[i] !== \")\") {\n\n\n const startIndex = i;\n while (i < xmlData.length && xmlData[i] !== \"|\" && xmlData[i] !== \")\") {\n i++;\n }\n let notation = xmlData.substring(startIndex, i);\n\n // Validate notation name\n notation = notation.trim();\n if (!validateEntityName(notation, { xmlVersion: this.xmlVersion })) {\n throw new Error(`Invalid notation name: \"${notation}\"`);\n }\n\n allowedNotations.push(notation);\n\n // Skip '|' separator or exit loop\n if (xmlData[i] === \"|\") {\n i++; // Move past '|'\n i = skipWhitespace(xmlData, i); // Skip optional whitespace after '|'\n }\n }\n\n if (xmlData[i] !== \")\") {\n throw new Error(\"Unterminated list of notations\");\n }\n i++; // Move past ')'\n\n // Store the allowed notations as part of the attribute type\n attributeType += \" (\" + allowedNotations.join(\"|\") + \")\";\n } else {\n // Handle simple types (e.g., CDATA, ID, IDREF, etc.)\n const startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n attributeType += xmlData.substring(startIndex, i);\n\n // Validate simple attribute type\n const validTypes = [\"CDATA\", \"ID\", \"IDREF\", \"IDREFS\", \"ENTITY\", \"ENTITIES\", \"NMTOKEN\", \"NMTOKENS\"];\n if (!this.suppressValidationErr && !validTypes.includes(attributeType.toUpperCase())) {\n throw new Error(`Invalid attribute type: \"${attributeType}\"`);\n }\n }\n\n // Skip whitespace after attribute type\n i = skipWhitespace(xmlData, i);\n\n // Read default value\n let defaultValue = \"\";\n if (xmlData.substring(i, i + 8).toUpperCase() === \"#REQUIRED\") {\n defaultValue = \"#REQUIRED\";\n i += 8;\n } else if (xmlData.substring(i, i + 7).toUpperCase() === \"#IMPLIED\") {\n defaultValue = \"#IMPLIED\";\n i += 7;\n } else {\n [i, defaultValue] = this.readIdentifierVal(xmlData, i, \"ATTLIST\");\n }\n\n return {\n elementName,\n attributeName,\n attributeType,\n defaultValue,\n index: i\n }\n }\n}\n\n\n\nconst skipWhitespace = (data, index) => {\n while (index < data.length && /\\s/.test(data[index])) {\n index++;\n }\n return index;\n};\n\n\n\nfunction hasSeq(data, seq, i) {\n for (let j = 0; j < seq.length; j++) {\n if (seq[j] !== data[i + j + 1]) return false;\n }\n return true;\n}\n\nfunction validateEntityName(name, xmlVersion) {\n if (isName(name, { xmlVersion: xmlVersion }))\n return name;\n else\n throw new Error(`Invalid entity name ${name}`);\n}","/**\n * Flat lookup table: maps Unicode code point → ASCII digit (0-9).\n * Only decimal digit characters (Unicode category Nd) are included.\n *\n * Strategy: Int32Array of size (maxCodePoint - minCodePoint + 1).\n * Value 0xFF means \"not a digit\". Value 0-9 is the ASCII digit value.\n * This gives O(1) lookup with no branching, no bisect, no loop.\n *\n * Memory: range is 0x0660 to 0x1FBF0 → ~129,936 entries × 1 byte = ~127 KB.\n * Acceptable for a one-time init; lookup is a single array index.\n */\n\n// All known Unicode Nd (decimal digit) script zero code points.\n// Each script has exactly 10 consecutive digits: zero+0 .. zero+9.\nconst SCRIPT_ZEROS = [\n // Basic Latin (ASCII) — included for completeness / pass-through\n 0x0030, // 0-9\n\n // Arabic scripts\n 0x0660, // Arabic-Indic ٠١٢٣٤٥٦٧٨٩\n 0x06F0, // Extended Arabic-Indic (Urdu/Persian/Sindhi) ۰۱۲۳\n\n // Indic scripts\n 0x0966, // Devanagari ०१२३४५६७८९\n 0x09E6, // Bengali ০১২৩৪৫৬৭৮৯\n 0x0A66, // Gurmukhi ੦੧੨੩੪੫੬੭੮੯\n 0x0AE6, // Gujarati ૦૧૨૩૪૫૬૭૮૯\n 0x0B66, // Odia ୦୧୨୩୪୫୬୭୮୯\n 0x0BE6, // Tamil ௦௧௨௩௪௫௬௭௮௯\n 0x0C66, // Telugu ౦౧౨౩౪౫౬౭౮౯\n 0x0CE6, // Kannada ೦೧೨೩೪೫೬೭೮೯\n 0x0D66, // Malayalam ൦൧൨൩൪൫൬൭൮൯\n 0x0DE6, // Sinhala Archaic ෦෧෨෩෪෫෬෭෮෯\n\n // Southeast Asian scripts\n 0x0E50, // Thai ๐๑๒๓๔๕๖๗๘๙\n 0x0ED0, // Lao ໐໑໒໓໔໕໖໗໘໙\n 0x0F20, // Tibetan ༠༡༢༣༤༥༦༧༨༩\n 0x1040, // Myanmar ၀၁၂၃၄၅၆၇၈၉\n 0x1090, // Myanmar Shan ႐႑႒႓႔႕႖႗႘႙\n 0x17E0, // Khmer ០១២៣៤៥៦៧៨៩\n 0x1810, // Mongolian ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙\n 0x1946, // Limbu ᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏\n 0x19D0, // New Tai Lue ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙\n 0x1A80, // Tai Tham Hora ᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉\n 0x1A90, // Tai Tham Tham ᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙\n 0x1B50, // Balinese ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙\n 0x1BB0, // Sundanese ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹\n 0x1C40, // Lepcha ᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉\n 0x1C50, // Ol Chiki ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙\n\n // Fullwidth (CJK context)\n 0xFF10, // Fullwidth 0123456789\n\n // Mathematical digit variants (Unicode math block)\n 0x1D7CE, // Mathematical Bold\n 0x1D7D8, // Mathematical Double-Struck\n 0x1D7E2, // Mathematical Sans-Serif\n 0x1D7EC, // Mathematical Sans-Serif Bold\n 0x1D7F6, // Mathematical Monospace\n\n // Other scripts\n 0x104A0, // Osmanya 𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩\n 0x10D30, // Hanifi Rohingya 𐴰𐴱𐴲𐴳𐴴𐴵𐴶𐴷𐴸𐴹\n 0x11066, // Brahmi 𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯\n 0x110F0, // Sora Sompeng 𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹\n 0x11136, // Chakma 𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿\n 0x111D0, // Sharada 𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙\n 0x112F0, // Khudawadi 𑋰𑋱𑋲𑋳𑋴𑋵𑋶𑋷𑋸𑋹\n 0x11450, // Newa 𑑐𑑑𑑒𑑓𑑔𑑕𑑖𑑗𑑘𑑙\n 0x114D0, // Tirhuta 𑓐𑓑𑓒𑓓𑓔𑓕𑓖𑓗𑓘𑓙\n 0x11650, // Modi 𑙐𑙑𑙒𑙓𑙔𑙕𑙖𑙗𑙘𑙙\n 0x116C0, // Takri 𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉\n 0x11730, // Ahom 𑜰𑜱𑜲𑜳𑜴𑜵𑜶𑜷𑜸𑜹\n 0x118E0, // Warang Citi 𑣠𑣡𑣢𑣣𑣤𑣥𑣦𑣧𑣨𑣩\n 0x11950, // Dives Akuru 𑥐𑥑𑥒𑥓𑥔𑥕𑥖𑥗𑥘𑥙\n 0x11BF0, // Khitan Small Script \n 0x11C50, // Bhaiksuki 𑱐𑱑𑱒𑱓𑱔𑱕𑱖𑱗𑱘𑱙\n 0x11D50, // Masaram Gondi 𑵐𑵑𑵒𑵓𑵔𑵕𑵖𑵗𑵘𑵙\n 0x11DA0, // Gunjala Gondi 𑶠𑶡𑶢𑶣𑶤𑶥𑶦𑶧𑶨𑶩\n 0x11F50, // Kawi 𑽐𑽑𑽒𑽓𑽔𑽕𑽖𑽗𑽘𑽙\n 0x16A60, // Mro 𖩠𖩡𖩢𖩣𖩤𖩥𖩦𖩧𖩨𖩩\n 0x16AC0, // Tangsa 𖫀𖫁𖫂𖫃𖫄𖫅𖫆𖫇𖫈𖫉\n 0x16B50, // Pahawh Hmong 𖭐𖭑𖭒𖭓𖭔𖭕𖭖𖭗𖭘𖭙\n 0x1E140, // Nyiakeng Puachue Hmong 𞅀𞅁𞅂𞅃𞅄𞅅𞅆𞅇𞅈𞅉\n 0x1E2F0, // Wancho 𞋰𞋱𞋲𞋳𞋴𞋵𞋶𞋷𞋸𞋹\n 0x1E4F0, // Nag Mundari 𞓰𞓱𞓲𞓳𞓴𞓵𞓶𞓷𞓸𞓹\n 0x1E950, // Adlam 𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙\n 0x1FBF0, // Segmented digit symbols 🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹\n];\n\n// Build a sparse Map for scripts above 0xFFFF (surrogate-pair range).\n// These can't go into a flat Uint8Array indexed by code point efficiently.\nconst NOT_DIGIT = 0xFF;\nconst HIGH_MAP = new Map(); // codePoint → digit value (0-9)\n\nconst LOW_MAX = 0xFFFF;\nconst LOW_MIN = 0x0660; // first non-ASCII digit script\n\n// Flat Uint8Array covering 0x0660 .. 0xFFFF\nconst TABLE_OFFSET = LOW_MIN;\nconst TABLE_SIZE = LOW_MAX - LOW_MIN + 1;\nconst TABLE = new Uint8Array(TABLE_SIZE).fill(NOT_DIGIT);\n\nfor (const zero of SCRIPT_ZEROS) {\n for (let d = 0; d < 10; d++) {\n const cp = zero + d;\n if (cp <= LOW_MAX) {\n TABLE[cp - TABLE_OFFSET] = d;\n } else {\n HIGH_MAP.set(cp, d);\n }\n }\n}\n\nexport { TABLE, TABLE_OFFSET, HIGH_MAP, NOT_DIGIT };\n","'use strict';\n\nimport { TABLE, TABLE_OFFSET, HIGH_MAP, NOT_DIGIT } from './digitTable.js';\n\nconst CHAR_0 = 48; // '0'.charCodeAt(0)\nconst CHAR_9 = 57; // '9'.charCodeAt(0)\nconst CHAR_MINUS = 45; // '-'.charCodeAt(0)\n\n// Unicode minus/hyphen variants worth normalizing to ASCII '-' in numeric context:\n// U+2212 MINUS SIGN − (mathematically correct minus)\n// U+FF0D FULLWIDTH HYPHEN-MINUS - (Japanese fullwidth context)\n// U+FE63 SMALL HYPHEN-MINUS ﹣ (small form variant)\n//\n// NOT normalized (deliberate):\n// U+2013 EN DASH – (punctuation, not a numeric sign)\n// U+2014 EM DASH — (punctuation)\n// U+2010 HYPHEN ‐ (typographic hyphen)\n//\n// Rationale: only characters a human or locale formatter would plausibly use\n// as a numeric minus sign are normalized. Dashes used for punctuation are left\n// alone to avoid mangling non-numeric strings.\nconst MINUS_SET = new Set([0x2212, 0xFF0D, 0xFE63]);\n\n/**\n * Normalize all Unicode decimal digit characters in a string to ASCII (0-9),\n * and normalize Unicode minus variants to ASCII '-' (U+002D).\n *\n * Non-digit, non-minus characters are passed through unchanged.\n *\n * Performance design:\n * - Fast path: if the string has no convertible characters, return it unchanged\n * (zero allocation).\n * - BMP digits (0x0660..0xFFFF excl. surrogates): flat Uint8Array lookup (O(1)).\n * - Supplementary plane digits (> 0xFFFF, encoded as surrogate pairs): Map lookup.\n * - Minus variants: checked inline with a small fixed Set.\n *\n * @param {string} str\n * @returns {string}\n */\nfunction anynum(str) {\n if (typeof str !== 'string') return str;\n\n const len = str.length;\n if (len === 0) return str;\n\n // Scan for first character needing conversion.\n // If none found, return original string (zero allocation).\n let firstHit = -1;\n\n for (let i = 0; i < len; i++) {\n const cc = str.charCodeAt(i);\n\n // ASCII digit or ASCII minus — already normalized, skip fast\n if ((cc >= CHAR_0 && cc <= CHAR_9) || cc === CHAR_MINUS) continue;\n\n // Below first unicode digit script — check minus variants only\n if (cc < TABLE_OFFSET) {\n if (MINUS_SET.has(cc)) { firstHit = i; break; }\n continue;\n }\n\n // Surrogate pairs live in BMP range 0xD800-0xDFFF — check before TABLE\n if (cc >= 0xD800 && cc <= 0xDBFF) {\n if (i + 1 < len) {\n const low = str.charCodeAt(i + 1);\n if (low >= 0xDC00 && low <= 0xDFFF) {\n const cp = 0x10000 + ((cc - 0xD800) << 10) + (low - 0xDC00);\n if (HIGH_MAP.has(cp)) { firstHit = i; break; }\n }\n }\n continue;\n }\n\n // BMP non-surrogate: flat table lookup; also check minus variants in this range\n if (TABLE[cc - TABLE_OFFSET] !== NOT_DIGIT || MINUS_SET.has(cc)) {\n firstHit = i;\n break;\n }\n }\n\n // Nothing to replace — return original, zero allocation\n if (firstHit === -1) return str;\n\n // Build result: copy unchanged prefix, then convert from firstHit onward\n const chars = [];\n\n if (firstHit > 0) chars.push(str.slice(0, firstHit));\n\n for (let i = firstHit; i < len; i++) {\n const cc = str.charCodeAt(i);\n\n // ASCII digit or ASCII minus — pass through\n if ((cc >= CHAR_0 && cc <= CHAR_9) || cc === CHAR_MINUS) {\n chars.push(str[i]);\n continue;\n }\n\n // Below TABLE_OFFSET — check minus variants, else pass through\n if (cc < TABLE_OFFSET) {\n chars.push(MINUS_SET.has(cc) ? '-' : str[i]);\n continue;\n }\n\n // Surrogate pairs\n if (cc >= 0xD800 && cc <= 0xDBFF) {\n if (i + 1 < len) {\n const low = str.charCodeAt(i + 1);\n if (low >= 0xDC00 && low <= 0xDFFF) {\n const cp = 0x10000 + ((cc - 0xD800) << 10) + (low - 0xDC00);\n const d = HIGH_MAP.get(cp);\n if (d !== undefined) {\n chars.push(String.fromCharCode(d + 48));\n i++; // consume low surrogate\n continue;\n }\n }\n }\n chars.push(str[i]);\n continue;\n }\n\n // BMP non-surrogate: flat table lookup + minus variants\n if (MINUS_SET.has(cc)) {\n chars.push('-');\n continue;\n }\n const d = TABLE[cc - TABLE_OFFSET];\n chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) : str[i]);\n }\n\n return chars.join('');\n}\n\nexport { anynum };\nexport default anynum;","const hexRegex = /^[-+]?0x[a-fA-F0-9]+$/;\nconst binRegex = /^0b[01]+$/;\nconst octRegex = /^0o[0-7]+$/;\nconst numRegex = /^([\\-\\+])?(0*)([0-9]*(\\.[0-9]*)?)$/;\n\nimport anynum from \"anynum\";\n\nconst consider = {\n hex: true,\n binary: false,\n octal: false,\n leadingZeros: true,\n decimalPoint: \"\\.\",\n eNotation: true,\n //skipLike: /regex/,\n infinity: \"original\", // \"null\", \"infinity\" (Infinity type), \"string\" (\"Infinity\" (the string literal))\n unicode: false,\n};\n\nexport default function toNumber(str, options = {}) {\n options = Object.assign({}, consider, options);\n if (!str || typeof str !== \"string\") return str;\n\n let trimmedStr = str.trim();\n\n if (trimmedStr.length === 0) return str;\n else if (options.skipLike !== undefined && options.skipLike.test(trimmedStr)) return str;\n else if (trimmedStr === \"0\") return 0;\n\n if (options.unicode) {\n trimmedStr = anynum(trimmedStr);\n if (trimmedStr === \"0\") return 0; // re-check after normalization\n }\n if (options.hex && hexRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 16);\n } else if (options.binary && binRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 2);\n } else if (options.octal && octRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 8);\n } else if (!isFinite(trimmedStr)) { //Infinity\n return handleInfinity(str, Number(trimmedStr), options);\n } else if (trimmedStr.includes('e') || trimmedStr.includes('E')) { //eNotation\n return resolveEnotation(str, trimmedStr, options);\n } else {\n //separate negative sign, leading zeros, and rest number\n const match = numRegex.exec(trimmedStr);\n // +00.123 => [ , '+', '00', '.123', ..\n if (match) {\n const sign = match[1] || \"\";\n const leadingZeros = match[2];\n let numTrimmedByZeros = trimZeros(match[3]); //complete num without leading zeros\n const decimalAdjacentToLeadingZeros = sign ? // 0., -00., 000.\n str[leadingZeros.length + 1] === \".\"\n : str[leadingZeros.length] === \".\";\n\n //trim ending zeros for floating number\n if (!options.leadingZeros //leading zeros are not allowed\n && (leadingZeros.length > 1\n || (leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros))) {\n // 00, 00.3, +03.24, 03, 03.24\n return str;\n }\n else {//no leading zeros or leading zeros are allowed\n const num = Number(trimmedStr);\n const parsedStr = String(num);\n\n if (num === 0) return num;\n if (parsedStr.search(/[eE]/) !== -1) { //given number is long and parsed to eNotation\n if (options.eNotation) return num;\n else return str;\n } else if (trimmedStr.indexOf(\".\") !== -1) { //floating number\n if (parsedStr === \"0\") return num; //0.0\n else if (parsedStr === numTrimmedByZeros) return num; //0.456. 0.79000\n else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num;\n else return str;\n }\n\n let n = leadingZeros ? numTrimmedByZeros : trimmedStr;\n if (leadingZeros) {\n // -009 => -9\n return (n === parsedStr) || (sign + n === parsedStr) ? num : str\n } else {\n // +9\n return (n === parsedStr) || (n === sign + parsedStr) ? num : str\n }\n }\n } else { //non-numeric string\n return str;\n }\n }\n}\n\nconst eNotationRegx = /^([-+])?(0*)(\\d*(\\.\\d*)?[eE][-\\+]?\\d+)$/;\nfunction resolveEnotation(str, trimmedStr, options) {\n if (!options.eNotation) return str;\n const notation = trimmedStr.match(eNotationRegx);\n if (notation) {\n let sign = notation[1] || \"\";\n const eChar = notation[3].indexOf(\"e\") === -1 ? \"E\" : \"e\";\n const leadingZeros = notation[2];\n const eAdjacentToLeadingZeros = sign ? // 0E.\n str[leadingZeros.length + 1] === eChar\n : str[leadingZeros.length] === eChar;\n\n if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) return str;\n else if (leadingZeros.length === 1\n && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) {\n return Number(trimmedStr);\n } else if (leadingZeros.length > 0) {\n // Has leading zeros — only accept if leadingZeros option allows it\n if (options.leadingZeros && !eAdjacentToLeadingZeros) {\n trimmedStr = (notation[1] || \"\") + notation[3];\n return Number(trimmedStr);\n } else return str;\n } else {\n // No leading zeros — always valid e-notation, parse it\n return Number(trimmedStr);\n }\n } else {\n return str;\n }\n}\n\n/**\n * \n * @param {string} numStr without leading zeros\n * @returns \n */\nfunction trimZeros(numStr) {\n if (numStr && numStr.indexOf(\".\") !== -1) {//float\n numStr = numStr.replace(/0+$/, \"\"); //remove ending zeros\n if (numStr === \".\") numStr = \"0\";\n else if (numStr[0] === \".\") numStr = \"0\" + numStr;\n else if (numStr[numStr.length - 1] === \".\") numStr = numStr.substring(0, numStr.length - 1);\n return numStr;\n }\n return numStr;\n}\n\nfunction parse_int(numStr, base) {\n const str = numStr.trim();\n if (base === 2 || base === 8) numStr = str.substring(2);\n\n if (parseInt) return parseInt(numStr, base);\n else if (Number.parseInt) return Number.parseInt(numStr, base);\n else if (window && window.parseInt) return window.parseInt(numStr, base);\n else throw new Error(\"parseInt, Number.parseInt, window.parseInt are not supported\");\n}\n\n/**\n * Handle infinite values based on user option\n * @param {string} str - original input string\n * @param {number} num - parsed number (Infinity or -Infinity)\n * @param {object} options - user options\n * @returns {string|number|null} based on infinity option\n */\nfunction handleInfinity(str, num, options) {\n const isPositive = num === Infinity;\n\n switch (options.infinity.toLowerCase()) {\n case \"null\":\n return null;\n case \"infinity\":\n return num; // Return Infinity or -Infinity\n case \"string\":\n return isPositive ? \"Infinity\" : \"-Infinity\";\n case \"original\":\n default:\n return str; // Return original string like \"1e1000\"\n }\n}","export default function getIgnoreAttributesFn(ignoreAttributes) {\n if (typeof ignoreAttributes === 'function') {\n return ignoreAttributes\n }\n if (Array.isArray(ignoreAttributes)) {\n return (attrName) => {\n for (const pattern of ignoreAttributes) {\n if (typeof pattern === 'string' && attrName === pattern) {\n return true\n }\n if (pattern instanceof RegExp && pattern.test(attrName)) {\n return true\n }\n }\n }\n }\n return () => false\n}","/**\n * Expression - Parses and stores a tag pattern expression\n * \n * Patterns are parsed once and stored in an optimized structure for fast matching.\n * \n * @example\n * const expr = new Expression(\"root.users.user\");\n * const expr2 = new Expression(\"..user[id]:first\");\n * const expr3 = new Expression(\"root/users/user\", { separator: '/' });\n */\nexport default class Expression {\n /**\n * Create a new Expression\n * @param {string} pattern - Pattern string (e.g., \"root.users.user\", \"..user[id]\")\n * @param {Object} options - Configuration options\n * @param {string} options.separator - Path separator (default: '.')\n */\n constructor(pattern, options = {}, data) {\n this.pattern = pattern;\n this.separator = options.separator || '.';\n this.segments = this._parse(pattern);\n this.data = data;\n // Cache expensive checks for performance (O(1) instead of O(n))\n this._hasDeepWildcard = this.segments.some(seg => seg.type === 'deep-wildcard');\n this._hasAttributeCondition = this.segments.some(seg => seg.attrName !== undefined);\n this._hasPositionSelector = this.segments.some(seg => seg.position !== undefined);\n }\n\n /**\n * Parse pattern string into segments\n * @private\n * @param {string} pattern - Pattern to parse\n * @returns {Array} Array of segment objects\n */\n _parse(pattern) {\n const segments = [];\n\n // Split by separator but handle \"..\" specially\n let i = 0;\n let currentPart = '';\n\n while (i < pattern.length) {\n if (pattern[i] === this.separator) {\n // Check if next char is also separator (deep wildcard)\n if (i + 1 < pattern.length && pattern[i + 1] === this.separator) {\n // Flush current part if any\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n currentPart = '';\n }\n // Add deep wildcard\n segments.push({ type: 'deep-wildcard' });\n i += 2; // Skip both separators\n } else {\n // Regular separator\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n currentPart = '';\n i++;\n }\n } else {\n currentPart += pattern[i];\n i++;\n }\n }\n\n // Flush remaining part\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n\n return segments;\n }\n\n /**\n * Parse a single segment\n * @private\n * @param {string} part - Segment string (e.g., \"user\", \"ns::user\", \"user[id]\", \"ns::user:first\")\n * @returns {Object} Segment object\n */\n _parseSegment(part) {\n const segment = { type: 'tag' };\n\n // NEW NAMESPACE SYNTAX (v2.0):\n // ============================\n // Namespace uses DOUBLE colon (::)\n // Position uses SINGLE colon (:)\n // \n // Examples:\n // \"user\" → tag\n // \"user:first\" → tag + position\n // \"user[id]\" → tag + attribute\n // \"user[id]:first\" → tag + attribute + position\n // \"ns::user\" → namespace + tag\n // \"ns::user:first\" → namespace + tag + position\n // \"ns::user[id]\" → namespace + tag + attribute\n // \"ns::user[id]:first\" → namespace + tag + attribute + position\n // \"ns::first\" → namespace + tag named \"first\" (NO ambiguity!)\n //\n // This eliminates all ambiguity:\n // :: = namespace separator\n // : = position selector\n // [] = attributes\n\n // Step 1: Extract brackets [attr] or [attr=value]\n let bracketContent = null;\n let withoutBrackets = part;\n\n const bracketMatch = part.match(/^([^\\[]+)(\\[[^\\]]*\\])(.*)$/);\n if (bracketMatch) {\n withoutBrackets = bracketMatch[1] + bracketMatch[3];\n if (bracketMatch[2]) {\n const content = bracketMatch[2].slice(1, -1);\n if (content) {\n bracketContent = content;\n }\n }\n }\n\n // Step 2: Check for namespace (double colon ::)\n let namespace = undefined;\n let tagAndPosition = withoutBrackets;\n\n if (withoutBrackets.includes('::')) {\n const nsIndex = withoutBrackets.indexOf('::');\n namespace = withoutBrackets.substring(0, nsIndex).trim();\n tagAndPosition = withoutBrackets.substring(nsIndex + 2).trim(); // Skip ::\n\n if (!namespace) {\n throw new Error(`Invalid namespace in pattern: ${part}`);\n }\n }\n\n // Step 3: Parse tag and position (single colon :)\n let tag = undefined;\n let positionMatch = null;\n\n if (tagAndPosition.includes(':')) {\n const colonIndex = tagAndPosition.lastIndexOf(':'); // Use last colon for position\n const tagPart = tagAndPosition.substring(0, colonIndex).trim();\n const posPart = tagAndPosition.substring(colonIndex + 1).trim();\n\n // Verify position is a valid keyword\n const isPositionKeyword = ['first', 'last', 'odd', 'even'].includes(posPart) ||\n /^nth\\(\\d+\\)$/.test(posPart);\n\n if (isPositionKeyword) {\n tag = tagPart;\n positionMatch = posPart;\n } else {\n // Not a valid position keyword, treat whole thing as tag\n tag = tagAndPosition;\n }\n } else {\n tag = tagAndPosition;\n }\n\n if (!tag) {\n throw new Error(`Invalid segment pattern: ${part}`);\n }\n\n segment.tag = tag;\n if (namespace) {\n segment.namespace = namespace;\n }\n\n // Step 4: Parse attributes\n if (bracketContent) {\n if (bracketContent.includes('=')) {\n const eqIndex = bracketContent.indexOf('=');\n segment.attrName = bracketContent.substring(0, eqIndex).trim();\n segment.attrValue = bracketContent.substring(eqIndex + 1).trim();\n } else {\n segment.attrName = bracketContent.trim();\n }\n }\n\n // Step 5: Parse position selector\n if (positionMatch) {\n const nthMatch = positionMatch.match(/^nth\\((\\d+)\\)$/);\n if (nthMatch) {\n segment.position = 'nth';\n segment.positionValue = parseInt(nthMatch[1], 10);\n } else {\n segment.position = positionMatch;\n }\n }\n\n return segment;\n }\n\n /**\n * Get the number of segments\n * @returns {number}\n */\n get length() {\n return this.segments.length;\n }\n\n /**\n * Check if expression contains deep wildcard\n * @returns {boolean}\n */\n hasDeepWildcard() {\n return this._hasDeepWildcard;\n }\n\n /**\n * Check if expression has attribute conditions\n * @returns {boolean}\n */\n hasAttributeCondition() {\n return this._hasAttributeCondition;\n }\n\n /**\n * Check if expression has position selectors\n * @returns {boolean}\n */\n hasPositionSelector() {\n return this._hasPositionSelector;\n }\n\n /**\n * Get string representation\n * @returns {string}\n */\n toString() {\n return this.pattern;\n }\n}","/**\n * ExpressionSet - An indexed collection of Expressions for efficient bulk matching\n *\n * Instead of iterating all expressions on every tag, ExpressionSet pre-indexes\n * them at insertion time by depth and terminal tag name. At match time, only\n * the relevant bucket is evaluated — typically reducing checks from O(E) to O(1)\n * lookup plus O(small bucket) matches.\n *\n * Three buckets are maintained:\n * - `_byDepthAndTag` — exact depth + exact tag name (tightest, used first)\n * - `_wildcardByDepth` — exact depth + wildcard tag `*` (depth-matched only)\n * - `_deepWildcards` — expressions containing `..` (cannot be depth-indexed)\n *\n * @example\n * import { Expression, ExpressionSet } from 'fast-xml-tagger';\n *\n * // Build once at config time\n * const stopNodes = new ExpressionSet();\n * stopNodes.add(new Expression('root.users.user'));\n * stopNodes.add(new Expression('root.config.setting'));\n * stopNodes.add(new Expression('..script'));\n *\n * // Query on every tag — hot path\n * if (stopNodes.matchesAny(matcher)) { ... }\n */\nexport default class ExpressionSet {\n constructor() {\n /** @type {Map<string, import('./Expression.js').default[]>} depth:tag → expressions */\n this._byDepthAndTag = new Map();\n\n /** @type {Map<number, import('./Expression.js').default[]>} depth → wildcard-tag expressions */\n this._wildcardByDepth = new Map();\n\n /** @type {import('./Expression.js').default[]} expressions containing deep wildcard (..) */\n this._deepWildcards = [];\n\n /** @type {Map<string, import('./Expression.js').default[]>} terminalTag → deep wildcard expressions */\n this._deepByTerminalTag = new Map();\n\n /** @type {Set<string>} pattern strings already added — used for deduplication */\n this._patterns = new Set();\n\n /** @type {boolean} whether the set is sealed against further additions */\n this._sealed = false;\n }\n\n /**\n * Add an Expression to the set.\n * Duplicate patterns (same pattern string) are silently ignored.\n *\n * @param {import('./Expression.js').default} expression - A pre-constructed Expression instance\n * @returns {this} for chaining\n * @throws {TypeError} if called after seal()\n *\n * @example\n * set.add(new Expression('root.users.user'));\n * set.add(new Expression('..script'));\n */\n add(expression) {\n if (this._sealed) {\n throw new TypeError(\n 'ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.'\n );\n }\n\n // Deduplicate by pattern string\n if (this._patterns.has(expression.pattern)) return this;\n this._patterns.add(expression.pattern);\n\n if (expression.hasDeepWildcard()) {\n const lastSeg = expression.segments[expression.segments.length - 1];\n if (lastSeg && lastSeg.type !== 'deep-wildcard' && lastSeg.tag !== '*') {\n const tag = lastSeg.tag;\n if (!this._deepByTerminalTag.has(tag)) this._deepByTerminalTag.set(tag, []);\n this._deepByTerminalTag.get(tag).push(expression);\n } else {\n this._deepWildcards.push(expression);\n }\n return this;\n }\n\n const depth = expression.length;\n const lastSeg = expression.segments[expression.segments.length - 1];\n const tag = lastSeg?.tag;\n\n if (!tag || tag === '*') {\n // Can index by depth but not by tag\n if (!this._wildcardByDepth.has(depth)) this._wildcardByDepth.set(depth, []);\n this._wildcardByDepth.get(depth).push(expression);\n } else {\n // Tightest bucket: depth + tag\n const key = `${depth}:${tag}`;\n if (!this._byDepthAndTag.has(key)) this._byDepthAndTag.set(key, []);\n this._byDepthAndTag.get(key).push(expression);\n }\n\n return this;\n }\n\n /**\n * Add multiple expressions at once.\n *\n * @param {import('./Expression.js').default[]} expressions - Array of Expression instances\n * @returns {this} for chaining\n *\n * @example\n * set.addAll([\n * new Expression('root.users.user'),\n * new Expression('root.config.setting'),\n * ]);\n */\n addAll(expressions) {\n for (const expr of expressions) this.add(expr);\n return this;\n }\n\n /**\n * Check whether a pattern string is already present in the set.\n *\n * @param {import('./Expression.js').default} expression\n * @returns {boolean}\n */\n has(expression) {\n return this._patterns.has(expression.pattern);\n }\n\n /**\n * Number of expressions in the set.\n * @type {number}\n */\n get size() {\n return this._patterns.size;\n }\n\n /**\n * Seal the set against further modifications.\n * Useful to prevent accidental mutations after config is built.\n * Calling add() or addAll() on a sealed set throws a TypeError.\n *\n * @returns {this}\n */\n seal() {\n this._sealed = true;\n return this;\n }\n\n /**\n * Whether the set has been sealed.\n * @type {boolean}\n */\n get isSealed() {\n return this._sealed;\n }\n\n /**\n * Test whether the matcher's current path matches any expression in the set.\n *\n * Evaluation order (cheapest → most expensive):\n * 1. Exact depth + tag bucket — O(1) lookup, typically 0–2 expressions\n * 2. Depth-only wildcard bucket — O(1) lookup, rare\n * 3. Deep-wildcard list — always checked, but usually small\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {boolean} true if any expression matches the current path\n *\n * @example\n * if (stopNodes.matchesAny(matcher)) {\n * // handle stop node\n * }\n */\n matchesAny(matcher) {\n return this.findMatch(matcher) !== null;\n }\n /**\n * Find and return the first Expression that matches the matcher's current path.\n *\n * Uses the same evaluation order as matchesAny (cheapest → most expensive):\n * 1. Exact depth + tag bucket\n * 2. Depth-only wildcard bucket\n * 3. Deep-wildcard list\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {import('./Expression.js').default | null} the first matching Expression, or null\n *\n * @example\n * const expr = stopNodes.findMatch(matcher);\n * if (expr) {\n * // access expr.config, expr.pattern, etc.\n * }\n */\n findMatch(matcher) {\n const depth = matcher.getDepth();\n const tag = matcher.getCurrentTag();\n\n // 1. Tightest bucket — most expressions live here\n const exactKey = `${depth}:${tag}`;\n const exactBucket = this._byDepthAndTag.get(exactKey);\n if (exactBucket) {\n for (let i = 0; i < exactBucket.length; i++) {\n if (matcher.matches(exactBucket[i])) return exactBucket[i];\n }\n }\n\n // 2. Depth-matched wildcard-tag expressions\n const wildcardBucket = this._wildcardByDepth.get(depth);\n if (wildcardBucket) {\n for (let i = 0; i < wildcardBucket.length; i++) {\n if (matcher.matches(wildcardBucket[i])) return wildcardBucket[i];\n }\n }\n\n // 3. Deep wildcards — indexed by terminal tag, then unindexed fallback\n const deepBucket = this._deepByTerminalTag.get(tag);\n if (deepBucket) {\n for (let i = 0; i < deepBucket.length; i++) {\n if (matcher.matches(deepBucket[i])) return deepBucket[i];\n }\n }\n for (let i = 0; i < this._deepWildcards.length; i++) {\n if (matcher.matches(this._deepWildcards[i])) return this._deepWildcards[i];\n }\n\n return null;\n }\n}\n","import ExpressionSet from \"./ExpressionSet.js\";\n\n/**\n * MatcherView - A lightweight read-only view over a Matcher's internal state.\n *\n * Created once by Matcher and reused across all callbacks. Holds a direct\n * reference to the parent Matcher so it always reflects current parser state\n * with zero copying or freezing overhead.\n *\n * Users receive this via {@link Matcher#readOnly} or directly from parser\n * callbacks. It exposes all query and matching methods but has no mutation\n * methods — misuse is caught at the TypeScript level rather than at runtime.\n *\n * @example\n * const matcher = new Matcher();\n * const view = matcher.readOnly();\n *\n * matcher.push(\"root\", {});\n * view.getCurrentTag(); // \"root\"\n * view.getDepth(); // 1\n */\nexport class MatcherView {\n /**\n * @param {Matcher} matcher - The parent Matcher instance to read from.\n */\n constructor(matcher) {\n this._matcher = matcher;\n }\n\n /**\n * Get the path separator used by the parent matcher.\n * @returns {string}\n */\n get separator() {\n return this._matcher.separator;\n }\n\n /**\n * Get current tag name.\n * @returns {string|undefined}\n */\n getCurrentTag() {\n const path = this._matcher.path;\n return path.length > 0 ? path[path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace.\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n const path = this._matcher.path;\n return path.length > 0 ? path[path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value.\n * @param {string} attrName\n * @returns {*}\n */\n getAttrValue(attrName) {\n const path = this._matcher.path;\n if (path.length === 0) return undefined;\n return path[path.length - 1].values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAttr(attrName) {\n const path = this._matcher.path;\n if (path.length === 0) return false;\n const current = path[path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get the value of a \"kept\" attribute from the nearest ancestor (or\n * current node) that declared it via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {*}\n */\n getAnyParentAttr(attrName) {\n return this._matcher.getAnyParentAttr(attrName);\n }\n\n /**\n * Check whether any ancestor (or the current node) kept the given\n * attribute via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAnyParentAttr(attrName) {\n return this._matcher.hasAnyParentAttr(attrName);\n }\n\n /**\n * Get current node's sibling position (child index in parent).\n * @returns {number}\n */\n getPosition() {\n const path = this._matcher.path;\n if (path.length === 0) return -1;\n return path[path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name).\n * @returns {number}\n */\n getCounter() {\n const path = this._matcher.path;\n if (path.length === 0) return -1;\n return path[path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition).\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth.\n * @returns {number}\n */\n getDepth() {\n return this._matcher.path.length;\n }\n\n /**\n * Get path as string.\n * @param {string} [separator] - Optional separator (uses default if not provided)\n * @param {boolean} [includeNamespace=true]\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n return this._matcher.toString(separator, includeNamespace);\n }\n\n /**\n * Get path as array of tag names.\n * @returns {string[]}\n */\n toArray() {\n return this._matcher.path.map(n => n.tag);\n }\n\n /**\n * Match current path against an Expression.\n * @param {Expression} expression\n * @returns {boolean}\n */\n matches(expression) {\n return this._matcher.matches(expression);\n }\n\n /**\n * Match any expression in the given set against the current path.\n * @param {ExpressionSet} exprSet\n * @returns {boolean}\n */\n matchesAny(exprSet) {\n return exprSet.matchesAny(this._matcher);\n }\n}\n\n/**\n * Matcher - Tracks current path in XML/JSON tree and matches against Expressions.\n *\n * The matcher maintains a stack of nodes representing the current path from root to\n * current tag. It only stores attribute values for the current (top) node to minimize\n * memory usage. Sibling tracking is used to auto-calculate position and counter.\n *\n * Use {@link Matcher#readOnly} to obtain a {@link MatcherView} safe to pass to\n * user callbacks — it always reflects current state with no Proxy overhead.\n *\n * @example\n * const matcher = new Matcher();\n * matcher.push(\"root\", {});\n * matcher.push(\"users\", {});\n * matcher.push(\"user\", { id: \"123\", type: \"admin\" });\n *\n * const expr = new Expression(\"root.users.user\");\n * matcher.matches(expr); // true\n */\nexport default class Matcher {\n /**\n * Create a new Matcher.\n * @param {Object} [options={}]\n * @param {string} [options.separator='.'] - Default path separator\n */\n constructor(options = {}) {\n this.separator = options.separator || '.';\n this.path = [];\n this.siblingStacks = [];\n // Each path node: { tag, values, position, counter, namespace? }\n // values only present for current (last) node\n // Each siblingStacks entry: Map<tagName, count> tracking occurrences at each level\n this._pathStringCache = null;\n this._view = new MatcherView(this);\n\n // Kept-attribute stack: only populated when push() is called with options.keep.\n this._keptAttrs = [];\n }\n\n /**\n * Push a new tag onto the path.\n * @param {string} tagName\n * @param {Object|null} [attrValues=null]\n * @param {string|null} [namespace=null]\n * @param {Object|null} [options=null]\n * @param {string[]} [options.keep] - Names of attributes (from attrValues)\n */\n push(tagName, attrValues = null, namespace = null, options = null) {\n this._pathStringCache = null;\n\n // Remove values from previous current node (now becoming ancestor)\n if (this.path.length > 0) {\n this.path[this.path.length - 1].values = undefined;\n }\n\n // Get or create sibling tracking for current level\n const currentLevel = this.path.length;\n let level = this.siblingStacks[currentLevel];\n if (!level) {\n // `counts` tells same-name siblings apart (the \"counter\" — nth <item>\n // among other <item>s). `total` is every child seen at this level so\n // far, kept as a running number instead of re-added from `counts` on\n // every push — a parent with many differently-named children would\n // otherwise cost more per child the more distinct names it has.\n level = { counts: new Map(), total: 0 };\n this.siblingStacks[currentLevel] = level;\n }\n\n // Create a unique key for sibling tracking that includes namespace\n const siblingKey = namespace ? `${namespace}:${tagName}` : tagName;\n\n // Calculate counter (how many times this tag appeared at this level)\n const counter = level.counts.get(siblingKey) || 0;\n\n // Position = total children at this level seen before this one.\n const position = level.total;\n\n // Update sibling count for this tag, and the level's running total.\n level.counts.set(siblingKey, counter + 1);\n level.total++;\n\n // Create new node\n const node = {\n tag: tagName,\n position: position,\n counter: counter\n };\n\n if (namespace !== null && namespace !== undefined) {\n node.namespace = namespace;\n }\n\n if (attrValues !== null && attrValues !== undefined) {\n node.values = attrValues;\n }\n\n this.path.push(node);\n\n // Depth of the node we just pushed (1-based, matches this.path.length)\n const depth = this.path.length;\n\n // Copy only the requested attributes into the kept-attrs stack. This is\n // the one part of push() whose cost scales with input (O(keep.length))\n // rather than being O(1) — by design, since the caller is explicitly\n // opting in for specific attribute names. No options/keep => zero added\n // cost beyond the two property reads below.\n const keep = options !== null ? options.keep : null;\n if (keep !== null && keep !== undefined && keep.length > 0 && attrValues) {\n for (let i = 0; i < keep.length; i++) {\n const name = keep[i];\n if (attrValues[name] !== undefined) {\n this._keptAttrs.push({ depth, name, value: attrValues[name] });\n }\n }\n }\n }\n\n /**\n * Pop the last tag from the path.\n * @returns {Object|undefined} The popped node\n */\n pop() {\n if (this.path.length === 0) return undefined;\n this._pathStringCache = null;\n\n const node = this.path.pop();\n\n if (this.siblingStacks.length > this.path.length + 1) {\n this.siblingStacks.length = this.path.length + 1;\n }\n\n // Drop any kept attributes that belonged to the popped node (or deeper).\n // _keptAttrs is depth-ordered (push only ever appends increasing depths),\n // so this is a backward scan that stops at the first surviving entry —\n // typically O(1) since kept attrs are rare by design.\n const poppedDepth = this.path.length + 1;\n while (\n this._keptAttrs.length > 0 &&\n this._keptAttrs[this._keptAttrs.length - 1].depth >= poppedDepth\n ) {\n this._keptAttrs.pop();\n }\n\n return node;\n }\n\n /**\n * Update current node's attribute values.\n * Useful when attributes are parsed after push.\n * @param {Object} attrValues\n */\n updateCurrent(attrValues) {\n if (this.path.length > 0) {\n const current = this.path[this.path.length - 1];\n if (attrValues !== null && attrValues !== undefined) {\n current.values = attrValues;\n }\n }\n }\n\n /**\n * Get current tag name.\n * @returns {string|undefined}\n */\n getCurrentTag() {\n return this.path.length > 0 ? this.path[this.path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace.\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n return this.path.length > 0 ? this.path[this.path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value.\n * @param {string} attrName\n * @returns {*}\n */\n getAttrValue(attrName) {\n if (this.path.length === 0) return undefined;\n return this.path[this.path.length - 1].values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAttr(attrName) {\n if (this.path.length === 0) return false;\n const current = this.path[this.path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get the value of a \"kept\" attribute from the nearest ancestor (or\n * current node) that declared it via `push(tag, attrs, ns, { keep: [...] })`.\n * Unlike getAttrValue(), this works regardless of how deep the path has\n * gone since the attribute was pushed — but only for attribute names that\n * were explicitly marked with `keep` at push time. Cost is proportional to\n * the number of currently-kept attributes (typically 0-3), not path depth.\n * @param {string} attrName\n * @returns {*} the value, or undefined if no ancestor kept this attribute\n */\n getAnyParentAttr(attrName) {\n const kept = this._keptAttrs;\n for (let i = kept.length - 1; i >= 0; i--) {\n if (kept[i].name === attrName) return kept[i].value;\n }\n return undefined;\n }\n\n /**\n * Check whether any ancestor (or the current node) kept the given\n * attribute via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAnyParentAttr(attrName) {\n const kept = this._keptAttrs;\n for (let i = kept.length - 1; i >= 0; i--) {\n if (kept[i].name === attrName) return true;\n }\n return false;\n }\n\n /**\n * Get current node's sibling position (child index in parent).\n * @returns {number}\n */\n getPosition() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name).\n * @returns {number}\n */\n getCounter() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition).\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth.\n * @returns {number}\n */\n getDepth() {\n return this.path.length;\n }\n\n /**\n * Get path as string.\n * @param {string} [separator] - Optional separator (uses default if not provided)\n * @param {boolean} [includeNamespace=true]\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n const sep = separator || this.separator;\n const isDefault = (sep === this.separator && includeNamespace === true);\n\n if (isDefault) {\n if (this._pathStringCache !== null) {\n return this._pathStringCache;\n }\n const result = this.path.map(n =>\n (n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n this._pathStringCache = result;\n return result;\n }\n\n return this.path.map(n =>\n (includeNamespace && n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n }\n\n /**\n * Get path as array of tag names.\n * @returns {string[]}\n */\n toArray() {\n return this.path.map(n => n.tag);\n }\n\n /**\n * Reset the path to empty.\n */\n reset() {\n this._pathStringCache = null;\n this.path = [];\n this.siblingStacks = [];\n this._keptAttrs = [];\n }\n\n /**\n * Match current path against an Expression.\n * @param {Expression} expression\n * @returns {boolean}\n */\n matches(expression) {\n const segments = expression.segments;\n\n if (segments.length === 0) {\n return false;\n }\n\n if (expression.hasDeepWildcard()) {\n return this._matchWithDeepWildcard(segments);\n }\n\n return this._matchSimple(segments);\n }\n\n /**\n * @private\n */\n _matchSimple(segments) {\n if (this.path.length !== segments.length) {\n return false;\n }\n\n for (let i = 0; i < segments.length; i++) {\n if (!this._matchSegment(segments[i], this.path[i], i === this.path.length - 1)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * @private\n */\n _matchWithDeepWildcard(segments) {\n let pathIdx = this.path.length - 1;\n let segIdx = segments.length - 1;\n\n while (segIdx >= 0 && pathIdx >= 0) {\n const segment = segments[segIdx];\n\n if (segment.type === 'deep-wildcard') {\n segIdx--;\n\n if (segIdx < 0) {\n return true;\n }\n\n const nextSeg = segments[segIdx];\n let found = false;\n\n for (let i = pathIdx; i >= 0; i--) {\n if (this._matchSegment(nextSeg, this.path[i], i === this.path.length - 1)) {\n pathIdx = i - 1;\n segIdx--;\n found = true;\n break;\n }\n }\n\n if (!found) {\n return false;\n }\n } else {\n if (!this._matchSegment(segment, this.path[pathIdx], pathIdx === this.path.length - 1)) {\n return false;\n }\n pathIdx--;\n segIdx--;\n }\n }\n\n return segIdx < 0;\n }\n\n /**\n * @private\n */\n _matchSegment(segment, node, isCurrentNode) {\n if (segment.tag !== '*' && segment.tag !== node.tag) {\n return false;\n }\n\n if (segment.namespace !== undefined) {\n if (segment.namespace !== '*' && segment.namespace !== node.namespace) {\n return false;\n }\n }\n\n if (segment.attrName !== undefined) {\n if (!isCurrentNode) {\n return false;\n }\n\n if (!node.values || !(segment.attrName in node.values)) {\n return false;\n }\n\n if (segment.attrValue !== undefined) {\n if (String(node.values[segment.attrName]) !== String(segment.attrValue)) {\n return false;\n }\n }\n }\n\n if (segment.position !== undefined) {\n if (!isCurrentNode) {\n return false;\n }\n\n const counter = node.counter ?? 0;\n\n if (segment.position === 'first' && counter !== 0) {\n return false;\n } else if (segment.position === 'odd' && counter % 2 !== 1) {\n return false;\n } else if (segment.position === 'even' && counter % 2 !== 0) {\n return false;\n } else if (segment.position === 'nth' && counter !== segment.positionValue) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Match any expression in the given set against the current path.\n * @param {ExpressionSet} exprSet\n * @returns {boolean}\n */\n matchesAny(exprSet) {\n return exprSet.matchesAny(this);\n }\n\n /**\n * Create a snapshot of current state.\n * @returns {Object}\n */\n snapshot() {\n return {\n path: this.path.map(node => ({ ...node })),\n siblingStacks: this.siblingStacks.map(level => level ? { counts: new Map(level.counts), total: level.total } : level),\n keptAttrs: this._keptAttrs.map(entry => ({ ...entry }))\n };\n }\n\n /**\n * Restore state from snapshot.\n * @param {Object} snapshot\n */\n restore(snapshot) {\n this._pathStringCache = null;\n this.path = snapshot.path.map(node => ({ ...node }));\n this.siblingStacks = snapshot.siblingStacks.map(level => level ? { counts: new Map(level.counts), total: level.total } : level);\n this._keptAttrs = (snapshot.keptAttrs || []).map(entry => ({ ...entry }));\n }\n\n /**\n * Return the read-only {@link MatcherView} for this matcher.\n *\n * The same instance is returned on every call — no allocation occurs.\n * It always reflects the current parser state and is safe to pass to\n * user callbacks without risk of accidental mutation.\n *\n * @returns {MatcherView}\n *\n * @example\n * const view = matcher.readOnly();\n * // pass view to callbacks — it stays in sync automatically\n * view.matches(expr); // ✓\n * view.getCurrentTag(); // ✓\n * // view.push(...) // ✗ method does not exist — caught by TypeScript\n */\n readOnly() {\n return this._view;\n }\n}\n","/**\n * HTML context patterns.\n *\n * Detects XSS vectors that are dangerous when a string ends up rendered as HTML.\n * All patterns use bounded quantifiers to ensure linear-time matching (ReDoS-safe).\n *\n * Each entry is { pattern: RegExp, id: string, description: string }\n * so callers can inspect which rule fired if they need to.\n */\n\nconst HTML_PATTERNS = [\n {\n id: 'html-script-open',\n description: '<script opening tag',\n pattern: /<script[\\s>/]/i,\n },\n {\n id: 'html-script-close',\n description: '</script closing tag',\n pattern: /<\\/script[\\s>]/i,\n },\n {\n id: 'html-javascript-protocol',\n description: 'javascript: URI scheme (with optional whitespace/encoding)',\n // Handles javascript:, j\\u0061vascript:, and whitespace variants\n pattern: /j[\\t\\n\\r ]*a[\\t\\n\\r ]*v[\\t\\n\\r ]*a[\\t\\n\\r ]*s[\\t\\n\\r ]*c[\\t\\n\\r ]*r[\\t\\n\\r ]*i[\\t\\n\\r ]*p[\\t\\n\\r ]*t[\\t\\n\\r ]*:/i,\n },\n {\n id: 'html-vbscript-protocol',\n description: 'vbscript: URI scheme',\n pattern: /vbscript[\\t\\n\\r ]*:/i,\n },\n {\n id: 'html-data-html',\n description: 'data:text/html URI — can execute scripts in browsers',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*text\\/html/i,\n },\n {\n id: 'html-data-xhtml',\n description: 'data:application/xhtml+xml URI',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*application\\/xhtml/i,\n },\n {\n id: 'html-data-svg',\n description: 'data:image/svg+xml URI — can execute scripts',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*image\\/svg\\+xml/i,\n },\n {\n id: 'html-inline-event-handler',\n description: 'Inline event handler attributes: onclick=, onerror=, onload=, etc.',\n // \\bon ensures we match a word boundary so \"phonetic=\" is not caught\n pattern: /\\bon\\w{1,30}\\s*=/i,\n },\n {\n id: 'html-entity-obfuscated-script',\n description: 'HTML-entity-encoded <script (e.g. <script or <script)',\n // Entities include optional trailing semicolon: < or < (both valid in HTML5)\n pattern: /(?:�*3[Cc];?|�*60;?|<)\\s*script/i,\n },\n {\n id: 'html-entity-obfuscated-javascript',\n description: 'HTML-entity-encoded javascript: (partial — catches common j or j for \"j\")',\n pattern: /(?:�*6[Aa];?|�*106;?)\\s*(?:�*61;?|a)[\\s\\S]{0,80}script\\s*:/i,\n },\n {\n id: 'html-style-expression',\n description: 'CSS expression() — IE-era code execution in style attributes',\n pattern: /style[\\s\\S]{0,20}expression\\s*\\(/i,\n },\n {\n id: 'html-object-embed',\n description: '<object or <embed tags that can load active content',\n pattern: /<(?:object|embed)[\\s>/]/i,\n },\n {\n id: 'html-base-tag',\n description: '<base href= — can hijack all relative URLs on a page',\n pattern: /<base[\\s>]/i,\n },\n {\n id: 'html-meta-refresh',\n description: '<meta http-equiv=\"refresh\" — can redirect users',\n pattern: /<meta[\\s\\S]{0,40}http-equiv[\\s\\S]{0,20}refresh/i,\n },\n {\n id: 'html-srcdoc',\n description: 'srcdoc= attribute on iframes — embeds HTML that can run scripts',\n pattern: /srcdoc\\s*=/i,\n },\n {\n id: 'html-iframe',\n description: '<iframe tag',\n pattern: /<iframe[\\s>/]/i,\n },\n {\n id: 'html-form',\n description: '<form tag — can be used for phishing / credential harvesting injection',\n pattern: /<form[\\s>/]/i,\n },\n];\n\nexport default HTML_PATTERNS;\n","/**\n * XML context patterns.\n *\n * Detects injection vectors that are specifically dangerous when a string\n * is inserted into an XML document (not HTML rendering context).\n *\n * Key distinction from HTML: these patterns target parser-level attacks —\n * things that can confuse or subvert an XML parser, trigger external entity\n * resolution, or inject DTD content. HTML rendering concerns (XSS) belong\n * in the HTML context.\n */\n\nconst XML_PATTERNS = [\n {\n id: 'xml-cdata-injection',\n description: 'CDATA section injection: <![CDATA[ breaks out of text node context',\n pattern: /<!\\[CDATA\\[/i,\n },\n {\n id: 'xml-cdata-close',\n description: 'CDATA close sequence: ]]> can terminate an enclosing CDATA section',\n pattern: /\\]\\]>/,\n },\n {\n id: 'xml-processing-instruction',\n description: 'XML processing instruction: <?xml-stylesheet or <?php etc.',\n pattern: /<\\?(?:xml[\\- ]|php|asp)/i,\n },\n {\n id: 'xml-doctype-injection',\n description: 'DOCTYPE declaration embedded in content — can define entities',\n // Match <!DOCTYPE followed by end-of-string, whitespace, or [ (internal subset)\n pattern: /<!DOCTYPE(?:[\\s[]|$)/i,\n },\n {\n id: 'xml-entity-system',\n description: 'SYSTEM keyword — used in external entity declarations (XXE)',\n pattern: /\\bSYSTEM\\s+[\"']/i,\n },\n {\n id: 'xml-entity-public',\n description: 'PUBLIC keyword — used in external entity declarations (XXE)',\n pattern: /\\bPUBLIC\\s+[\"']/i,\n },\n {\n id: 'xml-entity-declaration',\n description: '<!ENTITY declaration — defines entities, potential XXE or entity expansion',\n pattern: /<!ENTITY[\\s%]/i,\n },\n {\n id: 'xml-billion-laughs',\n description: 'Entity reference chaining / billion laughs: repeated &eX; style references',\n // Heuristic: 3+ consecutive entity refs suggests expansion attack\n pattern: /(?:&\\w{1,20};){3,}/,\n },\n {\n id: 'xml-namespace-confusion',\n description: 'xmlns: attribute injection — can redefine namespaces to confuse parsers',\n pattern: /\\bxmlns\\s*(?::\\w{1,40})?\\s*=/i,\n },\n {\n id: 'xml-comment-injection',\n description: '<!-- comment injection — can hide content from some parsers',\n pattern: /<!--/,\n },\n {\n id: 'xml-comment-close',\n description: '--> closes an enclosing XML comment',\n pattern: /-->/,\n },\n {\n id: 'xml-pi-close',\n description: '?> closes an enclosing processing instruction',\n pattern: /\\?>/,\n },\n];\n\nexport default XML_PATTERNS;\n","/**\n * SVG context patterns.\n *\n * SVG is XML-based but renders in browsers, giving it a unique attack surface\n * that combines XML parser behaviour with browser rendering and JavaScript execution.\n *\n * Many of these vectors bypass HTML sanitizers that don't understand SVG semantics\n * (DOMPurify has documented bypass vulnerabilities specifically in SVG/XML context).\n */\n\nconst SVG_PATTERNS = [\n {\n id: 'svg-script-element',\n description: '<script element inside SVG executes JavaScript',\n pattern: /<script[\\s>/]/i,\n },\n {\n id: 'svg-xlink-href-javascript',\n description: 'xlink:href with javascript: — classic SVG XSS via <a> or <use>',\n pattern: /xlink\\s*:\\s*href\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-href-javascript',\n description: 'href= with javascript: in SVG context (<a>, <animate>, etc.)',\n pattern: /href\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-foreignobject',\n description: '<foreignObject embeds HTML inside SVG — can execute scripts',\n pattern: /<foreignObject[\\s>/]/i,\n },\n {\n id: 'svg-use-external',\n description: '<use xlink:href or href pointing to external resource (non-fragment URL)',\n // Match <use with href= where the value starts with a non-# character (external URL)\n // [\\\"'][^#] catches quoted values not starting with #; [^\\\"'#\\s>] catches unquoted\n pattern: /<use[\\s\\S]{0,60}(?:xlink\\s*:\\s*)?href\\s*=\\s*(?:[\"'][^#]|[^\"'#\\s>])/i,\n },\n {\n id: 'svg-animate-href',\n description: '<animate attributeName=\"href\" — can dynamically change href to javascript:',\n pattern: /<animate[\\s\\S]{0,80}attributeName\\s*=\\s*[\"'][\\s]*href[\"']/i,\n },\n {\n id: 'svg-animate-xlinkhref',\n description: '<animate attributeName=\"xlink:href\"',\n pattern: /<animate[\\s\\S]{0,80}attributeName\\s*=\\s*[\"'][\\s]*xlink\\s*:\\s*href[\"']/i,\n },\n {\n id: 'svg-set-javascript',\n description: '<set to=\"javascript:...\" — sets an attribute to a javascript: URI',\n pattern: /<set[\\s\\S]{0,80}to\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-event-handler',\n description: 'SVG-specific event handler attributes: onload=, onerror=, onactivate=, etc.',\n pattern: /\\bon(?:load|error|activate|begin|end|repeat|focus|blur|click|mouse\\w{1,20}|key\\w{1,20})\\s*=/i,\n },\n {\n id: 'svg-handler-generic',\n description: 'Generic on* handler catch-all for SVG attributes',\n pattern: /\\bon\\w{1,30}\\s*=/i,\n },\n {\n id: 'svg-filter-feimage',\n description: '<feImage href= — filter primitive that can load external resources',\n pattern: /<feImage[\\s\\S]{0,80}(?:xlink\\s*:\\s*)?href\\s*=/i,\n },\n {\n id: 'svg-image-external',\n description: '<image xlink:href with http/https or javascript protocol',\n pattern: /<image[\\s\\S]{0,80}(?:xlink\\s*:\\s*)?href\\s*=\\s*[\"']?\\s*(?:https?|javascript)\\s*:/i,\n },\n {\n id: 'svg-style-javascript',\n description: 'style= attribute containing javascript: (e.g. background:url(javascript:...))',\n pattern: /style\\s*=[\\s\\S]{0,60}javascript\\s*:/i,\n },\n];\n\nexport default SVG_PATTERNS;\n","/**\n * SQL context patterns — high-precision rules only.\n *\n * These rules have very low false-positive risk and are safe to apply to\n * general user text (names, descriptions, search queries, etc.).\n * All patterns are ReDoS-safe — unlike the `sql-injection` npm package\n * which has an active CVE on its own detection regexes.\n *\n * For exhaustive coverage including noisier heuristics (comment sequences,\n * hex literals, stacked queries with semicolons), use 'SQL-STRICT' instead.\n * Apply 'SQL-STRICT' only to strings that are specifically SQL fragments,\n * not to general free-text fields.\n */\n\nconst SQL_PATTERNS = [\n {\n id: 'sql-block-comment-open',\n description: 'SQL block comment open: /* ... */ — unusual in legitimate user text',\n pattern: /\\/\\*/,\n },\n {\n id: 'sql-union-select',\n description: 'UNION SELECT — most common SQL injection aggregation attack',\n pattern: /\\bUNION\\s{1,20}(?:ALL\\s{1,20})?SELECT\\b/i,\n },\n {\n id: 'sql-drop-table',\n description: 'DROP TABLE — destructive DDL injection',\n pattern: /\\bDROP\\s{1,20}TABLE\\b/i,\n },\n {\n id: 'sql-drop-database',\n description: 'DROP DATABASE — destructive DDL injection',\n pattern: /\\bDROP\\s{1,20}DATABASE\\b/i,\n },\n {\n id: 'sql-insert-into',\n description: 'INSERT INTO — data injection',\n pattern: /\\bINSERT\\s{1,20}INTO\\b/i,\n },\n {\n id: 'sql-delete-from',\n description: 'DELETE FROM — data deletion injection',\n pattern: /\\bDELETE\\s{1,20}FROM\\b/i,\n },\n {\n id: 'sql-update-set',\n description: 'UPDATE ... SET — data modification injection',\n // Allows arbitrary content between UPDATE and SET (table name, alias, etc.)\n pattern: /\\bUPDATE\\b[\\s\\S]{1,60}\\bSET\\b/i,\n },\n {\n id: 'sql-exec-xp',\n description: 'EXEC xp_ — MSSQL extended stored procedure execution',\n pattern: /\\bEXEC(?:UTE)?\\s{1,20}xp_/i,\n },\n {\n id: 'sql-tautology-string',\n description: \"Classic string tautology: ' OR '1'='1 or \\\" OR \\\"1\\\"=\\\"1\\\"\",\n // Last quote is optional — injection may truncate it: ' OR '1'='1--\n pattern: /'\\s{0,10}OR\\s{0,10}'[^']{0,20}'\\s*=\\s*'[^']{0,20}/i,\n },\n {\n id: 'sql-tautology-numeric',\n description: 'Numeric tautology: OR 1=1',\n pattern: /\\bOR\\s{1,10}1\\s*=\\s*1\\b/i,\n },\n {\n id: 'sql-always-true-zero',\n description: 'Numeric tautology: OR 0=0',\n pattern: /\\bOR\\s{1,10}0\\s*=\\s*0\\b/i,\n },\n {\n id: 'sql-sleep-benchmark',\n description: 'Time-based blind injection: SLEEP() or BENCHMARK()',\n pattern: /\\b(?:SLEEP|BENCHMARK)\\s*\\(/i,\n },\n {\n id: 'sql-waitfor-delay',\n description: 'MSSQL time-based blind injection: WAITFOR DELAY',\n pattern: /\\bWAITFOR\\s{1,20}DELAY\\b/i,\n },\n {\n id: 'sql-char-function',\n description: 'CHAR() function — used to obfuscate injected strings',\n pattern: /\\bCHAR\\s*\\(\\s*\\d{1,3}/i,\n },\n {\n id: 'sql-information-schema',\n description: 'INFORMATION_SCHEMA — reconnaissance query for table/column enumeration',\n pattern: /\\bINFORMATION_SCHEMA\\b/i,\n },\n];\n\nexport default SQL_PATTERNS;\n","/**\n * SHELL context patterns.\n *\n * Detects shell injection vectors and path traversal patterns.\n * Designed for use when a string will be passed to a shell command,\n * used as a file path, or interpolated into OS-level operations.\n */\n\nconst SHELL_PATTERNS = [\n {\n id: 'shell-path-traversal-unix',\n description: 'Unix path traversal: ../ — climbing the directory tree',\n pattern: /\\.\\.\\//,\n },\n {\n id: 'shell-path-traversal-windows',\n description: 'Windows path traversal: ..\\\\ — climbing the directory tree',\n pattern: /\\.\\.\\\\/,\n },\n {\n id: 'shell-path-traversal-encoded',\n description: 'URL-encoded path traversal: %2e%2e or %2f variants',\n pattern: /%2e%2e|%2f\\.\\.|\\.\\.%2f/i,\n },\n {\n id: 'shell-null-byte',\n description: 'Null byte injection: \\\\x00 or %00 — truncates strings in C-backed functions',\n pattern: /\\x00|%00/,\n },\n {\n id: 'shell-semicolon',\n description: 'Semicolon command separator: cmd1; cmd2',\n pattern: /;/,\n },\n {\n id: 'shell-pipe',\n description: 'Pipe operator: cmd1 | cmd2',\n pattern: /\\|/,\n },\n {\n id: 'shell-and-operator',\n description: 'AND operator: cmd1 && cmd2',\n pattern: /&&/,\n },\n {\n id: 'shell-or-operator',\n description: 'OR operator: cmd1 || cmd2',\n pattern: /\\|\\|/,\n },\n {\n id: 'shell-backtick',\n description: 'Backtick command substitution: `cmd`',\n pattern: /`/,\n },\n {\n id: 'shell-dollar-paren',\n description: 'Dollar-paren command substitution: $(cmd)',\n pattern: /\\$\\(/,\n },\n {\n id: 'shell-dollar-brace',\n description: 'Dollar-brace variable expansion: ${var} — can be abused for injection',\n pattern: /\\$\\{/,\n },\n {\n id: 'shell-redirect-out',\n description: 'Output redirection: cmd > file or cmd >> file',\n pattern: />{1,2}/,\n },\n {\n id: 'shell-redirect-in',\n description: 'Input redirection: cmd < file',\n pattern: /</,\n },\n {\n id: 'shell-newline-injection',\n description: 'Newline injection: \\\\n or \\\\r — can inject new shell commands',\n pattern: /[\\n\\r]/,\n },\n {\n id: 'shell-glob-star',\n description: 'Glob expansion: * or ? — can expand to unintended files',\n // Only flag when combined with path separators to reduce false positives\n pattern: /[/\\\\][*?]/,\n },\n {\n id: 'shell-absolute-root',\n description: 'Absolute root path injection: string starting with / or \\\\ (Windows UNC)',\n pattern: /^(?:\\/|\\\\\\\\)/,\n },\n {\n id: 'shell-windows-drive',\n description: 'Windows drive letter path injection: C:\\\\ or D:/',\n pattern: /^[a-zA-Z]:[/\\\\]/,\n },\n {\n id: 'shell-curl-wget',\n description: 'curl/wget with URL or flags — can exfiltrate data or download payloads',\n // Require a URL scheme (http/https/ftp) or a flag (-) to reduce false positives\n // \"curl is a tool\" won't match; \"curl http://...\" or \"curl -s ...\" will\n pattern: /\\b(?:curl|wget)\\s+(?:https?:\\/\\/|ftp:\\/\\/|-)/i,\n },\n];\n\nexport default SHELL_PATTERNS;\n","/**\n * REDOS context patterns.\n *\n * Detects strings that, if used as regular expressions, could cause\n * catastrophic backtracking (ReDoS — Regular Expression Denial of Service).\n *\n * These patterns detect the structural forms that lead to exponential or\n * polynomial backtracking in NFA-based regex engines (V8, PCRE, Java, etc.).\n *\n * Use this context when user-supplied strings will be compiled into RegExp objects.\n */\n\nconst REDOS_PATTERNS = [\n {\n id: 'redos-nested-quantifier-plus',\n description: 'Nested + quantifier inside a group with outer quantifier: (a+)+, (.+b)*, etc.',\n // Matches any group containing a + quantifier, with an outer * or + — catches (a+)+, (.+b)*, etc.\n pattern: /\\([^)]*\\+[^)]*\\)[+*]/,\n },\n {\n id: 'redos-nested-quantifier-star',\n description: 'Nested * quantifier: (a*)* or (a*)+ — catastrophic backtracking',\n pattern: /\\([^)]*\\*[^)]*\\)[*+]/,\n },\n {\n id: 'redos-nested-groups',\n description: 'Doubly nested quantified groups: ((a+)+) — guaranteed catastrophic',\n pattern: /\\(\\([^)]{0,40}\\)[+*]\\)[+*]/,\n },\n {\n id: 'redos-alternation-overlap',\n description: 'Overlapping alternation under quantifier: (a|a)+ — ambiguous NFA paths',\n // Detect repeated identical alternatives under a quantifier\n pattern: /\\(([^|()]{1,20})\\|(?:\\1)(?:\\|[^|()]{1,20}){0,5}\\)[+*?]{1,2}/,\n },\n {\n id: 'redos-star-plus-concat',\n description: '(x*x)+ pattern — triggers super-linear backtracking',\n pattern: /\\([^)]{0,10}\\*[^)]{0,10}\\)[+*]/,\n },\n {\n id: 'redos-dot-star-greedy',\n description: '(.*){n,} or (.+){n,} — repeated greedy dot quantifiers',\n pattern: /\\(\\.[*+]\\)\\{?\\d/,\n },\n {\n id: 'redos-large-repetition',\n description: 'Very large fixed or range repetition count {1000,} or {1000,n} — denial of service via backtracking',\n // Matches { followed by 4+ digits (≥1000), then optional ,digits }\n pattern: /\\{\\d{4,}(?:,\\d*)?\\}/,\n },\n {\n id: 'redos-catastrophic-alternation',\n description: 'Long alternation with many similar branches — polynomial backtracking risk',\n // Heuristic: 10+ pipe-separated alternatives in a single group\n pattern: /\\([^)]{0,200}(?:\\|[^|)]{0,50}){9,}\\)/,\n },\n];\n\nexport default REDOS_PATTERNS;\n","/**\n * NOSQL context patterns.\n *\n * Detects injection vectors specific to NoSQL databases (primarily MongoDB)\n * and JavaScript-evaluated queries.\n *\n * Attack categories:\n * 1. MongoDB query operator injection: $where, $ne, $gt, $regex, $or, $and, etc.\n * These operators, when injected into a JSON query object, can bypass\n * authentication or exfiltrate data without knowing passwords.\n *\n * 2. JavaScript execution: $where clauses execute arbitrary JS server-side.\n *\n * 3. Prototype pollution: __proto__, constructor.prototype — can corrupt\n * the prototype chain of all objects in the Node.js process.\n *\n * Pattern note: MongoDB operators appear as JSON keys. In JSON, keys are\n * quoted: {\"$where\": ...} so the pattern must allow an optional closing\n * quote between the operator name and the colon: /\\$where[\"'\\s]*:/\n */\n\n// Shared suffix: optional closing quote/whitespace before the colon\n// Handles: $op: (bare), \"$op\": (JSON), '$op': (single-quoted)\nconst SEP = /[\"'\\s]*:/;\nconst sep = '[\"\\'\\\\s]*:';\n\nconst NOSQL_PATTERNS = [\n // ─── MongoDB $ operator injection ────────────────────────────────────────\n {\n id: 'nosql-where-operator',\n description: '$where — executes arbitrary JavaScript server-side in MongoDB',\n pattern: new RegExp(`\\\\$where${sep}`, 'i'),\n },\n {\n id: 'nosql-ne-operator',\n description: '$ne — \"not equal\" operator used to bypass equality checks',\n pattern: new RegExp(`\\\\$ne${sep}`, 'i'),\n },\n {\n id: 'nosql-gt-operator',\n description: '$gt — \"greater than\" used to bypass password/value checks',\n pattern: new RegExp(`\\\\$gte?${sep}`, 'i'),\n },\n {\n id: 'nosql-lt-operator',\n description: '$lt / $lte — \"less than\" bypass variants',\n pattern: new RegExp(`\\\\$lte?${sep}`, 'i'),\n },\n {\n id: 'nosql-regex-operator',\n description: '$regex — can be used to extract data character by character (blind injection)',\n pattern: new RegExp(`\\\\$regex${sep}`, 'i'),\n },\n {\n id: 'nosql-or-operator',\n description: '$or — logical OR; used to create always-true conditions',\n pattern: new RegExp(`\\\\$or${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-and-operator',\n description: '$and — logical AND operator injection',\n pattern: new RegExp(`\\\\$and${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-nor-operator',\n description: '$nor — logical NOR operator injection',\n pattern: new RegExp(`\\\\$nor${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-exists-operator',\n description: '$exists — can enumerate fields to determine schema',\n pattern: new RegExp(`\\\\$exists${sep}`, 'i'),\n },\n {\n id: 'nosql-in-operator',\n description: '$in — matches any value in a list; can enumerate values',\n pattern: new RegExp(`\\\\$in${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-expr-operator',\n description: '$expr — allows aggregation expressions in queries (MongoDB 3.6+)',\n pattern: new RegExp(`\\\\$expr${sep}`, 'i'),\n },\n {\n id: 'nosql-function-operator',\n description: '$function — executes arbitrary JavaScript in MongoDB 4.4+',\n pattern: new RegExp(`\\\\$function${sep}`, 'i'),\n },\n {\n id: 'nosql-accumulator-operator',\n description: '$accumulator — custom aggregation with arbitrary JS execution',\n pattern: new RegExp(`\\\\$accumulator${sep}`, 'i'),\n },\n // ─── Prototype pollution ─────────────────────────────────────────────────\n {\n id: 'nosql-proto-pollution',\n description: '__proto__ — prototype pollution via object key injection',\n pattern: /__proto__/,\n },\n {\n id: 'nosql-constructor-prototype',\n description: 'constructor.prototype — alternative prototype pollution vector (dot notation or JSON key)',\n // Matches dot-notation (obj.constructor.prototype) and JSON key adjacency\n // (\"constructor\": {\"prototype\": ...})\n pattern: /constructor[\\s\"':.,{\\[]*prototype/i,\n },\n {\n id: 'nosql-proto-bracket',\n description: '[\"__proto__\"] — bracket-notation prototype pollution',\n pattern: /\\[[\"']__proto__[\"']\\]/,\n },\n];\n\nexport default NOSQL_PATTERNS;\n","/**\n * LOG context patterns.\n *\n * Detects injection vectors that are dangerous when a string is written\n * to a log file, passed to a logging framework, or interpolated into\n * a log message that will be parsed or displayed.\n *\n * Attack categories:\n * 1. CRLF injection — injects fake log lines by embedding newlines\n * 2. Log4Shell (CVE-2021-44228) — ${jndi:...} triggers JNDI lookup in Log4j\n * 3. SSTI in log templates — {{...}}, #{...} trigger template evaluation\n * if the log message is passed through a template engine\n * 4. Null byte injection — truncates log entries in some implementations\n * 5. ANSI escape injection — manipulates terminal output when logs are\n * tailed in a terminal (colour codes, cursor movement, etc.)\n *\n * Note: Newline characters (\\n, \\r) will produce false positives for\n * multi-line legitimate values. Use this context only for single-line\n * log field values (usernames, IDs, request parameters, etc.).\n */\n\nconst LOG_PATTERNS = [\n // ─── CRLF / newline injection ─────────────────────────────────────────────\n {\n id: 'log-crlf-injection',\n description: 'CRLF injection: literal \\\\r or \\\\n embeds fake log lines',\n pattern: /[\\r\\n]/,\n },\n {\n id: 'log-url-encoded-crlf',\n description: 'URL-encoded CRLF: %0d, %0a, %0D, %0A — decoded by some log parsers',\n pattern: /%0[dDaA]/,\n },\n {\n id: 'log-unicode-newline',\n description: 'Unicode newline variants: U+2028 (line separator), U+2029 (paragraph separator)',\n pattern: /[\\u2028\\u2029]/,\n },\n\n // ─── Log4Shell / JNDI injection (CVE-2021-44228) ─────────────────────────\n {\n id: 'log-log4shell-jndi',\n description: 'Log4Shell: ${jndi:...} triggers remote code execution in Apache Log4j',\n pattern: /\\$\\{jndi\\s*:/i,\n },\n {\n id: 'log-log4shell-obfuscated',\n description: 'Obfuscated Log4Shell: ${::-j}... lookup-bypass prefix used to evade WAF detection',\n // ${::- is the Log4j lookup-bypass escape sequence; presence alone is suspicious\n pattern: /\\$\\{::-/,\n },\n {\n id: 'log-log4j-lookup',\n description: 'Log4j lookup syntax: ${env:...}, ${sys:...}, ${ctx:...} — data exfiltration',\n pattern: /\\$\\{(?:env|sys|ctx|main|map|sd|web|docker|k8s|spring)\\s*:/i,\n },\n\n // ─── Server-Side Template Injection (SSTI) in log messages ───────────────\n {\n id: 'log-ssti-double-brace',\n description: 'SSTI double-brace: {{expression}} — Jinja2, Twig, Handlebars, etc.',\n pattern: /\\{\\{[\\s\\S]{0,80}\\}\\}/,\n },\n {\n id: 'log-ssti-hash-brace',\n description: 'SSTI hash-brace: #{expression} — Thymeleaf, Velocity, Ruby ERB',\n pattern: /#\\{[\\s\\S]{0,80}\\}/,\n },\n {\n id: 'log-ssti-dollar-brace',\n description: 'SSTI/EL injection: ${expression with operators or method calls} — JSP EL, Freemarker, SpEL',\n // Require that the ${...} content looks like an expression, not a plain variable name.\n // Flags if the content contains: . ( * + operators, or known SSTI keywords.\n // This avoids flagging ${PATH}, ${HOME} etc. (plain shell variables).\n pattern: /\\$\\{[^}]*(?:\\.|\\(|\\*|\\+|\\bclass\\b|\\bruntime\\b|\\bprocess\\b|\\bexec\\b)[^}]{0,80}\\}/i,\n },\n {\n id: 'log-ssti-percent-tag',\n description: 'SSTI ERB/ASP tag: <%= expression %> — Ruby ERB, ASP',\n pattern: /<%=[\\s\\S]{0,80}%>/,\n },\n\n // ─── Null byte ────────────────────────────────────────────────────────────\n {\n id: 'log-null-byte',\n description: 'Null byte: \\\\x00 or %00 — can truncate log entries in C-backed loggers',\n pattern: /\\x00|%00/,\n },\n\n // ─── ANSI escape injection ────────────────────────────────────────────────\n {\n id: 'log-ansi-escape',\n description: 'ANSI escape sequence: ESC[ — can manipulate terminal output when logs are tailed',\n pattern: /\\x1b\\[/,\n },\n];\n\nexport default LOG_PATTERNS;\n","/**\n * SQL-STRICT context patterns.\n *\n * Extends the base 'SQL' context with three additional rules that are\n * effective at detecting real injections but carry a higher false-positive\n * risk on general free-text input.\n *\n * Use 'SQL-STRICT' when:\n * - The string is specifically a SQL fragment or database identifier\n * - You control the input domain (e.g. a dedicated SQL search field)\n * - You can tolerate occasional false positives in exchange for broader coverage\n *\n * Use 'SQL' (not STRICT) when:\n * - The field is general user text (names, descriptions, comments)\n * - False positives would block legitimate content (e.g. \"see note -- above\")\n *\n * Rules moved here from 'SQL' due to false-positive risk:\n *\n * sql-line-comment — \"--\" fires on \"see note -- above\", \"value--\", CSS var(--primary)\n * sql-stacked-query — \"; SELECT\" fires on legitimate prose with semicolons + SQL words\n * sql-hex-encoding — \"0xDEAD\" fires on hex values in technical docs and log output\n */\n\nimport SQL_PATTERNS from './sql.js';\n\nconst SQL_STRICT_EXTRA = [\n {\n id: 'sql-line-comment',\n description: 'SQL line comment: -- followed by whitespace or end of string',\n pattern: /--(?:\\s|$)/,\n },\n {\n id: 'sql-stacked-query',\n description: 'Stacked queries: semicolon immediately followed by a SQL keyword',\n pattern: /;\\s{0,10}(?:SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC)\\b/i,\n },\n {\n id: 'sql-hex-encoding',\n description: 'Hex-encoded string injection: 0x41414141 style (MySQL)',\n pattern: /\\b0x[0-9a-f]{4,}/i,\n },\n];\n\n// SQL-STRICT = all base SQL rules + the three noisy extras\nconst SQL_STRICT_PATTERNS = [...SQL_PATTERNS, ...SQL_STRICT_EXTRA];\n\nexport default SQL_STRICT_PATTERNS;\n","/**\n * is-unsafe v2\n *\n * Zero-dependency, DOM-free, pure predicate for detecting unsafe strings\n * across HTML, XML, SVG, SQL, SQL-STRICT, SHELL, REDOS, NOSQL, and LOG contexts.\n *\n * v2 change: contexts are imported as named pattern arrays rather than resolved\n * via a string-keyed registry. This makes each context independently\n * tree-shakeable — bundlers can drop any context you never import.\n *\n * @module is-unsafe\n */\n\n// ─── Context pattern arrays (named exports) ────────────────────────────────\n// Import only the ones you need. Each is independently tree-shakeable.\n\nexport { default as HTML } from './contexts/html.js';\nexport { default as XML } from './contexts/xml.js';\nexport { default as SVG } from './contexts/svg.js';\nexport { default as SQL } from './contexts/sql.js';\nexport { default as SHELL } from './contexts/shell.js';\nexport { default as REDOS } from './contexts/redos.js';\nexport { default as NOSQL } from './contexts/nosql.js';\nexport { default as LOG } from './contexts/log.js';\n\n// SQL-STRICT needs a quoted identifier because of the hyphen\nimport SQL_STRICT from './contexts/sql-strict.js';\nexport { SQL_STRICT };\n\n// ─── VALID_CONTEXTS convenience re-export ─────────────────────────────────\n// Importing this pulls in ALL contexts. Use it only when you need all of them\n// (e.g. for validation UI, tooling, or exhaustive audits).\n// If you only need a subset, import the named contexts directly instead.\n\nimport HTML from './contexts/html.js';\nimport XML from './contexts/xml.js';\nimport SVG from './contexts/svg.js';\nimport SQL from './contexts/sql.js';\nimport SHELL from './contexts/shell.js';\nimport REDOS from './contexts/redos.js';\nimport NOSQL from './contexts/nosql.js';\nimport LOG from './contexts/log.js';\n\n// ─── Attach labels to named contexts ──────────────────────────────────────\n// Each built-in PatternList carries its canonical name so matchList can read\n// list.label directly — no registry lookup needed at match time.\n// Custom PatternLists default to 'CUSTOM' unless the caller sets list.label.\n\nHTML.label = 'HTML';\nXML.label = 'XML';\nSVG.label = 'SVG';\nSQL.label = 'SQL';\nSQL_STRICT.label = 'SQL-STRICT';\nSHELL.label = 'SHELL';\nREDOS.label = 'REDOS';\nNOSQL.label = 'NOSQL';\nLOG.label = 'LOG';\n\nexport const VALID_CONTEXTS = Object.freeze({\n HTML,\n XML,\n SVG,\n SQL,\n 'SQL-STRICT': SQL_STRICT,\n SHELL,\n REDOS,\n NOSQL,\n LOG,\n});\n\n// ─── Types ────────────────────────────────────────────────────────────────\n\n/**\n * @typedef {{ id: string, description: string, pattern: RegExp }} Rule\n */\n\n/**\n * @typedef {Rule[]} PatternList\n */\n\n/**\n * @typedef {Object} MatchResult\n * @property {string} context - Label identifying which context matched ('HTML', 'CUSTOM', etc.)\n * @property {string} id - Rule identifier\n * @property {string} description - Human-readable description of what was matched\n * @property {RegExp} pattern - The pattern that matched\n */\n\n// ─── Internal helpers ──────────────────────────────────────────────────────\n\n/**\n * @param {unknown} value\n */\nfunction assertString(value) {\n if (typeof value !== 'string') {\n throw new TypeError(\n `is-unsafe: first argument must be a string, got ${typeof value}`\n );\n }\n}\n\n/**\n * @param {unknown} context\n */\nfunction assertContext(context) {\n if (context instanceof RegExp) return;\n\n if (Array.isArray(context)) {\n if (context.length === 0) {\n throw new TypeError('is-unsafe: context must not be an empty array');\n }\n // Detect array-of-arrays vs flat pattern list\n if (Array.isArray(context[0])) {\n // Array of PatternLists\n for (const list of context) {\n if (!Array.isArray(list) || list.length === 0) {\n throw new TypeError(\n 'is-unsafe: each context in the array must be a non-empty pattern array (PatternList)'\n );\n }\n }\n }\n // else: flat PatternList — trust it, no deep validation needed\n return;\n }\n\n throw new TypeError(\n `is-unsafe: second argument must be a PatternList (e.g. HTML), ` +\n `an array of PatternLists (e.g. [HTML, XML]), or a RegExp. Got: ${typeof context}`\n );\n}\n\n/**\n * Normalise any valid context arg into an array of PatternLists.\n *\n * @param {Rule[]|Rule[][]|RegExp} context\n * @returns {{ lists: Rule[][]|null, regex: RegExp|null }}\n */\nfunction normalise(context) {\n if (context instanceof RegExp) return { lists: null, regex: context };\n // Distinguish PatternList (array of rule objects) from array of PatternLists\n if (Array.isArray(context[0])) return { lists: context, regex: null };\n return { lists: [context], regex: null };\n}\n\n/**\n * Test value against a single PatternList. Returns the first MatchResult or null.\n *\n * @param {string} value\n * @param {Rule[]} list\n * @returns {MatchResult|null}\n */\nfunction matchList(value, list) {\n const label = list.label ?? 'CUSTOM';\n for (const rule of list) {\n if (rule.pattern.test(value)) {\n return { context: label, id: rule.id, description: rule.description, pattern: rule.pattern };\n }\n }\n return null;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────\n\n/**\n * Returns `true` if `value` is unsafe in the given context(s), `false` otherwise.\n *\n * @param {string} value - The string to test\n * @param {PatternList | PatternList[] | RegExp} context\n * - A PatternList imported from is-unsafe (e.g. `HTML`, `XML`)\n * - An array of PatternLists — returns true if unsafe in **any** of them\n * - A custom RegExp — returns true if the pattern matches\n * @returns {boolean}\n *\n * @example\n * import { isUnsafe, HTML, SQL } from 'is-unsafe';\n *\n * isUnsafe('<script>alert(1)</script>', HTML) // true\n * isUnsafe('hello world', HTML) // false\n * isUnsafe('value', [HTML, SQL]) // false\n * isUnsafe('value', /my-pattern/i) // false\n */\nfunction isUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n const { lists, regex } = normalise(context);\n\n if (regex) return regex.test(value);\n\n for (const list of lists) {\n if (matchList(value, list) !== null) return true;\n }\n return false;\n}\n\n/**\n * Like `isUnsafe`, but returns the first `MatchResult` describing **why**\n * the value was flagged, or `null` if it is safe.\n *\n * @param {string} value\n * @param {PatternList | PatternList[] | RegExp} context\n * @returns {MatchResult|null}\n *\n * @example\n * import { whyUnsafe, HTML } from 'is-unsafe';\n *\n * whyUnsafe('<script>alert(1)</script>', HTML)\n * // { context: 'HTML', id: 'html-script-open', description: '...', pattern: /.../ }\n */\nfunction whyUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n const { lists, regex } = normalise(context);\n\n if (regex) {\n return regex.test(value)\n ? { context: 'CUSTOM', id: 'custom-regex', description: 'Matched caller-supplied pattern', pattern: regex }\n : null;\n }\n\n for (const list of lists) {\n const result = matchList(value, list);\n if (result !== null) return result;\n }\n return null;\n}\n\n/**\n * Returns **all** matching rules across the given context(s), or an empty\n * array if the value is safe. Useful for comprehensive auditing.\n *\n * @param {string} value\n * @param {PatternList | PatternList[] | RegExp} context\n * @returns {MatchResult[]}\n */\nfunction allUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n const { lists, regex } = normalise(context);\n const results = [];\n\n if (regex) {\n if (regex.test(value)) {\n results.push({ context: 'CUSTOM', id: 'custom-regex', description: 'Matched caller-supplied pattern', pattern: regex });\n }\n return results;\n }\n\n for (const list of lists) {\n const label = list.label ?? 'CUSTOM';\n for (const rule of list) {\n if (rule.pattern.test(value)) {\n results.push({ context: label, id: rule.id, description: rule.description, pattern: rule.pattern });\n }\n }\n }\n\n return results;\n}\n\nexport { isUnsafe, whyUnsafe, allUnsafe };\nexport default isUnsafe;\n","'use strict';\n///@ts-check\n\nimport { getAllMatches, isExist, DANGEROUS_PROPERTY_NAMES, criticalProperties } from '../util.js';\nimport xmlNode from './xmlNode.js';\nimport DocTypeReader from './DocTypeReader.js';\nimport toNumber from \"strnum\";\nimport getIgnoreAttributesFn from \"../ignoreAttributes.js\";\nimport { Expression, Matcher } from 'path-expression-matcher';\nimport { ExpressionSet } from 'path-expression-matcher';\nimport { EntityDecoder, XML, CURRENCY, COMMON_HTML, ENTITY_ACTION } from '@nodable/entities';\nimport { isUnsafe, HTML as HTML_CONTEXT, XML as XML_CONTEXT } from \"is-unsafe\"\n\n\n// const regx =\n// '<((!\\\\[CDATA\\\\[([\\\\s\\\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\\\/)(NAME)\\\\s*>))([^<]*)'\n// .replace(/NAME/g, util.nameRegexp);\n\n//const tagsRegx = new RegExp(\"<(\\\\/?[\\\\w:\\\\-\\._]+)([^>]*)>(\\\\s*\"+cdataRegx+\")*([^<]+)?\",\"g\");\n//const tagsRegx = new RegExp(\"<(\\\\/?)((\\\\w*:)?([\\\\w:\\\\-\\._]+))([^>]*)>([^<]*)(\"+cdataRegx+\"([^<]*))*([^<]+)?\",\"g\");\n\n// Helper functions for attribute and namespace handling\n\n/**\n * Extract raw attributes (without prefix) from prefixed attribute map\n * @param {object} prefixedAttrs - Attributes with prefix from buildAttributesMap\n * @param {object} options - Parser options containing attributeNamePrefix\n * @returns {object} Raw attributes for matcher\n */\nfunction extractRawAttributes(prefixedAttrs, options) {\n if (!prefixedAttrs) return {};\n\n // Handle attributesGroupName option\n const attrs = options.attributesGroupName\n ? prefixedAttrs[options.attributesGroupName]\n : prefixedAttrs;\n\n if (!attrs) return {};\n\n const rawAttrs = {};\n for (const key in attrs) {\n // Remove the attribute prefix to get raw name\n if (key.startsWith(options.attributeNamePrefix)) {\n const rawName = key.substring(options.attributeNamePrefix.length);\n rawAttrs[rawName] = attrs[key];\n } else {\n // Attribute without prefix (shouldn't normally happen, but be safe)\n rawAttrs[key] = attrs[key];\n }\n }\n return rawAttrs;\n}\n\n/**\n * Extract namespace from raw tag name\n * @param {string} rawTagName - Tag name possibly with namespace (e.g., \"soap:Envelope\")\n * @returns {string|undefined} Namespace or undefined\n */\nfunction extractNamespace(rawTagName) {\n if (!rawTagName || typeof rawTagName !== 'string') return undefined;\n\n const colonIndex = rawTagName.indexOf(':');\n if (colonIndex !== -1 && colonIndex > 0) {\n const ns = rawTagName.substring(0, colonIndex);\n // Don't treat xmlns as a namespace\n if (ns !== 'xmlns') {\n return ns;\n }\n }\n return undefined;\n}\n\nexport default class OrderedObjParser {\n constructor(options, externalEntities) {\n this.options = options;\n this.currentNode = null;\n this.tagsNodeStack = [];\n this.parseXml = parseXml;\n this.parseTextData = parseTextData;\n this.resolveNameSpace = resolveNameSpace;\n this.buildAttributesMap = buildAttributesMap;\n this.isItStopNode = isItStopNode;\n this.replaceEntitiesValue = replaceEntitiesValue;\n this.readStopNodeData = readStopNodeData;\n this.saveTextToParentTag = saveTextToParentTag;\n this.addChild = addChild;\n this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)\n this.entityExpansionCount = 0;\n this.currentExpandedLength = 0;\n this.doctypefound = false;\n let namedEntities = { ...XML };\n if (this.options.entityDecoder) {\n this.entityDecoder = this.options.entityDecoder\n } else {\n if (typeof this.options.htmlEntities === \"object\") namedEntities = this.options.htmlEntities;\n else if (this.options.htmlEntities === true) namedEntities = { ...COMMON_HTML, ...CURRENCY };\n this.entityDecoder = new EntityDecoder({\n namedEntities: { ...namedEntities, ...externalEntities },\n numericAllowed: this.options.htmlEntities,\n limit: {\n maxTotalExpansions: this.options.processEntities.maxTotalExpansions,\n maxExpandedLength: this.options.processEntities.maxExpandedLength,\n applyLimitsTo: this.options.processEntities.appliesTo,\n },\n // onExternalEntity: (name, value) => isUnsafe(value) ? 'block' : 'allow',\n onInputEntity: (name, value) =>\n //TODO: VALID_CONTEXTS.HTML should be set only if this.options.htmlEntities\n isUnsafe(value, [HTML_CONTEXT, XML_CONTEXT]) ? ENTITY_ACTION.BLOCK : ENTITY_ACTION.ALLOW,\n\n //postCheck: resolved => resolved\n });\n }\n\n // Initialize path matcher for path-expression-matcher\n this.matcher = new Matcher();\n this.readonlyMatcher = this.matcher.readOnly();\n\n // Flag to track if current node is a stop node (optimization)\n this.isCurrentNodeStopNode = false;\n\n // Pre-compile stopNodes expressions\n this.stopNodeExpressionsSet = new ExpressionSet();\n const stopNodesOpts = this.options.stopNodes;\n if (stopNodesOpts && stopNodesOpts.length > 0) {\n for (let i = 0; i < stopNodesOpts.length; i++) {\n const stopNodeExp = stopNodesOpts[i];\n if (typeof stopNodeExp === 'string') {\n // Convert string to Expression object\n this.stopNodeExpressionsSet.add(new Expression(stopNodeExp));\n } else if (stopNodeExp instanceof Expression) {\n // Already an Expression object\n this.stopNodeExpressionsSet.add(stopNodeExp);\n }\n }\n this.stopNodeExpressionsSet.seal();\n }\n }\n\n}\n\n\n/**\n * @param {string} val\n * @param {string} tagName\n * @param {string|Matcher} jPath - jPath string or Matcher instance based on options.jPath\n * @param {boolean} dontTrim\n * @param {boolean} hasAttributes\n * @param {boolean} isLeafNode\n * @param {boolean} escapeEntities\n */\nfunction parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {\n const options = this.options;\n if (val !== undefined) {\n if (options.trimValues && !dontTrim) {\n val = val.trim();\n }\n if (val.length > 0) {\n if (!escapeEntities) val = this.replaceEntitiesValue(val, tagName, jPath);\n\n // Pass jPath string or matcher based on options.jPath setting\n const jPathOrMatcher = options.jPath ? jPath.toString() : jPath;\n const newval = options.tagValueProcessor(tagName, val, jPathOrMatcher, hasAttributes, isLeafNode);\n if (newval === null || newval === undefined) {\n //don't parse\n return val;\n } else if (typeof newval !== typeof val || newval !== val) {\n //overwrite\n return newval;\n } else if (options.trimValues) {\n return parseValue(val, options.parseTagValue, options.numberParseOptions);\n } else {\n const trimmedVal = val.trim();\n if (trimmedVal === val) {\n return parseValue(val, options.parseTagValue, options.numberParseOptions);\n } else {\n return val;\n }\n }\n }\n }\n}\n\nfunction resolveNameSpace(tagname) {\n if (this.options.removeNSPrefix) {\n const tags = tagname.split(':');\n const prefix = tagname.charAt(0) === '/' ? '/' : '';\n if (tags[0] === 'xmlns') {\n return '';\n }\n if (tags.length === 2) {\n tagname = prefix + tags[1];\n }\n }\n return tagname;\n}\n\n//TODO: change regex to capture NS\n//const attrsRegx = new RegExp(\"([\\\\w\\\\-\\\\.\\\\:]+)\\\\s*=\\\\s*(['\\\"])((.|\\n)*?)\\\\2\",\"gm\");\nconst attrsRegx = new RegExp('([^\\\\s=]+)\\\\s*(=\\\\s*([\\'\"])([\\\\s\\\\S]*?)\\\\3)?', 'gm');\n\nfunction buildAttributesMap(attrStr, jPath, tagName, force = false) {\n const options = this.options;\n if (force === true || (options.ignoreAttributes !== true && typeof attrStr === 'string')) {\n // attrStr = attrStr.replace(/\\r?\\n/g, ' ');\n //attrStr = attrStr || attrStr.trim();\n\n const matches = getAllMatches(attrStr, attrsRegx);\n const len = matches.length; //don't make it inline\n const attrs = {};\n\n // Pre-process values once: trim + entity replacement\n // Reused in both matcher update and second pass\n const processedVals = new Array(len);\n let hasRawAttrs = false;\n const rawAttrsForMatcher = {};\n\n for (let i = 0; i < len; i++) {\n const attrName = this.resolveNameSpace(matches[i][1]);\n const oldVal = matches[i][4];\n\n if (attrName.length && oldVal !== undefined) {\n let val = oldVal;\n if (options.trimValues) val = val.trim();\n val = this.replaceEntitiesValue(val, tagName, this.readonlyMatcher);\n processedVals[i] = val;\n\n rawAttrsForMatcher[attrName] = val;\n hasRawAttrs = true;\n }\n }\n\n // Update matcher ONCE before second pass, if applicable\n if (hasRawAttrs && typeof jPath === 'object' && jPath.updateCurrent) {\n jPath.updateCurrent(rawAttrsForMatcher);\n }\n\n // Hoist toString() once — path doesn't change during attribute processing\n const jPathStr = options.jPath ? jPath.toString() : this.readonlyMatcher;\n\n // Second pass: apply processors, build final attrs\n let hasAttrs = false;\n for (let i = 0; i < len; i++) {\n const attrName = this.resolveNameSpace(matches[i][1]);\n\n if (this.ignoreAttributesFn(attrName, jPathStr)) continue;\n\n let aName = options.attributeNamePrefix + attrName;\n\n if (attrName.length) {\n if (options.transformAttributeName) {\n aName = options.transformAttributeName(aName);\n }\n aName = sanitizeName(aName, options);\n\n if (matches[i][4] !== undefined) {\n // Reuse already-processed value — no double entity replacement\n const oldVal = processedVals[i];\n\n const newVal = options.attributeValueProcessor(attrName, oldVal, jPathStr);\n if (newVal === null || newVal === undefined) {\n attrs[aName] = oldVal;\n } else if (typeof newVal !== typeof oldVal || newVal !== oldVal) {\n attrs[aName] = newVal;\n } else {\n attrs[aName] = parseValue(oldVal, options.parseAttributeValue, options.numberParseOptions);\n }\n hasAttrs = true;\n } else if (options.allowBooleanAttributes) {\n attrs[aName] = true;\n hasAttrs = true;\n }\n }\n }\n\n if (!hasAttrs) return;\n\n if (options.attributesGroupName && !options.preserveOrder) {\n const attrCollection = {};\n attrCollection[options.attributesGroupName] = attrs;\n return attrCollection;\n }\n return attrs;\n }\n}\nconst parseXml = function (xmlData) {\n xmlData = xmlData.replace(/\\r\\n?/g, \"\\n\"); //TODO: remove this line\n const xmlObj = new xmlNode('!xml');\n let currentNode = xmlObj;\n let textData = \"\";\n\n // Reset matcher for new document\n this.matcher.reset();\n this.entityDecoder.reset();\n\n // Reset entity expansion counters for this document\n this.entityExpansionCount = 0;\n this.currentExpandedLength = 0;\n this.doctypefound = false;\n const options = this.options;\n const docTypeReader = new DocTypeReader(options.processEntities);\n const xmlLen = xmlData.length;\n for (let i = 0; i < xmlLen; i++) {//for each char in XML data\n const ch = xmlData[i];\n if (ch === '<') {\n // const nextIndex = i+1;\n // const _2ndChar = xmlData[nextIndex];\n const c1 = xmlData.charCodeAt(i + 1);\n if (c1 === 47) {//Closing Tag '/'\n const closeIndex = findClosingIndex(xmlData, \">\", i, \"Closing Tag is not closed.\")\n let tagName = xmlData.substring(i + 2, closeIndex).trim();\n\n if (options.removeNSPrefix) {\n const colonIndex = tagName.indexOf(\":\");\n if (colonIndex !== -1) {\n tagName = tagName.substr(colonIndex + 1);\n }\n }\n\n tagName = transformTagName(options.transformTagName, tagName, \"\", options).tagName;\n\n if (currentNode) {\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);\n }\n\n //check if last tag of nested tag was unpaired tag\n const lastTagName = this.matcher.getCurrentTag();\n if (tagName && options.unpairedTagsSet.has(tagName)) {\n throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);\n }\n if (lastTagName && options.unpairedTagsSet.has(lastTagName)) {\n // Pop the unpaired tag\n this.matcher.pop();\n this.tagsNodeStack.pop();\n }\n // Pop the closing tag\n this.matcher.pop();\n this.isCurrentNodeStopNode = false; // Reset flag when closing tag\n\n currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope\n textData = \"\";\n i = closeIndex;\n } else if (c1 === 63) { //'?'\n\n let tagData = readTagExp(xmlData, i, false, \"?>\");\n if (!tagData) throw new Error(\"Pi Tag is not closed.\");\n\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);\n const attsMap = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName, true);\n if (attsMap) {\n const ver = attsMap[this.options.attributeNamePrefix + \"version\"];\n this.entityDecoder.setXmlVersion(Number(ver) || 1.0);\n docTypeReader.setXmlVersion(Number(ver) || 1.0);\n }\n if ((options.ignoreDeclaration && tagData.tagName === \"?xml\") || options.ignorePiTags) {\n //do nothing\n } else {\n\n const childNode = new xmlNode(tagData.tagName);\n childNode.add(options.textNodeName, \"\");\n\n if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent && options.ignoreAttributes !== true) {\n childNode[\":@\"] = attsMap\n }\n this.addChild(currentNode, childNode, this.readonlyMatcher, i);\n }\n\n\n i = tagData.closeIndex + 1;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 45\n && xmlData.charCodeAt(i + 3) === 45) { //'!--'\n const endIndex = findClosingIndex(xmlData, \"-->\", i + 4, \"Comment is not closed.\")\n if (options.commentPropName) {\n const comment = xmlData.substring(i + 4, endIndex - 2);\n\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);\n\n currentNode.add(options.commentPropName, [{ [options.textNodeName]: comment }]);\n }\n i = endIndex;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 68) { //'!D'\n if (this.doctypefound) throw new Error(\"Multiple DOCTYPE declarations found.\");\n this.doctypefound = true;\n const result = docTypeReader.readDocType(xmlData, i);\n this.entityDecoder.addInputEntities(result.entities);\n i = result.i;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 91) { // '!['\n const closeIndex = findClosingIndex(xmlData, \"]]>\", i, \"CDATA is not closed.\") - 2;\n const tagExp = xmlData.substring(i + 9, closeIndex);\n\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);\n\n let val = this.parseTextData(tagExp, currentNode.tagname, this.readonlyMatcher, true, false, true, true);\n if (val == undefined) val = \"\";\n\n //cdata should be set even if it is 0 length string\n if (options.cdataPropName) {\n currentNode.add(options.cdataPropName, [{ [options.textNodeName]: tagExp }]);\n } else {\n currentNode.add(options.textNodeName, val);\n }\n\n i = closeIndex + 2;\n } else {//Opening tag\n let result = readTagExp(xmlData, i, options.removeNSPrefix);\n\n // Safety check: readTagExp can return undefined\n if (!result) {\n // Log context for debugging\n const context = xmlData.substring(Math.max(0, i - 50), Math.min(xmlLen, i + 50));\n throw new Error(`readTagExp returned undefined at position ${i}. Context: \"${context}\"`);\n }\n\n let tagName = result.tagName;\n const rawTagName = result.rawTagName;\n let tagExp = result.tagExp;\n let attrExpPresent = result.attrExpPresent;\n let closeIndex = result.closeIndex;\n\n ({ tagName, tagExp } = transformTagName(options.transformTagName, tagName, tagExp, options));\n\n if (options.strictReservedNames &&\n (tagName === options.commentPropName\n || tagName === options.cdataPropName\n || tagName === options.textNodeName\n || tagName === options.attributesGroupName\n )) {\n throw new Error(`Invalid tag name: ${tagName}`);\n }\n\n //save text as child node\n if (currentNode && textData) {\n if (currentNode.tagname !== '!xml') {\n //when nested tag is found\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher, false);\n }\n }\n\n //check if last tag was unpaired tag\n const lastTag = currentNode;\n if (lastTag && options.unpairedTagsSet.has(lastTag.tagname)) {\n currentNode = this.tagsNodeStack.pop();\n this.matcher.pop();\n }\n\n // Clean up self-closing syntax BEFORE processing attributes\n // This is where tagExp gets the trailing / removed\n let isSelfClosing = false;\n if (tagExp.length > 0 && tagExp.lastIndexOf(\"/\") === tagExp.length - 1) {\n isSelfClosing = true;\n if (tagName[tagName.length - 1] === \"/\") {\n tagName = tagName.substr(0, tagName.length - 1);\n tagExp = tagName;\n } else {\n tagExp = tagExp.substr(0, tagExp.length - 1);\n }\n\n // Re-check attrExpPresent after cleaning\n attrExpPresent = (tagName !== tagExp);\n }\n\n // Now process attributes with CLEAN tagExp (no trailing /)\n let prefixedAttrs = null;\n let rawAttrs = {};\n let namespace = undefined;\n\n // Extract namespace from rawTagName\n namespace = extractNamespace(rawTagName);\n\n // Push tag to matcher FIRST (with empty attrs for now) so callbacks see correct path\n if (tagName !== xmlObj.tagname) {\n this.matcher.push(tagName, {}, namespace);\n }\n\n // Now build attributes - callbacks will see correct matcher state\n if (tagName !== tagExp && attrExpPresent) {\n // Build attributes (returns prefixed attributes for the tree)\n // Note: buildAttributesMap now internally updates the matcher with raw attributes\n prefixedAttrs = this.buildAttributesMap(tagExp, this.matcher, tagName);\n\n if (prefixedAttrs) {\n // Extract raw attributes (without prefix) for our use\n //TODO: seems a performance overhead\n rawAttrs = extractRawAttributes(prefixedAttrs, options);\n }\n }\n\n // Now check if this is a stop node (after attributes are set)\n if (tagName !== xmlObj.tagname) {\n this.isCurrentNodeStopNode = this.isItStopNode();\n }\n\n const startIndex = i;\n if (this.isCurrentNodeStopNode) {\n let tagContent = \"\";\n\n // For self-closing tags, content is empty\n if (isSelfClosing) {\n i = result.closeIndex;\n }\n //unpaired tag\n else if (options.unpairedTagsSet.has(tagName)) {\n i = result.closeIndex;\n }\n //normal tag\n else {\n //read until closing tag is found\n const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);\n if (!result) throw new Error(`Unexpected end of ${rawTagName}`);\n i = result.i;\n tagContent = result.tagContent;\n }\n\n const childNode = new xmlNode(tagName);\n\n if (prefixedAttrs) {\n childNode[\":@\"] = prefixedAttrs;\n }\n\n // For stop nodes, store raw content as-is without any processing\n childNode.add(options.textNodeName, tagContent);\n\n this.matcher.pop(); // Pop the stop node tag\n this.isCurrentNodeStopNode = false; // Reset flag\n\n this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);\n } else {\n //selfClosing tag\n if (isSelfClosing) {\n ({ tagName, tagExp } = transformTagName(options.transformTagName, tagName, tagExp, options));\n\n const childNode = new xmlNode(tagName);\n if (prefixedAttrs) {\n childNode[\":@\"] = prefixedAttrs;\n }\n this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);\n this.matcher.pop(); // Pop self-closing tag\n this.isCurrentNodeStopNode = false; // Reset flag\n }\n else if (options.unpairedTagsSet.has(tagName)) {//unpaired tag\n const childNode = new xmlNode(tagName);\n if (prefixedAttrs) {\n childNode[\":@\"] = prefixedAttrs;\n }\n this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);\n this.matcher.pop(); // Pop unpaired tag\n this.isCurrentNodeStopNode = false; // Reset flag\n i = result.closeIndex;\n // Continue to next iteration without changing currentNode\n continue;\n }\n //opening tag\n else {\n const childNode = new xmlNode(tagName);\n if (this.tagsNodeStack.length > options.maxNestedTags) {\n throw new Error(\"Maximum nested tags exceeded\");\n }\n this.tagsNodeStack.push(currentNode);\n\n if (prefixedAttrs) {\n childNode[\":@\"] = prefixedAttrs;\n }\n this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);\n currentNode = childNode;\n }\n textData = \"\";\n i = closeIndex;\n }\n }\n } else {\n textData += xmlData[i];\n }\n }\n return xmlObj.child;\n}\n\nfunction addChild(currentNode, childNode, matcher, startIndex) {\n // unset startIndex if not requested\n if (!this.options.captureMetaData) startIndex = undefined;\n\n // Pass jPath string or matcher based on options.jPath setting\n const jPathOrMatcher = this.options.jPath ? matcher.toString() : matcher;\n const result = this.options.updateTag(childNode.tagname, jPathOrMatcher, childNode[\":@\"])\n if (result === false) {\n //do nothing\n } else if (typeof result === \"string\") {\n childNode.tagname = result\n currentNode.addChild(childNode, startIndex);\n } else {\n currentNode.addChild(childNode, startIndex);\n }\n}\n\n/**\n * @param {object} val - Entity object with regex and val properties\n * @param {string} tagName - Tag name\n * @param {string|Matcher} jPath - jPath string or Matcher instance based on options.jPath\n */\nfunction replaceEntitiesValue(val, tagName, jPath) {\n const entityConfig = this.options.processEntities;\n\n if (!entityConfig || !entityConfig.enabled) {\n return val;\n }\n\n // Check if tag is allowed to contain entities\n if (entityConfig.allowedTags) {\n const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;\n const allowed = Array.isArray(entityConfig.allowedTags)\n ? entityConfig.allowedTags.includes(tagName)\n : entityConfig.allowedTags(tagName, jPathOrMatcher);\n\n if (!allowed) {\n return val;\n }\n }\n\n // Apply custom tag filter if provided\n if (entityConfig.tagFilter) {\n const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;\n if (!entityConfig.tagFilter(tagName, jPathOrMatcher)) {\n return val; // Skip based on custom filter\n }\n }\n\n return this.entityDecoder.decode(val);\n}\n\n\nfunction saveTextToParentTag(textData, parentNode, matcher, isLeafNode) {\n if (textData) { //store previously collected data as textNode\n if (isLeafNode === undefined) isLeafNode = parentNode.child.length === 0\n\n textData = this.parseTextData(textData,\n parentNode.tagname,\n matcher,\n false,\n parentNode[\":@\"] ? Object.keys(parentNode[\":@\"]).length !== 0 : false,\n isLeafNode);\n\n if (textData !== undefined && textData !== \"\")\n parentNode.add(this.options.textNodeName, textData);\n textData = \"\";\n }\n return textData;\n}\n\n/**\n * @param {Array<Expression>} stopNodeExpressions - Array of compiled Expression objects\n * @param {Matcher} matcher - Current path matcher\n */\nfunction isItStopNode() {\n if (this.stopNodeExpressionsSet.size === 0) return false;\n\n return this.matcher.matchesAny(this.stopNodeExpressionsSet);\n}\n\n/**\n * Returns the tag Expression and where it is ending handling single-double quotes situation\n * @param {string} xmlData \n * @param {number} i starting index\n * @returns \n */\nfunction tagExpWithClosingIndex(xmlData, i, closingChar = \">\") {\n //TODO: ignore boolean attributes in tag expression\n //TODO: if ignore attributes, dont read full attribute expression but the end. But read for xml declaration\n let attrBoundary = 0;\n const len = xmlData.length;\n const closeCode0 = closingChar.charCodeAt(0);\n const closeCode1 = closingChar.length > 1 ? closingChar.charCodeAt(1) : -1;\n\n let result = '';\n let segmentStart = i;\n\n for (let index = i; index < len; index++) {\n const code = xmlData.charCodeAt(index);\n\n if (attrBoundary) {\n if (code === attrBoundary) attrBoundary = 0;\n } else if (code === 34 || code === 39) { // \" or '\n attrBoundary = code;\n } else if (code === closeCode0) {\n if (closeCode1 !== -1) {\n if (xmlData.charCodeAt(index + 1) === closeCode1) {\n result += xmlData.substring(segmentStart, index);\n return { data: result, index };\n }\n } else {\n result += xmlData.substring(segmentStart, index);\n return { data: result, index };\n }\n } else if (code === 9 && !attrBoundary) { // \\t - only replace with space outside attribute values\n // Flush accumulated segment, add space, start new segment\n result += xmlData.substring(segmentStart, index) + ' ';\n segmentStart = index + 1;\n }\n }\n}\n\nfunction findClosingIndex(xmlData, str, i, errMsg) {\n const closingIndex = xmlData.indexOf(str, i);\n if (closingIndex === -1) {\n throw new Error(errMsg)\n } else {\n return closingIndex + str.length - 1;\n }\n}\n\nfunction findClosingChar(xmlData, char, i, errMsg) {\n const closingIndex = xmlData.indexOf(char, i);\n if (closingIndex === -1) throw new Error(errMsg);\n return closingIndex; // no offset needed\n}\n\nfunction readTagExp(xmlData, i, removeNSPrefix, closingChar = \">\") {\n const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar);\n if (!result) return;\n let tagExp = result.data;\n const closeIndex = result.index;\n const separatorIndex = tagExp.search(/\\s/);\n let tagName = tagExp;\n let attrExpPresent = true;\n if (separatorIndex !== -1) {//separate tag name and attributes expression\n tagName = tagExp.substring(0, separatorIndex);\n tagExp = tagExp.substring(separatorIndex + 1).trimStart();\n }\n\n const rawTagName = tagName;\n if (removeNSPrefix) {\n const colonIndex = tagName.indexOf(\":\");\n if (colonIndex !== -1) {\n tagName = tagName.substr(colonIndex + 1);\n attrExpPresent = tagName !== result.data.substr(colonIndex + 1);\n }\n }\n\n return {\n tagName: tagName,\n tagExp: tagExp,\n closeIndex: closeIndex,\n attrExpPresent: attrExpPresent,\n rawTagName: rawTagName,\n }\n}\n/**\n * find paired tag for a stop node\n * @param {string} xmlData \n * @param {string} tagName \n * @param {number} i \n */\nfunction readStopNodeData(xmlData, tagName, i) {\n const startIndex = i;\n // Starting at 1 since we already have an open tag\n let openTagCount = 1;\n\n const xmllen = xmlData.length;\n for (; i < xmllen; i++) {\n if (xmlData[i] === \"<\") {\n const c1 = xmlData.charCodeAt(i + 1);\n if (c1 === 47) {//close tag '/'\n const closeIndex = findClosingChar(xmlData, \">\", i, `${tagName} is not closed`);\n let closeTagName = xmlData.substring(i + 2, closeIndex).trim();\n if (closeTagName === tagName) {\n openTagCount--;\n if (openTagCount === 0) {\n return {\n tagContent: xmlData.substring(startIndex, i),\n i: closeIndex\n }\n }\n }\n i = closeIndex;\n } else if (c1 === 63) { //?\n const closeIndex = findClosingIndex(xmlData, \"?>\", i + 1, \"StopNode is not closed.\")\n i = closeIndex;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 45\n && xmlData.charCodeAt(i + 3) === 45) { // '!--'\n const closeIndex = findClosingIndex(xmlData, \"-->\", i + 3, \"StopNode is not closed.\")\n i = closeIndex;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 91) { // '!['\n const closeIndex = findClosingIndex(xmlData, \"]]>\", i, \"StopNode is not closed.\") - 2;\n i = closeIndex;\n } else {\n const tagData = readTagExp(xmlData, i, false)\n\n if (tagData) {\n const openTagName = tagData && tagData.tagName;\n if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length - 1] !== \"/\") {\n openTagCount++;\n }\n i = tagData.closeIndex;\n }\n }\n }\n }//end for loop\n}\n\nfunction parseValue(val, shouldParse, options) {\n if (shouldParse && typeof val === 'string') {\n //console.log(options)\n const newval = val.trim();\n if (newval === 'true') return true;\n else if (newval === 'false') return false;\n else return toNumber(val, options);\n } else {\n if (isExist(val)) {\n return val;\n } else {\n return '';\n }\n }\n}\n\nfunction fromCodePoint(str, base, prefix) {\n const codePoint = Number.parseInt(str, base);\n\n if (codePoint >= 0 && codePoint <= 0x10FFFF) {\n return String.fromCodePoint(codePoint);\n } else {\n return prefix + str + \";\";\n }\n}\n\nfunction transformTagName(fn, tagName, tagExp, options) {\n if (fn) {\n const newTagName = fn(tagName);\n if (tagExp === tagName) {\n tagExp = newTagName\n }\n tagName = newTagName;\n }\n tagName = sanitizeName(tagName, options);\n return { tagName, tagExp };\n}\n\n\n\nfunction sanitizeName(name, options) {\n if (criticalProperties.includes(name)) {\n throw new Error(`[SECURITY] Invalid name: \"${name}\" is a reserved JavaScript keyword that could cause prototype pollution`);\n } else if (DANGEROUS_PROPERTY_NAMES.includes(name)) {\n return options.onDangerousProperty(name);\n }\n return name;\n}","'use strict';\n\nimport XmlNode from './xmlNode.js';\nimport { Matcher } from 'path-expression-matcher';\n\nconst METADATA_SYMBOL = XmlNode.getMetaDataSymbol();\n\n/**\n * Helper function to strip attribute prefix from attribute map\n * @param {object} attrs - Attributes with prefix (e.g., {\"@_class\": \"code\"})\n * @param {string} prefix - Attribute prefix to remove (e.g., \"@_\")\n * @returns {object} Attributes without prefix (e.g., {\"class\": \"code\"})\n */\nfunction stripAttributePrefix(attrs, prefix) {\n if (!attrs || typeof attrs !== 'object') return {};\n if (!prefix) return attrs;\n\n const rawAttrs = {};\n for (const key in attrs) {\n if (key.startsWith(prefix)) {\n const rawName = key.substring(prefix.length);\n rawAttrs[rawName] = attrs[key];\n } else {\n // Attribute without prefix (shouldn't normally happen, but be safe)\n rawAttrs[key] = attrs[key];\n }\n }\n return rawAttrs;\n}\n\n/**\n * \n * @param {array} node \n * @param {any} options \n * @param {Matcher} matcher - Path matcher instance\n * @returns \n */\nexport default function prettify(node, options, matcher, readonlyMatcher) {\n return compress(node, options, matcher, readonlyMatcher);\n}\n\n/**\n * @param {array} arr \n * @param {object} options \n * @param {Matcher} matcher - Path matcher instance\n * @returns object\n */\nfunction compress(arr, options, matcher, readonlyMatcher) {\n let text;\n const compressedObj = {}; //This is intended to be a plain object\n for (let i = 0; i < arr.length; i++) {\n const tagObj = arr[i];\n const property = propName(tagObj);\n\n // Push current property to matcher WITH RAW ATTRIBUTES (no prefix)\n if (property !== undefined && property !== options.textNodeName) {\n const rawAttrs = stripAttributePrefix(\n tagObj[\":@\"] || {},\n options.attributeNamePrefix\n );\n matcher.push(property, rawAttrs);\n }\n\n if (property === options.textNodeName) {\n if (text === undefined) text = tagObj[property];\n else text += \"\" + tagObj[property];\n } else if (property === undefined) {\n continue;\n } else if (tagObj[property]) {\n\n let val = compress(tagObj[property], options, matcher, readonlyMatcher);\n const isLeaf = isLeafTag(val, options);\n\n if (Object.keys(val).length === 0 && options.alwaysCreateTextNode) {\n val[options.textNodeName] = \"\";\n }\n\n if (tagObj[\":@\"]) {\n assignAttributes(val, tagObj[\":@\"], readonlyMatcher, options);\n } else if (Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode) {\n val = val[options.textNodeName];\n } else if (Object.keys(val).length === 0) {\n if (options.alwaysCreateTextNode) val[options.textNodeName] = \"\";\n else val = \"\";\n }\n\n if (tagObj[METADATA_SYMBOL] !== undefined && typeof val === \"object\" && val !== null) {\n val[METADATA_SYMBOL] = tagObj[METADATA_SYMBOL]; // copy over metadata\n }\n\n\n if (compressedObj[property] !== undefined && Object.prototype.hasOwnProperty.call(compressedObj, property)) {\n if (!Array.isArray(compressedObj[property])) {\n compressedObj[property] = [compressedObj[property]];\n }\n compressedObj[property].push(val);\n } else {\n //TODO: if a node is not an array, then check if it should be an array\n //also determine if it is a leaf node\n\n // Pass jPath string or readonlyMatcher based on options.jPath setting\n const jPathOrMatcher = options.jPath ? readonlyMatcher.toString() : readonlyMatcher;\n if (options.isArray(property, jPathOrMatcher, isLeaf)) {\n compressedObj[property] = [val];\n } else {\n compressedObj[property] = val;\n }\n }\n\n // Pop property from matcher after processing\n if (property !== undefined && property !== options.textNodeName) {\n matcher.pop();\n }\n }\n\n }\n // if(text && text.length > 0) compressedObj[options.textNodeName] = text;\n if (typeof text === \"string\") {\n if (text.length > 0) compressedObj[options.textNodeName] = text;\n } else if (text !== undefined) compressedObj[options.textNodeName] = text;\n\n\n return compressedObj;\n}\n\nfunction propName(obj) {\n const keys = Object.keys(obj);\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n if (key !== \":@\") return key;\n }\n}\n\nfunction assignAttributes(obj, attrMap, readonlyMatcher, options) {\n if (attrMap) {\n const keys = Object.keys(attrMap);\n const len = keys.length; //don't make it inline\n for (let i = 0; i < len; i++) {\n const atrrName = keys[i]; // This is the PREFIXED name (e.g., \"@_class\")\n\n // Strip prefix for matcher path (for isArray callback)\n const rawAttrName = atrrName.startsWith(options.attributeNamePrefix)\n ? atrrName.substring(options.attributeNamePrefix.length)\n : atrrName;\n\n // For attributes, we need to create a temporary path\n // Pass jPath string or matcher based on options.jPath setting\n const jPathOrMatcher = options.jPath\n ? readonlyMatcher.toString() + \".\" + rawAttrName\n : readonlyMatcher;\n\n if (options.isArray(atrrName, jPathOrMatcher, true, true)) {\n obj[atrrName] = [attrMap[atrrName]];\n } else {\n obj[atrrName] = attrMap[atrrName];\n }\n }\n }\n}\n\nfunction isLeafTag(obj, options) {\n const { textNodeName } = options;\n const propCount = Object.keys(obj).length;\n\n if (propCount === 0) {\n return true;\n }\n\n if (\n propCount === 1 &&\n (obj[textNodeName] || typeof obj[textNodeName] === \"boolean\" || obj[textNodeName] === 0)\n ) {\n return true;\n }\n\n return false;\n}","import { buildOptions } from './OptionsBuilder.js';\nimport OrderedObjParser from './OrderedObjParser.js';\nimport prettify from './node2json.js';\nimport { validate } from \"../validator.js\";\nimport XmlNode from './xmlNode.js';\n\nexport default class XMLParser {\n\n constructor(options) {\n this.externalEntities = {};\n this.options = buildOptions(options);\n\n }\n /**\n * Parse XML dats to JS object \n * @param {string|Uint8Array} xmlData \n * @param {boolean|Object} validationOption \n */\n parse(xmlData, validationOption) {\n if (typeof xmlData !== \"string\" && xmlData.toString) {\n xmlData = xmlData.toString();\n } else if (typeof xmlData !== \"string\") {\n throw new Error(\"XML data is accepted in String or Bytes[] form.\")\n }\n\n if (validationOption) {\n if (validationOption === true) validationOption = {}; //validate with default options\n\n const result = validate(xmlData, validationOption);\n if (result !== true) {\n throw Error(`${result.err.msg}:${result.err.line}:${result.err.col}`)\n }\n }\n const orderedObjParser = new OrderedObjParser(this.options, this.externalEntities);\n // orderedObjParser.entityDecoder.setExternalEntities(this.externalEntities);\n const orderedResult = orderedObjParser.parseXml(xmlData);\n if (this.options.preserveOrder || orderedResult === undefined) return orderedResult;\n else return prettify(orderedResult, this.options, orderedObjParser.matcher, orderedObjParser.readonlyMatcher);\n }\n\n /**\n * Add Entity which is not by default supported by this library\n * @param {string} key \n * @param {string} value \n */\n addEntity(key, value) {\n if (value.indexOf(\"&\") !== -1) {\n throw new Error(\"Entity value can't have '&'\")\n } else if (key.indexOf(\"&\") !== -1 || key.indexOf(\";\") !== -1) {\n throw new Error(\"An entity must be set without '&' and ';'. Eg. use '#xD' for '
'\")\n } else if (value === \"&\") {\n throw new Error(\"An entity with value '&' is not permitted\");\n } else {\n this.externalEntities[key] = value;\n }\n }\n\n /**\n * Returns a Symbol that can be used to access the metadata\n * property on a node.\n * \n * If Symbol is not available in the environment, an ordinary property is used\n * and the name of the property is here returned.\n * \n * The XMLMetaData property is only present when `captureMetaData`\n * is true in the options.\n */\n static getMetaDataSymbol() {\n return XmlNode.getMetaDataSymbol();\n }\n}","import { XMLParser } from 'fast-xml-parser';\n\nexport type ContentFeedFormat = 'rss' | 'atom';\n\nexport interface ParsedContentFeedItem {\n title: string;\n url: string;\n guid: string | null;\n author: string | null;\n publishedAt: Date | null;\n updatedAt: Date | null;\n summary: string | null;\n categories: string[];\n}\n\nexport interface ParsedContentFeed {\n format: ContentFeedFormat;\n title: string | null;\n homepageUrl: string | null;\n items: ParsedContentFeedItem[];\n}\n\nfunction asArray<T>(value: T | T[] | null | undefined): T[] {\n if (value === null || value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\nfunction textFromNode(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string' || typeof value === 'number') {\n return String(value).trim();\n }\n if (typeof value !== 'object') return '';\n\n const record = value as Record<string, unknown>;\n return String(record['#text'] ?? record.__cdata ?? record.href ?? '').trim();\n}\n\nfunction firstText(...values: unknown[]): string {\n for (const value of values) {\n const text = textFromNode(value);\n if (text) return text;\n }\n return '';\n}\n\nfunction parseDate(value: unknown): Date | null {\n const text = textFromNode(value);\n if (!text) return null;\n const parsed = new Date(text);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\nfunction stripHtml(value: string): string {\n return value\n .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n .replace(/<style\\b[^<]*(?:(?!<\\/style>)<[^<]*)*<\\/style>/gi, '')\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/'/g, \"'\")\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction excerpt(value: unknown): string | null {\n const text = stripHtml(textFromNode(value));\n if (!text) return null;\n return text.length > 500 ? `${text.slice(0, 497).trim()}...` : text;\n}\n\nfunction parseCategories(value: unknown): string[] {\n return Array.from(\n new Set(\n asArray(value)\n .map((entry) => {\n if (entry && typeof entry === 'object') {\n return firstText(entry, (entry as Record<string, unknown>).term);\n }\n return textFromNode(entry);\n })\n .map((entry) => entry.trim())\n .filter(Boolean),\n ),\n );\n}\n\nfunction parseAuthor(value: unknown): string | null {\n if (Array.isArray(value)) return parseAuthor(value[0]);\n if (value && typeof value === 'object') {\n return firstText((value as Record<string, unknown>).name, value) || null;\n }\n return textFromNode(value) || null;\n}\n\nfunction resolveUrl(value: string, baseUrl?: string | null): string {\n if (!value) return '';\n if (!baseUrl) return value;\n\n try {\n return new URL(value, baseUrl).toString();\n } catch {\n return value;\n }\n}\n\nfunction parseRssLink(value: unknown, baseUrl?: string | null): string {\n if (value && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n return resolveUrl(\n firstText(record.href, record['#text'], record.__cdata),\n baseUrl,\n );\n }\n return resolveUrl(textFromNode(value), baseUrl);\n}\n\nfunction parseAtomLink(value: unknown, baseUrl?: string | null): string {\n const links = asArray(value);\n const alternate =\n links.find((entry) => {\n if (!entry || typeof entry !== 'object') return false;\n return (\n String((entry as Record<string, unknown>).rel ?? 'alternate') ===\n 'alternate'\n );\n }) ?? links[0];\n\n if (alternate && typeof alternate === 'object') {\n return resolveUrl(\n firstText((alternate as Record<string, unknown>).href, alternate),\n baseUrl,\n );\n }\n return resolveUrl(textFromNode(alternate), baseUrl);\n}\n\nfunction dedupeItems(items: ParsedContentFeedItem[]): ParsedContentFeedItem[] {\n const seen = new Set<string>();\n return items.filter((item) => {\n const key = item.guid || item.url;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nexport function parseContentFeed(\n xml: string,\n baseUrl?: string | null,\n): ParsedContentFeed {\n const parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '',\n cdataPropName: '__cdata',\n textNodeName: '#text',\n removeNSPrefix: true,\n trimValues: true,\n });\n // Parsed XML is an untyped node tree; every field is read through the\n // unknown-tolerant helpers above, so a loose record shape is sufficient.\n type FeedNode = Record<string, unknown>;\n const doc = parser.parse(xml) as {\n rss?: { channel?: FeedNode };\n channel?: FeedNode;\n feed?: FeedNode;\n };\n\n if (doc.rss?.channel || doc.channel) {\n const channel = (doc.rss?.channel ?? doc.channel) as FeedNode;\n return {\n format: 'rss',\n title: firstText(channel.title) || null,\n homepageUrl: parseRssLink(channel.link, baseUrl) || null,\n items: dedupeItems(\n asArray<FeedNode>(channel.item as FeedNode | FeedNode[] | undefined)\n .map(\n (item): ParsedContentFeedItem => ({\n title: firstText(item.title),\n url:\n parseRssLink(item.link, baseUrl) ||\n resolveUrl(firstText(item.guid), baseUrl),\n guid: firstText(item.guid, item.id) || null,\n author: parseAuthor(item.author ?? item.creator),\n publishedAt: parseDate(\n item.pubDate ?? item.published ?? item.date,\n ),\n updatedAt: parseDate(item.updated),\n summary: excerpt(\n item.description ?? item.summary ?? item.content,\n ),\n categories: parseCategories(item.category),\n }),\n )\n .filter((item) => item.title && item.url),\n ),\n };\n }\n\n if (doc.feed) {\n const feed = doc.feed;\n return {\n format: 'atom',\n title: firstText(feed.title) || null,\n homepageUrl: parseAtomLink(feed.link, baseUrl) || null,\n items: dedupeItems(\n asArray<FeedNode>(feed.entry as FeedNode | FeedNode[] | undefined)\n .map(\n (entry): ParsedContentFeedItem => ({\n title: firstText(entry.title),\n url: parseAtomLink(entry.link, baseUrl),\n guid: firstText(entry.id) || null,\n author: parseAuthor(entry.author),\n publishedAt: parseDate(entry.published ?? entry.updated),\n updatedAt: parseDate(entry.updated),\n summary: excerpt(entry.summary ?? entry.content),\n categories: parseCategories(entry.category),\n }),\n )\n .filter((item) => item.title && item.url),\n ),\n };\n }\n\n throw new Error('Unsupported feed format');\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentFeedFormat } from './content-feed-parser';\n\nexport type ContentFeedSourceStatus =\n | 'active'\n | 'paused'\n | 'error'\n | 'archived';\n\nexport interface ContentFeedSourceOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n name?: string;\n feedUrl?: string;\n homepageUrl?: string | null;\n format?: ContentFeedFormat | null;\n status?: ContentFeedSourceStatus;\n defaultCategory?: string | null;\n sourceGroup?: string | null;\n pollIntervalMinutes?: number;\n etag?: string | null;\n lastModified?: string | null;\n lastFetchedAt?: Date | string | null;\n lastSuccessAt?: Date | string | null;\n lastError?: string | null;\n metadata?: Record<string, unknown> | string;\n createdAt?: Date | string;\n updatedAt?: Date | string;\n}\n\nconst SOURCE_STATUSES = new Set<ContentFeedSourceStatus>([\n 'active',\n 'paused',\n 'error',\n 'archived',\n]);\n\nfunction parseDate(value: Date | string | null | undefined): Date | null {\n if (!value) return null;\n if (value instanceof Date) return value;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\nfunction parseMetadata(\n value: Record<string, unknown> | string | undefined,\n): Record<string, unknown> {\n if (!value) return {};\n if (typeof value === 'object') return structuredClone(value);\n\n try {\n const parsed = JSON.parse(value);\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction normalizePollIntervalMinutes(value: unknown): number {\n const parsed = Number(value);\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 15;\n}\n\nfunction normalizeStatus(value: unknown): ContentFeedSourceStatus {\n return typeof value === 'string' &&\n SOURCE_STATUSES.has(value as ContentFeedSourceStatus)\n ? (value as ContentFeedSourceStatus)\n : 'active';\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_feed_sources',\n conflictColumns: ['tenant_id', 'feed_url'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentFeedSource extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @field({ required: true })\n name = '';\n\n @field({ required: true })\n feedUrl = '';\n\n homepageUrl: string | null = null;\n format: ContentFeedFormat | null = null;\n @field({ type: 'text', required: true, default: 'active' })\n status: ContentFeedSourceStatus = 'active';\n defaultCategory: string | null = null;\n sourceGroup: string | null = null;\n pollIntervalMinutes = 15;\n etag: string | null = null;\n lastModified: string | null = null;\n lastFetchedAt: Date | null = null;\n lastSuccessAt: Date | null = null;\n lastError: string | null = null;\n metadata: Record<string, unknown> = {};\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentFeedSourceOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.name !== undefined) this.name = options.name;\n if (options.feedUrl !== undefined) this.feedUrl = options.feedUrl;\n if (options.homepageUrl !== undefined)\n this.homepageUrl = options.homepageUrl;\n if (options.format !== undefined) this.format = options.format;\n if (options.status !== undefined)\n this.status = normalizeStatus(options.status);\n if (options.defaultCategory !== undefined)\n this.defaultCategory = options.defaultCategory;\n if (options.sourceGroup !== undefined)\n this.sourceGroup = options.sourceGroup;\n if (options.pollIntervalMinutes !== undefined) {\n this.pollIntervalMinutes = normalizePollIntervalMinutes(\n options.pollIntervalMinutes,\n );\n }\n if (options.etag !== undefined) this.etag = options.etag;\n if (options.lastModified !== undefined)\n this.lastModified = options.lastModified;\n if (options.lastFetchedAt !== undefined)\n this.lastFetchedAt = parseDate(options.lastFetchedAt);\n if (options.lastSuccessAt !== undefined)\n this.lastSuccessAt = parseDate(options.lastSuccessAt);\n if (options.lastError !== undefined) this.lastError = options.lastError;\n if (options.metadata !== undefined)\n this.metadata = parseMetadata(options.metadata);\n if (options.createdAt !== undefined)\n this.createdAt = parseDate(options.createdAt) ?? new Date();\n if (options.updatedAt !== undefined)\n this.updatedAt = parseDate(options.updatedAt) ?? new Date();\n }\n\n getMetadata(): Record<string, unknown> {\n return structuredClone(this.metadata);\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = structuredClone(metadata);\n this.updatedAt = new Date();\n }\n\n markFetchStarted(at = new Date()): void {\n this.lastFetchedAt = at;\n this.updatedAt = at;\n }\n\n markFetchSucceeded(\n at = new Date(),\n headers: { etag?: string | null; lastModified?: string | null } = {},\n ): void {\n this.status = 'active';\n this.lastFetchedAt = at;\n this.lastSuccessAt = at;\n this.lastError = null;\n if (headers.etag !== undefined) this.etag = headers.etag;\n if (headers.lastModified !== undefined)\n this.lastModified = headers.lastModified;\n this.updatedAt = at;\n }\n\n markFetchFailed(error: unknown, at = new Date()): void {\n this.status = 'error';\n this.lastFetchedAt = at;\n this.lastError = error instanceof Error ? error.message : String(error);\n this.updatedAt = at;\n }\n\n protected override transformJSON(\n json: Record<string, unknown>,\n ): Record<string, unknown> {\n return {\n ...json,\n tenantId: this.tenantId,\n name: this.name,\n feedUrl: this.feedUrl,\n homepageUrl: this.homepageUrl,\n format: this.format,\n status: this.status,\n defaultCategory: this.defaultCategory,\n sourceGroup: this.sourceGroup,\n pollIntervalMinutes: this.pollIntervalMinutes,\n etag: this.etag,\n lastModified: this.lastModified,\n lastFetchedAt: this.lastFetchedAt?.toISOString() ?? null,\n lastSuccessAt: this.lastSuccessAt?.toISOString() ?? null,\n lastError: this.lastError,\n metadata: this.metadata,\n createdAt: this.createdAt.toISOString(),\n updatedAt: this.updatedAt.toISOString(),\n };\n }\n}\n","import type { SmrtCollectionOptions } from '@happyvertical/smrt-core';\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n ContentFeedSource,\n type ContentFeedSourceStatus,\n} from './content-feed-source';\n\nexport interface ContentFeedSourceCollectionOptions\n extends SmrtCollectionOptions {}\n\nexport class ContentFeedSourceCollection extends SmrtCollection<ContentFeedSource> {\n static readonly _itemClass = ContentFeedSource;\n\n async findActive(tenantId?: string | null): Promise<ContentFeedSource[]> {\n const where: Record<string, unknown> = { status: 'active' };\n if (tenantId !== undefined) where.tenantId = tenantId;\n return this.list({ where, orderBy: 'name ASC' });\n }\n\n async findByStatus(\n status: ContentFeedSourceStatus,\n tenantId?: string | null,\n ): Promise<ContentFeedSource[]> {\n const where: Record<string, unknown> = { status };\n if (tenantId !== undefined) where.tenantId = tenantId;\n return this.list({ where, orderBy: 'name ASC' });\n }\n\n async findByFeedUrl(\n feedUrl: string,\n tenantId?: string | null,\n ): Promise<ContentFeedSource | null> {\n const where: Record<string, unknown> = { feedUrl };\n if (tenantId !== undefined) where.tenantId = tenantId;\n const rows = await this.list({ where, limit: 1 });\n return rows[0] ?? null;\n }\n}\n","/**\n * Content subclasses for STI (Single Table Inheritance)\n *\n * These classes inherit from Content and automatically use the shared\n * contents table with _meta_type discriminator.\n */\n\nimport { foreignKey, smrt } from '@happyvertical/smrt-core';\nimport { Content, type ContentOptions } from './content';\n\n/**\n * Article content type\n *\n * Represents editorial content like blog posts, news articles, and written pieces.\n */\n@smrt({\n tableStrategy: 'sti',\n api: false,\n mcp: false,\n cli: false,\n})\nexport class Article extends Content {\n constructor(options: ContentOptions = {}) {\n super(options);\n // Article-specific initialization can go here\n }\n}\n\n/**\n * ContentDocument content type\n *\n * Represents structured documents like PDFs, reports, and technical documentation.\n */\n@smrt({\n tableStrategy: 'sti',\n api: false,\n mcp: false,\n cli: false,\n})\nexport class ContentDocument extends Content {\n constructor(options: ContentOptions = {}) {\n super(options);\n // ContentDocument-specific initialization can go here\n }\n}\n\nexport interface MirrorOptions extends ContentOptions {\n feedSourceId?: string | null;\n sourceGuid?: string | null;\n sourceName?: string | null;\n sourceHomepageUrl?: string | null;\n externalUrl?: string | null;\n originalPublishedAt?: Date | string | null;\n dedupeKey?: string | null;\n}\n\nfunction parseOptionalDate(\n value: Date | string | null | undefined,\n): Date | null {\n if (!value) return null;\n if (value instanceof Date) return value;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\n/**\n * Mirror content type\n *\n * Represents mirrored/cached content from external feed or web sources.\n */\n@smrt({\n tableStrategy: 'sti',\n api: false,\n mcp: false,\n cli: false,\n})\nexport class Mirror extends Content {\n @foreignKey('ContentFeedSource')\n feedSourceId: string | null = null;\n sourceGuid: string | null = null;\n sourceName: string | null = null;\n sourceHomepageUrl: string | null = null;\n externalUrl: string | null = null;\n originalPublishedAt: Date | null = null;\n dedupeKey: string | null = null;\n\n constructor(options: MirrorOptions = {}) {\n super(options);\n if (options.feedSourceId !== undefined)\n this.feedSourceId = options.feedSourceId;\n if (options.sourceGuid !== undefined) this.sourceGuid = options.sourceGuid;\n if (options.sourceName !== undefined) this.sourceName = options.sourceName;\n if (options.sourceHomepageUrl !== undefined)\n this.sourceHomepageUrl = options.sourceHomepageUrl;\n if (options.externalUrl !== undefined)\n this.externalUrl = options.externalUrl;\n if (options.originalPublishedAt !== undefined) {\n this.originalPublishedAt = parseOptionalDate(options.originalPublishedAt);\n }\n if (options.dedupeKey !== undefined) this.dedupeKey = options.dedupeKey;\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n feedSourceId: this.feedSourceId,\n sourceGuid: this.sourceGuid,\n sourceName: this.sourceName,\n sourceHomepageUrl: this.sourceHomepageUrl,\n externalUrl: this.externalUrl,\n originalPublishedAt: this.originalPublishedAt?.toISOString() ?? null,\n dedupeKey: this.dedupeKey,\n };\n }\n}\n","/**\n * Shared SSRF guard for content's outbound fetches (feed sync, mirroring).\n *\n * Any code path that fetches a caller-supplied URL — RSS/Atom feeds, the\n * `Mirror` content type, link previews — is an SSRF vector: an attacker who can\n * influence the URL can make the server fetch `169.254.169.254` cloud metadata,\n * `localhost` admin panels, or other internal services. This module rejects\n * URLs that resolve to private, loopback, link-local, CGNAT, or cloud-metadata\n * ranges before any request is made, and re-validates redirect hops.\n *\n * Unlike a literal-IP-only check, {@link assertSafeRemoteUrl} resolves hostnames\n * via DNS so a public name pointing at a private IP is also caught (S5 #1388).\n */\nimport { lookup as dnsLookup } from 'node:dns/promises';\nimport { isIP } from 'node:net';\n\nexport type ResolvedAddress = { address: string; family?: number };\nexport type ResolveHostname = (hostname: string) => Promise<ResolvedAddress[]>;\n\nexport interface SafeRemoteUrlOptions {\n /** Skip the private-network checks (trusted callers only, e.g. local dev). */\n allowPrivateNetworkHosts?: boolean;\n /** Injectable resolver for tests; defaults to {@link defaultResolveHostname}. */\n resolveHostname?: ResolveHostname;\n}\n\nexport async function defaultResolveHostname(\n hostname: string,\n): Promise<ResolvedAddress[]> {\n return dnsLookup(hostname, { all: true, verbatim: false });\n}\n\nexport function isBlockedIPv4(address: string): boolean {\n const parts = address.split('.');\n if (parts.length !== 4) return true;\n // Strict per-octet validation: `Number('')` is 0, so without a digit check a\n // malformed input like `1..2.3` would parse to `[1,0,2,3]` and be treated as a\n // valid (and possibly allowed) address. Anything not 1-3 digits in 0-255 is\n // unparseable → blocked (review #1562).\n const octets = parts.map((part) =>\n /^\\d{1,3}$/.test(part) ? Number(part) : Number.NaN,\n );\n if (octets.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) {\n return true;\n }\n\n const [first, second] = octets;\n return (\n first === 0 ||\n first === 10 ||\n first === 127 ||\n first >= 224 ||\n (first === 100 && second >= 64 && second <= 127) ||\n (first === 169 && second === 254) ||\n (first === 172 && second >= 16 && second <= 31) ||\n (first === 192 && second === 168) ||\n (first === 198 && (second === 18 || second === 19))\n );\n}\n\n/**\n * Expand an IPv6 string to its 8 hextets (numbers), or null if unparseable.\n * Handles `::` compression, an embedded dotted-IPv4 tail, and bracketed forms.\n */\nfunction expandIPv6(address: string): number[] | null {\n let work = address.toLowerCase().replace(/^\\[|\\]$/g, '');\n if (work.includes('.')) {\n // Embedded dotted IPv4 tail (e.g. ::ffff:127.0.0.1) → convert to 2 hextets.\n const m = work.match(/(\\d{1,3}(?:\\.\\d{1,3}){3})$/);\n if (!m) return null;\n const o = m[1].split('.').map(Number);\n if (o.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;\n const hi = ((o[0] << 8) | o[1]).toString(16);\n const lo = ((o[2] << 8) | o[3]).toString(16);\n work = `${work.slice(0, work.length - m[1].length)}${hi}:${lo}`;\n }\n const halves = work.split('::');\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(':') : [];\n const tail = halves.length === 2 && halves[1] ? halves[1].split(':') : [];\n const groups =\n halves.length === 2\n ? [\n ...head,\n ...Array(Math.max(0, 8 - head.length - tail.length)).fill('0'),\n ...tail,\n ]\n : head;\n if (groups.length !== 8) return null;\n const hextets = groups.map((g) =>\n /^[0-9a-f]{1,4}$/.test(g) ? Number.parseInt(g, 16) : Number.NaN,\n );\n return hextets.some((n) => Number.isNaN(n)) ? null : hextets;\n}\n\nexport function isBlockedIPv6(address: string): boolean {\n const hextets = expandIPv6(address);\n if (hextets) {\n // IPv4-mapped (::ffff:a.b.c.d) and IPv4-compatible (::a.b.c.d, deprecated)\n // both embed an IPv4 in the last 2 hextets — decode and apply the IPv4\n // blocklist so a loopback/private IPv4 can't be smuggled through any IPv6\n // encoding (compressed, expanded, dotted, or hex) (review #1562, P1).\n const firstFiveZero = hextets.slice(0, 5).every((h) => h === 0);\n if (firstFiveZero && (hextets[5] === 0xffff || hextets[5] === 0)) {\n const [, , , , , , g6, g7] = hextets;\n const ipv4 = `${g6 >> 8}.${g6 & 0xff}.${g7 >> 8}.${g7 & 0xff}`;\n return isBlockedIPv4(ipv4);\n }\n }\n\n const normalized = address.toLowerCase().replace(/^\\[|\\]$/g, '');\n return (\n normalized === '::' ||\n normalized === '::1' ||\n normalized.startsWith('fc') ||\n normalized.startsWith('fd') ||\n /^fe[89ab]/.test(normalized) ||\n normalized.startsWith('ff')\n );\n}\n\nexport function isBlockedAddress(address: string): boolean {\n const family = isIP(address);\n if (family === 4) return isBlockedIPv4(address);\n if (family === 6) return isBlockedIPv6(address);\n // Anything that isn't a recognisable IP literal (after DNS resolution should\n // have produced one) is treated as unsafe.\n return true;\n}\n\n/**\n * Parse and validate a remote URL for outbound fetching. Rejects non-http(s)\n * schemes, embedded credentials, and hosts that resolve to non-public ranges.\n * Returns the parsed {@link URL} on success; throws a descriptive `Error`\n * otherwise.\n */\nexport async function assertSafeRemoteUrl(\n rawUrl: string,\n options: SafeRemoteUrlOptions = {},\n): Promise<URL> {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new Error('Remote URL must be an absolute URL');\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new Error('Remote URL must use http or https');\n }\n if (url.username || url.password) {\n throw new Error('Remote URL must not include credentials');\n }\n if (!url.hostname) {\n throw new Error('Remote URL must include a hostname');\n }\n\n if (options.allowPrivateNetworkHosts) return url;\n\n const resolver = options.resolveHostname ?? defaultResolveHostname;\n const addresses =\n isIP(url.hostname) === 0\n ? await resolver(url.hostname)\n : [{ address: url.hostname }];\n\n if (\n !addresses.length ||\n addresses.some(({ address }) => isBlockedAddress(address))\n ) {\n throw new Error('Remote URL must resolve to a public network address');\n }\n\n return url;\n}\n\n/** Redirect status codes that reroute a request to a new Location. */\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\nconst DEFAULT_MAX_REDIRECTS = 5;\nconst DEFAULT_RESOLVE_TIMEOUT_MS = 10_000;\n\nexport interface SafeRedirectOptions extends SafeRemoteUrlOptions {\n /** Maximum redirect hops to follow before failing. Default 5. */\n maxRedirects?: number;\n /** Per-hop timeout in ms. Default 10s. */\n timeoutMs?: number;\n /** Injectable fetch (primarily for tests). Defaults to global `fetch`. */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Resolve a URL's redirect chain and return the final safe {@link URL}, with\n * EVERY hop re-validated through {@link assertSafeRemoteUrl}.\n *\n * Use this before handing a URL to a fetcher that follows redirects on its own\n * (e.g. `fetchDocument`): the up-front {@link assertSafeRemoteUrl} check alone\n * can't stop an allowed public host from `30x`-redirecting into an internal /\n * loopback / metadata host, which would defeat the SSRF guard (review #1562).\n *\n * Redirects are followed with `GET` + `redirect: 'manual'` to match downstream\n * GET-based fetchers; response bodies are discarded (the redirect bodies are\n * empty and the terminal body is left for the caller to re-fetch), so this does\n * not double-download content.\n */\nexport async function resolveSafeFinalUrl(\n rawUrl: string,\n options: SafeRedirectOptions = {},\n): Promise<URL> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const maxRedirects = options.maxRedirects ?? DEFAULT_MAX_REDIRECTS;\n const timeoutMs = options.timeoutMs ?? DEFAULT_RESOLVE_TIMEOUT_MS;\n let current = await assertSafeRemoteUrl(rawUrl, options);\n\n for (let hop = 0; hop <= maxRedirects; hop += 1) {\n const response = await fetchImpl(current, {\n method: 'GET',\n redirect: 'manual',\n signal: AbortSignal.timeout(timeoutMs),\n });\n // Release the connection without downloading the body.\n try {\n await response.body?.cancel();\n } catch {\n // best-effort\n }\n\n if (!REDIRECT_STATUSES.has(response.status)) {\n return current;\n }\n const location = response.headers.get('location');\n if (!location) return current;\n // Re-validate the resolved redirect target before following it.\n current = await assertSafeRemoteUrl(\n new URL(location, current).toString(),\n options,\n );\n }\n\n throw new Error('Remote URL exceeded the maximum number of redirects');\n}\n\n/**\n * Strip userinfo (`user:pass@`) from a URL so it can be safely logged or echoed\n * in an error message. Returns a placeholder for unparseable input. Never let a\n * credential-bearing URL reach logs/errors verbatim (review #1562).\n */\nexport function redactUrlCredentials(raw: string): string {\n try {\n const url = new URL(raw);\n if (url.username || url.password) {\n url.username = '';\n url.password = '';\n return url.toString();\n }\n return raw;\n } catch {\n return '[unparseable url]';\n }\n}\n","import { writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { AIClientOptions } from '@happyvertical/ai';\nimport { fetchDocument } from '@happyvertical/documents';\nimport { ensureDirectoryExists } from '@happyvertical/files';\nimport { createLogger } from '@happyvertical/logger';\nimport type { SmrtCollectionOptions } from '@happyvertical/smrt-core';\nimport { SmrtCollection, smrt } from '@happyvertical/smrt-core';\nimport type { Image } from '@happyvertical/smrt-images';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { makeSlug } from '@happyvertical/utils';\nimport YAML from 'yaml';\nimport { htmlToMarkdown, resolveBodyFormat } from './body-format';\nimport { Content } from './content';\nimport {\n getEffectiveContentGovernanceConfig,\n loadPersistedContentGovernanceDefinitions,\n resolveEffectiveContentGovernance,\n} from './content-governance';\nimport {\n type ResolveHostname,\n redactUrlCredentials,\n resolveSafeFinalUrl,\n} from './safe-remote-url';\nimport { serializeContent, serializeFact } from './serialization';\nimport type {\n ThumbnailOptions,\n ThumbnailStrategy,\n} from './thumbnail-generator';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Options accepted by {@link Contents.generateMissingThumbnails}. Also reused\n * (as a `Partial`) for the `thumbnail` config block passed through the\n * collection constructor from `smrt.config.js`.\n */\nexport interface GenerateMissingThumbnailsOptions {\n /**\n * Thumbnail generation strategy\n */\n strategy: ThumbnailStrategy;\n\n /**\n * Optional filter for content to process\n */\n where?: Record<string, unknown>;\n\n /**\n * Maximum number of thumbnails to generate\n */\n limit?: number;\n\n // Headline card options\n brandColor?: string;\n backgroundColor?: string;\n logoUrl?: string;\n template?: 'default' | 'news' | 'minimal';\n\n // Static map options\n mapProvider?: 'mapbox' | 'google';\n zoom?: number;\n\n // AI options\n style?: 'photorealistic' | 'illustration' | 'abstract' | 'minimal';\n\n // Common options\n width?: number;\n height?: number;\n}\n\n/**\n * Configuration options for Contents collection\n */\nexport interface ContentsOptions extends SmrtCollectionOptions {\n /**\n * Directory to store content files\n */\n contentDir?: string;\n\n /**\n * Default thumbnail-generation settings sourced from `smrt.config.js`. Merged\n * under per-call options in `generateMissingThumbnails`.\n */\n thumbnail?: Partial<GenerateMissingThumbnailsOptions>;\n}\n\nfunction isAIClientOptions(\n ai: SmrtCollectionOptions['ai'],\n): ai is AIClientOptions {\n return (\n !!ai &&\n typeof ai === 'object' &&\n !('embed' in ai) &&\n !('generateImage' in ai)\n );\n}\n\n/**\n * Collection for managing Content objects\n *\n * The Contents collection provides functionality for managing and manipulating\n * collections of Content objects, including saving to the filesystem and\n * mirroring content from remote URLs.\n */\n@smrt({\n api: {\n include: [\n 'browseFacts',\n 'getBySlug',\n 'getGovernanceDefinitionsAction',\n 'resolveGovernanceAction',\n ],\n routes: {\n browseFacts: {\n scope: 'collection',\n method: 'GET',\n path: 'facts',\n },\n getBySlug: {\n scope: 'collection',\n method: 'GET',\n path: 'by-slug',\n },\n getGovernanceDefinitionsAction: {\n scope: 'collection',\n method: 'GET',\n path: 'governance',\n },\n resolveGovernanceAction: {\n scope: 'collection',\n method: 'GET',\n path: 'governance/resolve',\n },\n },\n },\n mcp: false,\n cli: false,\n})\nexport class Contents extends SmrtCollection<Content> {\n /**\n * Class constructor for collection items\n */\n static _itemClass = Content;\n\n /**\n * Configuration options\n */\n public options: ContentsOptions = {} as ContentsOptions;\n\n /**\n * Directory to store content files\n */\n public contentDir?: string;\n\n /**\n * Cache for loaded content\n */\n public loaded: Map<string, Content>;\n\n /**\n * Creates a new Contents collection\n *\n * Use the static `create()` method inherited from SmrtCollection for proper initialization.\n *\n * @param options - Configuration options\n */\n constructor(options: ContentsOptions = {}) {\n super(options);\n this.options = options;\n this.loaded = new Map();\n }\n\n /**\n * Gets the database interface\n *\n * @returns Database interface\n */\n getDb() {\n return this._db;\n }\n\n /**\n * Initializes the collection\n *\n * @returns Promise that resolves to this instance\n */\n public async initialize(): Promise<this> {\n await super.initialize();\n return this;\n }\n\n private async getFactCollection() {\n const { FactCollection } = await import('@happyvertical/smrt-facts');\n return FactCollection.create(this.options);\n }\n\n public async browseFacts(\n options: {\n q?: string;\n query?: string;\n limit?: number | string;\n offset?: number | string;\n minSimilarity?: number | string;\n includeSuperseded?: boolean | string;\n latestOnly?: boolean | string;\n tenantId?: string | null;\n } = {},\n ) {\n try {\n const facts = await this.getFactCollection();\n const query = options.query || options.q || '';\n const limit =\n options.limit !== undefined ? Number(options.limit) : undefined;\n const offset =\n options.offset !== undefined ? Number(options.offset) : undefined;\n const minSimilarity =\n options.minSimilarity !== undefined\n ? Number(options.minSimilarity)\n : undefined;\n const includeSuperseded =\n options.includeSuperseded === true ||\n options.includeSuperseded === 'true';\n const latestOnly =\n options.latestOnly === undefined\n ? true\n : options.latestOnly === true || options.latestOnly === 'true';\n\n const results = await facts.browseCatalog(query, {\n limit: Number.isFinite(limit) ? limit : undefined,\n offset: Number.isFinite(offset) ? offset : undefined,\n minSimilarity: Number.isFinite(minSimilarity)\n ? minSimilarity\n : undefined,\n includeSuperseded,\n latestOnly,\n tenantId: options.tenantId ?? null,\n });\n\n return results.map(serializeFact);\n } catch (error) {\n // Gracefully handle missing facts table (cross-package dependency)\n if (\n typeof error === 'object' &&\n error !== null &&\n (error as { code?: unknown }).code === 'DB_SCHEMA_MISSING'\n ) {\n return [];\n }\n throw error;\n }\n }\n\n public async getBySlug(\n options: {\n slug?: string;\n context?: string;\n status?: string;\n tenantId?: string | null;\n } = {},\n ) {\n if (!options.slug) {\n throw new Error('slug is required');\n }\n\n const where: {\n slug: string;\n context: string;\n tenantId?: string | null;\n } = {\n slug: options.slug,\n context: options.context || '',\n };\n if (options.tenantId !== undefined) {\n where.tenantId = options.tenantId;\n }\n\n const content = await this.get(where);\n\n if (!content) {\n return null;\n }\n\n if (options.status && content.status !== options.status) {\n return null;\n }\n\n return serializeContent(content);\n }\n\n public async getGovernanceDefinitionsAction(\n options: { tenantId?: string | null } = {},\n ) {\n const [effective, persisted] = await Promise.all([\n getEffectiveContentGovernanceConfig({\n db: this.db,\n tenantId: options.tenantId,\n }),\n loadPersistedContentGovernanceDefinitions({\n db: this.db,\n tenantId: options.tenantId,\n }),\n ]);\n\n return {\n effective: {\n policies: effective.policies.map((policy) => ({\n ...policy,\n ...(persisted.policies.find((item) => item.key === policy.key) || {}),\n })),\n profiles: effective.profiles.map((profile) => ({\n ...profile,\n ...(persisted.profiles.find((item) => item.key === profile.key) ||\n {}),\n })),\n assignments: effective.assignments.map((assignment) => ({\n ...assignment,\n ...(persisted.assignments.find(\n (item) => item.key === assignment.key,\n ) || {}),\n })),\n },\n persisted: {\n policies: persisted.policies,\n profiles: persisted.profiles,\n assignments: persisted.assignments,\n },\n };\n }\n\n public async resolveGovernanceAction(\n options: {\n type?: string;\n variant?: string | null;\n tenantId?: string | null;\n } = {},\n ) {\n return resolveEffectiveContentGovernance({\n contentType: options.type || null,\n contentVariant: options.variant || null,\n db: this.db,\n tenantId: options.tenantId,\n });\n }\n\n /**\n * Mirrors content from a remote URL\n *\n * Downloads and stores content from a remote URL, extracting text\n * and saving it as a Content object.\n *\n * @param options - Mirror options\n * @param options.url - URL to mirror\n * @param options.mirrorDir - Directory for caching mirrored files\n * @param options.context - Context for the mirrored content\n * @returns Promise resolving to the mirrored Content object\n * @throws Error if URL is invalid or missing\n */\n public async mirror(options: {\n url: string;\n mirrorDir?: string;\n context?: string;\n /**\n * Skip the SSRF host-blocking checks. Only set this for fully trusted,\n * operator-supplied URLs (e.g. local development), never for URLs that\n * originate from end users or external content.\n */\n allowPrivateNetworkHosts?: boolean;\n /** Injectable DNS resolver (primarily for tests). */\n resolveHostname?: ResolveHostname;\n /** Injectable fetch for redirect resolution (primarily for tests). */\n fetchImpl?: typeof fetch;\n }) {\n if (!options.url) {\n throw new Error('No URL provided');\n }\n // Validate the URL AND block private/loopback/link-local/metadata hosts\n // before fetching — `mirror()` fetches an arbitrary caller-supplied URL,\n // which is a classic SSRF sink (e.g. http://169.254.169.254/ metadata).\n // `fetchDocument` follows redirects on its own, so we resolve the redirect\n // chain ourselves first — re-validating every hop — and hand it the\n // already-validated terminal URL, closing the public-host-30x-to-internal\n // bypass (S5 #1388 / review #1562).\n let url: URL;\n try {\n url = await resolveSafeFinalUrl(options.url, {\n allowPrivateNetworkHosts: options.allowPrivateNetworkHosts,\n resolveHostname: options.resolveHostname,\n fetchImpl: options.fetchImpl,\n });\n } catch (error) {\n // Never echo the raw URL — it may carry userinfo credentials (review #1562).\n const safeUrl = redactUrlCredentials(options.url);\n logger.error('Refusing to mirror unsafe URL', { error, url: safeUrl });\n throw new Error(`Invalid URL provided: ${safeUrl}`);\n }\n const existing = await this.get({ url: options.url });\n if (existing) {\n return existing;\n }\n\n // Fetch and process the document via the already-validated terminal URL.\n const doc = await fetchDocument(url.toString(), {\n cacheDir: options?.mirrorDir,\n });\n\n const filename = url.pathname.split('/').pop();\n const nameWithoutExtension = filename?.replace(/\\.[^/.]+$/, '');\n const title = nameWithoutExtension?.replace(/[-_]/g, ' ');\n const slug = makeSlug(title as string);\n\n // Extract text from all document parts\n const body = doc.parts.map((part) => part.content).join('\\n\\n');\n if (body) {\n const content = new Content({\n url: options.url,\n type: 'mirror',\n title,\n slug,\n context: options.context || '',\n body,\n });\n await content.initialize();\n await content.save();\n return content;\n }\n }\n\n /**\n * Writes a Content object to the filesystem as a markdown file\n *\n * @param options - Options for writing the content file\n * @param options.content - Content object to write\n * @param options.contentDir - Directory to write the file to\n * @returns Promise that resolves when the file is written\n * @throws Error if contentDir is not provided\n */\n public async writeContentFile(options: {\n content: Content;\n contentDir: string;\n }) {\n const { content, contentDir } = options;\n if (!contentDir) {\n throw new Error('No content dir provided');\n }\n\n const { body } = content;\n const frontMatter = {\n title: content.title,\n slug: content.slug,\n context: content.context,\n author: content.author,\n publish_date: content.publish_date,\n };\n\n let output = '';\n if (frontMatter && Object.keys(frontMatter).length > 0) {\n output += '---\\n';\n output += YAML.stringify(frontMatter);\n output += '---\\n';\n }\n\n // Filesystem exports are markdown regardless of the editor save format.\n let formattedBody = body || '';\n const bodyFormat = resolveBodyFormat(content.bodyFormat, body);\n if (bodyFormat === 'html') {\n formattedBody = htmlToMarkdown(body || '');\n } else if (body && !this.isMarkdown(body)) {\n formattedBody = this.formatAsMarkdown(body);\n }\n output += formattedBody;\n\n const pathParts = [\n contentDir,\n content.context || '', // if empty, use empty string\n content.slug,\n 'index.md',\n ].filter(Boolean); // remove empty strings\n\n const outputFile = path.join(...(pathParts as string[]));\n\n // `context` and `slug` are persisted, caller-influenced fields. Without a\n // guard, a value like `../../etc/cron.d/x` escapes `contentDir` and lets an\n // export overwrite arbitrary files (path traversal). Confirm the joined\n // path still lives under the resolved content directory (S5 #1388).\n const resolvedDir = path.resolve(contentDir);\n const resolvedFile = path.resolve(outputFile);\n if (\n resolvedFile !== resolvedDir &&\n !resolvedFile.startsWith(resolvedDir + path.sep)\n ) {\n throw new Error(\n 'Refusing to write content file outside of the content directory',\n );\n }\n\n await ensureDirectoryExists(path.dirname(outputFile));\n await writeFile(outputFile, output);\n }\n\n /**\n * Checks if text appears to be in markdown format\n *\n * @param text - Text to check\n * @returns Boolean indicating if the text contains markdown syntax\n */\n private isMarkdown(text: string): boolean {\n // Basic check for common markdown indicators\n const markdownIndicators = [\n /^#\\s/m, // Headers\n /\\*\\*.+\\*\\*/, // Bold\n /\\*.+\\*/, // Italic\n /\\[.+\\]\\(.+\\)/, // Links\n /^\\s*[-*+]\\s/m, // Lists\n /^\\s*\\d+\\.\\s/m, // Numbered lists\n /```[\\s\\S]*```/, // Code blocks\n /^\\s*>/m, // Blockquotes\n ];\n\n return markdownIndicators.some((indicator) => indicator.test(text));\n }\n\n /**\n * Formats plain text as simple markdown\n *\n * @param text - Plain text to format\n * @returns Text formatted as basic markdown\n */\n private formatAsMarkdown(text: string): string {\n // Basic formatting of plain text to markdown\n return text\n .split(/\\n\\n+/)\n .map((paragraph) => paragraph.trim())\n .filter(Boolean)\n .join('\\n\\n');\n }\n\n /**\n * Synchronizes content to the filesystem\n *\n * Writes all article-type Content objects to the filesystem\n * as markdown files.\n *\n * @param options - Sync options\n * @param options.contentDir - Directory to write content files to\n * @returns Promise that resolves when synchronization is complete\n */\n public async syncContentDir(options: { contentDir?: string }) {\n const contentFilter = {\n type: 'article',\n };\n\n const contents = await this.list({ where: contentFilter });\n for (const content of contents) {\n await this.writeContentFile({\n content,\n contentDir: options.contentDir || this.options.contentDir || '',\n });\n }\n }\n\n /**\n * Generate thumbnails for content that doesn't have one\n *\n * @param options - Options for bulk thumbnail generation\n * @returns Promise resolving to result object with generated images and failed content IDs\n *\n * @example Generate headline cards for all published articles\n * ```typescript\n * const result = await contents.generateMissingThumbnails({\n * strategy: 'headline-card',\n * where: { type: 'article', status: 'published' },\n * brandColor: '#1a56db'\n * });\n * console.log(`Generated ${result.images.length} thumbnails`);\n * if (result.failed.length > 0) {\n * console.warn(`Failed to generate ${result.failed.length} thumbnails`);\n * }\n * ```\n */\n public async generateMissingThumbnails(\n options: GenerateMissingThumbnailsOptions,\n ): Promise<{\n images: Image[];\n failed: Array<{ contentId: string; error: string }>;\n }> {\n // Merge with thumbnail config from smrt.config.js (passed via constructor)\n // This allows CLI users to configure defaults in smrt.config.js:\n // thumbnail: { strategy: 'headline-card', brandColor: '#1976d2' }\n const configDefaults = this.options?.thumbnail || {};\n const mergedOptions = {\n ...configDefaults,\n ...options,\n };\n\n // Build query for content missing thumbnails\n const whereClause = {\n ...mergedOptions.where,\n thumbnailAssetId: null,\n };\n\n const contents = await this.list({\n where: whereClause,\n limit: mergedOptions.limit,\n });\n\n const generatedImages: Image[] = [];\n const failed: Array<{ contentId: string; error: string }> = [];\n\n for (const content of contents) {\n try {\n // Build thumbnail options based on strategy\n let thumbnailOptions: ThumbnailOptions;\n\n switch (mergedOptions.strategy) {\n case 'headline-card':\n thumbnailOptions = {\n strategy: 'headline-card',\n brandColor: mergedOptions.brandColor,\n backgroundColor: mergedOptions.backgroundColor,\n logoUrl: mergedOptions.logoUrl,\n template: mergedOptions.template,\n width: mergedOptions.width,\n height: mergedOptions.height,\n };\n break;\n\n case 'static-map':\n thumbnailOptions = {\n strategy: 'static-map',\n mapProvider: mergedOptions.mapProvider,\n zoom: mergedOptions.zoom,\n width: mergedOptions.width,\n height: mergedOptions.height,\n };\n break;\n\n case 'ai-generate':\n thumbnailOptions = {\n strategy: 'ai-generate',\n style: mergedOptions.style,\n width: mergedOptions.width,\n height: mergedOptions.height,\n ai: isAIClientOptions(this.options.ai)\n ? this.options.ai\n : undefined,\n };\n break;\n\n default:\n throw new Error(`Unknown strategy: ${mergedOptions.strategy}`);\n }\n\n const image = await content.generateThumbnail(thumbnailOptions);\n generatedImages.push(image);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n logger.error(\n `Failed to generate thumbnail for content ${content.id}: ${errorMessage}`,\n );\n failed.push({\n contentId: content.id ?? 'unknown',\n error: errorMessage,\n });\n }\n }\n\n return { images: generatedImages, failed };\n }\n\n // ============================================\n // Tenant Helper Methods\n // ============================================\n\n /**\n * Find all content belonging to a specific tenant\n *\n * @param tenantId - The tenant ID to filter by\n * @returns Promise resolving to array of Content objects for the tenant\n *\n * @example\n * ```typescript\n * const tenantContent = await contents.findByTenant('tenant-123');\n * ```\n */\n async findByTenant(tenantId: string): Promise<Content[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global content (not associated with any tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Promise resolving to array of global Content objects\n *\n * @example\n * ```typescript\n * const globalContent = await contents.findGlobal();\n * ```\n */\n async findGlobal(): Promise<Content[]> {\n return queryGlobal<Content>(this);\n }\n\n /**\n * Find content for a tenant including global content.\n *\n * This returns both tenant-specific content and global content (tenantId is null),\n * useful for showing a tenant their content plus any shared/global resources.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - The tenant ID to include\n * @returns Promise resolving to array of Content objects (tenant + global)\n *\n * @example\n * ```typescript\n * const allAccessibleContent = await contents.findWithGlobals('tenant-123');\n * ```\n */\n async findWithGlobals(tenantId: string): Promise<Content[]> {\n return queryWithGlobals<Content>(this, tenantId, 'Content.findWithGlobals');\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport {\n ObjectRegistry,\n type SmrtCollectionOptions,\n} from '@happyvertical/smrt-core';\nimport {\n type ParsedContentFeedItem,\n parseContentFeed,\n} from './content-feed-parser';\nimport type { ContentFeedSource } from './content-feed-source';\nimport { Mirror } from './content-types';\nimport { Contents } from './contents';\nimport { assertSafeRemoteUrl, type ResolveHostname } from './safe-remote-url';\n\nexport interface ContentFeedSyncOptions extends SmrtCollectionOptions {\n fetch?: typeof fetch;\n maxItems?: number;\n maxResponseBytes?: number;\n fetchTimeoutMs?: number;\n allowPrivateNetworkHosts?: boolean;\n resolveHostname?: ResolveHostname;\n status?: 'published' | 'draft';\n now?: () => Date;\n}\n\nexport interface ContentFeedSyncResult {\n source: ContentFeedSource;\n fetched: boolean;\n notModified: boolean;\n imported: number;\n updated: number;\n skipped: number;\n}\n\ntype QueryableCollection = Pick<Contents, 'query'>;\n\nconst DEFAULT_MAX_RESPONSE_BYTES = 2_000_000;\nconst DEFAULT_FETCH_TIMEOUT_MS = 10_000;\nconst MAX_FEED_REDIRECTS = 5;\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\nconst FALLBACK_MIRROR_META_TYPE = '@happyvertical/smrt-content:Mirror';\n\nfunction getMirrorMetaType(): string {\n return (\n ObjectRegistry.getClassByConstructor(Mirror)?.qualifiedName ??\n FALLBACK_MIRROR_META_TYPE\n );\n}\n\nfunction slugify(value: string): string {\n const slug = value\n .toLowerCase()\n .replace(/&/g, ' and ')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80);\n return slug || 'feed';\n}\n\nfunction normalizeUrlIdentity(value: string): string {\n try {\n const url = new URL(value);\n url.hash = '';\n url.searchParams.sort();\n return url.toString().replace(/\\/$/, '');\n } catch {\n return value.trim();\n }\n}\n\nfunction hashString(value: string): string {\n let hash = 5381;\n for (let index = 0; index < value.length; index += 1) {\n hash = (hash * 33) ^ value.charCodeAt(index);\n }\n return (hash >>> 0).toString(36);\n}\n\nfunction createDedupeKey(\n source: ContentFeedSource,\n item: ParsedContentFeedItem,\n): string {\n return `feed:${source.id ?? source.feedUrl}:${item.guid || normalizeUrlIdentity(item.url)}`;\n}\n\nasync function validateFeedFetchUrl(\n feedUrl: string,\n options: ContentFeedSyncOptions,\n): Promise<URL> {\n return assertSafeRemoteUrl(feedUrl, {\n allowPrivateNetworkHosts: options.allowPrivateNetworkHosts,\n resolveHostname: options.resolveHostname,\n });\n}\n\nfunction createTimeoutSignal(timeoutMs: number): AbortSignal | undefined {\n if (timeoutMs <= 0) return undefined;\n return AbortSignal.timeout(timeoutMs);\n}\n\n/**\n * Fetch a feed following redirects manually so every hop's target is\n * re-validated through {@link validateFeedFetchUrl}. `fetch()`'s default\n * `redirect: 'follow'` would let an allowed public feed 30x-redirect to an\n * internal/link-local/metadata host, defeating the up-front SSRF check\n * (S5 #1388). Returns the response together with the final validated URL so\n * the parser uses the redirect target as the feed base.\n */\nasync function fetchFeedWithRedirectGuard(\n fetchImpl: typeof fetch,\n startUrl: URL,\n headers: Record<string, string>,\n options: ContentFeedSyncOptions,\n): Promise<{ response: Response; finalUrl: URL }> {\n let current = startUrl;\n\n for (let hop = 0; hop <= MAX_FEED_REDIRECTS; hop += 1) {\n const response = await fetchImpl(current, {\n headers,\n redirect: 'manual',\n signal: createTimeoutSignal(\n options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS,\n ),\n });\n\n // Only the redirect statuses (not 304 Not Modified / 305 / 306) reroute the\n // request; everything else (200, 304, 4xx, 5xx) is returned to the caller.\n if (REDIRECT_STATUSES.has(response.status)) {\n const location = response.headers.get('location');\n if (!location) {\n throw new Error('Feed redirect response missing Location header');\n }\n // Re-run the full SSRF validation against the resolved redirect target.\n current = await validateFeedFetchUrl(\n new URL(location, current).toString(),\n options,\n );\n continue;\n }\n\n return { response, finalUrl: current };\n }\n\n throw new Error('Feed URL exceeded the maximum number of redirects');\n}\n\nasync function readResponseText(\n response: Response,\n maxBytes: number,\n): Promise<string> {\n const contentLength = response.headers.get('content-length');\n if (contentLength && Number(contentLength) > maxBytes) {\n throw new Error(`Feed response exceeds ${maxBytes} bytes`);\n }\n\n if (!response.body) {\n const text = await response.text();\n if (new TextEncoder().encode(text).byteLength > maxBytes) {\n throw new Error(`Feed response exceeds ${maxBytes} bytes`);\n }\n return text;\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(`Feed response exceeds ${maxBytes} bytes`);\n }\n chunks.push(value);\n }\n\n const buffer = new Uint8Array(totalBytes);\n let offset = 0;\n for (const chunk of chunks) {\n buffer.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return new TextDecoder().decode(buffer);\n}\n\nasync function findExistingMirror(\n contents: QueryableCollection,\n source: ContentFeedSource,\n item: ParsedContentFeedItem,\n dedupeKey: string,\n): Promise<{ id: string; status: string | null } | null> {\n const tenantWhere = source.tenantId ? 'tenant_id = ?' : 'tenant_id IS NULL';\n const params: unknown[] = [getMirrorMetaType()];\n if (source.tenantId) params.push(source.tenantId);\n\n if (item.guid) {\n const result = await contents.query(\n `SELECT id, status\n FROM contents\n WHERE _meta_type = ?\n AND ${tenantWhere}\n AND feed_source_id = ?\n AND source_guid = ?\n LIMIT 1`,\n [...params, source.id, item.guid],\n );\n if (result[0]) return result[0] as { id: string; status: string | null };\n }\n\n const result = await contents.query(\n `SELECT id, status\n FROM contents\n WHERE _meta_type = ?\n AND ${tenantWhere}\n AND feed_source_id = ?\n AND dedupe_key = ?\n LIMIT 1`,\n [...params, source.id, dedupeKey],\n );\n return (\n (result[0] as { id: string; status: string | null } | undefined) ?? null\n );\n}\n\nasync function upsertMirrorItem(\n contents: QueryableCollection,\n source: ContentFeedSource,\n item: ParsedContentFeedItem,\n status: 'published' | 'draft',\n now: Date,\n): Promise<'imported' | 'updated'> {\n const dedupeKey = createDedupeKey(source, item);\n const existing = await findExistingMirror(contents, source, item, dedupeKey);\n const publishedAt = (item.publishedAt ?? item.updatedAt ?? now).toISOString();\n const sourceSlug = slugify(\n source.sourceGroup || source.name || source.feedUrl,\n );\n const category = source.defaultCategory || item.categories[0] || null;\n const tags = JSON.stringify(item.categories);\n\n if (existing) {\n await contents.query(\n `UPDATE contents\n SET title = ?,\n name = ?,\n description = ?,\n source = ?,\n original_url = ?,\n url = ?,\n feed_source_id = ?,\n source_guid = ?,\n source_name = ?,\n source_homepage_url = ?,\n external_url = ?,\n original_published_at = ?,\n dedupe_key = ?,\n publish_date = ?,\n author = ?,\n category = ?,\n tags = ?,\n status = ?,\n updated_at = ?\n WHERE id = ?`,\n [\n item.title,\n item.title,\n item.summary,\n source.name,\n item.url,\n item.url,\n source.id,\n item.guid,\n source.name,\n source.homepageUrl,\n item.url,\n publishedAt,\n dedupeKey,\n publishedAt,\n item.author,\n category,\n tags,\n existing.status || status,\n now.toISOString(),\n existing.id,\n ],\n );\n return 'updated';\n }\n\n const id = randomUUID();\n await contents.query(\n `INSERT INTO contents (\n id, slug, context, _meta_type, title, name, description, body,\n status, state, metadata, source, original_url, url, feed_source_id,\n source_guid, source_name, source_homepage_url, external_url,\n original_published_at, dedupe_key, tenant_id, publish_date, author,\n category, tags, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n id,\n `mirror-${sourceSlug}-${hashString(dedupeKey)}`,\n '',\n getMirrorMetaType(),\n item.title,\n item.title,\n item.summary,\n '',\n status,\n 'active',\n '{}',\n source.name,\n item.url,\n item.url,\n source.id,\n item.guid,\n source.name,\n source.homepageUrl,\n item.url,\n publishedAt,\n dedupeKey,\n source.tenantId,\n publishedAt,\n item.author,\n category,\n tags,\n now.toISOString(),\n now.toISOString(),\n ],\n );\n return 'imported';\n}\n\nexport async function syncContentFeedSource(\n source: ContentFeedSource,\n options: ContentFeedSyncOptions = {},\n): Promise<ContentFeedSyncResult> {\n if (source.status === 'paused' || source.status === 'archived') {\n return {\n source,\n fetched: false,\n notModified: false,\n imported: 0,\n updated: 0,\n skipped: 0,\n };\n }\n\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (!fetchImpl) throw new Error('No fetch implementation available');\n\n const headers: Record<string, string> = {\n Accept:\n 'application/rss+xml, application/atom+xml, application/xml, text/xml',\n 'User-Agent': '@happyvertical/smrt-content feed sync',\n };\n if (source.etag) headers['If-None-Match'] = source.etag;\n if (source.lastModified) headers['If-Modified-Since'] = source.lastModified;\n\n const now = options.now?.() ?? new Date();\n source.markFetchStarted(now);\n await source.save();\n\n try {\n const feedUrl = await validateFeedFetchUrl(source.feedUrl, options);\n const { response, finalUrl } = await fetchFeedWithRedirectGuard(\n fetchImpl,\n feedUrl,\n headers,\n options,\n );\n\n if (response.status === 304) {\n source.markFetchSucceeded(now);\n await source.save();\n return {\n source,\n fetched: true,\n notModified: true,\n imported: 0,\n updated: 0,\n skipped: 0,\n };\n }\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const parsed = parseContentFeed(\n await readResponseText(\n response,\n options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES,\n ),\n finalUrl.toString(),\n );\n source.format = parsed.format;\n source.homepageUrl = source.homepageUrl || parsed.homepageUrl;\n if (!source.name && parsed.title) source.name = parsed.title;\n\n const contents = await Contents.create(options);\n let imported = 0;\n let updated = 0;\n let skipped = 0;\n\n for (const item of parsed.items.slice(0, options.maxItems)) {\n try {\n const result = await upsertMirrorItem(\n contents,\n source,\n item,\n options.status ?? 'published',\n now,\n );\n if (result === 'imported') imported += 1;\n else updated += 1;\n } catch {\n skipped += 1;\n }\n }\n\n source.markFetchSucceeded(now, {\n etag: response.headers.get('etag'),\n lastModified: response.headers.get('last-modified'),\n });\n await source.save();\n\n return {\n source,\n fetched: true,\n notModified: false,\n imported,\n updated,\n skipped,\n };\n } catch (error) {\n source.markFetchFailed(error, now);\n await source.save();\n throw error;\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { FactContentRelationship } from '@happyvertical/smrt-facts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n buildContentGovernanceAssignmentKey,\n type ContentGovernanceAssignmentDefinition,\n getEffectiveContentGovernanceConfig,\n} from './content-governance';\n\nexport interface ContentGovernanceAssignmentOptions extends SmrtObjectOptions {\n key?: string;\n label?: string;\n contentType?: string;\n contentVariant?: string | null;\n enabled?: boolean;\n factLinkingEnabled?: boolean;\n transparencyEnabled?: boolean;\n publicationProfileKey?: string | null;\n correctionProfileKey?: string | null;\n enforcePublishReadiness?: boolean;\n defaultFactRelationship?: FactContentRelationship;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_governance_assignments',\n conflictColumns: ['key'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentGovernanceAssignment extends SmrtObject {\n @field({ required: true })\n key = '';\n\n label = '';\n\n @field({ required: true })\n contentType = '';\n\n contentVariant = '';\n enabled = true;\n factLinkingEnabled = false;\n transparencyEnabled = false;\n publicationProfileKey = '';\n correctionProfileKey = '';\n enforcePublishReadiness = false;\n defaultFactRelationship: FactContentRelationship = 'supports';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentGovernanceAssignmentOptions = {}) {\n super(options);\n if (options.contentType !== undefined)\n this.contentType = options.contentType;\n if (options.contentVariant !== undefined)\n this.contentVariant = options.contentVariant || '';\n if (options.label !== undefined) this.label = options.label;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.factLinkingEnabled !== undefined)\n this.factLinkingEnabled = options.factLinkingEnabled;\n if (options.transparencyEnabled !== undefined)\n this.transparencyEnabled = options.transparencyEnabled;\n if (options.publicationProfileKey !== undefined)\n this.publicationProfileKey = options.publicationProfileKey || '';\n if (options.correctionProfileKey !== undefined)\n this.correctionProfileKey = options.correctionProfileKey || '';\n if (options.enforcePublishReadiness !== undefined)\n this.enforcePublishReadiness = options.enforcePublishReadiness;\n if (options.defaultFactRelationship !== undefined)\n this.defaultFactRelationship = options.defaultFactRelationship;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n this.key =\n options.key ||\n buildContentGovernanceAssignmentKey(\n this.contentType,\n this.contentVariant,\n );\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n if (this.metadata && typeof this.metadata === 'object') {\n return { ...(this.metadata as Record<string, unknown>) };\n }\n\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n\n toDefinition(): ContentGovernanceAssignmentDefinition {\n return {\n key: this.key,\n label: this.label || '',\n contentType: this.contentType,\n contentVariant: this.contentVariant || '',\n enabled: this.enabled !== false,\n factLinkingEnabled: this.factLinkingEnabled === true,\n transparencyEnabled: this.transparencyEnabled === true,\n publicationProfileKey: this.publicationProfileKey || null,\n correctionProfileKey: this.correctionProfileKey || null,\n enforcePublishReadiness: this.enforcePublishReadiness === true,\n defaultFactRelationship: this.defaultFactRelationship || 'supports',\n metadata: this.getMetadata(),\n };\n }\n\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n\n this.key = buildContentGovernanceAssignmentKey(\n this.contentType,\n this.contentVariant,\n );\n\n const effective = await getEffectiveContentGovernanceConfig({\n db: this.db,\n });\n const availableProfileKeys = new Set(\n effective.profiles.map((profile) => profile.key),\n );\n const missingProfiles = [\n this.publicationProfileKey,\n this.correctionProfileKey,\n ].filter(\n (profileKey) => profileKey && !availableProfileKeys.has(profileKey),\n );\n\n if (missingProfiles.length > 0) {\n throw new Error(\n `Cannot save governance assignment \"${this.key}\" because these profiles are missing: ${missingProfiles.join(', ')}`,\n );\n }\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentGovernanceAssignment } from './content-governance-assignment';\n\nexport class ContentGovernanceAssignmentCollection extends SmrtCollection<ContentGovernanceAssignment> {\n static readonly _itemClass = ContentGovernanceAssignment;\n\n async getByKey(key: string): Promise<ContentGovernanceAssignment | null> {\n return this.get({ key });\n }\n\n async resolveForContent(options: {\n contentType: string;\n contentVariant?: string | null;\n }): Promise<ContentGovernanceAssignment | null> {\n const exact =\n (await this.get({\n key: `${options.contentType}::${options.contentVariant || ''}`,\n })) || null;\n\n if (exact) {\n return exact;\n }\n\n return this.get({\n key: `${options.contentType}::`,\n });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type ContentReviewKind,\n type ContentReviewPolicyDefinition,\n getEffectiveContentGovernanceConfig,\n getFallbackPolicyKind,\n hasStaticContentGovernancePolicy,\n} from './content-governance';\n\nexport interface ContentGovernancePolicyOptions extends SmrtObjectOptions {\n key?: string;\n label?: string;\n kind?: ContentReviewKind;\n instructions?: string;\n enabled?: boolean;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_governance_policies',\n conflictColumns: ['key'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentGovernancePolicy extends SmrtObject {\n @field({ required: true })\n key = '';\n\n label = '';\n kind: ContentReviewKind = 'custom';\n instructions = '';\n enabled = true;\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentGovernancePolicyOptions = {}) {\n super(options);\n if (options.key !== undefined) this.key = options.key;\n if (options.label !== undefined) this.label = options.label;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.instructions !== undefined)\n this.instructions = options.instructions;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n if (this.metadata && typeof this.metadata === 'object') {\n return { ...(this.metadata as Record<string, unknown>) };\n }\n\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n\n toDefinition(): ContentReviewPolicyDefinition {\n return {\n key: this.key,\n label: this.label || this.key,\n kind: this.kind || getFallbackPolicyKind(this.key),\n instructions: this.instructions || '',\n enabled: this.enabled !== false,\n metadata: this.getMetadata(),\n };\n }\n\n override async delete(): Promise<void> {\n const effective = await getEffectiveContentGovernanceConfig({\n db: this.db,\n });\n const hasFallback = hasStaticContentGovernancePolicy(this.key);\n const referencedBy = effective.profiles\n .filter((profile) =>\n profile.requirements.some(\n (requirement) => requirement.policyKey === this.key,\n ),\n )\n .map((profile) => profile.key);\n\n if (!hasFallback && referencedBy.length > 0) {\n throw new Error(\n `Cannot delete governance policy \"${this.key}\" because it is referenced by profiles: ${referencedBy.join(', ')}`,\n );\n }\n\n return super.delete();\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentGovernancePolicy } from './content-governance-policy';\n\nexport class ContentGovernancePolicyCollection extends SmrtCollection<ContentGovernancePolicy> {\n static readonly _itemClass = ContentGovernancePolicy;\n\n async getByKey(key: string): Promise<ContentGovernancePolicy | null> {\n return this.get({ key });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type ContentGovernanceProfileDefinition,\n type ContentReviewRequirement,\n getEffectiveContentGovernanceConfig,\n hasStaticContentGovernanceProfile,\n} from './content-governance';\n\nexport interface ContentGovernanceProfileOptions extends SmrtObjectOptions {\n key?: string;\n label?: string;\n description?: string;\n enabled?: boolean;\n requirements?: string | ContentReviewRequirement[];\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_governance_profiles',\n conflictColumns: ['key'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentGovernanceProfile extends SmrtObject {\n @field({ required: true })\n key = '';\n\n label = '';\n description = '';\n enabled = true;\n requirements = '[]';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentGovernanceProfileOptions = {}) {\n super(options);\n if (options.key !== undefined) this.key = options.key;\n if (options.label !== undefined) this.label = options.label;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.requirements !== undefined) {\n this.requirements =\n typeof options.requirements === 'string'\n ? options.requirements\n : JSON.stringify(options.requirements);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getRequirements(): ContentReviewRequirement[] {\n if (Array.isArray(this.requirements)) {\n return this.requirements as ContentReviewRequirement[];\n }\n\n try {\n return this.requirements ? JSON.parse(this.requirements) : [];\n } catch {\n return [];\n }\n }\n\n getMetadata(): Record<string, unknown> {\n if (this.metadata && typeof this.metadata === 'object') {\n return { ...(this.metadata as Record<string, unknown>) };\n }\n\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n\n toDefinition(): ContentGovernanceProfileDefinition {\n return {\n key: this.key,\n label: this.label || this.key,\n description: this.description || '',\n enabled: this.enabled !== false,\n requirements: this.getRequirements(),\n metadata: this.getMetadata(),\n };\n }\n\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n\n const effective = await getEffectiveContentGovernanceConfig({\n db: this.db,\n });\n const availablePolicyKeys = new Set(\n effective.policies.map((policy) => policy.key),\n );\n const missingPolicies = this.getRequirements()\n .map((requirement) => requirement.policyKey)\n .filter((policyKey) => !availablePolicyKeys.has(policyKey));\n\n if (missingPolicies.length > 0) {\n throw new Error(\n `Cannot save governance profile \"${this.key}\" because these policies are missing: ${missingPolicies.join(', ')}`,\n );\n }\n }\n\n override async delete(): Promise<void> {\n const effective = await getEffectiveContentGovernanceConfig({\n db: this.db,\n });\n const hasFallback = hasStaticContentGovernanceProfile(this.key);\n const referencedBy = effective.assignments\n .filter(\n (assignment) =>\n assignment.publicationProfileKey === this.key ||\n assignment.correctionProfileKey === this.key,\n )\n .map((assignment) => assignment.key || assignment.contentType);\n\n if (!hasFallback && referencedBy.length > 0) {\n throw new Error(\n `Cannot delete governance profile \"${this.key}\" because it is referenced by assignments: ${referencedBy.join(', ')}`,\n );\n }\n\n return super.delete();\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentGovernanceProfile } from './content-governance-profile';\n\nexport class ContentGovernanceProfileCollection extends SmrtCollection<ContentGovernanceProfile> {\n static readonly _itemClass = ContentGovernanceProfile;\n\n async getByKey(key: string): Promise<ContentGovernanceProfile | null> {\n return this.get({ key });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n ContentReviewFinding,\n ContentReviewKind,\n ContentReviewStatus,\n} from './content-governance';\n\nexport interface ContentReviewOptions extends SmrtObjectOptions {\n contentId?: string;\n contentVersionId?: string;\n kind?: ContentReviewKind;\n policyKey?: string;\n status?: ContentReviewStatus;\n summary?: string;\n findings?: string | ContentReviewFinding[];\n reviewer?: string;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_reviews',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class ContentReview extends SmrtObject {\n @foreignKey('Content', { required: true })\n contentId = '';\n\n @foreignKey('ContentVersion')\n contentVersionId = '';\n kind: ContentReviewKind = 'custom';\n policyKey = '';\n status: ContentReviewStatus = 'pending';\n summary = '';\n findings = '[]';\n reviewer = 'system';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentReviewOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.contentVersionId !== undefined)\n this.contentVersionId = options.contentVersionId;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.policyKey !== undefined) this.policyKey = options.policyKey;\n if (options.status !== undefined) this.status = options.status;\n if (options.summary !== undefined) this.summary = options.summary;\n if (options.reviewer !== undefined) this.reviewer = options.reviewer;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.findings !== undefined) {\n this.findings =\n typeof options.findings === 'string'\n ? options.findings\n : JSON.stringify(options.findings);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getFindings(): ContentReviewFinding[] {\n if (Array.isArray(this.findings)) {\n return this.findings as ContentReviewFinding[];\n }\n\n try {\n return this.findings ? JSON.parse(this.findings) : [];\n } catch {\n return [];\n }\n }\n\n getMetadata(): Record<string, unknown> {\n if (this.metadata && typeof this.metadata === 'object') {\n return { ...(this.metadata as Record<string, unknown>) };\n }\n\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport type {\n ContentReviewKind,\n ContentReviewResult,\n} from './content-governance';\nimport { ContentReview } from './content-review';\n\nexport class ContentReviewCollection extends SmrtCollection<ContentReview> {\n static readonly _itemClass = ContentReview;\n\n async listForContent(\n contentId: string,\n kind?: ContentReviewKind,\n ): Promise<ContentReview[]> {\n return this.list({\n where: kind ? { contentId, kind } : { contentId },\n orderBy: 'created_at DESC',\n });\n }\n\n async getLatestForContent(\n contentId: string,\n kind?: ContentReviewKind,\n ): Promise<ContentReview | null> {\n const reviews = await this.listForContent(contentId, kind);\n return reviews[0] ?? null;\n }\n\n async listForContentByPolicyKey(\n contentId: string,\n policyKey: string,\n ): Promise<ContentReview[]> {\n return this.list({\n where: { contentId, policyKey },\n orderBy: 'created_at DESC',\n });\n }\n\n async getLatestForPolicyKey(\n contentId: string,\n policyKey: string,\n ): Promise<ContentReview | null> {\n const reviews = await this.listForContentByPolicyKey(contentId, policyKey);\n return reviews[0] ?? null;\n }\n\n async createFromResult(options: {\n contentId: string;\n contentVersionId?: string | null;\n kind: ContentReviewKind;\n policyKey: string;\n reviewer?: string;\n result: ContentReviewResult;\n metadata?: Record<string, unknown>;\n tenantId?: string | null;\n }): Promise<ContentReview> {\n return this.create({\n contentId: options.contentId,\n contentVersionId: options.contentVersionId || '',\n kind: options.kind,\n policyKey: options.policyKey,\n status: options.result.status,\n summary: options.result.summary,\n findings: JSON.stringify(options.result.findings),\n reviewer: options.reviewer || 'system',\n metadata: JSON.stringify(options.metadata || {}),\n tenantId: options.tenantId ?? null,\n });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentVersionKind } from './content-governance';\nimport { normalizeContentTransparency } from './content-transparency';\n\n/**\n * Shape of the loosely-structured `metadata` JSON bag stored on a content\n * version. Known fields are typed; unrecognized keys remain accessible via the\n * index signature.\n */\nexport interface ContentVersionMetadata {\n /**\n * Fingerprint of the snapshot captured for the latest publication version,\n * used to detect post-publication drift.\n */\n publicationSnapshotFingerprint?: string;\n [key: string]: unknown;\n}\n\nexport interface ContentVersionOptions extends SmrtObjectOptions {\n contentId?: string;\n version?: number;\n kind?: ContentVersionKind;\n title?: string;\n description?: string;\n body?: string;\n status?: string;\n summary?: string;\n snapshot?: string | Record<string, unknown>;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_versions',\n conflictColumns: ['content_id', 'version'],\n api: {\n include: ['list', 'get', 'create', 'getTransparencyAction'],\n routes: {\n getTransparencyAction: { method: 'GET', path: 'transparency' },\n },\n },\n mcp: { include: ['list', 'get', 'create'] },\n cli: true,\n})\nexport class ContentVersion extends SmrtObject {\n @foreignKey('Content', { required: true })\n contentId = '';\n\n version = 1;\n kind: ContentVersionKind = 'manual';\n title = '';\n description = '';\n body = '';\n status = 'draft';\n summary = '';\n snapshot = '{}';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentVersionOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.version !== undefined) this.version = options.version;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.title !== undefined) this.title = options.title;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.body !== undefined) this.body = options.body;\n if (options.status !== undefined) this.status = options.status;\n if (options.summary !== undefined) this.summary = options.summary;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.snapshot !== undefined) {\n this.snapshot =\n typeof options.snapshot === 'string'\n ? options.snapshot\n : JSON.stringify(options.snapshot);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getSnapshot(): Record<string, unknown> {\n try {\n return this.snapshot ? JSON.parse(this.snapshot) : {};\n } catch {\n return {};\n }\n }\n\n getMetadata(): ContentVersionMetadata {\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n\n getTransparency() {\n const metadata = this.getMetadata();\n const snapshot = this.getSnapshot();\n\n return normalizeContentTransparency(metadata.transparency, {\n snapshotKind: this.kind === 'publication' ? 'published' : 'preview',\n contentId: this.contentId || (snapshot.contentId as string) || null,\n currentContentStatus:\n this.status || (snapshot.status as string) || 'draft',\n publicationVersion: {\n id: (this.id as string) || null,\n version: this.version ?? null,\n kind: this.kind || null,\n summary: this.summary || '',\n createdAt:\n this.createdAt instanceof Date ? this.createdAt.toISOString() : null,\n },\n });\n }\n\n async getTransparencyAction() {\n return this.getTransparency();\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport type { FactContentRelationship } from '@happyvertical/smrt-facts';\nimport type { Content } from './content';\nimport type { CreateContentVersionOptions } from './content-governance';\nimport { resolveEffectiveContentGovernance } from './content-governance';\nimport { ContentVersion } from './content-version';\n\nexport class ContentVersionCollection extends SmrtCollection<ContentVersion> {\n static readonly _itemClass = ContentVersion;\n\n private buildSnapshotFactRelationships(\n snapshot: Record<string, unknown>,\n defaultRelationship: FactContentRelationship,\n ): Map<FactContentRelationship, string[]> {\n const byRelationship = new Map<FactContentRelationship, string[]>();\n const rawLinks: unknown[] = Array.isArray(snapshot.factLinks)\n ? snapshot.factLinks\n : [];\n\n for (const rawLink of rawLinks) {\n const link = rawLink as {\n factId?: unknown;\n relationship?: unknown;\n } | null;\n const factId =\n typeof link?.factId === 'string' && link.factId.length > 0\n ? link.factId\n : null;\n const relationship =\n typeof link?.relationship === 'string' && link.relationship.length > 0\n ? (link.relationship as FactContentRelationship)\n : defaultRelationship;\n\n if (!factId) {\n continue;\n }\n\n byRelationship.set(relationship, [\n ...(byRelationship.get(relationship) || []),\n factId,\n ]);\n }\n\n if (\n byRelationship.size === 0 &&\n Array.isArray(snapshot.factIds) &&\n snapshot.factIds.length > 0\n ) {\n byRelationship.set(\n defaultRelationship,\n snapshot.factIds.filter(\n (factId: unknown): factId is string =>\n typeof factId === 'string' && factId.length > 0,\n ),\n );\n }\n\n return byRelationship;\n }\n\n async listForContent(contentId: string): Promise<ContentVersion[]> {\n return this.list({\n where: { contentId },\n orderBy: 'version ASC',\n });\n }\n\n async getLatestForContent(contentId: string): Promise<ContentVersion | null> {\n const versions = await this.listForContent(contentId);\n return versions.length > 0 ? versions[versions.length - 1] : null;\n }\n\n async getLatestPublishedForContent(\n contentId: string,\n ): Promise<ContentVersion | null> {\n const versions = await this.list({\n where: {\n contentId,\n kind: 'publication',\n },\n orderBy: 'version DESC',\n });\n\n return versions[0] || null;\n }\n\n async getVersion(\n contentId: string,\n versionNumber: number,\n ): Promise<ContentVersion | null> {\n return this.get({\n contentId,\n version: versionNumber,\n });\n }\n\n async getNextVersionNumber(contentId: string): Promise<number> {\n const latest = await this.getLatestForContent(contentId);\n return latest ? latest.version + 1 : 1;\n }\n\n async createSnapshot(\n content: Content,\n options: CreateContentVersionOptions = {},\n ): Promise<ContentVersion> {\n if (!content.id) {\n throw new Error('Cannot create a version for unsaved content');\n }\n\n const version = await this.getNextVersionNumber(content.id as string);\n const governance = await resolveEffectiveContentGovernance({\n contentType: content.type,\n contentVariant: content.variant,\n db: this.db,\n tenantId: content.tenantId ?? null,\n });\n const [references, referenceEdges, assets, factsState] = await Promise.all([\n typeof content.getReferences === 'function'\n ? content.getReferences()\n : [],\n // Capture per-edge citation pins so restore can reconstruct them.\n // `getReferences()` resolves to Content objects and loses targetVersion.\n typeof content.getReferenceEdges === 'function'\n ? content.getReferenceEdges()\n : Promise.resolve([]),\n typeof content.getAssets === 'function' ? content.getAssets() : [],\n typeof content.getFactsState === 'function' &&\n governance.factLinkingEnabled\n ? content.getFactsState()\n : {\n factIds: [],\n facts: [],\n factLinks: [],\n },\n ]);\n\n const baseSnapshot = {\n id: content.id,\n slug: content.slug,\n context: content.context,\n name: content.name,\n type: content.type,\n variant: content.variant,\n fileKey: content.fileKey,\n author: content.author,\n title: content.title,\n description: content.description,\n body: content.body,\n bodyFormat: content.bodyFormat,\n publish_date: content.publish_date,\n url: content.url,\n source: content.source,\n original_url: content.original_url,\n language: content.language,\n tags: [...content.tags],\n category: content.category,\n status: content.status,\n state: content.state,\n metadata: content.metadata,\n thumbnailAssetId: content.thumbnailAssetId,\n referenceIds: references.map((reference) => reference.id).filter(Boolean),\n // Full edges with citation pins; `referenceIds` retained for back-compat\n // with snapshots written before pin-aware restore (#1387 #3).\n referenceEdges: referenceEdges.filter((edge) => Boolean(edge.targetId)),\n assetIds: assets.map((asset) => asset.id).filter(Boolean),\n factIds: factsState.factIds,\n factLinks: factsState.factLinks,\n tenantId: content.tenantId,\n _meta_type: content.toJSON()._meta_type,\n };\n const snapshot = {\n ...baseSnapshot,\n ...(options.snapshot || {}),\n };\n const versionSlugBase =\n snapshot.slug ||\n content.slug ||\n content.name ||\n content.title ||\n content.id;\n const versionSlug = `${versionSlugBase}-v${version}`;\n\n return this.create({\n slug: versionSlug,\n context: content.context || '',\n contentId: content.id as string,\n version,\n kind: options.kind || 'manual',\n title: snapshot.title || '',\n description: snapshot.description || '',\n body: snapshot.body || '',\n status: snapshot.status || 'draft',\n summary: options.summary || '',\n snapshot: JSON.stringify(snapshot),\n metadata: JSON.stringify(options.metadata || {}),\n tenantId: content.tenantId,\n });\n }\n\n async restoreIntoContent(\n content: Content,\n versionNumber: number,\n ): Promise<Content> {\n if (!content.id) {\n throw new Error('Cannot restore an unsaved content item');\n }\n\n const version = await this.getVersion(content.id as string, versionNumber);\n if (!version) {\n throw new Error(\n `Content version ${versionNumber} not found for content ${content.id}`,\n );\n }\n\n const snapshot = version.getSnapshot();\n const keysToRestore = [\n 'name',\n 'type',\n 'variant',\n 'fileKey',\n 'author',\n 'title',\n 'description',\n 'body',\n 'bodyFormat',\n 'publish_date',\n 'url',\n 'source',\n 'original_url',\n 'language',\n 'tags',\n 'category',\n 'status',\n 'state',\n 'metadata',\n 'thumbnailAssetId',\n ];\n\n // Restore snapshot values onto the live Content instance by field name.\n // Indexing a class instance by an arbitrary string key requires a record\n // view; the keys are a fixed, known set of Content fields.\n const writableContent = content as unknown as Record<string, unknown>;\n for (const key of keysToRestore) {\n if (snapshot[key] !== undefined) {\n writableContent[key] = snapshot[key];\n }\n }\n\n // Reference edges with citation pins (#1387 #3). Newer snapshots carry\n // `referenceEdges` ({ targetId, targetVersion }); older ones only have\n // `referenceIds`. Either way, seed the pending `referenceIds` from the\n // target ids so `save()` reconciles the set (adds missing, removes extra),\n // then re-apply the saved pins below so restoring \"to vN\" reconstructs the\n // citation pins that existed at vN instead of dropping them to unpinned.\n const snapshotEdges: Array<{\n targetId: string;\n targetVersion: number | null;\n }> = Array.isArray(snapshot.referenceEdges)\n ? (snapshot.referenceEdges as unknown[])\n .filter(\n (edge): edge is { targetId: string; targetVersion?: unknown } =>\n !!edge &&\n typeof edge === 'object' &&\n typeof (edge as { targetId?: unknown }).targetId === 'string' &&\n (edge as { targetId: string }).targetId.length > 0,\n )\n .map((edge) => ({\n targetId: edge.targetId,\n targetVersion:\n typeof edge.targetVersion === 'number'\n ? edge.targetVersion\n : null,\n }))\n : Array.isArray(snapshot.referenceIds)\n ? snapshot.referenceIds\n .filter(\n (id: unknown): id is string =>\n typeof id === 'string' && id.length > 0,\n )\n .map((targetId: string) => ({ targetId, targetVersion: null }))\n : [];\n\n if (\n Array.isArray(snapshot.referenceEdges) ||\n Array.isArray(snapshot.referenceIds)\n ) {\n writableContent.referenceIds = snapshotEdges.map((edge) => edge.targetId);\n }\n\n if (Array.isArray(snapshot.assetIds)) {\n writableContent.assetIds = [...snapshot.assetIds];\n }\n\n await content.save();\n\n // Re-apply the citation pin of EVERY snapshot edge — including UNPINNED\n // ones (`targetVersion: null`). `save()` only reconciles the target-id set\n // (adds missing / removes extra edges) and leaves the pin of an edge that\n // already existed untouched. So restoring an *unpinned* snapshot over an\n // edge that is currently *pinned* must explicitly clear that pin, otherwise\n // the live pin survives the restore and drift never resets. Passing\n // `addReference(target, { targetVersion: null })` clears the pin in place\n // (the junction's `attach` updates the row when `null !== existing`), while\n // a non-null value (re)sets it — so \"restore to vN\" reconstructs exactly\n // the pins that existed at vN.\n //\n // We pass the resolved Content object (not the raw id) because\n // `addReference(string)` treats the string as a URL, not a content id.\n // `addReference` is idempotent on (source, target) and only adjusts\n // targetVersion.\n if (\n snapshotEdges.length > 0 &&\n typeof content.getReferences === 'function' &&\n typeof content.addReference === 'function'\n ) {\n const resolvedReferences = await content.getReferences();\n const resolvedById = new Map(\n resolvedReferences\n .filter((reference) => reference.id)\n .map((reference) => [reference.id as string, reference]),\n );\n for (const edge of snapshotEdges) {\n const target = resolvedById.get(edge.targetId);\n if (target) {\n await content.addReference(target, {\n targetVersion: edge.targetVersion,\n });\n }\n }\n }\n\n const governance = await resolveEffectiveContentGovernance({\n contentType: content.type,\n contentVariant: content.variant,\n db: this.db,\n tenantId: content.tenantId ?? null,\n });\n\n if (\n governance.isGoverned &&\n governance.factLinkingEnabled &&\n typeof content.getFactLinks === 'function' &&\n typeof content.syncFacts === 'function'\n ) {\n const desiredByRelationship = this.buildSnapshotFactRelationships(\n snapshot,\n governance.defaultFactRelationship,\n );\n const currentLinks = await content.getFactLinks();\n const currentRelationships = new Set(\n currentLinks.map(\n (link) =>\n (link.relationship as FactContentRelationship) ||\n governance.defaultFactRelationship,\n ),\n );\n const relationshipsToSync = new Set<FactContentRelationship>([\n ...currentRelationships,\n ...desiredByRelationship.keys(),\n ]);\n\n for (const relationship of relationshipsToSync) {\n await content.syncFacts(\n desiredByRelationship.get(relationship) || [],\n relationship,\n );\n }\n }\n\n return content;\n }\n}\n","import yaml from 'yaml';\nimport type { Content } from './content';\n\n/**\n * Converts a Content object to a string with YAML frontmatter\n *\n * @param content - Content object to convert\n * @returns String with YAML frontmatter and body content\n */\nexport function contentToString(content: Content) {\n const { body, ...frontmatter } = content;\n const separator = '---';\n const frontmatterYAML = yaml.stringify(frontmatter);\n return `${separator}\\n${frontmatterYAML}\\n${separator}\\n${body}`;\n}\n\n/**\n * Converts a string with YAML frontmatter to a Content object\n *\n * @param data - String with YAML frontmatter and body content\n * @returns Object with parsed frontmatter and body content\n */\nexport function stringToContent(data: string) {\n const separator = '---';\n const frontmatterStart = data.indexOf(separator);\n\n let frontmatter = {};\n let body = data;\n\n if (frontmatterStart !== -1) {\n const frontmatterEnd = data.indexOf(\n separator,\n frontmatterStart + separator.length,\n );\n\n if (frontmatterEnd !== -1) {\n const frontmatterYAML = data\n .substring(frontmatterStart + separator.length, frontmatterEnd)\n .trim();\n // yaml.parse() THROWS on malformed YAML — `|| {}` only covers an\n // empty/null parse. Wrap so a bad frontmatter block degrades to \"no\n // frontmatter\" instead of crashing the caller (#1387).\n try {\n frontmatter = yaml.parse(frontmatterYAML) || {};\n } catch {\n frontmatter = {};\n }\n body = data.substring(frontmatterEnd + separator.length).trim();\n }\n }\n\n return {\n ...frontmatter,\n body,\n };\n}\n"],"x_google_ignoreList":[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54],"mappings":"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CJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EAEb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EAEb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;AACF;;;;;;;;;;;;ACjEA,IAAM,eAAe;CACnB;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EAGb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;AACF;;;;;;;;;;;;;;;;AChEA,IAAM,eAAe;CACnB;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EA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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["contents","content","defaultOptions","validateEntityName","DEFAULT_XML_ENTITIES","METADATA_SYMBOL","isName","SQL_STRICT","HTML_CONTEXT","XML_CONTEXT","xmlNode","left","total","result"],"sources":["../src/__smrt-register__.ts","../src/asset-associable.ts","../src/content-asset.ts","../src/content-assets.ts","../src/content-prompts.ts","../src/content-reference.ts","../src/content-references.ts","../src/database-utils.ts","../src/serialization.ts","../src/thumbnail-generator.ts","../src/content.ts","../src/content-chat-prompts.ts","../src/content-chat-session.ts","../src/content-chat-handlers.ts","../src/content-contribution-config.ts","../src/content-contribution.ts","../src/content-contribution-attachment.ts","../src/content-contribution-attachments.ts","../src/content-contribution-revision.ts","../src/content-contribution-revisions.ts","../src/content-contribution-type.ts","../src/content-contribution-types.ts","../src/content-contributor.ts","../src/content-contributors.ts","../src/content-contributions.ts","../src/content-correction.ts","../src/content-corrections.ts","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/util.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/validator.js","../../../node_modules/.pnpm/@nodable+entities@3.0.0/node_modules/@nodable/entities/src/entities.js","../../../node_modules/.pnpm/@nodable+entities@3.0.0/node_modules/@nodable/entities/src/EntityDecoder.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js","../../../node_modules/.pnpm/xml-naming@0.3.0/node_modules/xml-naming/src/index.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js","../../../node_modules/.pnpm/anynum@1.0.1/node_modules/anynum/digitTable.js","../../../node_modules/.pnpm/anynum@1.0.1/node_modules/anynum/anynum.js","../../../node_modules/.pnpm/strnum@2.4.1/node_modules/strnum/strnum.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/ignoreAttributes.js","../../../node_modules/.pnpm/path-expression-matcher@1.6.2/node_modules/path-expression-matcher/src/Expression.js","../../../node_modules/.pnpm/path-expression-matcher@1.6.2/node_modules/path-expression-matcher/src/ExpressionSet.js","../../../node_modules/.pnpm/path-expression-matcher@1.6.2/node_modules/path-expression-matcher/src/Matcher.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/html.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/xml.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/svg.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/sql.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/shell.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/redos.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/nosql.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/log.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/contexts/sql-strict.js","../../../node_modules/.pnpm/is-unsafe@2.0.0/node_modules/is-unsafe/src/index.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/node2json.js","../../../node_modules/.pnpm/fast-xml-parser@5.10.1/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js","../src/content-feed-parser.ts","../src/content-feed-source.ts","../src/content-feed-sources.ts","../src/content-types.ts","../src/content-query.ts","../src/safe-remote-url.ts","../src/contents.ts","../src/content-feed-sync.ts","../src/content-governance-assignment.ts","../src/content-governance-assignments.ts","../src/content-governance-policy.ts","../src/content-governance-policies.ts","../src/content-governance-profile.ts","../src/content-governance-profiles.ts","../src/content-review.ts","../src/content-reviews.ts","../src/content-version.ts","../src/content-versions.ts","../src/utils.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","import type { Asset } from '@happyvertical/smrt-assets';\n\n/**\n * Contract for objects that participate in the content/asset association\n * pattern.\n *\n * Any class that exposes asset-relationship methods (e.g. `Content` and its\n * STI subclasses) implements this interface explicitly so consumers can rely\n * on the methods existing instead of falling back to `typeof === 'function'`\n * duck-typing checks.\n *\n * @example\n * ```ts\n * import type { AssetAssociable } from '@happyvertical/smrt-content';\n *\n * async function attachThumbnail(\n * target: AssetAssociable,\n * image: Asset,\n * ): Promise<void> {\n * // No defensive runtime checks needed — the contract guarantees the method.\n * await target.addAsset(image, 'thumbnail', 0);\n * }\n * ```\n */\nexport interface AssetAssociable {\n /**\n * Get all assets associated with this object.\n *\n * @param relationship - Optional filter by relationship type\n * (e.g. `'thumbnail'`, `'attachment'`).\n * @returns Array of associated assets. Returns an empty array if the object\n * has not been persisted yet.\n */\n getAssets(relationship?: string): Promise<Asset[]>;\n\n /**\n * Associate an asset with this object via a typed relationship.\n *\n * @param asset - The asset to associate. Must be persisted (have an `id`).\n * @param relationship - Relationship type. Must match\n * `/^[a-zA-Z_][a-zA-Z0-9_]*$/`. Defaults to `'attachment'`.\n * @param sortOrder - Non-negative integer for display order.\n * @throws if either side is unsaved or the relationship/sort order is invalid.\n */\n addAsset(\n asset: Asset,\n relationship?: string,\n sortOrder?: number,\n ): Promise<void>;\n\n /**\n * Remove an associated asset.\n *\n * @param assetId - The asset ID to detach.\n * @param relationship - Optional specific relationship to remove. If omitted,\n * all relationships between this object and the asset are removed.\n */\n removeAsset(assetId: string, relationship?: string): Promise<void>;\n}\n\n/**\n * Contract for objects exposing typed access to a `metadata` JSON field.\n *\n * Use alongside an explicit interface (such as {@link AssetAssociable}) to\n * give consumers a stable contract for reading/writing the loose JSON bag,\n * without leaking the `metadata: Record<string, any>` type into call sites.\n */\nexport interface MetadataAccessor<\n TMetadata extends Record<string, unknown> = Record<string, unknown>,\n> {\n /**\n * Get the full metadata record. Always returns an object (never `null`).\n * The returned reference is the live object — callers should treat it as\n * read-only and use {@link MetadataAccessor.setMetadata} or\n * {@link MetadataAccessor.updateMetadata} to mutate it safely.\n */\n getMetadata(): TMetadata;\n\n /**\n * Replace the entire metadata record.\n *\n * @param metadata - The new metadata object. `null`/`undefined` clears it.\n */\n setMetadata(metadata: TMetadata | null | undefined): void;\n\n /**\n * Shallow-merge the supplied patch over the existing metadata.\n *\n * @param patch - Partial metadata. Keys present in the patch overwrite the\n * existing record; keys absent from the patch are preserved.\n * @returns The merged metadata record.\n */\n updateMetadata(patch: Partial<TMetadata>): TMetadata;\n}\n\n/**\n * Runtime type guard for {@link AssetAssociable}.\n *\n * The interface exists primarily so that statically-typed consumers can drop\n * defensive `typeof === 'function'` checks. This guard is for the rare cases\n * where a value enters the system as `unknown` (deserialised payload, plugin\n * input, etc.) and the caller needs to confirm shape before delegating.\n *\n * @example\n * ```ts\n * if (isAssetAssociable(input)) {\n * await input.addAsset(asset, 'attachment');\n * }\n * ```\n */\nexport function isAssetAssociable(value: unknown): value is AssetAssociable {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<AssetAssociable>;\n return (\n typeof candidate.getAssets === 'function' &&\n typeof candidate.addAsset === 'function' &&\n typeof candidate.removeAsset === 'function'\n );\n}\n\n/**\n * Runtime type guard for {@link MetadataAccessor}.\n *\n * Mirrors {@link isAssetAssociable} for the metadata-accessor contract.\n */\nexport function isMetadataAccessor(value: unknown): value is MetadataAccessor {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<MetadataAccessor>;\n return (\n typeof candidate.getMetadata === 'function' &&\n typeof candidate.setMetadata === 'function' &&\n typeof candidate.updateMetadata === 'function'\n );\n}\n\n/**\n * Returns `true` if `value` is a plain object (not an array, not `null`,\n * not a class instance with a custom prototype). Used by `Content`'s metadata\n * accessors to enforce the \"record-shaped\" contract — arrays and other\n * non-record objects are normalised to `{}` rather than silently leaked\n * through.\n *\n * @internal\n */\nexport function isPlainMetadataRecord(\n value: unknown,\n): value is Record<string, unknown> {\n if (!value || typeof value !== 'object') return false;\n if (Array.isArray(value)) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === null || proto === Object.prototype;\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface ContentAssetOptions extends SmrtObjectOptions {\n contentId?: string;\n assetId?: string;\n relationship?: string;\n sortOrder?: number;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_assets',\n conflictColumns: ['content_id', 'asset_id', 'relationship'],\n api: false,\n mcp: false,\n cli: false,\n})\nexport class ContentAsset extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('Content', { required: true })\n contentId = '';\n\n @crossPackageRef('@happyvertical/smrt-assets:Asset', { required: true })\n assetId = '';\n\n @field({ required: true })\n relationship = 'attachment';\n\n @field()\n sortOrder = 0;\n\n constructor(options: ContentAssetOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.assetId) this.assetId = options.assetId;\n if (options.relationship) this.relationship = options.relationship;\n if (options.sortOrder !== undefined) this.sortOrder = options.sortOrder;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n}\n","import type { SmrtCollectionOptions } from '@happyvertical/smrt-core';\nimport { SmrtJunction, smrt } from '@happyvertical/smrt-core';\nimport { ContentAsset } from './content-asset';\n\nexport interface ContentAssetCollectionOptions extends SmrtCollectionOptions {}\n\n@smrt({\n api: false,\n mcp: false,\n cli: false,\n})\nexport class ContentAssetCollection extends SmrtJunction<ContentAsset> {\n static readonly _itemClass = ContentAsset;\n protected leftField = 'contentId';\n protected rightField = 'assetId';\n}\n","import {\n definePrompt,\n type ResolvedPromptAI,\n} from '@happyvertical/smrt-prompts';\n\nexport const smrtContentReviewPrompt = definePrompt({\n key: 'smrtContent.review',\n template: `Content review request\n\nContent ID: {contentId}\nReview kind: {kind}\nPolicy key: {policyKey}\nTitle: {contentTitle}\nDescription: {contentDescription}\n\nBody:\n{contentBody}\n\n{reviewPrompt}`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport const smrtContentApplyCorrectionPrompt = definePrompt({\n key: 'smrtContent.applyCorrection',\n template: `You are revising an article draft to apply a factual correction.\n\nReturn only the fully revised body text, with no commentary.\n\nCurrent body:\n{body}\n\nCorrection summary:\n{summary}\n\nIncorrect text to fix:\n{incorrectText}\n\nCorrected text to incorporate:\n{correctedText}`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport const smrtContentThumbnailAIGeneratePrompt = definePrompt({\n key: 'smrtContent.thumbnail.aiGenerate',\n template: `Create a {style} thumbnail image for an article titled \"{title}\". {descriptionClause}Style: {styleHint}. The image should be suitable for a news article or blog post thumbnail.`,\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport function promptMessageOptions(ai: ResolvedPromptAI) {\n return {\n ...(ai.params || {}),\n ...(ai.model ? { model: ai.model } : {}),\n ...(typeof ai.temperature === 'number'\n ? { temperature: ai.temperature }\n : {}),\n ...(typeof ai.maxTokens === 'number' ? { maxTokens: ai.maxTokens } : {}),\n };\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface ContentReferenceOptions extends SmrtObjectOptions {\n sourceId?: string;\n targetId?: string;\n tenantId?: string | null;\n // ContentVersion.version pinned at citation time. Optional: references\n // created without a pin behave as before (they track the live target).\n // When set, callers can compare against the target's latest version to\n // surface drift between what was cited and what the target now says.\n targetVersion?: number | null;\n createdAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_references',\n conflictColumns: ['source_id', 'target_id'],\n})\nexport class ContentReference extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey('Content', { required: true })\n sourceId = '';\n\n @foreignKey('Content', { required: true })\n targetId = '';\n\n @field({ type: 'integer', nullable: true })\n targetVersion: number | null = null;\n\n @field()\n createdAt = new Date();\n\n constructor(options: ContentReferenceOptions = {}) {\n super(options);\n if (options.sourceId) this.sourceId = options.sourceId;\n if (options.targetId) this.targetId = options.targetId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.targetVersion !== undefined)\n this.targetVersion = options.targetVersion;\n if (options.createdAt) this.createdAt = options.createdAt;\n }\n}\n","import type {\n JunctionAttachOptions,\n SmrtCollectionOptions,\n} from '@happyvertical/smrt-core';\nimport { SmrtJunction, smrt } from '@happyvertical/smrt-core';\nimport { ContentReference } from './content-reference';\n\nexport interface ContentReferencesOptions extends SmrtCollectionOptions {}\n\n/**\n * The `attach()` override below restores find-or-create idempotency for\n * `(sourceId, targetId)` — duplicate calls return the existing row\n * unchanged, preserving `id` and `createdAt`. This matters because\n * `ContentReference` rows are externally addressable via\n * `/api/v1/contentreferences/[id]`.\n *\n * Two REST entry points (both auto-generated by the scanner):\n * - `POST /api/v1/contentreferences` (from model CRUD) calls\n * `collection.create()` which is upsert-based — id/createdAt get\n * rewritten on conflict. Convenient for callers that don't care\n * about row id stability.\n * - `POST /api/v1/contentreferences/attach` (from the override below)\n * is idempotent. Use this for stable URLs.\n *\n * Internal callers (`Content.addReference()`) always hit the idempotent\n * path because they call the collection directly.\n *\n * The `/attach` route exists because R2 round-7 added `@smrt()` to this\n * class, which made the scanner pick up the override as a custom\n * collection method route. Pre-R2 had a `/link` route from the\n * pre-rename method name.\n */\n// Decorator with empty config — only needed so the scanner detects the\n// class. See FactContentCollection for the full rationale.\n@smrt()\nexport class ContentReferences extends SmrtJunction<ContentReference> {\n static readonly _itemClass = ContentReference;\n protected leftField = 'sourceId';\n protected rightField = 'targetId';\n // content_references has no sort_order column — preserve insertion order\n // by sorting on created_at, and disable setLinks position auto-indexing\n // so it doesn't try to write integer indices into the timestamp column.\n protected sortField: string | null = 'createdAt';\n protected positionField: string | null = null;\n\n async getForSource(sourceId: string): Promise<ContentReference[]> {\n return (await this.list({\n where: { sourceId },\n orderBy: 'created_at ASC',\n })) as ContentReference[];\n }\n\n async getForTarget(targetId: string): Promise<ContentReference[]> {\n return (await this.list({\n where: { targetId },\n orderBy: 'created_at ASC',\n })) as ContentReference[];\n }\n\n /**\n * Find-or-create idempotency: if a reference already exists for\n * (sourceId, targetId), return the existing row unchanged instead of\n * upserting a new row. This preserves the existing row's `id` and\n * `createdAt`, which is important because reference rows are\n * externally addressable via `/api/v1/contentreferences/[id]`.\n *\n * The base `SmrtJunction.attach` flow (this.create → db.upsert) would\n * overwrite both columns on every duplicate call.\n *\n * Reference pinning (main): `opts.targetVersion` pins the citation to a\n * specific `ContentVersion.version` for drift detection. Re-attaching an\n * existing edge with a different `targetVersion` updates the pin in place;\n * `undefined` leaves an existing pin untouched, while a brand-new row\n * defaults the pin to `null` (unpinned).\n */\n async attach(\n sourceId: string,\n targetId: string,\n opts: JunctionAttachOptions = {},\n ): Promise<ContentReference> {\n const targetVersion = opts.targetVersion as number | null | undefined;\n const existing = (await this.get({\n sourceId,\n targetId,\n })) as ContentReference | null;\n if (existing) {\n if (\n targetVersion !== undefined &&\n existing.targetVersion !== targetVersion\n ) {\n existing.targetVersion = targetVersion;\n await existing.save();\n }\n return existing;\n }\n return super.attach(sourceId, targetId, {\n ...opts,\n targetVersion: targetVersion ?? null,\n });\n }\n\n async unlink(sourceId: string, targetId: string): Promise<void> {\n await this.detach(sourceId, targetId);\n }\n}\n","export function isMissingTableError(\n error: unknown,\n tableName: string,\n): boolean {\n const message = String(\n (error as Error)?.message || error || '',\n ).toLowerCase();\n\n return (\n message.includes(tableName.toLowerCase()) &&\n (message.includes('no such table') ||\n message.includes('does not exist') ||\n message.includes('relation'))\n );\n}\n\nexport function getQueryRows(result: unknown): Record<string, unknown>[] {\n return Array.isArray(result)\n ? (result as Record<string, unknown>[])\n : Array.isArray((result as { rows?: Record<string, unknown>[] })?.rows)\n ? ((result as { rows: Record<string, unknown>[] }).rows ?? [])\n : [];\n}\n","/**\n * Plain JSON shape produced by serializing a SMRT model instance. Values are\n * intentionally `unknown` — callers spread these records into API responses and\n * narrow individual fields where they need a concrete type.\n */\ntype SerializedRecord = Record<string, unknown>;\n\n/**\n * Minimal structural view of the model instances passed to the serializers.\n * Every model used here exposes `toJSON()` plus optional accessor methods for\n * its JSON-backed fields; the accessors are typed loosely because each model\n * returns a different concrete shape.\n */\ninterface SerializableModel {\n toJSON?: () => unknown;\n getMetadata?: () => unknown;\n getSnapshot?: () => unknown;\n getFindings?: () => unknown;\n getAllowedChannels?: () => unknown;\n getIntakeRules?: () => unknown;\n getPromotion?: () => unknown;\n getRevisions?: () => Promise<unknown[]> | unknown[];\n getAttachments?: () => Promise<unknown[]> | unknown[];\n getContributor?: () => Promise<unknown> | unknown;\n getReferences?: () => Promise<unknown[]> | unknown[];\n getAssets?: () => Promise<unknown[]> | unknown[];\n getReferenceDrift?: () => Promise<unknown> | unknown;\n metadata?: unknown;\n}\n\nfunction asModel(value: unknown): SerializableModel {\n return value && typeof value === 'object' ? (value as SerializableModel) : {};\n}\n\nfunction toJSON(value: unknown): SerializedRecord {\n const model = asModel(value);\n if (typeof model.toJSON === 'function') {\n const serialized = model.toJSON();\n return serialized && typeof serialized === 'object'\n ? (serialized as SerializedRecord)\n : {};\n }\n\n return value && typeof value === 'object' ? (value as SerializedRecord) : {};\n}\n\nexport function serializeFact(fact: unknown) {\n const model = asModel(fact);\n const data = toJSON(fact);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeFactLink(link: unknown) {\n const model = asModel(link);\n const data = toJSON(link);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentVersion(version: unknown) {\n const model = asModel(version);\n const data = toJSON(version);\n return {\n ...data,\n snapshot:\n typeof model.getSnapshot === 'function'\n ? model.getSnapshot()\n : data.snapshot || {},\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentReview(review: unknown) {\n const model = asModel(review);\n const data = toJSON(review);\n return {\n ...data,\n findings:\n typeof model.getFindings === 'function'\n ? model.getFindings()\n : data.findings || [],\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentCorrection(correction: unknown) {\n const model = asModel(correction);\n const data = toJSON(correction);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentContributor(contributor: unknown) {\n const model = asModel(contributor);\n const data = toJSON(contributor);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentContributionType(contributionType: unknown) {\n const model = asModel(contributionType);\n const data = toJSON(contributionType);\n return {\n ...data,\n allowedChannels:\n typeof model.getAllowedChannels === 'function'\n ? model.getAllowedChannels()\n : data.allowedChannels || [],\n intakeRules:\n typeof model.getIntakeRules === 'function'\n ? model.getIntakeRules()\n : data.intakeRules || {},\n promotion:\n typeof model.getPromotion === 'function'\n ? model.getPromotion()\n : data.promotion || {},\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentContributionRevision(revision: unknown) {\n const model = asModel(revision);\n const data = toJSON(revision);\n return {\n ...data,\n sourceMessageId: data.sourceMessageId || null,\n sourceThreadKey: data.sourceThreadKey || null,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentContributionAttachment(attachment: unknown) {\n const model = asModel(attachment);\n const data = toJSON(attachment);\n return {\n ...data,\n revisionId: data.revisionId || null,\n fileKey: data.fileKey || null,\n sourceUri: data.sourceUri || null,\n promotedAssetId: data.promotedAssetId || null,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport async function serializeContentContribution(contribution: unknown) {\n const model = asModel(contribution);\n const [revisions, attachments, contributor] = await Promise.all([\n typeof model.getRevisions === 'function' ? model.getRevisions() : [],\n typeof model.getAttachments === 'function' ? model.getAttachments() : [],\n typeof model.getContributor === 'function' ? model.getContributor() : null,\n ]);\n\n return {\n ...toJSON(contribution),\n contributor: contributor ? serializeContentContributor(contributor) : null,\n revisions: revisions.map(serializeContentContributionRevision),\n attachments: attachments.map(serializeContentContributionAttachment),\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : model.metadata || {},\n };\n}\n\nexport function serializeContentReviewProfileEvaluation(profile: unknown) {\n const data = toJSON(profile);\n return {\n ...data,\n requirements: Array.isArray(data.requirements) ? data.requirements : [],\n };\n}\n\nexport function serializeContentReviewPolicy(policy: unknown) {\n return {\n ...toJSON(policy),\n };\n}\n\nexport function serializeContentGovernanceProfile(profile: unknown) {\n const model = asModel(profile);\n const data = toJSON(profile);\n return {\n ...data,\n requirements: Array.isArray(data.requirements) ? data.requirements : [],\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentGovernanceAssignment(assignment: unknown) {\n const model = asModel(assignment);\n const data = toJSON(assignment);\n return {\n ...data,\n metadata:\n typeof model.getMetadata === 'function'\n ? model.getMetadata()\n : data.metadata || {},\n };\n}\n\nexport function serializeContentGovernanceState(state: unknown) {\n const data = toJSON(state);\n return {\n ...data,\n reviewPolicies: Array.isArray(data.reviewPolicies)\n ? data.reviewPolicies.map(serializeContentReviewPolicy)\n : [],\n availableProfiles: Array.isArray(data.availableProfiles)\n ? data.availableProfiles.map(serializeContentGovernanceProfile)\n : [],\n reviewProfiles: Array.isArray(data.reviewProfiles)\n ? data.reviewProfiles.map(serializeContentReviewProfileEvaluation)\n : [],\n };\n}\n\n/**\n * Reference-drift edge keyed by target content id. Mirrors the shape returned\n * by `Content.getReferenceDrift()`.\n */\ninterface ReferenceDriftEdge {\n citedVersion: number | null;\n currentVersion: number | null;\n isDrifted: boolean;\n}\n\nexport async function serializeContent(content: unknown) {\n const model = asModel(content);\n const [references, assets] = await Promise.all([\n typeof model.getReferences === 'function' ? model.getReferences() : [],\n typeof model.getAssets === 'function' ? model.getAssets() : [],\n ]);\n\n // Only resolve drift when there are references to drift against — list\n // endpoints serializing many ref-less items shouldn't pay the version\n // lookup cost.\n const drift =\n references.length > 0 && typeof model.getReferenceDrift === 'function'\n ? await model.getReferenceDrift()\n : [];\n\n const driftByTargetId = new Map<string, ReferenceDriftEdge>(\n Array.isArray(drift)\n ? drift\n .map((entry) => asModel(entry))\n .filter(\n (entry): entry is SerializableModel & { targetId: string } =>\n typeof (entry as { targetId?: unknown }).targetId === 'string',\n )\n .map((entry) => {\n const edge = entry as {\n targetId: string;\n citedVersion?: unknown;\n currentVersion?: unknown;\n isDrifted?: unknown;\n };\n return [\n edge.targetId,\n {\n citedVersion: (edge.citedVersion as number | null) ?? null,\n currentVersion: (edge.currentVersion as number | null) ?? null,\n isDrifted: Boolean(edge.isDrifted),\n },\n ] satisfies [string, ReferenceDriftEdge];\n })\n : [],\n );\n\n return {\n ...toJSON(content),\n referenceIds: references\n .map((reference) => asModel(reference) as { id?: unknown })\n .map((reference) => reference.id)\n .filter(Boolean),\n references: references.map((reference) => {\n const base = toJSON(reference);\n const edge =\n typeof base.id === 'string' ? driftByTargetId.get(base.id) : null;\n return edge\n ? {\n ...base,\n citedVersion: edge.citedVersion,\n currentVersion: edge.currentVersion,\n isDrifted: edge.isDrifted,\n }\n : base;\n }),\n assetIds: assets\n .map((asset) => (asModel(asset) as { id?: unknown }).id)\n .filter(Boolean),\n assets: assets.map((asset) => toJSON(asset)),\n };\n}\n","/**\n * Thumbnail Generator for Content\n *\n * Generates thumbnail images for content using various strategies:\n * - headline-card: Draws article title on branded background\n * - static-map: Uses static maps API for location-based content\n * - ai-generate: Uses AI image generation for creative thumbnails\n */\n\nimport type { AIClient, AIClientOptions } from '@happyvertical/ai';\nimport { fetchStaticMap, type StaticMapProvider } from '@happyvertical/geo';\nimport {\n generateHeadlineCard,\n type HeadlineCardTemplate,\n} from '@happyvertical/images';\nimport type { DatabaseConfig } from '@happyvertical/smrt-core';\nimport type { Image } from '@happyvertical/smrt-images';\nimport { ImageCollection } from '@happyvertical/smrt-images';\nimport {\n type ResolvedPrompt,\n resolvePrompt,\n} from '@happyvertical/smrt-prompts';\nimport type { Content } from './content';\nimport {\n promptMessageOptions,\n smrtContentThumbnailAIGeneratePrompt,\n} from './content-prompts';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Available thumbnail generation strategies\n */\nexport type ThumbnailStrategy = 'headline-card' | 'static-map' | 'ai-generate';\n\n/**\n * Base options for all strategies\n */\ninterface BaseThumbnailOptions {\n /**\n * Generation strategy\n */\n strategy: ThumbnailStrategy;\n\n /**\n * Width in pixels\n * @default 1200\n */\n width?: number;\n\n /**\n * Height in pixels\n * @default 630\n */\n height?: number;\n}\n\n/**\n * Options for headline card strategy\n */\nexport interface HeadlineCardThumbnailOptions extends BaseThumbnailOptions {\n strategy: 'headline-card';\n\n /**\n * Primary brand color (hex)\n * @default '#3b82f6'\n */\n brandColor?: string;\n\n /**\n * Background color (hex)\n * @default '#ffffff'\n */\n backgroundColor?: string;\n\n /**\n * Optional subtitle/category text\n */\n subtitle?: string;\n\n /**\n * Optional logo URL\n */\n logoUrl?: string;\n\n /**\n * Template style\n * @default 'default'\n */\n template?: HeadlineCardTemplate;\n}\n\n/**\n * Options for static map strategy\n */\nexport interface StaticMapThumbnailOptions extends BaseThumbnailOptions {\n strategy: 'static-map';\n\n /**\n * Map provider\n * @default 'mapbox'\n */\n mapProvider?: StaticMapProvider;\n\n /**\n * Zoom level (1-20)\n * @default 14\n */\n zoom?: number;\n\n /**\n * Marker color\n * @default 'e74c3c'\n */\n markerColor?: string;\n\n /**\n * Mapbox style (if using mapbox provider)\n */\n mapboxStyle?: string;\n\n /**\n * Google map type (if using google provider)\n */\n googleMapType?: 'roadmap' | 'satellite' | 'terrain' | 'hybrid';\n}\n\n/**\n * Options for AI generation strategy\n */\nexport interface AIGenerateThumbnailOptions extends BaseThumbnailOptions {\n strategy: 'ai-generate';\n\n /**\n * AI provider configuration\n */\n ai?: AIClientOptions | AIClient;\n\n /**\n * Custom prompt for image generation\n * If not provided, generates based on content title/body\n */\n prompt?: string;\n\n /**\n * Style hint for image generation\n * @default 'photorealistic'\n */\n style?: 'photorealistic' | 'illustration' | 'abstract' | 'minimal';\n}\n\n/**\n * Union type for all thumbnail options\n */\nexport type ThumbnailOptions =\n | HeadlineCardThumbnailOptions\n | StaticMapThumbnailOptions\n | AIGenerateThumbnailOptions;\n\n// ============================================================================\n// Generator Class\n// ============================================================================\n\n/**\n * Options for ThumbnailGenerator\n */\nexport interface ThumbnailGeneratorOptions {\n /**\n * Database configuration for storing generated images\n */\n db?: DatabaseConfig;\n\n /**\n * Alias for `db` — mirrors the `SmrtClassOptions.persistence` alias so\n * callers can pass the same options shape they use for SmrtObject/Collection.\n *\n * @deprecated Prefer `db`. Retained for parity with `SmrtClassOptions`.\n */\n persistence?: DatabaseConfig;\n\n /**\n * AI client configuration for AI-generated thumbnails\n */\n ai?: AIClientOptions | AIClient;\n}\n\ninterface ImageGenerationClient {\n generateImage(\n prompt: string,\n options?: Record<string, unknown>,\n ): Promise<{\n images?: Array<{ data?: Buffer | string }>;\n }>;\n}\n\nfunction isImageGenerationClient(\n value: AIClientOptions | AIClient,\n): value is AIClient & ImageGenerationClient {\n return (\n !!value &&\n typeof value === 'object' &&\n typeof (value as Record<string, unknown>).generateImage === 'function'\n );\n}\n\nfunction isAIClientOptions(\n value: AIClientOptions | AIClient,\n): value is AIClientOptions {\n return (\n !!value && typeof value === 'object' && !isImageGenerationClient(value)\n );\n}\n\n/**\n * ThumbnailGenerator creates thumbnails for content using various strategies\n */\nexport class ThumbnailGenerator {\n constructor(\n private content: Content,\n private options: ThumbnailGeneratorOptions = {},\n ) {\n // Normalize the `persistence` alias to `db` once so every downstream call\n // (prompt resolution, ImageCollection.create, save sites) sees the same\n // database regardless of which option name the caller used. Without this,\n // a caller passing `persistence: ...` would have prompt resolution honor\n // the alias while image saving silently used `undefined`.\n if (!this.options.db && this.options.persistence) {\n this.options.db = this.options.persistence;\n }\n }\n\n /**\n * Generate a thumbnail using the specified strategy\n */\n async generate(options: ThumbnailOptions): Promise<Image> {\n switch (options.strategy) {\n case 'headline-card':\n return this.generateHeadlineCard(options);\n case 'static-map':\n return this.generateStaticMap(options);\n case 'ai-generate':\n return this.generateWithAI(options);\n default:\n throw new Error(\n // `options` is narrowed to `never` here (exhaustive switch); read the\n // runtime discriminant through a minimal structural view.\n `Unknown thumbnail strategy: ${(options as { strategy: string }).strategy}`,\n );\n }\n }\n\n /**\n * Generate a headline card thumbnail\n */\n private async generateHeadlineCard(\n options: HeadlineCardThumbnailOptions,\n ): Promise<Image> {\n const title = this.content.title || this.content.name || 'Untitled';\n\n const result = await generateHeadlineCard(title, {\n width: options.width ?? 1200,\n height: options.height ?? 630,\n brandColor: options.brandColor,\n backgroundColor: options.backgroundColor,\n subtitle: options.subtitle ?? this.content.category ?? undefined,\n logoUrl: options.logoUrl,\n template: options.template,\n });\n\n return this.createImageFromBuffer(result.buffer, {\n width: result.width,\n height: result.height,\n mimeType: result.mimeType,\n name: `${this.content.id}-headline.png`,\n });\n }\n\n /**\n * Generate a static map thumbnail\n */\n private async generateStaticMap(\n options: StaticMapThumbnailOptions,\n ): Promise<Image> {\n // Coordinates live in the loose `metadata` bag (typed `unknown` values);\n // read them at a documented `string | number` boundary for arithmetic.\n const coordinateMetadata = this.content.metadata as Record<\n string,\n string | number | null | undefined\n >;\n const rawLatitude = coordinateMetadata?.latitude ?? coordinateMetadata?.lat;\n const rawLongitude =\n coordinateMetadata?.longitude ??\n coordinateMetadata?.lng ??\n coordinateMetadata?.lon;\n\n if (rawLatitude == null || rawLongitude == null) {\n throw new Error(\n 'Content metadata must contain latitude and longitude for static-map strategy',\n );\n }\n\n // Parse and validate coordinates\n // Use unary + for strict parsing (rejects \"45invalid\" unlike parseFloat)\n const latitude =\n typeof rawLatitude === 'string' ? +rawLatitude : rawLatitude;\n const longitude =\n typeof rawLongitude === 'string' ? +rawLongitude : rawLongitude;\n\n if (!Number.isFinite(latitude) || latitude < -90 || latitude > 90) {\n throw new Error(\n `Invalid latitude value \"${rawLatitude}\" in content metadata; expected a number between -90 and 90.`,\n );\n }\n\n if (!Number.isFinite(longitude) || longitude < -180 || longitude > 180) {\n throw new Error(\n `Invalid longitude value \"${rawLongitude}\" in content metadata; expected a number between -180 and 180.`,\n );\n }\n\n type FetchStaticMapOptions = NonNullable<\n Parameters<typeof fetchStaticMap>[2]\n >;\n const mapboxStyle = options.mapboxStyle as\n | FetchStaticMapOptions['mapboxStyle']\n | undefined;\n\n const result = await fetchStaticMap(latitude, longitude, {\n provider: options.mapProvider ?? 'mapbox',\n width: options.width ?? 1200,\n height: options.height ?? 630,\n zoom: options.zoom ?? 14,\n markerColor: options.markerColor,\n mapboxStyle,\n googleMapType: options.googleMapType,\n });\n\n return this.createImageFromBuffer(result.buffer, {\n width: result.width,\n height: result.height,\n mimeType: result.mimeType,\n name: `${this.content.id}-map.png`,\n });\n }\n\n /**\n * Generate a thumbnail using AI image generation\n */\n private async generateWithAI(\n options: AIGenerateThumbnailOptions,\n ): Promise<Image> {\n // Dynamic import to avoid requiring AI package when not using this strategy\n const { getAI } = await import('@happyvertical/ai');\n\n const aiInput = options.ai ?? this.options.ai;\n if (!aiInput) {\n throw new Error(\n 'AI configuration required for ai-generate strategy. Provide via options.ai or constructor options.',\n );\n }\n\n const ai = isImageGenerationClient(aiInput)\n ? aiInput\n : isAIClientOptions(aiInput)\n ? await getAI(aiInput)\n : (() => {\n throw new Error(\n 'AI client does not support image generation for ai-generate thumbnails.',\n );\n })();\n\n // Generate prompt if not provided. When the caller supplies a literal\n // prompt we skip prompt resolution entirely (no tenant override path).\n // When we resolve from the registry we also forward the resolved AI\n // options (model, params) so `editable: { model, params }` actually\n // takes effect for thumbnail generation.\n const width = options.width ?? 1200;\n const height = options.height ?? 630;\n let prompt: string;\n let aiOverrideOptions: Record<string, unknown> = {};\n if (options.prompt) {\n prompt = options.prompt;\n } else {\n const built = await this.buildAIPrompt(options.style ?? 'photorealistic');\n prompt = built.text;\n aiOverrideOptions = promptMessageOptions(built.ai);\n }\n\n const result = await ai.generateImage(prompt, {\n ...aiOverrideOptions,\n size: `${width}x${height}`,\n outputFormat: 'buffer',\n });\n\n if (!result.images || result.images.length === 0) {\n throw new Error('AI image generation returned no results');\n }\n\n // Handle buffer or base64 responses\n let buffer: Buffer;\n const imageData = result.images[0].data;\n if (Buffer.isBuffer(imageData)) {\n buffer = imageData;\n } else if (typeof imageData === 'string') {\n // Could be base64 or URL - try base64 first\n if (imageData.startsWith('http')) {\n const response = await fetch(imageData);\n if (!response.ok) {\n throw new Error(\n `AI image generation URL fetch failed: ${response.status} ${response.statusText}`,\n );\n }\n buffer = Buffer.from(await response.arrayBuffer());\n } else {\n buffer = Buffer.from(imageData, 'base64');\n }\n } else {\n throw new Error('AI image generation returned unexpected format');\n }\n\n return this.createImageFromBuffer(buffer, {\n width: options.width ?? 1200,\n height: options.height ?? 630,\n mimeType: 'image/png',\n name: `${this.content.id}-ai.png`,\n });\n }\n\n /**\n * Build a prompt for AI image generation based on content.\n *\n * Resolves via `@happyvertical/smrt-prompts` so tenants can override the\n * template/profile/model/params at runtime. Only non-PII content fields\n * (title, description) and the caller-supplied style hint are passed.\n * Internal IDs and the freeform `metadata` blob are intentionally excluded.\n *\n * Returns the full ResolvedPrompt (text + ai config) so the caller can\n * forward `model`/`params` overrides to `ai.generateImage()`. Returning\n * only the text would silently drop the editable model/params overrides.\n */\n private async buildAIPrompt(style: string): Promise<ResolvedPrompt> {\n const title = this.content.title || 'Untitled';\n const description = this.content.description || '';\n\n const stylePrompts: Record<string, string> = {\n photorealistic:\n 'photorealistic, high quality, professional photography, 8k resolution',\n illustration:\n 'digital illustration, clean vector art, modern design, vibrant colors',\n abstract:\n 'abstract art, geometric shapes, modern minimalist, artistic interpretation',\n minimal:\n 'minimalist design, simple shapes, clean composition, subtle colors',\n };\n\n const styleHint = stylePrompts[style] || stylePrompts.photorealistic;\n\n return resolvePrompt(smrtContentThumbnailAIGeneratePrompt.key, {\n db: this.options.db,\n tenantId: this.content.tenantId,\n variables: {\n style,\n title,\n styleHint,\n descriptionClause: description\n ? `The article is about: ${description}. `\n : '',\n },\n });\n }\n\n /**\n * Create an Image object from a buffer\n */\n private async createImageFromBuffer(\n buffer: Buffer,\n metadata: {\n width: number;\n height: number;\n mimeType: string;\n name: string;\n },\n ): Promise<Image> {\n const images = await ImageCollection.create({\n db: this.options.db,\n });\n\n // Create the image record. `SmrtCollection.create()` already persists\n // (upsert) the row, so a follow-up `image.save()` was redundant (#1387).\n const image = await images.create({\n name: metadata.name,\n mimeType: metadata.mimeType,\n width: metadata.width,\n height: metadata.height,\n sourceUri: `data:${metadata.mimeType};base64,${buffer.toString('base64')}`,\n });\n\n return image;\n }\n}\n","import { type Asset, AssetCollection } from '@happyvertical/smrt-assets';\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport type {\n Fact,\n FactClaimSupportAssessment,\n FactClaimSupportStatus,\n FactContent,\n FactContentRelationship,\n FactEvidence,\n FactEvidenceStatus,\n FactExtractionCandidate,\n FactSource,\n} from '@happyvertical/smrt-facts';\nimport type { Image } from '@happyvertical/smrt-images';\nimport { ImageCollection } from '@happyvertical/smrt-images';\nimport { resolvePrompt } from '@happyvertical/smrt-prompts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { AssetAssociable, MetadataAccessor } from './asset-associable';\nimport { isPlainMetadataRecord } from './asset-associable';\nimport type { ContentBodyFormat } from './body-format';\nimport { isContentBodyFormat } from './body-format';\nimport { ContentAssetCollection } from './content-assets';\nimport {\n buildContentGovernanceAssignmentKey,\n buildContentReviewPrompt,\n type ContentGovernanceState,\n type ContentReviewFinding,\n type ContentReviewProfileEvaluation,\n type CreateContentVersionOptions,\n getAcceptedContentReviewStatuses,\n getContentReviewKind,\n getContentReviewPolicy,\n getContentReviewProfileKeys,\n getContentReviewRequirements,\n type IssueContentCorrectionOptions,\n parseContentReviewResponse,\n type ResolvedContentGovernance,\n type RunContentReviewOptions,\n resolveConfiguredContentGovernance,\n resolveEffectiveContentGovernance,\n} from './content-governance';\nimport {\n promptMessageOptions,\n smrtContentApplyCorrectionPrompt,\n smrtContentReviewPrompt,\n} from './content-prompts';\nimport { ContentReferences } from './content-references';\nimport type { ContentReview } from './content-review';\nimport { normalizeContentTransparency } from './content-transparency';\nimport { isMissingTableError } from './database-utils';\nimport {\n serializeContent,\n serializeContentCorrection,\n serializeContentReview,\n serializeContentVersion,\n serializeFact,\n serializeFactLink,\n} from './serialization';\nimport type { ThumbnailOptions } from './thumbnail-generator';\nimport { ThumbnailGenerator } from './thumbnail-generator';\n\nconst USED_FACT_RELATIONSHIPS = new Set<FactContentRelationship>([\n 'supports',\n 'referenced_in',\n 'contradicts',\n]);\nconst FACT_AUDIT_GENERATED_BY = 'content.factAudit';\nconst FACT_AUDIT_DOMAIN = 'content-audit';\n\ntype FactAuditSourceMaterial = {\n sourceKind: string;\n sourceId: string;\n sourceUrl: string;\n sourceTitle: string;\n locator: string;\n text: string;\n};\n\ntype FactAuditSourceSelector = {\n sourceKind: string;\n sourceId: string;\n};\n\ntype FactAuditResourceRepairOptions = {\n sources?: FactAuditSourceSelector[];\n maxFactsPerSource?: number;\n context?: string;\n};\n\ntype FactAuditClaimRecheckOptions = {\n claimFactIds?: string[];\n sourceIds?: string[];\n sources?: FactAuditSourceSelector[];\n maxCandidateEvidence?: number;\n};\n\ntype FactEvidenceStatusUpdateOptions = {\n evidenceIds?: string[];\n status?: FactEvidenceStatus;\n reason?: string;\n};\n\ntype FactAuditClaim = {\n id: string | null;\n fact: Record<string, unknown>;\n supportStatus: FactClaimSupportStatus;\n claimQuote: string | null;\n rationale: string | null;\n confidence: number | null;\n relationship: string | null;\n linkMetadata: Record<string, unknown>;\n evidence: Record<string, unknown>[];\n matchedFacts: Array<{\n fact: Record<string, unknown>;\n evidence: Record<string, unknown>[];\n }>;\n};\n\ntype FactAuditResourceClaim = {\n id: string | null;\n fact: Record<string, unknown>;\n sourceKind: string | null;\n sourceId: string | null;\n sourceUrl: string | null;\n sourceTitle: string | null;\n locator: string | null;\n quote: string | null;\n status: FactEvidenceStatus;\n confidence: number | null;\n evidence: Record<string, unknown>[];\n};\n\n/**\n * Minimal structural view of a metadata-bearing SMRT record (fact link,\n * fact, evidence, source, version, review, correction). The fact-audit and\n * transparency code paths interact with these entities loosely via their\n * accessor methods rather than importing the concrete cross-package classes\n * (which would create circular dependencies). `getMetadata`/`setMetadata`\n * are optional because some paths receive plain serialized records.\n */\ninterface MetadataBearer {\n getMetadata?: () => Record<string, unknown>;\n setMetadata?: (metadata: Record<string, unknown>) => void;\n updateMetadata?: (patch: Record<string, unknown>) => unknown;\n metadata?: unknown;\n}\n\n/**\n * Structural view of a content↔fact link as consumed by the fact-audit\n * pipeline. Backed by `FactContent` from `@happyvertical/smrt-facts`.\n */\ninterface FactAuditLinkLike extends MetadataBearer {\n factId?: string | null;\n relationship?: string | null;\n save?: () => Promise<unknown>;\n delete?: () => Promise<unknown>;\n}\n\n/**\n * Structural view of a fact-evidence record as consumed by the fact-audit\n * pipeline. Backed by `FactEvidence` from `@happyvertical/smrt-facts`.\n */\ninterface FactAuditEvidenceLike extends MetadataBearer {\n id?: string | null;\n factId?: string | null;\n status?: string | null;\n sourceKind?: string | null;\n sourceId?: string | null;\n sourceUrl?: string | null;\n sourceTitle?: string | null;\n locator?: string | null;\n quote?: string | null;\n confidence?: number | null;\n evidenceKey?: string | null;\n tenantId?: string | null;\n delete?: () => Promise<unknown>;\n}\n\n/**\n * Structural view of a fact-source record as consumed by the fact-audit\n * pipeline. Backed by `FactSource` from `@happyvertical/smrt-facts`.\n */\ninterface FactAuditSourceLike extends MetadataBearer {\n id?: string | null;\n sourceType?: string | null;\n delete?: () => Promise<unknown>;\n}\n\n/**\n * Loosely-read extra fields the fact-audit source scanner probes on an\n * {@link Asset}. These are not declared `Asset` fields (assets vary by\n * provider); they are read defensively and normalized via `normalizeAuditText`.\n */\ninterface FactAuditAssetExtraFields {\n text?: unknown;\n body?: unknown;\n title?: unknown;\n filename?: unknown;\n url?: unknown;\n sourceUrl?: unknown;\n fileKey?: unknown;\n}\n\n/**\n * Structural view of a support candidate carried through claim assessment.\n * The `evidence` array holds the serialized evidence summaries built in\n * {@link Content.getCurrentFactAuditSupportCandidates}.\n */\ninterface FactAuditSupportCandidate {\n id: string;\n statement: string;\n evidence: Array<{ id?: string | null; [key: string]: unknown }>;\n}\n\n/**\n * Loose view of a serialized record produced by the `serialize*` helpers\n * (which return index-signature records that erase named keys at the type\n * level). Adds back the few keys the transparency snapshot reads while keeping\n * the rest as `unknown`.\n */\ninterface SerializedRecord {\n id?: string | null;\n status?: unknown;\n usedInArticle?: boolean;\n metadata?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Transient, non-persisted fields synchronized into junction links during\n * `save()`. They are attached dynamically from constructor options rather\n * than declared as ORM-managed fields, so callers narrow `this` to this\n * shape instead of reaching in untyped.\n */\ninterface ContentTransientLinkIds {\n referenceIds?: string[];\n assetIds?: string[];\n}\n\ntype FactAuditState = {\n counts: Record<FactClaimSupportStatus | 'total', number>;\n claims: FactAuditClaim[];\n resourceClaims: FactAuditResourceClaim[];\n warnings: string[];\n generatedBy: string;\n latestAuditRunId: string | null;\n};\n\nfunction normalizeFingerprintValue(value: unknown): unknown {\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => normalizeFingerprintValue(entry));\n }\n\n if (value && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, entryValue]) => [\n key,\n normalizeFingerprintValue(entryValue),\n ]),\n );\n }\n\n return value ?? null;\n}\n\nfunction hashFingerprint(input: string): string {\n let hash = 5381;\n\n for (let index = 0; index < input.length; index += 1) {\n hash = (hash * 33) ^ input.charCodeAt(index);\n }\n\n return `fp-${(hash >>> 0).toString(16).padStart(8, '0')}`;\n}\n\nfunction createFingerprint(value: unknown): string {\n return hashFingerprint(JSON.stringify(normalizeFingerprintValue(value)));\n}\n\nfunction normalizeAuditText(value: unknown): string {\n return String(value ?? '')\n .trim()\n .replace(/\\s+/g, ' ');\n}\n\n/**\n * Extract a human-readable message from an unknown caught value. Mirrors the\n * previous `error.message || error` template interpolation without relying on\n * an `any`-typed catch binding.\n */\nfunction errorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n if (\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n typeof (error as { message?: unknown }).message === 'string'\n ) {\n return (error as { message: string }).message;\n }\n return String(error);\n}\n\nfunction createFactAuditRunId(contentId: string): string {\n return `fact-audit-${hashFingerprint(\n `${contentId}:${new Date().toISOString()}:${Math.random()}`,\n )}`;\n}\n\nfunction parseAuditMetadata(value: unknown): Record<string, unknown> {\n if (!value) return {};\n if (typeof value === 'object') return value as Record<string, unknown>;\n try {\n return JSON.parse(String(value)) as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nfunction getLinkMetadata(link: MetadataBearer): Record<string, unknown> {\n return typeof link?.getMetadata === 'function' ? link.getMetadata() : {};\n}\n\nfunction getFactMetadata(fact: MetadataBearer): Record<string, unknown> {\n return typeof fact?.getMetadata === 'function'\n ? fact.getMetadata()\n : parseAuditMetadata(fact?.metadata);\n}\n\nfunction getGeneratedFactAuditMetadata(\n link: MetadataBearer,\n): Record<string, unknown> | null {\n const metadata = getLinkMetadata(link);\n if (metadata.generatedBy === FACT_AUDIT_GENERATED_BY) {\n return metadata;\n }\n\n const nested = metadata.factAudit;\n if (\n nested &&\n typeof nested === 'object' &&\n (nested as Record<string, unknown>).generatedBy === FACT_AUDIT_GENERATED_BY\n ) {\n return nested as Record<string, unknown>;\n }\n\n return null;\n}\n\nfunction getEvidenceMetadata(\n evidence: MetadataBearer,\n): Record<string, unknown> {\n return typeof evidence?.getMetadata === 'function'\n ? evidence.getMetadata()\n : parseAuditMetadata(evidence?.metadata);\n}\n\nfunction isGeneratedFactAuditEvidence(\n evidence: MetadataBearer,\n contentId: string,\n): boolean {\n const metadata = getEvidenceMetadata(evidence);\n return (\n metadata.generatedBy === FACT_AUDIT_GENERATED_BY &&\n metadata.contentId === contentId\n );\n}\n\nfunction isGeneratedArticleClaimFact(\n fact: MetadataBearer,\n contentId: string,\n): boolean {\n const metadata = getFactMetadata(fact);\n if (metadata.generatedBy !== FACT_AUDIT_GENERATED_BY) {\n return false;\n }\n\n const role = metadata.auditFactRole || metadata.factAuditRole;\n const isArticleClaim =\n role === 'article-claim' || metadata.claimOnly === true;\n\n return isArticleClaim && metadata.contentId === contentId;\n}\n\nfunction normalizeFactEvidenceStatus(\n value: unknown,\n): FactEvidenceStatus | null {\n const allowed: FactEvidenceStatus[] = [\n 'supports',\n 'contradicts',\n 'unclear',\n 'irrelevant',\n 'invalid',\n ];\n\n return allowed.includes(value as FactEvidenceStatus)\n ? (value as FactEvidenceStatus)\n : null;\n}\n\nfunction sourceMatchesSelector(\n source: FactAuditSourceMaterial,\n selector: FactAuditSourceSelector,\n): boolean {\n return (\n source.sourceKind === selector.sourceKind &&\n source.sourceId === selector.sourceId\n );\n}\n\nfunction filterAuditSources(\n sources: FactAuditSourceMaterial[],\n selectors: FactAuditSourceSelector[] | undefined,\n): FactAuditSourceMaterial[] {\n if (!selectors || selectors.length === 0) {\n return sources;\n }\n\n return sources.filter((source) =>\n selectors.some((selector) => sourceMatchesSelector(source, selector)),\n );\n}\n\nfunction getContentText(content: Content): string {\n return [content.title, content.description, content.body]\n .map(normalizeAuditText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\nfunction readNestedString(\n source: Record<string, unknown>,\n path: string[],\n): string | null {\n let current: unknown = source;\n for (const key of path) {\n if (!current || typeof current !== 'object') {\n return null;\n }\n current = (current as Record<string, unknown>)[key];\n }\n return typeof current === 'string' && current ? current : null;\n}\n\n/**\n * Coerce an unknown value into a plain record. Objects pass through; JSON\n * strings are parsed (falling back to `{}` on failure); anything else yields\n * an empty record. Used to read loosely-typed `metadata` fields that may be\n * stored either as parsed objects or JSON strings.\n */\nfunction asRecord(value: unknown): Record<string, unknown> {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n if (typeof value === 'string') {\n try {\n const parsed = JSON.parse(value);\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n }\n return {};\n}\n\n/**\n * Walk a nested record path, returning the record at the end of the path or\n * an empty record if any segment is missing/non-record-shaped.\n */\nfunction readNestedRecord(\n source: Record<string, unknown>,\n path: string[],\n): Record<string, unknown> {\n let current: unknown = source;\n for (const key of path) {\n if (!current || typeof current !== 'object') {\n return {};\n }\n current = (current as Record<string, unknown>)[key];\n }\n return asRecord(current);\n}\n\nfunction getPublicPrompt(metadata: Record<string, unknown>): string | null {\n return (\n readNestedString(metadata, [\n 'transparency',\n 'generation',\n 'publicPrompt',\n ]) ||\n readNestedString(metadata, ['generation', 'publicPrompt']) ||\n readNestedString(metadata, ['publicPrompt']) ||\n null\n );\n}\n\n/**\n * Options for Content initialization\n */\nexport interface ContentOptions extends SmrtObjectOptions {\n /**\n * Content type classification\n */\n type?: string | null;\n\n /**\n * Content variant for namespaced classification within types\n * Format: generator:domain:specific-type\n * Example: \"praeco:meeting:upcoming\"\n */\n variant?: string | null;\n\n /**\n * Reference to file storage key\n */\n fileKey?: string | null;\n\n /**\n * Author of the content\n */\n author?: string | null;\n\n /**\n * Content title\n */\n title?: string | null;\n\n /**\n * Short description or summary\n */\n description?: string | null;\n\n /**\n * Main content body text\n */\n body?: string | null;\n\n /**\n * Stored body format.\n */\n bodyFormat?: ContentBodyFormat | null;\n\n /**\n * Date when content was published\n */\n publish_date?: Date | null;\n\n /**\n * URL source of the content\n */\n url?: string | null;\n\n /**\n * Original source identifier\n */\n source?: string | null;\n\n /**\n * Publication status\n */\n status?: 'published' | 'draft' | 'review' | 'archived' | 'deleted' | null;\n\n /**\n * Content state flag\n */\n state?: 'deprecated' | 'active' | 'highlighted' | null;\n\n /**\n * Original URL of the content\n */\n original_url?: string | null;\n\n /**\n * Content language\n */\n language?: string | null;\n\n /**\n * Content tags\n */\n tags?: string[];\n\n /**\n * Hierarchical category path for URL routing\n * Format: 'parent/child' (e.g., 'politics/local')\n * Each content belongs to exactly ONE category\n */\n category?: string | null;\n\n /**\n * Additional metadata\n */\n metadata?: Record<string, unknown>;\n\n /**\n * ID of the thumbnail asset for this content\n */\n thumbnailAssetId?: string | null;\n\n /**\n * Transient reference IDs used by editors and API payloads.\n * These are synchronized into ContentReference links during save.\n */\n referenceIds?: string[];\n\n /**\n * Transient asset IDs used by editors and API payloads.\n */\n assetIds?: string[];\n\n /**\n * Tenant ID for multi-tenant isolation\n */\n tenantId?: string | null;\n}\n\n/**\n * Structured content object with metadata and body text\n *\n * Content represents any text-based content with metadata such as\n * title, author, description, and publishing information. It supports\n * referencing related content objects.\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: {\n include: [\n 'list',\n 'get',\n 'create',\n 'update',\n 'delete',\n 'getFactsState',\n 'syncFactsState',\n 'getFactAuditStateAction',\n 'repairFactAuditAction',\n 'repairFactEvidenceAction',\n 'recheckFactClaimsAction',\n 'updateFactEvidenceStatusAction',\n 'getGovernanceStateAction',\n 'listReviews',\n 'runReviewAction',\n 'listReviewProfilesAction',\n 'evaluateReviewProfileAction',\n 'getPublishedTransparencyAction',\n 'previewTransparencyAction',\n 'listCorrections',\n 'issueCorrectionAction',\n 'listVersions',\n 'mutateVersionAction',\n ],\n routes: {\n getFactsState: { method: 'GET', path: 'facts' },\n syncFactsState: { method: 'PUT', path: 'facts' },\n getFactAuditStateAction: { method: 'GET', path: 'fact-audit' },\n repairFactAuditAction: { method: 'POST', path: 'fact-audit/repair' },\n repairFactEvidenceAction: {\n method: 'POST',\n path: 'fact-audit/evidence/repair',\n },\n recheckFactClaimsAction: {\n method: 'POST',\n path: 'fact-audit/claims/recheck',\n },\n updateFactEvidenceStatusAction: {\n method: 'PUT',\n path: 'fact-audit/evidence/status',\n },\n getGovernanceStateAction: { method: 'GET', path: 'governance' },\n listReviews: { method: 'GET', path: 'reviews' },\n runReviewAction: { method: 'POST', path: 'reviews' },\n listReviewProfilesAction: { method: 'GET', path: 'review-profiles' },\n evaluateReviewProfileAction: {\n method: 'GET',\n path: 'review-profiles/[profileKey]',\n },\n getPublishedTransparencyAction: {\n method: 'GET',\n path: 'transparency',\n },\n previewTransparencyAction: {\n method: 'GET',\n path: 'transparency/preview',\n },\n listCorrections: { method: 'GET', path: 'corrections' },\n issueCorrectionAction: { method: 'POST', path: 'corrections' },\n listVersions: { method: 'GET', path: 'versions' },\n mutateVersionAction: { method: 'POST', path: 'versions' },\n },\n serializers: {\n item: {\n importPath: '$lib/server/content-api-serializers',\n exportName: 'serializeContent',\n },\n },\n },\n mcp: {\n include: ['list', 'get', 'create', 'update'], // AI tools for content management\n },\n cli: true, // Enable CLI commands for content management\n // Content's own list pages sort by publish date inside a tenant, not by\n // `created_at`, so the generated `(tenant_id, created_at)` ordering index\n // (#2363) does not serve them — this is the second access path on the same\n // table and it has to be declared (#2357, measured in #2340). Declared\n // indexes are appended before the automatic passes, so this one also stands\n // in for the standalone `contents_tenant_id_idx` (#2359): a btree serves\n // every prefix of its column list.\n indexes: [\n {\n name: 'contents_tenant_id_publish_date_idx',\n columns: ['tenantId', 'publish_date'],\n },\n ],\n})\nexport class Content\n extends SmrtObject\n implements AssetAssociable, MetadataAccessor<Record<string, unknown>>\n{\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global content\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Array of referenced content objects\n */\n protected references: Content[] = [];\n\n /**\n * Content type classification\n */\n public type: string | null = null;\n\n /**\n * Content variant for namespaced classification within types\n * Format: generator:domain:specific-type\n * Example: \"praeco:meeting:upcoming\"\n */\n public variant: string | null = null;\n\n /**\n * Reference to file storage key\n */\n public fileKey: string | null = null;\n\n /**\n * Author of the content\n */\n public author: string | null = null;\n\n /**\n * Human-readable name for SMRT framework compatibility\n */\n @field({ required: true })\n public name: string = '';\n\n /**\n * Content title\n */\n public title = '';\n\n /**\n * Short description or summary\n */\n public description: string | null = null;\n\n /**\n * Main content body text\n */\n public body = '';\n\n /**\n * Format used to persist the body field.\n */\n public bodyFormat: ContentBodyFormat | null = null;\n\n /**\n * Date when content was published\n */\n public publish_date: Date | null = null;\n\n /**\n * URL source of the content\n */\n public url: string | null = null;\n\n /**\n * Original source identifier\n */\n public source: string | null = null;\n\n /**\n * Original URL of the content\n */\n public original_url: string | null = null;\n\n /**\n * Content language\n */\n public language: string | null = null;\n\n /**\n * Content tags\n */\n public tags: string[] = [];\n\n /**\n * Hierarchical category path for URL routing\n * Format: 'parent/child' (e.g., 'politics/local')\n * Each content belongs to exactly ONE category\n */\n public category: string | null = null;\n\n /**\n * Publication status\n */\n public status: 'published' | 'draft' | 'review' | 'archived' | 'deleted' =\n 'draft';\n\n /**\n * Content state flag\n */\n public state: 'deprecated' | 'active' | 'highlighted' = 'active';\n\n /**\n * Additional JSON metadata for flexible schema extension\n */\n public metadata: Record<string, unknown> = {};\n\n /**\n * ID of the thumbnail asset for this content\n */\n @crossPackageRef('@happyvertical/smrt-assets:Asset')\n public thumbnailAssetId: string | null = null;\n\n /**\n * Creates a new Content instance\n */\n constructor(options: ContentOptions = {}) {\n super(options);\n this.type = options.type || null;\n this.variant = options.variant || null;\n this.fileKey = options.fileKey || null;\n this.author = options.author || null;\n if (options.name) this.name = options.name;\n this.title = options.title || '';\n this.description = options.description || null;\n this.body = options.body || '';\n this.bodyFormat = isContentBodyFormat(options.bodyFormat)\n ? options.bodyFormat\n : null;\n this.publish_date = options.publish_date || null;\n this.source = options.source || null;\n this.original_url = options.original_url || null;\n this.language = options.language || null;\n this.status = options.status || 'draft';\n this.tags = options.tags || [];\n this.category = options.category || null;\n this.state = options.state || 'active';\n this.metadata = options.metadata || {};\n this.thumbnailAssetId = options.thumbnailAssetId ?? null;\n const transient = this as Content & ContentTransientLinkIds;\n if (Array.isArray(options.referenceIds)) {\n transient.referenceIds = [...options.referenceIds];\n }\n if (Array.isArray(options.assetIds)) {\n transient.assetIds = [...options.assetIds];\n }\n }\n\n /**\n * Initializes this content object\n *\n * @returns Promise that resolves to this instance\n */\n async initialize(): Promise<this> {\n await super.initialize();\n return this;\n }\n\n protected override async validateBeforeSave(): Promise<void> {\n if (!this.name && this.title) {\n this.name = this.title;\n }\n\n if (!this.title && this.name) {\n this.title = this.name;\n }\n\n await super.validateBeforeSave();\n\n if (this.status !== 'published') {\n return;\n }\n\n const governance = await this.resolvePublicationGovernance();\n const profileKey = governance?.publicationProfileKey;\n\n if (\n !governance?.isGoverned ||\n !governance.enforcePublishReadiness ||\n !profileKey\n ) {\n return;\n }\n\n const evaluation = await this.evaluateReviewProfile(profileKey);\n const blockingRequirements = evaluation.requirements.filter(\n (requirement) => requirement.blocking && !requirement.satisfied,\n );\n\n if (blockingRequirements.length === 0) {\n return;\n }\n\n const details = blockingRequirements.map((requirement) => {\n if (requirement.missing) {\n return `${requirement.label} has not been run yet`;\n }\n\n if (requirement.stale) {\n return `${requirement.label} is stale and must be rerun`;\n }\n\n if (requirement.latestStatus) {\n return `${requirement.label} returned ${requirement.latestStatus}`;\n }\n\n return `${requirement.label} is not satisfied`;\n });\n\n throw new ValidationError(\n `Cannot publish content until the \"${profileKey}\" review profile is satisfied. ${details.join('; ')}`,\n 'VALIDATION_PUBLISH_READINESS',\n {\n profileKey,\n blockingRequirements: blockingRequirements.map((requirement) => ({\n policyKey: requirement.policyKey,\n label: requirement.label,\n missing: requirement.missing,\n stale: requirement.stale,\n latestStatus: requirement.latestStatus,\n })),\n },\n );\n }\n\n override async save() {\n const shouldConsiderPublicationSnapshot = this.status === 'published';\n\n let governance: ResolvedContentGovernance | null = null;\n let previous: Content | null = null;\n let previousPublicationFingerprint: string | null = null;\n\n if (shouldConsiderPublicationSnapshot) {\n governance = await this.resolvePublicationGovernance();\n\n if (governance?.isGoverned && governance.transparencyEnabled) {\n previous = await this.getPersistedContent();\n previousPublicationFingerprint =\n await this.getLatestPublicationSnapshotFingerprint();\n }\n }\n\n await super.save();\n await this.syncPendingReferenceIds();\n await this.syncPendingAssetIds();\n\n if (\n !shouldConsiderPublicationSnapshot ||\n !governance?.isGoverned ||\n !governance.transparencyEnabled\n ) {\n return this;\n }\n\n const nextPublicationFingerprint =\n await this.buildPublicationSnapshotFingerprint(governance);\n\n if (\n nextPublicationFingerprint &&\n nextPublicationFingerprint !== previousPublicationFingerprint\n ) {\n await this.createVersion({\n kind: 'publication',\n summary:\n previous?.status === 'published'\n ? 'Published content updated.'\n : 'Content published.',\n metadata: {\n publicationSnapshotFingerprint: nextPublicationFingerprint,\n publicationProfileKey: governance.publicationProfileKey,\n transparency: await this.buildTransparencySnapshot({\n snapshotKind: 'published',\n governance,\n }),\n },\n });\n }\n\n return this;\n }\n\n private async getReferenceCollection() {\n return ContentReferences.create({ db: this.db });\n }\n\n private async getFactCollection() {\n const { FactCollection } = await import('@happyvertical/smrt-facts');\n return FactCollection.create(this.options);\n }\n\n private async getFactContentCollection() {\n const { FactContentCollection } = await import('@happyvertical/smrt-facts');\n return FactContentCollection.create(this.options);\n }\n\n private async getFactSourceCollection() {\n const { FactSourceCollection } = await import('@happyvertical/smrt-facts');\n return FactSourceCollection.create(this.options);\n }\n\n private async getFactEvidenceCollection() {\n const { FactEvidenceCollection } = await import(\n '@happyvertical/smrt-facts'\n );\n return FactEvidenceCollection.create(this.options);\n }\n\n private async getContentVersionCollection() {\n const { ContentVersionCollection } = await import('./content-versions');\n return ContentVersionCollection.create(this.options);\n }\n\n private async getContentReviewCollection() {\n const { ContentReviewCollection } = await import('./content-reviews');\n return ContentReviewCollection.create(this.options);\n }\n\n private async getContentCorrectionCollection() {\n const { ContentCorrectionCollection } = await import(\n './content-corrections'\n );\n return ContentCorrectionCollection.create(this.options);\n }\n\n private async getContentsCollection() {\n const { Contents } = await import('./contents');\n return Contents.create({ db: this.db });\n }\n\n private getConfiguredGovernance(): ResolvedContentGovernance {\n return resolveConfiguredContentGovernance({\n contentType: this.type,\n contentVariant: this.variant,\n });\n }\n\n public async resolveGovernance(): Promise<ResolvedContentGovernance> {\n return resolveEffectiveContentGovernance({\n contentType: this.type,\n contentVariant: this.variant,\n db: this.db,\n tenantId: this.tenantId ?? null,\n });\n }\n\n private async hasPersistedGovernanceAssignments(): Promise<boolean> {\n if (!this.db || typeof this.db.query !== 'function') {\n return false;\n }\n\n try {\n const exactKey = buildContentGovernanceAssignmentKey(\n this.type || '',\n this.variant || '',\n );\n const typeOnlyKey = buildContentGovernanceAssignmentKey(this.type || '');\n const keys =\n exactKey === typeOnlyKey ? [exactKey] : [exactKey, typeOnlyKey];\n const placeholders = keys.map(() => '?').join(', ');\n const result = await this.db.query(\n `SELECT 1 AS matched FROM content_governance_assignments WHERE key IN (${placeholders}) LIMIT 1`,\n keys,\n );\n const rows = Array.isArray(result) ? result : (result?.rows ?? []);\n return rows.length > 0;\n } catch {\n return false;\n }\n }\n\n private async resolvePublicationGovernance(): Promise<ResolvedContentGovernance | null> {\n const configuredGovernance = this.getConfiguredGovernance();\n\n if (configuredGovernance.isGoverned) {\n return this.resolveGovernance();\n }\n\n if (!(await this.hasPersistedGovernanceAssignments())) {\n return null;\n }\n\n const governance = await this.resolveGovernance();\n return governance.isGoverned ? governance : null;\n }\n\n private async requireGovernance(\n feature = 'governance workflow',\n ): Promise<ResolvedContentGovernance> {\n const governance = await this.resolveGovernance();\n\n if (!governance.isGoverned) {\n throw new Error(\n `Governance is not enabled for content type \"${this.type || 'content'}\"${this.variant ? ` variant \"${this.variant}\"` : ''}, so ${feature} is unavailable.`,\n );\n }\n\n return governance;\n }\n\n private async requireFactLinking(\n feature = 'fact linking',\n ): Promise<ResolvedContentGovernance> {\n const governance = await this.requireGovernance(feature);\n\n if (!governance.factLinkingEnabled) {\n throw new Error(\n `Fact linking is not enabled for content type \"${this.type || 'content'}\"${this.variant ? ` variant \"${this.variant}\"` : ''}.`,\n );\n }\n\n return governance;\n }\n\n private async getPersistedContent(): Promise<Content | null> {\n if (!this.id) {\n return null;\n }\n\n const contents = await this.getContentsCollection();\n return (await contents.get({ id: this.id as string })) as Content | null;\n }\n\n private async buildReviewFingerprint(policyKey: string): Promise<string> {\n const governance = await this.resolveGovernance();\n const kind = getContentReviewKind(policyKey, governance.reviewPolicies);\n const policy = getContentReviewPolicy(policyKey, governance.reviewPolicies);\n const [references, facts, factLinks] = await Promise.all([\n this.getReferences(),\n kind === 'facts' && governance.factLinkingEnabled\n ? this.getFacts({\n latestOnly: true,\n includeSuperseded: false,\n })\n : Promise.resolve([]),\n kind === 'facts' && governance.factLinkingEnabled\n ? this.getFactLinks()\n : Promise.resolve([]),\n ]);\n\n return createFingerprint({\n scope: 'content-review',\n policyKey,\n kind,\n policyInstructions: policy?.instructions || '',\n content: {\n id: this.id || null,\n type: this.type,\n variant: this.variant,\n title: this.title,\n description: this.description,\n body: this.body,\n author: this.author,\n state: this.state,\n publishDate: this.publish_date,\n language: this.language,\n category: this.category,\n tags: this.tags,\n metadata: this.metadata,\n },\n referenceIds: references\n .map((reference) => reference.id)\n .filter(Boolean)\n .sort(),\n facts: facts.map((fact) => ({\n id: fact.id || null,\n // Pre-R3-C this was `parentId`; renamed to `previousFactId` in\n // smrt-facts. Existing cached fingerprints will invalidate, which\n // is the correct behaviour — the review surface (a key in the\n // hash) changed.\n previousFactId: fact.previousFactId || null,\n status: fact.status || null,\n textRefined: fact.textRefined || '',\n sourceCount: fact.sourceCount ?? 0,\n confidence: fact.confidence ?? null,\n metadata:\n typeof fact?.getMetadata === 'function' ? fact.getMetadata() : {},\n })),\n factLinks: factLinks.map((link) => ({\n factId: link.factId || null,\n relationship: link.relationship || null,\n metadata:\n typeof link?.getMetadata === 'function' ? link.getMetadata() : {},\n })),\n });\n }\n\n private async buildTransparencySnapshot(\n options: {\n snapshotKind?: 'preview' | 'published';\n governance?: ResolvedContentGovernance;\n } = {},\n ) {\n const snapshotKind = options.snapshotKind || 'preview';\n const governance = options.governance || (await this.resolveGovernance());\n\n if (!governance.isGoverned || !governance.transparencyEnabled) {\n return null;\n }\n\n const [\n references,\n facts,\n factLinks,\n reviews,\n corrections,\n versions,\n reviewProfiles,\n ] = await Promise.all([\n this.getReferences(),\n governance.factLinkingEnabled\n ? this.getFacts({\n latestOnly: true,\n includeSuperseded: false,\n })\n : Promise.resolve([]),\n governance.factLinkingEnabled ? this.getFactLinks() : Promise.resolve([]),\n this.listReviews(),\n this.listCorrections(),\n this.listVersions(),\n this.listReviewProfilesAction(),\n ]);\n\n const factSources = await this.getFactSourceCollection();\n const factSourcesByFactId = new Map<string, FactSource[]>();\n\n for (const fact of facts) {\n const factId = fact.id as string | undefined;\n if (!factId) {\n continue;\n }\n\n const sources = await factSources.getForFact(factId);\n factSourcesByFactId.set(factId, sources);\n }\n\n const usedFactIds = new Set(\n factLinks\n .filter((link) =>\n USED_FACT_RELATIONSHIPS.has(\n (link.relationship || 'related') as FactContentRelationship,\n ),\n )\n .map((link) => link.factId)\n .filter(Boolean),\n );\n\n const linkedFacts = facts.map((fact) => {\n const factId = fact.id as string | undefined;\n const link = factLinks.find((entry) => entry.factId === factId);\n const sources = (factId ? factSourcesByFactId.get(factId) : []) || [];\n\n return {\n ...serializeFact(fact),\n relationship: link?.relationship || null,\n linkMetadata:\n typeof link?.getMetadata === 'function' ? link.getMetadata() : {},\n usedInArticle: factId ? usedFactIds.has(factId) : false,\n sources: sources.map((source) => ({\n id: source.id || null,\n sourceType: source.sourceType || null,\n sourceUrl: source.sourceUrl || null,\n sourceTitle: source.sourceTitle || null,\n credibility: source.credibility ?? null,\n extractedAt: source.extractedAt || null,\n metadata:\n typeof source?.getMetadata === 'function'\n ? source.getMetadata()\n : {},\n })),\n };\n });\n\n const referenceGroups = await Promise.all(\n references.map(async (reference) => {\n const sourceUrls = [\n reference.url,\n reference.original_url,\n reference.source,\n ].filter(Boolean) as string[];\n\n const extractedFacts = new Map<string, Fact>();\n for (const sourceUrl of sourceUrls) {\n const matches = await factSources.list({\n where: { sourceUrl },\n orderBy: 'created_at ASC',\n });\n\n for (const match of matches) {\n if (!match.factId || extractedFacts.has(match.factId)) {\n continue;\n }\n\n const fact = await match.getFact();\n if (fact?.id) {\n extractedFacts.set(fact.id as string, fact);\n }\n }\n }\n\n const extractedFactRecords: SerializedRecord[] = [\n ...extractedFacts.values(),\n ].map((fact) => {\n const factId = fact.id as string | undefined;\n return {\n ...serializeFact(fact),\n usedInArticle: factId ? usedFactIds.has(factId) : false,\n };\n });\n\n return {\n id: reference.id || null,\n title: reference.title || reference.name || reference.url || null,\n url: reference.url || null,\n originalUrl: reference.original_url || null,\n type: reference.type || null,\n source: reference.source || null,\n usedFactIds: extractedFactRecords\n .filter((fact) => fact.id && usedFactIds.has(fact.id))\n .map((fact) => fact.id),\n extractedFacts: extractedFactRecords,\n };\n }),\n );\n\n const publicGeneration = readNestedRecord(this.metadata, [\n 'transparency',\n 'generation',\n ]);\n const generationMetadata = readNestedRecord(this.metadata, ['generation']);\n const serializedCorrections = (corrections as SerializedRecord[])\n .filter((correction) => correction.status === 'published')\n .map((correction) => {\n // `corrections` is already serialized to plain records, so the\n // metadata is the parsed object on `correction.metadata`.\n const correctionMetadata = asRecord(correction.metadata);\n\n return {\n ...serializeContentCorrection(correction),\n provenance: {\n autoGeneratedDraft: Boolean(correctionMetadata.autoGeneratedDraft),\n draftVersionId: correctionMetadata.draftVersionId || null,\n draftVersionNumber: correctionMetadata.draftVersionNumber || null,\n sourceCorrectionVersionId:\n correctionMetadata.sourceCorrectionVersionId || null,\n sourceCorrectionVersionNumber:\n correctionMetadata.sourceCorrectionVersionNumber || null,\n },\n };\n });\n const serializedVersionHistory = (versions as SerializedRecord[]).map(\n (version) => {\n // `versions` is already serialized to plain records.\n const versionMetadata = asRecord(version.metadata);\n\n return {\n id: version.id || null,\n version: version.version ?? null,\n kind: version.kind || null,\n summary: version.summary || '',\n createdAt: version.createdAt || null,\n provenance: {\n policyKey: versionMetadata.policyKey || null,\n reviewFingerprint:\n versionMetadata.reviewFingerprint ||\n versionMetadata.contentFingerprint ||\n null,\n factId: versionMetadata.factId || null,\n replacementFactId: versionMetadata.replacementFactId || null,\n sourceCorrectionVersionId:\n versionMetadata.sourceCorrectionVersionId || null,\n sourceCorrectionVersionNumber:\n versionMetadata.sourceCorrectionVersionNumber || null,\n correctionDraft: versionMetadata.correctionDraft || null,\n publicationSnapshotFingerprint:\n versionMetadata.publicationSnapshotFingerprint || null,\n },\n };\n },\n );\n\n return normalizeContentTransparency(\n {\n generatedAt: new Date().toISOString(),\n snapshotKind,\n contentId: (this.id as string) || null,\n currentContentStatus: this.status || null,\n publicationProfileKey: governance.publicationProfileKey || undefined,\n generation: {\n aiAssisted:\n publicGeneration.aiAssisted ??\n generationMetadata.aiAssisted ??\n Boolean(getPublicPrompt(this.metadata)),\n publicPrompt: getPublicPrompt(this.metadata),\n model: publicGeneration.model || generationMetadata.model || null,\n },\n factsUsed: linkedFacts.filter((fact) => fact.usedInArticle),\n linkedFacts,\n otherExtractedFacts: referenceGroups.flatMap((reference) =>\n reference.extractedFacts.filter((fact) => !fact.usedInArticle),\n ),\n references: referenceGroups,\n reviews,\n reviewProfiles,\n corrections: serializedCorrections,\n versionHistory: serializedVersionHistory,\n },\n {\n snapshotKind,\n contentId: (this.id as string) || null,\n currentContentStatus: this.status || null,\n publicationProfileKey: governance.publicationProfileKey || undefined,\n },\n );\n }\n\n /**\n * Fingerprint of the *content-bearing* publication surface only.\n *\n * This must converge: two byte-identical `save()`s of published content\n * have to produce the same fingerprint so the publication-version writer\n * (`save()`) does not append a redundant `ContentVersion` on every save.\n *\n * It therefore deliberately excludes everything that grows or carries a\n * timestamp/ordering with each save — `versionHistory`, `reviews`,\n * `corrections`, and any `generatedAt`/`createdAt`/`id` fields. The earlier\n * implementation fingerprinted the full transparency snapshot (which embeds\n * the growing `versionHistory`), so the stored fingerprint of vN predated vN\n * and the next save's recomputed fingerprint always differed → unbounded\n * redundant publication versions (#1387 blocker).\n *\n * The surface mirrors `buildReviewFingerprint`'s content block, plus the\n * pinned reference edges (`{ targetId, targetVersion }`) — a pin change is a\n * meaningful republication — and the publication profile key.\n */\n private async buildPublicationSnapshotFingerprint(\n governance: ResolvedContentGovernance,\n ): Promise<string | null> {\n if (!governance.isGoverned || !governance.transparencyEnabled) {\n return null;\n }\n\n const referenceCollection = await this.getReferenceCollection();\n const [referenceEdges, facts, factLinks] = await Promise.all([\n this.id ? referenceCollection.getForSource(this.id) : Promise.resolve([]),\n governance.factLinkingEnabled\n ? this.getFacts({ latestOnly: true, includeSuperseded: false })\n : Promise.resolve([]),\n governance.factLinkingEnabled ? this.getFactLinks() : Promise.resolve([]),\n ]);\n\n return createFingerprint({\n scope: 'content-publication',\n publicationProfileKey: governance.publicationProfileKey || null,\n content: {\n id: this.id || null,\n type: this.type,\n variant: this.variant,\n title: this.title,\n description: this.description,\n body: this.body,\n author: this.author,\n state: this.state,\n publishDate: this.publish_date,\n language: this.language,\n category: this.category,\n tags: this.tags,\n metadata: this.metadata,\n },\n // Pinned reference edges only — ordering-independent so it converges.\n references: referenceEdges\n .map((edge) => ({\n targetId: edge.targetId || null,\n targetVersion: edge.targetVersion ?? null,\n }))\n .sort((a, b) => String(a.targetId).localeCompare(String(b.targetId))),\n facts: facts\n .map((fact) => ({\n id: fact.id || null,\n previousFactId: fact.previousFactId || null,\n status: fact.status || null,\n textRefined: fact.textRefined || '',\n sourceCount: fact.sourceCount ?? 0,\n confidence: fact.confidence ?? null,\n metadata:\n typeof fact?.getMetadata === 'function' ? fact.getMetadata() : {},\n }))\n .sort((a, b) => String(a.id).localeCompare(String(b.id))),\n factLinks: factLinks\n .map((link) => ({\n factId: link.factId || null,\n relationship: link.relationship || null,\n metadata:\n typeof link?.getMetadata === 'function' ? link.getMetadata() : {},\n }))\n .sort((a, b) => String(a.factId).localeCompare(String(b.factId))),\n });\n }\n\n private async getLatestPublicationSnapshotFingerprint(): Promise<\n string | null\n > {\n const versions = await this.getVersions();\n const latestPublicationVersion = [...versions]\n .reverse()\n .find((version) => version.kind === 'publication');\n\n if (!latestPublicationVersion) {\n return null;\n }\n\n return (\n latestPublicationVersion.getMetadata().publicationSnapshotFingerprint ||\n null\n );\n }\n\n private async buildCorrectionDraftSnapshot(\n options: IssueContentCorrectionOptions,\n replacementFactId: string,\n ): Promise<{\n snapshot: Record<string, unknown>;\n metadata: Record<string, unknown>;\n }> {\n const correctedText =\n options.correctedText || options.correctedFactText || '';\n const incorrectText = options.incorrectText || '';\n let body = this.body;\n let generationMethod = 'metadata';\n\n if (incorrectText && correctedText && body.includes(incorrectText)) {\n body = body.replace(incorrectText, correctedText);\n generationMethod = 'replace';\n } else if (correctedText) {\n const ai = this.ai as {\n message?: (\n prompt: string,\n options?: Record<string, unknown>,\n ) => Promise<string>;\n };\n if (ai?.message) {\n const resolvedPrompt = await resolvePrompt(\n smrtContentApplyCorrectionPrompt.key,\n {\n db: this.options.db,\n tenantId: this.tenantId,\n variables: {\n body: this.body,\n correctedText,\n incorrectText: incorrectText || 'Not provided',\n summary: options.summary || '',\n },\n },\n );\n\n try {\n const proposedBody = (\n await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n )\n ).trim();\n if (proposedBody) {\n body = proposedBody;\n generationMethod = 'ai';\n }\n } catch {\n generationMethod = 'metadata';\n }\n }\n }\n\n return {\n snapshot: {\n title: this.title,\n description: this.description,\n body,\n status: 'draft',\n metadata: {\n ...(this.metadata || {}),\n governance: {\n ...asRecord(this.metadata.governance),\n correctionDraft: {\n summary: options.summary,\n incorrectText,\n correctedText,\n factId: options.factId || null,\n replacementFactId: replacementFactId || null,\n autoGenerated: true,\n generationMethod,\n },\n },\n },\n },\n metadata: {\n summary: options.summary,\n incorrectText,\n correctedText,\n factId: options.factId || null,\n replacementFactId: replacementFactId || null,\n autoGenerated: true,\n generationMethod,\n },\n };\n }\n\n private async getAssetCollection() {\n return AssetCollection.create({ db: this.db });\n }\n\n private async getContentAssetCollection() {\n return ContentAssetCollection.create({ db: this.db });\n }\n\n private async getContentAssetLinks(\n relationship?: string,\n ): Promise<Array<{ assetId: string; sortOrder: number }>> {\n if (!this.id) {\n return [];\n }\n\n try {\n const contentAssets = await this.getContentAssetCollection();\n const links = await contentAssets.byLeft(\n this.id,\n relationship ? { relationship } : {},\n );\n\n return links\n .filter((link) => link.assetId)\n .map((link) => ({\n assetId: link.assetId,\n sortOrder: link.sortOrder ?? 0,\n }));\n } catch (error) {\n if (isMissingTableError(error, 'content_assets')) {\n return [];\n }\n\n throw error;\n }\n }\n\n private async resolveAssetsForLinks(\n links: Array<{ assetId: string; sortOrder: number }>,\n ): Promise<Asset[]> {\n if (links.length === 0) {\n return [];\n }\n\n const assetIds = [...new Set(links.map((link) => link.assetId))];\n const assets = await this.getAssetCollection();\n const resolved = await assets.listByIds(assetIds);\n const assetsById = new Map(\n resolved\n .filter((asset) => asset.id)\n .map((asset) => [asset.id as string, asset]),\n );\n\n return links\n .map((link) => assetsById.get(link.assetId))\n .filter(Boolean) as Asset[];\n }\n\n private async resolveReferenceTarget(content: Content | string) {\n if (typeof content !== 'string') {\n return content;\n }\n\n const contents = await this.getContentsCollection();\n\n return (await contents.getOrUpsert(\n {\n url: content,\n tenantId: this.tenantId,\n },\n {\n name: content,\n title: content,\n type: 'reference',\n tenantId: this.tenantId,\n },\n )) as Content;\n }\n\n /**\n * Loads referenced content objects\n *\n * @returns Promise that resolves when references are loaded\n */\n public async loadReferences() {\n this.references = await this.getReferences();\n }\n\n private getPendingReferenceIds(): string[] | null {\n const pendingReferenceIds = (this as Content & ContentTransientLinkIds)\n .referenceIds;\n\n if (!Array.isArray(pendingReferenceIds)) {\n return null;\n }\n\n return [\n ...new Set(\n pendingReferenceIds.filter(\n (referenceId): referenceId is string =>\n typeof referenceId === 'string' &&\n referenceId.length > 0 &&\n referenceId !== this.id,\n ),\n ),\n ];\n }\n\n private getPendingAssetIds(): string[] | null {\n const pendingAssetIds = (this as Content & ContentTransientLinkIds)\n .assetIds;\n\n if (!Array.isArray(pendingAssetIds)) {\n return null;\n }\n\n return [\n ...new Set(\n pendingAssetIds.filter(\n (assetId): assetId is string =>\n typeof assetId === 'string' && assetId.length > 0,\n ),\n ),\n ];\n }\n\n private async syncPendingReferenceIds(): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const pendingReferenceIds = this.getPendingReferenceIds();\n if (pendingReferenceIds === null) {\n return;\n }\n\n const currentReferences = await this.getReferences();\n const currentReferenceIds = currentReferences\n .map((reference) => reference.id)\n .filter((referenceId): referenceId is string => Boolean(referenceId));\n const currentReferenceIdSet = new Set(currentReferenceIds);\n const pendingReferenceIdSet = new Set(pendingReferenceIds);\n\n for (const referenceId of currentReferenceIds) {\n if (!pendingReferenceIdSet.has(referenceId)) {\n await this.removeReference(referenceId);\n }\n }\n\n const referenceIdsToAdd = pendingReferenceIds.filter(\n (referenceId) => !currentReferenceIdSet.has(referenceId),\n );\n\n if (referenceIdsToAdd.length === 0) {\n this.references = await this.getReferences();\n return;\n }\n\n const contents = await this.getContentsCollection();\n const resolvedReferences = await contents.listByIds(referenceIdsToAdd);\n const referencesById = new Map(\n resolvedReferences\n .filter((reference) => reference.id)\n .map((reference) => [reference.id as string, reference]),\n );\n\n for (const referenceId of referenceIdsToAdd) {\n const reference = referencesById.get(referenceId);\n if (reference) {\n await this.addReference(reference);\n }\n }\n\n this.references = await this.getReferences();\n }\n\n private async syncPendingAssetIds(): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const pendingAssetIds = this.getPendingAssetIds();\n if (pendingAssetIds === null) {\n return;\n }\n\n const currentAssets = await this.getAssets();\n const currentAssetIds = currentAssets\n .map((asset) => asset.id)\n .filter((assetId): assetId is string => Boolean(assetId));\n const currentAssetIdSet = new Set(currentAssetIds);\n const pendingAssetIdSet = new Set(pendingAssetIds);\n\n for (const assetId of currentAssetIds) {\n if (!pendingAssetIdSet.has(assetId)) {\n await this.removeAsset(assetId);\n }\n }\n\n const assetIdsToAdd = pendingAssetIds.filter(\n (assetId) => !currentAssetIdSet.has(assetId),\n );\n\n if (assetIdsToAdd.length === 0) {\n return;\n }\n\n const assets = await this.getAssetCollection();\n const resolvedAssets = await assets.listByIds(assetIdsToAdd);\n const assetsById = new Map(\n resolvedAssets\n .filter((asset) => asset.id)\n .map((asset) => [asset.id as string, asset]),\n );\n\n for (const assetId of assetIdsToAdd) {\n const asset = assetsById.get(assetId);\n if (asset) {\n await this.addAsset(asset);\n }\n }\n }\n\n /**\n * Adds a reference to another content object.\n *\n * @param content - Content object or URL to reference\n * @param options.targetVersion - Optional ContentVersion.version to pin the\n * citation to. Pass the target's current version (typically the latest\n * publication) to enable drift detection later. Pass `null` or omit to\n * leave the reference untracked.\n * @returns Promise that resolves when the reference is added\n */\n public async addReference(\n content: Content | string,\n options: { targetVersion?: number | null } = {},\n ) {\n if (!this.id) {\n throw new Error('Cannot add reference to unsaved content');\n }\n\n const target = await this.resolveReferenceTarget(content);\n\n if (!target.id) {\n throw new Error('Cannot add reference to unsaved content');\n }\n if (this.id === target.id) {\n return;\n }\n\n const references = await this.getReferenceCollection();\n // R2 junction `attach` carries extra row fields via its opts bag, so the\n // tenant scope and main's citation pin (targetVersion) ride along together.\n await references.attach(this.id, target.id, {\n tenantId: this.tenantId,\n targetVersion: options.targetVersion,\n });\n this.references = await this.getReferences();\n }\n\n /**\n * Removes a reference to another content object\n *\n * @param targetId - ID of the referenced content to remove\n */\n public async removeReference(targetId: string) {\n if (!this.id) {\n return;\n }\n\n const references = await this.getReferenceCollection();\n await references.detach(this.id, targetId);\n this.references = this.references.filter(\n (reference) => reference.id !== targetId,\n );\n }\n\n /**\n * Gets all referenced content objects\n *\n * @returns Promise resolving to an array of referenced Content objects\n */\n public async getReferences() {\n if (!this.id) {\n return [];\n }\n\n const references = await this.getReferenceCollection();\n const linkedReferences = await references.byLeft(this.id);\n const targetIds = linkedReferences.map((reference) => reference.targetId);\n\n if (targetIds.length === 0) {\n this.references = [];\n return this.references;\n }\n\n const contents = await this.getContentsCollection();\n const resolved = await contents.listByIds(targetIds);\n const referencesById = new Map(\n resolved\n .filter((content) => content.id)\n .map((content) => [content.id as string, content]),\n );\n\n this.references = targetIds\n .map((targetId) => referencesById.get(targetId))\n .filter(Boolean) as Content[];\n return this.references;\n }\n\n /**\n * Returns the raw reference edges with their citation pins\n * (`{ targetId, targetVersion }`). Unlike `getReferences()` (which resolves\n * to `Content` objects and loses the per-edge `targetVersion`), this keeps\n * the pin so callers — notably version snapshots — can reconstruct pinned\n * citations on restore. See `ContentVersionCollection.restoreIntoContent`.\n */\n public async getReferenceEdges(): Promise<\n Array<{ targetId: string; targetVersion: number | null }>\n > {\n if (!this.id) {\n return [];\n }\n\n const references = await this.getReferenceCollection();\n const linkedReferences = await references.getForSource(this.id);\n return linkedReferences\n .filter((edge) => Boolean(edge.targetId))\n .map((edge) => ({\n targetId: edge.targetId as string,\n targetVersion: edge.targetVersion ?? null,\n }));\n }\n\n /**\n * Returns one entry per reference edge with the pinned `targetVersion` and\n * the target's latest version. Drift exists when both are present and\n * differ — callers can use this to surface \"the source you cited has been\n * updated\" affordances in editors or review tools.\n *\n * `currentVersion` is the target's latest **publication** `ContentVersion`,\n * because pins are taken against the latest publication (see `addReference`).\n * Auto-created `correction`/`draft`/`manual` versions bump the shared\n * `(content_id, version)` counter but do NOT republish, so comparing against\n * the max version of *any* kind produced false drift positives (#1387 #4).\n *\n * Unpinned references (`citedVersion === null`) are included with\n * `currentVersion` populated when available so callers can choose to\n * surface them as \"pinnable\" suggestions.\n */\n public async getReferenceDrift(): Promise<\n Array<{\n targetId: string;\n citedVersion: number | null;\n currentVersion: number | null;\n isDrifted: boolean;\n }>\n > {\n if (!this.id) {\n return [];\n }\n\n const references = await this.getReferenceCollection();\n const linkedReferences = await references.getForSource(this.id);\n if (linkedReferences.length === 0) {\n return [];\n }\n\n const versions = await this.getContentVersionCollection();\n const targetIds = linkedReferences.map((reference) => reference.targetId);\n\n // Single query for all target *publication* versions; pick the max per\n // contentId. Pins are taken against the latest publication, so only\n // publication versions count as drift (#1387 #4). Filtering by kind here\n // also avoids the N+1 of loading each version to inspect its kind.\n const allVersions = await versions.list({\n where: { contentId: targetIds, kind: 'publication' },\n orderBy: 'version DESC',\n });\n const latestByContentId = new Map<string, number>();\n for (const version of allVersions) {\n if (!latestByContentId.has(version.contentId)) {\n latestByContentId.set(version.contentId, version.version);\n }\n }\n\n return linkedReferences.map((reference) => {\n const currentVersion = latestByContentId.get(reference.targetId) ?? null;\n const citedVersion = reference.targetVersion ?? null;\n return {\n targetId: reference.targetId,\n citedVersion,\n currentVersion,\n isDrifted:\n citedVersion !== null &&\n currentVersion !== null &&\n citedVersion !== currentVersion,\n };\n });\n }\n\n public isGoverned(): boolean {\n return this.getConfiguredGovernance().isGoverned;\n }\n\n public async getFactLinks(\n options: { relationship?: FactContentRelationship } = {},\n ) {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.factLinkingEnabled || !this.id) {\n return [];\n }\n\n if (!this.id) {\n return [];\n }\n\n const links = await this.getFactContentCollection();\n return options.relationship\n ? links.byRight(this.id as string, { relationship: options.relationship })\n : links.byRight(this.id as string);\n }\n\n public async getFacts(\n options: {\n relationship?: FactContentRelationship;\n includeSuperseded?: boolean;\n latestOnly?: boolean;\n } = {},\n ): Promise<Fact[]> {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.factLinkingEnabled || !this.id) {\n return [];\n }\n\n if (!this.id) {\n return [];\n }\n\n const facts = await this.getFactCollection();\n return facts.getForContent(this.id as string, options);\n }\n\n public async addFact(\n fact: Fact | string,\n relationship?: FactContentRelationship,\n metadata?: Record<string, unknown>,\n ) {\n const governance = await this.requireFactLinking('fact association');\n\n if (!this.id) {\n throw new Error('Cannot associate an unsaved content item with a fact');\n }\n\n const factId = typeof fact === 'string' ? fact : (fact.id as string);\n if (!factId) {\n throw new Error('Fact ID is required to create a content-fact link');\n }\n\n const links = await this.getFactContentCollection();\n return links.attach(factId, this.id as string, {\n relationship: relationship || governance.defaultFactRelationship,\n metadata,\n });\n }\n\n public async removeFact(\n factId: string,\n relationship?: FactContentRelationship,\n ): Promise<void> {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.factLinkingEnabled) {\n return;\n }\n\n if (!this.id) {\n return;\n }\n\n const links = await this.getFactContentCollection();\n if (relationship) {\n await links.detach(factId, this.id as string, { relationship });\n return;\n }\n\n await links.detach(factId, this.id as string);\n }\n\n public async syncFacts(\n factIds: string[],\n relationship?: FactContentRelationship,\n ): Promise<{ added: string[]; kept: string[]; removed: string[] }> {\n const governance = await this.requireFactLinking('fact sync');\n\n if (!this.id) {\n throw new Error('Cannot sync facts for unsaved content');\n }\n\n const uniqueFactIds = [...new Set(factIds.filter(Boolean))];\n const links = await this.getFactContentCollection();\n const resolvedRelationship =\n relationship || governance.defaultFactRelationship;\n const existing = await links.byRight(this.id as string, {\n relationship: resolvedRelationship,\n });\n\n const existingIds = new Set(existing.map((link) => link.factId));\n const desiredIds = new Set(uniqueFactIds);\n\n const kept = uniqueFactIds.filter((factId) => existingIds.has(factId));\n const added = uniqueFactIds.filter((factId) => !existingIds.has(factId));\n const removed = existing\n .map((link) => link.factId)\n .filter((factId) => !desiredIds.has(factId));\n\n for (const factId of added) {\n await links.attach(factId, this.id as string, {\n relationship: resolvedRelationship,\n });\n }\n\n for (const factId of removed) {\n await links.detach(factId, this.id as string, {\n relationship: resolvedRelationship,\n });\n }\n\n return { added, kept, removed };\n }\n\n public async browseFacts(\n query = '',\n options: {\n limit?: number;\n offset?: number;\n minSimilarity?: number;\n includeSuperseded?: boolean;\n latestOnly?: boolean;\n } = {},\n ): Promise<Fact[]> {\n await this.requireFactLinking('fact catalog browsing');\n const facts = await this.getFactCollection();\n return facts.browseCatalog(query, {\n ...options,\n tenantId: this.tenantId,\n });\n }\n\n private async getFactAuditSourceMaterials(): Promise<{\n sources: FactAuditSourceMaterial[];\n warnings: string[];\n }> {\n const warnings: string[] = [];\n const sources: FactAuditSourceMaterial[] = [];\n const references = await this.getReferences();\n const assets = await this.getAssets();\n\n for (const reference of references) {\n const referenceId = (reference.id as string | undefined) || '';\n const text = getContentText(reference);\n // `sourceUrl` is not a declared Content field; read it structurally.\n const referenceSourceUrl = (reference as { sourceUrl?: unknown })\n .sourceUrl;\n const sourceUrl =\n normalizeAuditText(reference.url) ||\n normalizeAuditText(referenceSourceUrl) ||\n normalizeAuditText(reference.fileKey);\n const sourceTitle =\n normalizeAuditText(reference.title) ||\n normalizeAuditText(reference.name) ||\n sourceUrl ||\n referenceId;\n\n if (!text) {\n warnings.push(\n `Reference ${sourceTitle || referenceId} has no extracted text.`,\n );\n continue;\n }\n\n sources.push({\n sourceKind: 'content-reference',\n sourceId: referenceId,\n sourceUrl,\n sourceTitle,\n locator: sourceTitle,\n text,\n });\n }\n\n for (const asset of assets as Array<Asset & FactAuditAssetExtraFields>) {\n const metadata =\n typeof asset?.getMetadata === 'function'\n ? asset.getMetadata()\n : parseAuditMetadata(asset?.metadata);\n const text = [\n metadata.extractedText,\n metadata.text,\n metadata.ocrText,\n asset?.text,\n asset?.body,\n asset?.description,\n ]\n .map(normalizeAuditText)\n .filter(Boolean)\n .join('\\n\\n');\n const assetId = normalizeAuditText(asset?.id);\n const sourceTitle =\n normalizeAuditText(asset?.title) ||\n normalizeAuditText(asset?.name) ||\n normalizeAuditText(asset?.filename) ||\n assetId;\n const sourceUrl =\n normalizeAuditText(asset?.url) ||\n normalizeAuditText(asset?.sourceUrl) ||\n normalizeAuditText(asset?.fileKey);\n\n if (!text) {\n warnings.push(`Asset ${sourceTitle || assetId} has no extracted text.`);\n continue;\n }\n\n sources.push({\n sourceKind: 'asset',\n sourceId: assetId,\n sourceUrl,\n sourceTitle,\n locator: sourceTitle,\n text,\n });\n }\n\n return { sources, warnings };\n }\n\n private factMatchesTenant(fact: {\n tenantId?: string | null;\n tenant_id?: string | null;\n }): boolean {\n return (\n fact.tenantId === this.tenantId ||\n fact.tenant_id === this.tenantId ||\n (!fact.tenantId && !fact.tenant_id && !this.tenantId)\n );\n }\n\n private async findExactArticleClaimFact(\n statement: string,\n ): Promise<Fact | null> {\n const normalizedStatement = normalizeAuditText(statement);\n const facts = await this.getFactCollection();\n const linkedClaimFacts = await Promise.all(\n (await this.getFactLinks({ relationship: 'referenced_in' })).map((link) =>\n facts.get({ id: link.factId }),\n ),\n );\n const linkedMatch = linkedClaimFacts.find(\n (fact): fact is Fact =>\n Boolean(fact) &&\n this.factMatchesTenant(fact as Fact) &&\n normalizeAuditText((fact as Fact).textRefined) === normalizedStatement,\n );\n if (linkedMatch) {\n return linkedMatch;\n }\n\n const matches = await facts.list({\n where: { textRefined: normalizedStatement },\n orderBy: 'updated_at DESC',\n });\n\n return (\n matches.find(\n (fact) =>\n this.factMatchesTenant(fact) &&\n this.id &&\n isGeneratedArticleClaimFact(fact, this.id as string),\n ) || null\n );\n }\n\n private async safeAuditLink(\n factId: string,\n relationship: FactContentRelationship,\n metadata: Record<string, unknown>,\n ) {\n const links = await this.getFactContentCollection();\n const existing = (\n await links.byRight(this.id as string, { relationship })\n ).find((link) => link.factId === factId);\n\n if (existing) {\n const existingMetadata = getLinkMetadata(existing);\n if (existingMetadata.generatedBy === FACT_AUDIT_GENERATED_BY) {\n existing.setMetadata?.({\n ...existingMetadata,\n ...metadata,\n });\n } else {\n existing.setMetadata?.({\n ...existingMetadata,\n factAudit: {\n ...asRecord(existingMetadata.factAudit),\n ...metadata,\n },\n });\n }\n await existing.save();\n return existing;\n }\n\n return links.attach(factId, this.id as string, { relationship, metadata });\n }\n\n private async clearGeneratedFactAudit(): Promise<void> {\n if (!this.id) {\n return;\n }\n\n const [links, evidences] = await Promise.all([\n this.getFactLinks(),\n this.getFactEvidenceCollection(),\n ]);\n\n for (const link of links) {\n const metadata = getLinkMetadata(link);\n const nestedFactAudit = asRecord(metadata.factAudit);\n if (metadata.generatedBy === FACT_AUDIT_GENERATED_BY) {\n await link.delete();\n } else if (\n metadata.factAudit &&\n typeof metadata.factAudit === 'object' &&\n nestedFactAudit.generatedBy === FACT_AUDIT_GENERATED_BY\n ) {\n const { factAudit: _removed, ...preservedMetadata } = metadata;\n link.setMetadata?.(preservedMetadata);\n await link.save();\n }\n }\n\n const generatedEvidence = await evidences.list({\n where: { tenantId: this.tenantId ?? null },\n });\n for (const evidence of generatedEvidence) {\n const metadata =\n typeof evidence.getMetadata === 'function'\n ? evidence.getMetadata()\n : {};\n if (\n metadata.generatedBy === FACT_AUDIT_GENERATED_BY &&\n metadata.contentId === this.id\n ) {\n await evidence.delete();\n }\n }\n }\n\n private async clearGeneratedFactSourcesForSources(\n sources: FactAuditSourceMaterial[],\n ): Promise<string[]> {\n if (!this.id || sources.length === 0) {\n return [];\n }\n\n const sourceKeys = new Set(\n sources.map((source) => `${source.sourceKind}:${source.sourceId}`),\n );\n const factSources = await this.getFactSourceCollection();\n const generatedSources = await factSources.list({\n where: { tenantId: this.tenantId ?? null },\n });\n const deletedSourceIds: string[] = [];\n\n for (const source of generatedSources) {\n const metadata =\n typeof source.getMetadata === 'function' ? source.getMetadata() : {};\n const sourceKey = `${source.sourceType || ''}:${metadata.sourceId || ''}`;\n if (\n sourceKeys.has(sourceKey) &&\n metadata.generatedBy === FACT_AUDIT_GENERATED_BY &&\n metadata.contentId === this.id\n ) {\n if (typeof source.id === 'string') {\n deletedSourceIds.push(source.id);\n }\n await source.delete();\n }\n }\n\n return deletedSourceIds;\n }\n\n private async extractReferenceFactsForAudit(\n sources: FactAuditSourceMaterial[],\n options: {\n auditRunId: string;\n maxFactsPerSource?: number;\n context?: string;\n replaceGenerated?: boolean;\n },\n ) {\n const facts = await this.getFactCollection();\n const evidences = await this.getFactEvidenceCollection();\n const warnings: string[] = [];\n const referenceFacts = new Map<string, Fact>();\n let deletedEvidenceIds: string[] = [];\n let deletedSourceIds: string[] = [];\n\n if (options.replaceGenerated && sources.length > 0) {\n const replacement = await evidences.replaceGeneratedForSources(\n sources.map((source) => ({\n sourceKind: source.sourceKind,\n sourceId: source.sourceId,\n })),\n {\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id as string,\n tenantId: this.tenantId ?? null,\n },\n );\n deletedEvidenceIds = replacement.deletedEvidenceIds;\n deletedSourceIds =\n await this.clearGeneratedFactSourcesForSources(sources);\n }\n\n for (const source of sources) {\n let candidates: FactExtractionCandidate[] = [];\n try {\n candidates = await facts.extractCandidatesFromText(source.text, {\n domain: FACT_AUDIT_DOMAIN,\n sourceType: source.sourceKind,\n context: options.context || source.sourceTitle,\n maxFacts: options.maxFactsPerSource ?? 24,\n tenantId: this.tenantId,\n });\n } catch (error) {\n warnings.push(\n `Failed to extract facts from ${source.sourceTitle}: ${errorMessage(error)}`,\n );\n continue;\n }\n\n for (const candidate of candidates) {\n const result = await facts.reconcile({\n rawInput: candidate.statement,\n type: candidate.type || 'assertion',\n domain: FACT_AUDIT_DOMAIN,\n tenantId: this.tenantId,\n source: {\n sourceType: source.sourceKind,\n sourceUrl: source.sourceUrl,\n sourceTitle: source.sourceTitle,\n credibility: candidate.confidence ?? 0.75,\n metadata: {\n auditRunId: options.auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n sourceId: source.sourceId,\n quote: candidate.sourceExcerpt || null,\n locator: source.locator || null,\n },\n },\n });\n referenceFacts.set(result.fact.id as string, result.fact);\n\n await evidences.upsertEvidence({\n factId: result.fact.id as string,\n status: 'supports',\n sourceKind: source.sourceKind,\n sourceId: source.sourceId,\n sourceUrl: source.sourceUrl,\n sourceTitle: source.sourceTitle,\n quote: candidate.sourceExcerpt || candidate.statement,\n locator: source.locator,\n extractionMethod: 'ai-reference-fact',\n confidence: candidate.confidence ?? 0.75,\n tenantId: this.tenantId,\n metadata: {\n auditRunId: options.auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n candidateMetadata: candidate.metadata || {},\n },\n });\n }\n }\n\n return {\n referenceFacts,\n warnings,\n referenceFactsExtracted: referenceFacts.size,\n deletedEvidenceIds,\n deletedSourceIds,\n repairedSources: sources.map((source) => ({\n sourceKind: source.sourceKind,\n sourceId: source.sourceId,\n sourceTitle: source.sourceTitle,\n })),\n };\n }\n\n private async getCurrentFactAuditSupportCandidates(\n options: {\n referenceFacts?: Map<string, Fact>;\n sources?: FactAuditSourceSelector[];\n sourceIds?: string[];\n maxCandidateEvidence?: number;\n } = {},\n ) {\n const evidences = await this.getFactEvidenceCollection();\n const allFacts = await this.getFactCollection();\n const candidateFacts = new Map<string, FactAuditSupportCandidate>();\n const candidateEvidence = new Map<string, FactEvidence>();\n const sourceKeys = new Set(\n (options.sources || []).map(\n (source) => `${source.sourceKind}:${source.sourceId}`,\n ),\n );\n const sourceIds = new Set(options.sourceIds || []);\n const maxCandidateEvidence = Math.max(\n 1,\n options.maxCandidateEvidence ?? 120,\n );\n\n const evidenceEntries = options.referenceFacts\n ? (\n await Promise.all(\n [...options.referenceFacts.keys()].map((factId) =>\n evidences.getForFact(factId),\n ),\n )\n ).flat()\n : await evidences.list({\n where: { tenantId: this.tenantId ?? null },\n });\n\n for (const entry of evidenceEntries) {\n if (!isGeneratedFactAuditEvidence(entry, this.id as string)) {\n continue;\n }\n if (entry.sourceKind === 'content') {\n continue;\n }\n if (entry.status === 'irrelevant' || entry.status === 'invalid') {\n continue;\n }\n if (\n sourceKeys.size > 0 &&\n !sourceKeys.has(`${entry.sourceKind}:${entry.sourceId}`)\n ) {\n continue;\n }\n if (sourceIds.size > 0 && !sourceIds.has(entry.sourceId)) {\n continue;\n }\n if (candidateEvidence.size >= maxCandidateEvidence) {\n break;\n }\n\n const fact =\n options.referenceFacts?.get(entry.factId) ||\n (await allFacts.get({ id: entry.factId }));\n if (!fact) {\n continue;\n }\n\n const factId = fact.id as string;\n const existing = candidateFacts.get(factId) || {\n id: factId,\n statement: fact.textRefined || fact.textRaw || '',\n evidence: [],\n };\n const serializedEvidence = {\n id: entry.id || null,\n status: entry.status || 'supports',\n quote: entry.quote || null,\n sourceTitle: entry.sourceTitle || null,\n sourceUrl: entry.sourceUrl || null,\n locator: entry.locator || null,\n };\n existing.evidence.push(serializedEvidence);\n candidateFacts.set(factId, existing);\n if (typeof entry.id === 'string') {\n candidateEvidence.set(entry.id, entry);\n }\n }\n\n return {\n supportCandidates: [...candidateFacts.values()],\n candidateFactIds: new Set(candidateFacts.keys()),\n candidateEvidence,\n };\n }\n\n public async repairFactAudit(\n options: {\n maxReferenceFactsPerSource?: number;\n maxArticleClaims?: number;\n context?: string;\n } = {},\n ) {\n await this.requireFactLinking('fact audit repair');\n if (!this.id) {\n throw new Error('Cannot repair fact audit for unsaved content');\n }\n\n const auditRunId = createFactAuditRunId(this.id as string);\n const facts = await this.getFactCollection();\n const evidences = await this.getFactEvidenceCollection();\n const warnings: string[] = [];\n const articleText = getContentText(this);\n\n const sourceMaterials = await this.getFactAuditSourceMaterials();\n warnings.push(...sourceMaterials.warnings);\n await this.clearGeneratedFactAudit();\n const referenceRepair = await this.extractReferenceFactsForAudit(\n sourceMaterials.sources,\n {\n auditRunId,\n maxFactsPerSource: options.maxReferenceFactsPerSource,\n context: options.context,\n },\n );\n warnings.push(...referenceRepair.warnings);\n const referenceFacts = referenceRepair.referenceFacts;\n\n let claims: FactExtractionCandidate[] = [];\n if (!articleText) {\n warnings.push('Article has no text to audit.');\n } else {\n try {\n claims = await facts.extractArticleClaims(articleText, {\n domain: FACT_AUDIT_DOMAIN,\n sourceType: 'article',\n context: options.context || this.title || this.slug || '',\n maxFacts: options.maxArticleClaims ?? 32,\n tenantId: this.tenantId,\n });\n } catch (error) {\n warnings.push(\n `Failed to extract article claims: ${errorMessage(error)}`,\n );\n }\n }\n\n const { supportCandidates, candidateFactIds, candidateEvidence } =\n await this.getCurrentFactAuditSupportCandidates({\n referenceFacts,\n });\n\n const findings: ContentReviewFinding[] = [];\n\n for (const claim of claims) {\n let assessment: FactClaimSupportAssessment;\n try {\n assessment = await facts.assessClaimSupport(\n claim.statement,\n supportCandidates,\n { tenantId: this.tenantId },\n );\n } catch (error) {\n warnings.push(\n `Failed to assess claim \"${claim.statement}\": ${errorMessage(error)}`,\n );\n assessment = {\n status: 'needs_review' as FactClaimSupportStatus,\n matchedFactIds: [],\n matchedEvidenceIds: [],\n rationale: 'Support assessment failed.',\n confidence: undefined,\n };\n }\n\n const matchedFactIds = assessment.matchedFactIds.filter((factId) =>\n candidateFactIds.has(factId),\n );\n let claimFact = await this.findExactArticleClaimFact(claim.statement);\n\n if (!claimFact) {\n claimFact = await facts.create({\n textRefined: claim.statement,\n textRaw: claim.statement,\n type: claim.type || 'assertion',\n domain: FACT_AUDIT_DOMAIN,\n status:\n assessment.status === 'unsupported' ||\n assessment.status === 'needs_review'\n ? 'pending'\n : 'active',\n sourceCount: 0,\n confidence: claim.confidence ?? assessment.confidence ?? 0.5,\n tenantId: this.tenantId,\n metadata: JSON.stringify({\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n auditFactRole: 'article-claim',\n claimOnly: matchedFactIds.length === 0,\n }),\n });\n } else if (\n this.id &&\n isGeneratedArticleClaimFact(claimFact, this.id as string)\n ) {\n claimFact.status =\n assessment.status === 'unsupported' ||\n assessment.status === 'needs_review'\n ? 'pending'\n : 'active';\n claimFact.confidence =\n claim.confidence ?? assessment.confidence ?? claimFact.confidence;\n claimFact.updateMetadata?.({\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n auditFactRole: 'article-claim',\n claimOnly: matchedFactIds.length === 0,\n });\n await claimFact.save();\n }\n\n const articleEvidence = await evidences.upsertEvidence({\n factId: claimFact.id as string,\n status: 'supports',\n sourceKind: 'content',\n sourceId: this.id as string,\n sourceTitle: this.title || this.slug || (this.id as string),\n quote: claim.sourceExcerpt || claim.statement,\n locator: this.title || this.slug || '',\n extractionMethod: 'ai-article-claim',\n confidence: claim.confidence ?? assessment.confidence ?? 0.5,\n tenantId: this.tenantId,\n metadata: {\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n contentId: this.id,\n supportStatus: assessment.status,\n },\n });\n\n let supportingEvidenceIds = assessment.matchedEvidenceIds.filter(\n (evidenceId) => candidateEvidence.has(evidenceId),\n );\n for (const matchedFactId of matchedFactIds) {\n if (supportingEvidenceIds.length > 0) {\n continue;\n }\n const candidate = supportCandidates.find(\n (entry) => entry.id === matchedFactId,\n );\n supportingEvidenceIds = [\n ...supportingEvidenceIds,\n ...(candidate?.evidence || [])\n .map((entry) => entry.id)\n .filter((id: unknown): id is string => typeof id === 'string'),\n ];\n }\n supportingEvidenceIds = [...new Set(supportingEvidenceIds)];\n\n const linkMetadata = {\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n supportStatus: assessment.status,\n claimQuote: claim.sourceExcerpt || claim.statement,\n claimFactId: claimFact.id as string,\n articleEvidenceId: articleEvidence.id || null,\n supportingFactIds: matchedFactIds,\n supportingEvidenceIds,\n rationale: assessment.rationale,\n confidence: assessment.confidence ?? claim.confidence ?? null,\n };\n\n await this.safeAuditLink(\n claimFact.id as string,\n 'referenced_in',\n linkMetadata,\n );\n\n for (const matchedFactId of matchedFactIds) {\n await this.safeAuditLink(\n matchedFactId,\n assessment.status === 'contradicted' ? 'contradicts' : 'supports',\n {\n ...linkMetadata,\n supportingEvidenceIds,\n },\n );\n }\n\n if (assessment.status !== 'supported') {\n findings.push({\n severity: assessment.status === 'contradicted' ? 'error' : 'warning',\n title:\n assessment.status === 'contradicted'\n ? 'Contradicted article claim'\n : 'Unsupported article claim',\n detail: assessment.rationale || 'The claim needs editorial review.',\n factId: claimFact.id as string,\n quote: claim.sourceExcerpt || claim.statement,\n ruleId: 'fact-audit',\n });\n }\n }\n\n const reviews = await this.getContentReviewCollection();\n await reviews.createFromResult({\n contentId: this.id as string,\n kind: 'facts',\n policyKey: 'facts',\n reviewer: 'system',\n result: {\n status: findings.length > 0 ? 'flagged' : 'passed',\n summary:\n findings.length > 0\n ? `${findings.length} article claim(s) need review.`\n : 'Article claims are supported by available evidence.',\n findings,\n },\n metadata: {\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n warnings,\n },\n tenantId: this.tenantId,\n });\n\n const state = await this.getFactAuditState();\n return {\n ...state,\n repair: {\n auditRunId,\n claimsExtracted: claims.length,\n referenceFactsExtracted: referenceRepair.referenceFactsExtracted,\n warnings,\n },\n };\n }\n\n public async repairFactAuditAction(\n options: {\n maxReferenceFactsPerSource?: number;\n maxArticleClaims?: number;\n context?: string;\n } = {},\n ) {\n return this.repairFactAudit(options);\n }\n\n public async repairFactEvidence(\n options: FactAuditResourceRepairOptions = {},\n ) {\n await this.requireFactLinking('fact evidence repair');\n if (!this.id) {\n throw new Error('Cannot repair fact evidence for unsaved content');\n }\n\n const auditRunId = createFactAuditRunId(this.id as string);\n const sourceMaterials = await this.getFactAuditSourceMaterials();\n const sources = filterAuditSources(\n sourceMaterials.sources,\n options.sources,\n );\n const warnings: string[] = [];\n\n if (options.sources?.length && sources.length === 0) {\n warnings.push('No matching resource text was available for repair.');\n }\n\n const repair = await this.extractReferenceFactsForAudit(sources, {\n auditRunId,\n maxFactsPerSource: options.maxFactsPerSource,\n context: options.context,\n replaceGenerated: true,\n });\n warnings.push(...repair.warnings);\n\n const state = await this.getFactAuditState();\n return {\n ...state,\n evidenceRepair: {\n auditRunId,\n referenceFactsExtracted: repair.referenceFactsExtracted,\n repairedSources: repair.repairedSources,\n deletedEvidenceIds: repair.deletedEvidenceIds,\n deletedSourceIds: repair.deletedSourceIds,\n warnings,\n },\n };\n }\n\n public async repairFactEvidenceAction(\n options: FactAuditResourceRepairOptions = {},\n ) {\n return this.repairFactEvidence(options);\n }\n\n private async clearGeneratedSupportLinksForClaim(\n claimFactId: string,\n articleEvidenceId: string | null,\n ): Promise<void> {\n const links = await this.getFactLinks();\n\n for (const link of links) {\n if (\n link.relationship !== 'supports' &&\n link.relationship !== 'contradicts'\n ) {\n continue;\n }\n\n const metadata = getGeneratedFactAuditMetadata(link);\n if (!metadata) {\n continue;\n }\n\n const matchesEvidence =\n articleEvidenceId &&\n typeof metadata.articleEvidenceId === 'string' &&\n metadata.articleEvidenceId === articleEvidenceId;\n const matchesClaim =\n typeof metadata.claimFactId === 'string' &&\n metadata.claimFactId === claimFactId;\n\n if (matchesEvidence || matchesClaim) {\n await link.delete();\n }\n }\n }\n\n public async recheckFactClaims(options: FactAuditClaimRecheckOptions = {}) {\n await this.requireFactLinking('claim support recheck');\n if (!this.id) {\n throw new Error('Cannot recheck fact claims for unsaved content');\n }\n\n const auditRunId = createFactAuditRunId(this.id as string);\n const facts = await this.getFactCollection();\n const evidences = await this.getFactEvidenceCollection();\n const links = await this.getFactContentCollection();\n const claimIdFilter = new Set(options.claimFactIds || []);\n const { supportCandidates, candidateFactIds, candidateEvidence } =\n await this.getCurrentFactAuditSupportCandidates({\n sources: options.sources,\n sourceIds: options.sourceIds,\n maxCandidateEvidence: options.maxCandidateEvidence,\n });\n const claimLinks = (\n await links.byRight(this.id as string, { relationship: 'referenced_in' })\n )\n .map((link) => ({\n link,\n metadata: getGeneratedFactAuditMetadata(link),\n }))\n .filter(\n (\n entry,\n ): entry is { link: FactContent; metadata: Record<string, unknown> } =>\n entry.metadata !== null &&\n (claimIdFilter.size === 0 || claimIdFilter.has(entry.link.factId)),\n );\n const warnings: string[] = [];\n let recheckedClaims = 0;\n\n for (const { link, metadata } of claimLinks) {\n const claimFact = await facts.get({ id: link.factId });\n if (!claimFact) {\n continue;\n }\n\n const claimText =\n normalizeAuditText(metadata.claimQuote) ||\n normalizeAuditText(claimFact.textRefined) ||\n normalizeAuditText(claimFact.textRaw);\n if (!claimText) {\n continue;\n }\n\n let assessment: FactClaimSupportAssessment;\n try {\n assessment = await facts.assessClaimSupport(\n claimText,\n supportCandidates,\n { tenantId: this.tenantId },\n );\n } catch (error) {\n warnings.push(\n `Failed to recheck claim \"${claimText}\": ${errorMessage(error)}`,\n );\n assessment = {\n status: 'needs_review',\n matchedFactIds: [],\n matchedEvidenceIds: [],\n rationale: 'Support assessment failed.',\n confidence: undefined,\n };\n }\n\n const matchedFactIds = assessment.matchedFactIds.filter((factId) =>\n candidateFactIds.has(factId),\n );\n let supportStatus = assessment.status;\n if (\n (supportStatus === 'supported' || supportStatus === 'contradicted') &&\n matchedFactIds.length === 0\n ) {\n supportStatus = 'needs_review';\n }\n let supportingEvidenceIds = assessment.matchedEvidenceIds.filter(\n (evidenceId) => candidateEvidence.has(evidenceId),\n );\n if (supportingEvidenceIds.length === 0) {\n for (const matchedFactId of matchedFactIds) {\n const candidate = supportCandidates.find(\n (entry) => entry.id === matchedFactId,\n );\n supportingEvidenceIds.push(\n ...(candidate?.evidence || [])\n .map((entry) => entry.id)\n .filter((id: unknown): id is string => typeof id === 'string'),\n );\n }\n }\n supportingEvidenceIds = [...new Set(supportingEvidenceIds)];\n\n const articleEvidenceId =\n typeof metadata.articleEvidenceId === 'string'\n ? metadata.articleEvidenceId\n : null;\n const nextMetadata = {\n ...metadata,\n auditRunId,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n supportStatus,\n supportingFactIds: matchedFactIds,\n supportingEvidenceIds,\n rationale: assessment.rationale,\n confidence: assessment.confidence ?? metadata.confidence ?? null,\n claimFactId: link.factId,\n };\n const existingMetadata = getLinkMetadata(link);\n if (existingMetadata.generatedBy === FACT_AUDIT_GENERATED_BY) {\n link.setMetadata?.(nextMetadata);\n } else {\n link.setMetadata?.({\n ...existingMetadata,\n factAudit: nextMetadata,\n });\n }\n await link.save();\n\n if (articleEvidenceId) {\n const articleEvidence = await evidences.get({ id: articleEvidenceId });\n if (articleEvidence) {\n articleEvidence.updateMetadata({\n auditRunId,\n supportStatus,\n });\n await articleEvidence.save();\n }\n }\n\n await this.clearGeneratedSupportLinksForClaim(\n link.factId,\n articleEvidenceId,\n );\n for (const matchedFactId of matchedFactIds) {\n await this.safeAuditLink(\n matchedFactId,\n supportStatus === 'contradicted' ? 'contradicts' : 'supports',\n {\n ...nextMetadata,\n articleEvidenceId,\n },\n );\n }\n\n recheckedClaims += 1;\n }\n\n const state = await this.getFactAuditState();\n return {\n ...state,\n claimRecheck: {\n auditRunId,\n recheckedClaims,\n candidateFacts: supportCandidates.length,\n candidateEvidence: candidateEvidence.size,\n warnings,\n },\n };\n }\n\n public async recheckFactClaimsAction(\n options: FactAuditClaimRecheckOptions = {},\n ) {\n return this.recheckFactClaims(options);\n }\n\n public async updateFactEvidenceStatus(\n options: FactEvidenceStatusUpdateOptions = {},\n ) {\n await this.requireFactLinking('evidence status update');\n if (!this.id) {\n throw new Error('Cannot update fact evidence for unsaved content');\n }\n\n const status = normalizeFactEvidenceStatus(options.status);\n if (!status) {\n throw new Error('A valid evidence status is required');\n }\n\n const requestedEvidenceIds = [\n ...new Set(\n (options.evidenceIds || []).filter(\n (id): id is string => typeof id === 'string' && id.length > 0,\n ),\n ),\n ];\n const evidences = await this.getFactEvidenceCollection();\n const sourceMaterials = await this.getFactAuditSourceMaterials();\n const allowedSourceKeys = new Set(\n sourceMaterials.sources.map(\n (source: FactAuditSourceMaterial) =>\n `${source.sourceKind}:${source.sourceId}`,\n ),\n );\n const allowedEvidenceIds: string[] = [];\n\n for (const evidenceId of requestedEvidenceIds) {\n const evidence = await evidences.get({ id: evidenceId });\n if (!evidence) {\n continue;\n }\n\n if (\n this.tenantId &&\n evidence.tenantId &&\n evidence.tenantId !== this.tenantId\n ) {\n continue;\n }\n\n const metadata = getEvidenceMetadata(evidence);\n const sourceKey = `${evidence.sourceKind || ''}:${\n evidence.sourceId || ''\n }`;\n if (\n metadata.contentId === this.id ||\n (evidence.sourceKind === 'content' && evidence.sourceId === this.id) ||\n allowedSourceKeys.has(sourceKey)\n ) {\n allowedEvidenceIds.push(evidenceId);\n }\n }\n\n const updated = await evidences.bulkUpdateStatus(\n allowedEvidenceIds,\n status,\n {\n reason: options.reason,\n },\n );\n const state = await this.getFactAuditState();\n\n return {\n ...state,\n evidenceStatusUpdate: {\n status,\n requestedEvidenceIds,\n updatedEvidenceIds: updated\n .map((entry) => entry.id)\n .filter((id: unknown): id is string => typeof id === 'string'),\n skippedEvidenceIds: requestedEvidenceIds.filter(\n (id) => !allowedEvidenceIds.includes(id),\n ),\n },\n };\n }\n\n public async updateFactEvidenceStatusAction(\n options: FactEvidenceStatusUpdateOptions = {},\n ) {\n return this.updateFactEvidenceStatus(options);\n }\n\n public async getFactAuditState(): Promise<FactAuditState> {\n if (!this.id) {\n return {\n counts: {\n total: 0,\n supported: 0,\n unsupported: 0,\n contradicted: 0,\n needs_review: 0,\n },\n claims: [],\n resourceClaims: [],\n warnings: [],\n generatedBy: FACT_AUDIT_GENERATED_BY,\n latestAuditRunId: null,\n };\n }\n\n const [facts, factLinks] = await Promise.all([\n this.getFacts({\n relationship: 'referenced_in',\n latestOnly: false,\n includeSuperseded: false,\n }),\n this.getFactLinks({ relationship: 'referenced_in' }),\n ]);\n const factMap = new Map(\n facts\n .filter((fact) => fact.id)\n .map((fact) => [fact.id as string, fact] as const),\n );\n const evidences = await this.getFactEvidenceCollection();\n const allFacts = await this.getFactCollection();\n const generatedLinks = factLinks\n .map((link) => ({\n link,\n metadata: getGeneratedFactAuditMetadata(link),\n }))\n .filter(\n (\n entry,\n ): entry is { link: FactContent; metadata: Record<string, unknown> } =>\n entry.metadata !== null,\n );\n const claims: FactAuditClaim[] = [];\n const resourceClaimsByKey = new Map<string, FactAuditResourceClaim>();\n const warnings: string[] = [];\n let latestAuditRunId: string | null = null;\n\n for (const { link, metadata } of generatedLinks) {\n const fact = factMap.get(link.factId);\n if (!fact) {\n continue;\n }\n\n // Opaque audit-link metadata values; cast at the read boundary.\n latestAuditRunId =\n (metadata.auditRunId as string | null) || latestAuditRunId;\n const status = (metadata.supportStatus ||\n 'needs_review') as FactClaimSupportStatus;\n const matchedFactIds = Array.isArray(metadata.supportingFactIds)\n ? metadata.supportingFactIds\n : [];\n const articleEvidenceId =\n typeof metadata.articleEvidenceId === 'string'\n ? metadata.articleEvidenceId\n : null;\n const supportingEvidenceIds = new Set(\n Array.isArray(metadata.supportingEvidenceIds)\n ? metadata.supportingEvidenceIds.filter(\n (id: unknown): id is string => typeof id === 'string',\n )\n : [],\n );\n const [allClaimEvidence, matchedFacts] = await Promise.all([\n evidences.getForFact(fact.id as string),\n Promise.all(\n matchedFactIds.map(async (factId: string) => {\n const matchedFact = await allFacts.get({ id: factId });\n const matchedEvidence = (await evidences.getForFact(factId)).filter(\n (entry) =>\n supportingEvidenceIds.size === 0\n ? entry.sourceKind !== 'content' || entry.sourceId !== this.id\n : supportingEvidenceIds.has(entry.id as string),\n );\n return matchedFact\n ? {\n fact: serializeFact(matchedFact),\n evidence: matchedEvidence.map((entry) => ({\n ...serializeFact(entry),\n metadata:\n typeof entry.getMetadata === 'function'\n ? entry.getMetadata()\n : {},\n })),\n }\n : null;\n }),\n ),\n ]);\n const claimEvidence = allClaimEvidence.filter((entry) =>\n articleEvidenceId\n ? entry.id === articleEvidenceId\n : entry.sourceKind === 'content' && entry.sourceId === this.id,\n );\n\n claims.push({\n id: fact.id as string,\n fact: serializeFact(fact),\n supportStatus: status,\n // Opaque audit-link metadata values; cast at the read boundary.\n claimQuote: (metadata.claimQuote as string | null) || null,\n rationale: (metadata.rationale as string | null) || null,\n confidence: (metadata.confidence as number | null) ?? null,\n relationship: link.relationship || null,\n linkMetadata: metadata,\n evidence: claimEvidence.map((entry) => ({\n ...serializeFact(entry),\n metadata:\n typeof entry.getMetadata === 'function' ? entry.getMetadata() : {},\n })),\n matchedFacts: matchedFacts.filter(\n Boolean,\n ) as FactAuditClaim['matchedFacts'],\n });\n }\n\n const generatedEvidence = await evidences.list({\n where: { tenantId: this.tenantId ?? null },\n });\n for (const evidence of generatedEvidence) {\n const metadata =\n typeof evidence.getMetadata === 'function'\n ? evidence.getMetadata()\n : {};\n if (\n metadata.generatedBy !== FACT_AUDIT_GENERATED_BY ||\n metadata.contentId !== this.id ||\n evidence.sourceKind === 'content'\n ) {\n continue;\n }\n\n const fact = await allFacts.get({ id: evidence.factId });\n if (!fact) {\n continue;\n }\n\n const key = [\n evidence.factId,\n evidence.sourceKind,\n evidence.sourceId,\n evidence.evidenceKey,\n ].join(':');\n const serializedEvidence = {\n ...serializeFact(evidence),\n metadata,\n };\n const existing = resourceClaimsByKey.get(key);\n if (existing) {\n existing.evidence.push(serializedEvidence);\n continue;\n }\n\n resourceClaimsByKey.set(key, {\n id: fact.id as string,\n fact: serializeFact(fact),\n sourceKind: evidence.sourceKind || null,\n sourceId: evidence.sourceId || null,\n sourceUrl: evidence.sourceUrl || null,\n sourceTitle: evidence.sourceTitle || null,\n locator: evidence.locator || null,\n quote: evidence.quote || null,\n status: evidence.status || 'supports',\n confidence: evidence.confidence ?? null,\n evidence: [serializedEvidence],\n });\n }\n\n const latestReview = (\n await this.getContentReviewCollection()\n ).getLatestForPolicyKey(this.id as string, 'facts');\n const review = await latestReview;\n const reviewMetadata =\n review && typeof review.getMetadata === 'function'\n ? review.getMetadata()\n : {};\n if (\n reviewMetadata.generatedBy === FACT_AUDIT_GENERATED_BY &&\n Array.isArray(reviewMetadata.warnings)\n ) {\n warnings.push(...reviewMetadata.warnings);\n }\n\n const counts = {\n total: claims.length,\n supported: 0,\n unsupported: 0,\n contradicted: 0,\n needs_review: 0,\n };\n for (const claim of claims) {\n counts[claim.supportStatus] += 1;\n }\n\n return {\n counts,\n claims,\n resourceClaims: [...resourceClaimsByKey.values()],\n warnings,\n generatedBy: FACT_AUDIT_GENERATED_BY,\n latestAuditRunId,\n };\n }\n\n public async getFactAuditStateAction() {\n return this.getFactAuditState();\n }\n\n public async getFactsState(\n options: { relationship?: FactContentRelationship } = {},\n ) {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.factLinkingEnabled) {\n return {\n factIds: [],\n facts: [],\n factLinks: [],\n };\n }\n\n const relationship = options.relationship;\n const [facts, factLinks] = await Promise.all([\n this.getFacts({\n relationship,\n latestOnly: true,\n includeSuperseded: false,\n }),\n this.getFactLinks(relationship ? { relationship } : {}),\n ]);\n\n return {\n factIds: facts.map((fact) => fact.id).filter(Boolean),\n facts: facts.map(serializeFact),\n factLinks: factLinks.map(serializeFactLink),\n };\n }\n\n public async syncFactsState(\n options: {\n factIds?: string[];\n relationship?: FactContentRelationship;\n } = {},\n ) {\n const governance = await this.requireFactLinking('fact sync');\n const relationship =\n options.relationship || governance.defaultFactRelationship;\n const sync = await this.syncFacts(options.factIds || [], relationship);\n const state = await this.getFactsState({ relationship });\n return {\n ...state,\n sync,\n };\n }\n\n public async createVersion(options: CreateContentVersionOptions = {}) {\n const versions = await this.getContentVersionCollection();\n return versions.createSnapshot(this, options);\n }\n\n public async getVersions() {\n if (!this.id) {\n return [];\n }\n\n const versions = await this.getContentVersionCollection();\n return versions.listForContent(this.id as string);\n }\n\n public async restoreFromVersion(versionNumber: number) {\n const versions = await this.getContentVersionCollection();\n return versions.restoreIntoContent(this, versionNumber);\n }\n\n public async getReviews(kind?: RunContentReviewOptions['kind']) {\n if (!this.id) {\n return [];\n }\n\n const reviews = await this.getContentReviewCollection();\n return reviews.listForContent(this.id as string, kind);\n }\n\n public async listReviews(\n options: { kind?: RunContentReviewOptions['kind'] } = {},\n ) {\n const reviews = await this.getReviews(options.kind);\n return reviews.map(serializeContentReview);\n }\n\n public async getReviewRequirements(\n profileKey: string,\n governance?: ResolvedContentGovernance,\n ) {\n const resolvedGovernance = governance || (await this.resolveGovernance());\n return getContentReviewRequirements(\n profileKey,\n resolvedGovernance.availableProfiles,\n );\n }\n\n public async getGovernanceState(): Promise<ContentGovernanceState> {\n const governance = await this.resolveGovernance();\n\n if (!governance.isGoverned) {\n return {\n ...governance,\n reviewProfiles: [],\n };\n }\n\n return {\n ...governance,\n reviewProfiles: await this.listReviewProfilesAction(),\n };\n }\n\n public async getGovernanceStateAction() {\n return this.getGovernanceState();\n }\n\n public async listReviewProfilesAction() {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned) {\n return [];\n }\n\n return Promise.all(\n getContentReviewProfileKeys(governance.availableProfiles).map(\n (profileKey) => this.evaluateReviewProfile(profileKey),\n ),\n );\n }\n\n public async evaluateReviewProfile(\n profileKey: string,\n ): Promise<ContentReviewProfileEvaluation> {\n const governance = await this.resolveGovernance();\n const requirements = await this.getReviewRequirements(\n profileKey,\n governance,\n );\n\n if (requirements.length === 0) {\n return {\n profileKey,\n ready: true,\n complete: true,\n requirements: [],\n };\n }\n\n const reviews = await this.getContentReviewCollection();\n const reviewFingerprintCache = new Map<string, string>();\n const evaluatedRequirements = await Promise.all(\n requirements.map(async (requirement) => {\n if (!reviewFingerprintCache.has(requirement.policyKey)) {\n reviewFingerprintCache.set(\n requirement.policyKey,\n await this.buildReviewFingerprint(requirement.policyKey),\n );\n }\n\n const latestReview =\n this.id && requirement.policyKey\n ? await reviews.getLatestForPolicyKey(\n this.id as string,\n requirement.policyKey,\n )\n : null;\n const acceptedStatuses = getAcceptedContentReviewStatuses(requirement);\n const latestStatus = latestReview?.status ?? null;\n const latestMetadata =\n typeof latestReview?.getMetadata === 'function'\n ? latestReview.getMetadata()\n : {};\n const currentFingerprint =\n reviewFingerprintCache.get(requirement.policyKey) || null;\n const reviewedFingerprint =\n latestMetadata?.reviewFingerprint ||\n latestMetadata?.contentFingerprint ||\n null;\n const missing = !latestReview;\n const stale =\n !missing &&\n !!reviewedFingerprint &&\n reviewedFingerprint !== currentFingerprint;\n const executed =\n latestStatus !== null && latestStatus !== 'pending' && !stale;\n const satisfied =\n !stale &&\n latestStatus !== null &&\n acceptedStatuses.includes(latestStatus);\n\n return {\n kind: getContentReviewKind(\n requirement.policyKey,\n governance.reviewPolicies,\n ),\n policyKey: requirement.policyKey,\n label:\n requirement.label ||\n getContentReviewPolicy(\n requirement.policyKey,\n governance.reviewPolicies,\n )?.label ||\n requirement.policyKey,\n blocking: requirement.blocking === true,\n acceptedStatuses,\n missing,\n stale,\n executed,\n satisfied,\n latestReviewId: (latestReview?.id as string) || null,\n latestStatus,\n latestSummary: latestReview?.summary || null,\n };\n }),\n );\n\n return {\n profileKey,\n ready: evaluatedRequirements\n .filter((requirement) => requirement.blocking)\n .every((requirement) => requirement.satisfied),\n complete: evaluatedRequirements.every(\n (requirement) => requirement.executed,\n ),\n requirements: evaluatedRequirements,\n };\n }\n\n public async evaluateReviewProfileAction(\n options: { profileKey?: string } = {},\n ) {\n if (!options.profileKey) {\n throw new Error('profileKey is required');\n }\n\n return this.evaluateReviewProfile(options.profileKey);\n }\n\n public async isReadyForReviewProfile(profileKey: string): Promise<boolean> {\n const evaluation = await this.evaluateReviewProfile(profileKey);\n return evaluation.ready;\n }\n\n public async getPublishedTransparency() {\n if (!this.id) {\n return null;\n }\n\n const versions = await this.getContentVersionCollection();\n const latestPublicationVersion =\n await versions.getLatestPublishedForContent(this.id as string);\n\n return latestPublicationVersion?.getTransparency() || null;\n }\n\n public async getPublishedTransparencyAction() {\n return this.getPublishedTransparency();\n }\n\n public async previewTransparency() {\n const governance = await this.resolveGovernance();\n if (!governance.isGoverned || !governance.transparencyEnabled) {\n return null;\n }\n\n return this.buildTransparencySnapshot({\n snapshotKind: 'preview',\n governance,\n });\n }\n\n public async previewTransparencyAction() {\n return this.previewTransparency();\n }\n\n public async runReview(options: RunContentReviewOptions = {}) {\n const governance = await this.requireGovernance('review execution');\n\n if (!this.id) {\n throw new Error('Cannot review unsaved content');\n }\n\n const policyKey = options.policyKey || options.kind || 'custom';\n const policy = getContentReviewPolicy(policyKey, governance.reviewPolicies);\n const kind =\n options.kind ||\n getContentReviewKind(policyKey, governance.reviewPolicies);\n const facts =\n options.facts !== undefined\n ? options.facts\n : governance.factLinkingEnabled &&\n (kind === 'facts' || Boolean(options.factIds?.length))\n ? await this.getFacts({\n latestOnly: true,\n includeSuperseded: false,\n })\n : [];\n const filteredFacts =\n options.factIds && options.factIds.length > 0\n ? facts.filter((fact) => options.factIds?.includes(fact.id as string))\n : facts;\n const reviewPrompt = buildContentReviewPrompt({\n kind,\n content: this,\n facts: filteredFacts,\n policy,\n customInstructions: options.instructions,\n });\n const resolvedPrompt = await resolvePrompt(smrtContentReviewPrompt.key, {\n db: this.options.db,\n tenantId: this.tenantId,\n variables: {\n contentBody: this.body,\n contentDescription: this.description ?? '',\n contentId: this.id ?? '',\n contentTitle: this.title,\n kind,\n policyKey: policy?.key || kind,\n reviewPrompt,\n },\n });\n const reviewFingerprint = await this.buildReviewFingerprint(policyKey);\n const ai = this.ai as {\n message?: (\n prompt: string,\n options?: Record<string, unknown>,\n ) => Promise<string>;\n };\n if (!ai?.message) {\n throw new Error('AI client is not configured for content reviews');\n }\n\n const rawResponse = await ai.message(\n resolvedPrompt.text,\n promptMessageOptions(resolvedPrompt.ai),\n );\n const result = parseContentReviewResponse(rawResponse);\n const version =\n options.createVersion === false\n ? null\n : await this.createVersion({\n kind: 'review',\n summary: result.summary,\n metadata: {\n kind,\n policyKey,\n reviewFingerprint,\n },\n });\n\n const reviews = await this.getContentReviewCollection();\n return reviews.createFromResult({\n contentId: this.id as string,\n contentVersionId: version?.id as string | undefined,\n kind,\n policyKey,\n reviewer: options.reviewer || 'system',\n result,\n metadata: {\n ...(options.metadata || {}),\n prompt: resolvedPrompt.text,\n rawResponse,\n reviewFingerprint,\n factIds: filteredFacts.map((fact) => fact.id),\n },\n tenantId: this.tenantId,\n });\n }\n\n public async runReviewAction(options: RunContentReviewOptions = {}) {\n let review: ContentReview;\n\n if (options.kind === 'facts') {\n review = await this.reviewFacts(options);\n } else if (options.kind === 'safety') {\n review = await this.reviewSafety(options);\n } else {\n review = await this.runReview(options);\n }\n\n return serializeContentReview(review);\n }\n\n public async reviewFacts(\n options: Omit<RunContentReviewOptions, 'kind'> = {},\n ) {\n return this.runReview({\n ...options,\n kind: 'facts',\n policyKey: options.policyKey || 'facts',\n });\n }\n\n public async reviewSafety(\n options: Omit<RunContentReviewOptions, 'kind'> = {},\n ) {\n const governance = await this.requireGovernance('safety review');\n const safetyPolicy = getContentReviewPolicy(\n options.policyKey || 'safety',\n governance.reviewPolicies,\n );\n const baseInstructions = safetyPolicy?.instructions || '';\n\n return this.runReview({\n ...options,\n kind: 'safety',\n policyKey: options.policyKey || 'safety',\n instructions:\n options.instructions && baseInstructions\n ? `${baseInstructions}\\n\\nAdditional app-level guidance:\\n${options.instructions}`\n : options.instructions || baseInstructions,\n });\n }\n\n public async getCorrections() {\n if (!this.id) {\n return [];\n }\n\n const corrections = await this.getContentCorrectionCollection();\n return corrections.listForContent(this.id as string);\n }\n\n public async listCorrections() {\n const corrections = await this.getCorrections();\n return corrections.map(serializeContentCorrection);\n }\n\n public async issueCorrection(options: IssueContentCorrectionOptions) {\n const governance = await this.requireGovernance('corrections');\n\n if (!this.id) {\n throw new Error('Cannot issue a correction for unsaved content');\n }\n\n let replacementFactId = '';\n if (\n governance.factLinkingEnabled &&\n options.factId &&\n options.correctedFactText\n ) {\n const facts = await this.getFactCollection();\n const existing = await facts.get({ id: options.factId });\n if (!existing) {\n throw new Error(`Fact not found for correction: ${options.factId}`);\n }\n\n // Create the correction branch directly so editorial corrections do not\n // block on synchronous embedding generation inside facts.branch().\n const replacement = await facts.create({\n textRefined: options.correctedFactText,\n textRaw: options.correctedFactText,\n type: existing.getType(),\n domain: existing.domain,\n status: 'active',\n tenantId: existing.tenantId ?? this.tenantId ?? null,\n previousFactId: options.factId,\n evolutionType: 'correction',\n });\n existing.status = 'superseded';\n await existing.save();\n replacementFactId = replacement.id as string;\n await this.addFact(replacementFactId);\n }\n\n const version =\n options.createVersion === false\n ? null\n : await this.createVersion({\n kind: 'correction',\n summary: options.summary,\n metadata: {\n factId: options.factId || null,\n replacementFactId: replacementFactId || null,\n },\n });\n const correctionDraft =\n options.createVersion === false\n ? null\n : await this.buildCorrectionDraftSnapshot(options, replacementFactId);\n const draftVersion =\n options.createVersion === false || !correctionDraft\n ? null\n : await this.createVersion({\n kind: 'draft',\n summary: `Auto-created correction draft: ${options.summary}`,\n snapshot: correctionDraft.snapshot,\n metadata: {\n ...correctionDraft.metadata,\n sourceCorrectionVersionId: (version?.id as string) || null,\n sourceCorrectionVersionNumber: version?.version ?? null,\n },\n });\n\n const corrections = await this.getContentCorrectionCollection();\n const shouldPublish = options.publish ?? this.status === 'published';\n return corrections.issue({\n contentId: this.id as string,\n contentVersionId: (version?.id as string) || '',\n factId: options.factId || '',\n replacementFactId,\n correctionType: options.correctionType || 'fact',\n status: shouldPublish ? 'published' : 'draft',\n summary: options.summary,\n incorrectText: options.incorrectText || '',\n correctedText: options.correctedText || options.correctedFactText || '',\n publicNote: options.publicNote || '',\n metadata: {\n ...(options.metadata || {}),\n autoGeneratedDraft: Boolean(draftVersion),\n draftVersionId: (draftVersion?.id as string) || null,\n draftVersionNumber: draftVersion?.version ?? null,\n sourceCorrectionVersionId: (version?.id as string) || null,\n sourceCorrectionVersionNumber: version?.version ?? null,\n correctionProfileKey: governance.correctionProfileKey || null,\n },\n tenantId: this.tenantId,\n publishedAt: shouldPublish ? new Date() : null,\n });\n }\n\n public async issueCorrectionAction(options: IssueContentCorrectionOptions) {\n const correction = await this.issueCorrection(options);\n return serializeContentCorrection(correction);\n }\n\n public async listVersions() {\n const versions = await this.getVersions();\n return versions.map(serializeContentVersion);\n }\n\n public async mutateVersionAction(\n options: CreateContentVersionOptions & {\n action?: string;\n versionNumber?: number | string;\n } = {},\n ) {\n if (options.action === 'restore') {\n const versionNumber = Number(options.versionNumber);\n if (!Number.isFinite(versionNumber)) {\n throw new Error('versionNumber is required to restore a version');\n }\n\n const restored = await this.restoreFromVersion(versionNumber);\n return serializeContent(restored);\n }\n\n const version = await this.createVersion(options);\n return serializeContentVersion(version);\n }\n\n /**\n * Note: toJSON() is inherited from SmrtObject\n *\n * The parent implementation handles:\n * - STI discriminator (_meta_type) for polymorphic queries\n * - Meta field extraction (_meta_data) for child-specific fields\n * - Automatic serialization of all fields from manifest\n *\n * DO NOT override toJSON() unless you call super.toJSON() first.\n * See issue #377 for details on why this override was removed.\n */\n\n // ============================================\n // Category Helper Methods\n // ============================================\n\n /**\n * Get category segments as array\n * @example 'politics/local' -> ['politics', 'local']\n */\n getCategorySegments(): string[] {\n if (!this.category) return [];\n return this.category.split('/').filter(Boolean);\n }\n\n /**\n * Get parent category path\n * @example 'politics/local/town' -> 'politics/local'\n * @example 'politics' -> null\n */\n getParentCategory(): string | null {\n const segments = this.getCategorySegments();\n if (segments.length <= 1) return null;\n return segments.slice(0, -1).join('/');\n }\n\n /**\n * Get root (top-level) category\n * @example 'politics/local/town' -> 'politics'\n */\n getRootCategory(): string | null {\n const segments = this.getCategorySegments();\n return segments[0] || null;\n }\n\n /**\n * Get all ancestor category paths (for breadcrumbs)\n * @example 'politics/local' -> ['politics', 'politics/local']\n */\n getAncestorPaths(): string[] {\n const segments = this.getCategorySegments();\n return segments.map((_, i) => segments.slice(0, i + 1).join('/'));\n }\n\n /**\n * Check if content belongs to a category (optionally including subcategories)\n * @param categoryPath - Category to check\n * @param includeChildren - If true, matches 'politics' for content in 'politics/local'\n */\n isInCategory(categoryPath: string, includeChildren = true): boolean {\n if (!this.category) return false;\n if (includeChildren) {\n return (\n this.category === categoryPath ||\n this.category.startsWith(`${categoryPath}/`)\n );\n }\n return this.category === categoryPath;\n }\n\n // ============================================\n // Asset Relationship Methods\n // ============================================\n\n /**\n * Get all assets associated with this content\n * @param relationship - Optional filter by relationship type (e.g., 'thumbnail', 'attachment')\n * @returns Promise resolving to array of assets\n */\n async getAssets(relationship?: string): Promise<Asset[]> {\n if (!this.id) {\n return [];\n }\n\n return this.resolveAssetsForLinks(\n await this.getContentAssetLinks(relationship),\n );\n }\n\n /**\n * Add an asset to this content with a relationship type\n * @param asset - The asset to associate\n * @param relationship - Relationship type (e.g., 'thumbnail', 'attachment', 'inline')\n * @param sortOrder - Optional sort order for display\n */\n async addAsset(\n asset: Asset,\n relationship = 'attachment',\n sortOrder = 0,\n ): Promise<void> {\n if (!this.id || !asset.id) {\n throw new Error('Cannot associate unsaved content or asset');\n }\n\n // Validate relationship - must start with letter/underscore, contain only alphanumeric and underscores\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(relationship)) {\n throw new Error(\n `Invalid relationship type \"${relationship}\"; must start with a letter or underscore and contain only letters, digits, and underscores`,\n );\n }\n\n // Validate sortOrder is a reasonable integer\n if (\n !Number.isInteger(sortOrder) ||\n sortOrder < 0 ||\n sortOrder > 2147483647\n ) {\n throw new Error(\n `Invalid sortOrder \"${sortOrder}\"; must be a non-negative integer`,\n );\n }\n\n const contentAssets = await this.getContentAssetCollection();\n await contentAssets.attach(this.id, asset.id, {\n relationship,\n sortOrder,\n tenantId: this.tenantId,\n });\n }\n\n /**\n * Remove an asset from this content\n * @param assetId - ID of the asset to remove\n * @param relationship - Optional specific relationship to remove (removes all if not specified)\n */\n async removeAsset(assetId: string, relationship?: string): Promise<void> {\n if (!this.id) {\n return;\n }\n\n try {\n const contentAssets = await this.getContentAssetCollection();\n await contentAssets.detach(\n this.id,\n assetId,\n relationship ? { relationship } : {},\n );\n } catch (error) {\n if (!isMissingTableError(error, 'content_assets')) {\n throw error;\n }\n }\n }\n\n // ============================================\n // Metadata Accessors (MetadataAccessor contract)\n // ============================================\n\n /**\n * Get the full metadata record. Always returns a plain object — never\n * `null`, never an array — so callers can safely read nested keys without\n * defensive checks.\n *\n * Pure read with no side-effect on `this.metadata`: if the field is\n * currently `null` (e.g. fresh from the DB) or non-record-shaped, an\n * empty object is returned but the field is **not** mutated. This avoids\n * accidentally marking the object dirty during a read, which would\n * otherwise cause SmrtObject's save lifecycle to write `{}` back over a\n * NULL column on the next save. Callers that want to normalise the\n * stored field should use {@link Content.setMetadata}.\n */\n getMetadata(): Record<string, unknown> {\n return isPlainMetadataRecord(this.metadata) ? this.metadata : {};\n }\n\n /**\n * Replace the full metadata record. Passing `null`/`undefined` (or any\n * non-record value such as an array) clears it to an empty object so\n * downstream readers can rely on the field always being a plain object.\n */\n setMetadata(metadata: Record<string, unknown> | null | undefined): void {\n this.metadata = isPlainMetadataRecord(metadata) ? { ...metadata } : {};\n }\n\n /**\n * Shallow-merge a patch over the current metadata. Returns the resulting\n * record so callers can chain reads without re-reading the field. Unlike\n * {@link Content.getMetadata}, this method does intentionally write back\n * to `this.metadata` because the merge is a write.\n */\n updateMetadata(\n patch: Partial<Record<string, unknown>>,\n ): Record<string, unknown> {\n const next = { ...this.getMetadata(), ...(patch ?? {}) };\n this.metadata = next;\n return next;\n }\n\n // ============================================\n // Thumbnail Convenience Methods\n // ============================================\n\n /**\n * Get the thumbnail image for this content\n * @returns Promise resolving to the thumbnail Image or null\n */\n async getThumbnail(): Promise<Image | null> {\n if (!this.thumbnailAssetId) {\n return null;\n }\n\n const images = await ImageCollection.create({\n db: this.options?.db,\n });\n\n return images.get({ id: this.thumbnailAssetId });\n }\n\n /**\n * Set the thumbnail image for this content\n * @param image - The image to set as thumbnail\n */\n async setThumbnail(image: Image): Promise<void> {\n // Add as asset with 'thumbnail' relationship\n await this.addAsset(image, 'thumbnail', 0);\n\n // Update thumbnailAssetId\n this.thumbnailAssetId = image.id ?? null;\n await this.save();\n }\n\n /**\n * Generate a thumbnail for this content using the specified strategy\n *\n * @param options - Thumbnail generation options including strategy\n * @returns Promise resolving to the generated Image\n *\n * @example Headline card thumbnail\n * ```typescript\n * const thumbnail = await content.generateThumbnail({\n * strategy: 'headline-card',\n * brandColor: '#1a56db',\n * logoUrl: 'https://example.com/logo.png'\n * });\n * ```\n *\n * @example Static map thumbnail (requires metadata.latitude/longitude)\n * ```typescript\n * const thumbnail = await content.generateThumbnail({\n * strategy: 'static-map',\n * mapProvider: 'mapbox'\n * });\n * ```\n *\n * @example AI-generated thumbnail\n * ```typescript\n * const thumbnail = await content.generateThumbnail({\n * strategy: 'ai-generate'\n * });\n * ```\n */\n async generateThumbnail(options: ThumbnailOptions): Promise<Image> {\n const generator = new ThumbnailGenerator(this, this.options);\n const image = await generator.generate(options);\n await this.setThumbnail(image);\n return image;\n }\n}\n","import { definePrompt } from '@happyvertical/smrt-prompts';\n\nexport const contentEditorSessionPrompt = definePrompt({\n key: 'content.chat.contentEditorSession',\n template:\n 'You are an AI assistant collaborating with the user to edit and improve a specific piece of content.',\n ai: {\n temperature: 0.7,\n maxTokens: 2000,\n },\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n\nexport const contentEditorInteractionPrompt = definePrompt({\n key: 'content.chat.contentEditorInteraction',\n template: `CURRENT CONTENT FORM STATE:\n- title: {title}\n- description: {description}\n- type: {type}\n- status: {status}\n- state: {state}\n- body:\n{body}\n{references}\n\nINSTRUCTIONS FOR CO-AUTHORING:\nYou are a content co-author. You can update any of the above fields.\n\nWhen you want to make changes to the content, output a JSON code block with a top-level \"fields\" object containing only the fields you want to change.\n\nValid field names: title, description, type, status, state, body.\nValid status values: draft, published, archived.\nValid state values: active, highlighted, deprecated.\nValid type values: article, document, mirror.\n\nRULES:\n- Only include fields you are actually changing.\n- If the user asks to write or rewrite content, update the \"body\" field.\n- If the user asks to change the title, update the \"title\" field.\n- You can change multiple fields at once.\n- Always respond conversationally before the JSON block.\n- The JSON block will be automatically applied to the editor.`,\n ai: {\n temperature: 0.7,\n maxTokens: 2000,\n },\n editable: {\n template: true,\n profile: true,\n model: true,\n params: true,\n },\n});\n","import type { ResolvedPrompt } from '@happyvertical/smrt-prompts';\n\ntype SessionContext = Record<string, unknown>;\n\nexport interface ContentChatAISelection {\n profile: string | null;\n provider: string | null;\n model: string | null;\n allowedModels: string[] | null;\n temperature: number | null;\n maxTokens: number | null;\n}\n\nexport interface ContentEditorInteractionInput {\n fields?: Record<string, unknown> | null;\n currentEditorState?: unknown;\n references?: string | null;\n}\n\nconst CONTENT_PROMPT_DELIMITER = 'SMRT_CONTENT_FIELD';\n\nfunction readString(\n context: SessionContext,\n key: keyof ContentChatAISelection,\n): string | null {\n const value = context[key];\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nfunction readNumber(\n context: SessionContext,\n key: keyof ContentChatAISelection,\n): number | null {\n const value = context[key];\n return typeof value === 'number' ? value : null;\n}\n\nexport function inferProviderFromModel(model: string): string {\n const normalized = model.toLowerCase();\n const providerPrefix = normalized.match(/^([a-z0-9_-]+)\\//)?.[1];\n if (providerPrefix === 'openrouter') {\n return 'openrouter';\n }\n if (providerPrefix === 'anthropic') {\n return 'anthropic';\n }\n if (providerPrefix === 'gemini' || providerPrefix === 'google') {\n return 'gemini';\n }\n if (providerPrefix === 'openai') {\n return 'openai';\n }\n\n const modelTokens = normalized.split(/[^a-z0-9]+/).filter(Boolean);\n if (\n modelTokens.includes('claude') ||\n modelTokens.includes('sonnet') ||\n modelTokens.includes('haiku') ||\n modelTokens.includes('opus')\n ) {\n return 'anthropic';\n }\n\n if (modelTokens.includes('gemini')) {\n return 'gemini';\n }\n\n return 'openai';\n}\n\nexport function getContentChatAISelection(\n context: SessionContext,\n): ContentChatAISelection {\n const allowedModels = context.allowedModels;\n return {\n profile: readString(context, 'profile'),\n provider: readString(context, 'provider'),\n model: readString(context, 'model'),\n allowedModels:\n Array.isArray(allowedModels) &&\n allowedModels.every((value) => typeof value === 'string')\n ? allowedModels\n : null,\n temperature: readNumber(context, 'temperature'),\n maxTokens: readNumber(context, 'maxTokens'),\n };\n}\n\nexport function resolveContentChatModelSelection(\n context: SessionContext,\n requestedModel: string | null | undefined,\n fallbackModel: string,\n): { model: string; provider: string } {\n const stored = getContentChatAISelection(context);\n const model = requestedModel || stored.model || fallbackModel;\n const provider =\n stored.provider && stored.model && model === stored.model\n ? stored.provider\n : inferProviderFromModel(model);\n\n return { model, provider };\n}\n\nexport function buildContentChatModelUpdates(\n context: SessionContext,\n requestedModel: string | null | undefined,\n fallbackModel: string,\n): Record<string, string | null> {\n const stored = getContentChatAISelection(context);\n const { model, provider } = resolveContentChatModelSelection(\n context,\n requestedModel,\n fallbackModel,\n );\n\n return {\n model,\n provider,\n ...(stored.profile && stored.model && model !== stored.model\n ? { profile: null }\n : {}),\n };\n}\n\nexport function buildContentEditorSessionContext(\n contentId: string,\n prompt: ResolvedPrompt,\n): SessionContext {\n return {\n contentId,\n ...(prompt.ai.profile ? { profile: prompt.ai.profile } : {}),\n ...(prompt.ai.provider ? { provider: prompt.ai.provider } : {}),\n ...(prompt.ai.model ? { model: prompt.ai.model } : {}),\n ...(prompt.ai.allowedModels\n ? { allowedModels: prompt.ai.allowedModels }\n : {}),\n ...(prompt.ai.temperature !== undefined\n ? { temperature: prompt.ai.temperature }\n : {}),\n ...(prompt.ai.maxTokens !== undefined\n ? { maxTokens: prompt.ai.maxTokens }\n : {}),\n };\n}\n\nfunction displayValue(value: unknown, fallback = '(empty)'): string {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : fallback;\n }\n\n if (value === undefined || value === null) {\n return fallback;\n }\n\n return String(value);\n}\n\nfunction delimitPromptValue(name: string, value: string): string {\n // Strip any forged SMRT_ delimiter markers from the user value so it can't\n // close the wrapper early or open a fake field. The previous `[^>]*` stopped\n // at the first `>` (so `<<<SMRT_X>injected>>>` slipped through) and was\n // case-sensitive (so `<<<smrt_...>>>` slipped through). `[\\s\\S]*?` is\n // non-greedy and `>`-tolerant, and the `i` flag closes the case bypass\n // (#1387 minor).\n const sanitizedValue = value.replace(/<<<\\s*(?:END_)?SMRT_[\\s\\S]*?>>>/gi, '');\n return `<<<${CONTENT_PROMPT_DELIMITER} name=${name}>>>\\n${sanitizedValue}\\n<<<END_${CONTENT_PROMPT_DELIMITER}>>>`;\n}\n\nexport function buildContentEditorInteractionVariables(\n input: ContentEditorInteractionInput,\n): Record<string, string> {\n const fields = input.fields ?? {};\n\n return {\n title: delimitPromptValue('title', displayValue(fields.title)),\n description: delimitPromptValue(\n 'description',\n displayValue(fields.description),\n ),\n type: displayValue(fields.type, 'article'),\n status: displayValue(fields.status, 'draft'),\n state: displayValue(fields.state, 'active'),\n body: delimitPromptValue(\n 'body',\n displayValue(fields.body ?? input.currentEditorState),\n ),\n references: input.references ?? '',\n };\n}\n","import { type AIClientOptions, getAI } from '@happyvertical/ai';\nimport { ChatService } from '@happyvertical/smrt-chat';\nimport { sendAgentReply } from '@happyvertical/smrt-chat/internal/agent-runtime';\nimport {\n type ResolvedPrompt,\n resolvePrompt,\n} from '@happyvertical/smrt-prompts';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { sanitizeHtml, stripHtml } from './body-format';\nimport {\n contentEditorInteractionPrompt,\n contentEditorSessionPrompt,\n} from './content-chat-prompts';\nimport {\n buildContentChatModelUpdates,\n buildContentEditorInteractionVariables,\n buildContentEditorSessionContext,\n getContentChatAISelection,\n resolveContentChatModelSelection,\n} from './content-chat-session';\n\nconst CONTENT_EDITOR_AGENT_ID = 'content_editor';\nconst REFERENCE_TITLE_MAX_LENGTH = 200;\nconst REFERENCE_BODY_MAX_LENGTH = 2000;\nconst MAX_REFERENCE_IDS = 20;\n\nexport type ContentChatAIConfig = AIClientOptions & {\n apiKey?: string;\n allowedModels?: string[];\n model?: string;\n defaultModel?: string;\n provider?: string;\n type?: string;\n};\n\nexport interface ContentChatCollectionLike {\n db: DatabaseInterface;\n // Accepts either a raw id or a bound `{ id, tenantId }` filter so the content\n // lookup can be tenant-bound at the query level (S5 #1392) rather than doing a\n // cross-tenant `get(id)` followed only by a post-filter. A real SmrtCollection\n // satisfies this — its `get` takes `string | Record<string, any>`.\n get(filter: string | Record<string, unknown>): Promise<unknown>;\n}\n\nexport interface ContentChatSessionInput {\n db: DatabaseInterface;\n contents?: ContentChatCollectionLike;\n tenantId?: string | null;\n profileId?: string | null;\n contentId: string;\n resolvePromptFn?: typeof resolvePrompt;\n}\n\nexport interface CreateContentChatThreadInput extends ContentChatSessionInput {\n title: string;\n sessionId: string;\n model?: string | null;\n initializeChatCollections?: () => Promise<void>;\n}\n\nexport interface ListContentChatThreadMessagesInput {\n db: DatabaseInterface;\n contents?: ContentChatCollectionLike;\n tenantId?: string | null;\n profileId?: string | null;\n contentId: string;\n threadId: string;\n}\n\nexport interface SendContentChatThreadMessageInput {\n contents: ContentChatCollectionLike;\n tenantId?: string | null;\n profileId?: string | null;\n contentId: string;\n threadId: string;\n content: string;\n sessionId: string;\n model?: string | null;\n currentEditorState?: unknown;\n referenceIds?: unknown;\n formFields?: Record<string, unknown> | null;\n aiConfig?: ContentChatAIConfig | null;\n env?: Record<string, string | undefined>;\n resolvePromptFn?: typeof resolvePrompt;\n getAIClient?: typeof getAI;\n resolveAI?: (\n input: ResolveContentChatAIInput,\n ) => Promise<ResolvedContentChatAI | null> | ResolvedContentChatAI | null;\n}\n\nexport interface ResolveContentChatAIInput {\n aiConfig: ContentChatAIConfig;\n env: Record<string, string | undefined>;\n sessionContext: Record<string, unknown>;\n requestedModel?: string | null;\n interactionPrompt: ResolvedPrompt;\n}\n\nexport interface ResolvedContentChatAI {\n clientOptions: ContentChatAIConfig;\n model: string;\n provider?: string | null;\n profile?: string | null;\n temperature?: number | null;\n maxTokens?: number | null;\n}\n\nfunction resolveTenantScope(value: string | null | undefined): string | null {\n return asNonEmptyString(value);\n}\n\nfunction resolveChatTenantId(value: string | null | undefined): string {\n return resolveTenantScope(value) ?? 'global';\n}\n\nfunction resolveProfileId(value: string | null | undefined): string {\n return value || 'system';\n}\n\nfunction asNonEmptyString(value: unknown): string | null {\n return typeof value === 'string' && value.trim().length > 0\n ? value.trim()\n : null;\n}\n\nfunction parseJsonObject(value: unknown): Record<string, unknown> {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n if (typeof value !== 'string' || value.length === 0) {\n return {};\n }\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction getSessionContext(session: unknown): Record<string, unknown> {\n const candidate = session as {\n getSessionContext?: () => Record<string, unknown>;\n sessionContext?: unknown;\n };\n return typeof candidate.getSessionContext === 'function'\n ? candidate.getSessionContext()\n : parseJsonObject(candidate.sessionContext);\n}\n\nfunction isActiveSession(session: unknown): boolean {\n const candidate = session as { isActive?: () => boolean; status?: string };\n return typeof candidate?.isActive === 'function'\n ? candidate.isActive()\n : candidate?.status === 'active';\n}\n\nfunction isSchemaMissingError(error: unknown): boolean {\n const message = String((error as { message?: unknown })?.message ?? '');\n return (\n (error as { code?: string })?.code === 'DB_SCHEMA_MISSING' ||\n /^no such table:\\s+(?:agent_sessions|chat_(?:rooms|threads|messages|participants|reactions))\\b/i.test(\n message,\n ) ||\n /relation [\"']?(?:agent_sessions|chat_(?:rooms|threads|messages|participants|reactions))[\"']? does not exist/i.test(\n message,\n )\n );\n}\n\nexport function contentChatSessionMatchesContent(\n session: unknown,\n contentId: string,\n): boolean {\n const context = getSessionContext(session);\n return asNonEmptyString(context.contentId) === contentId;\n}\n\n/**\n * Stable agent-session key scoping a content-editor session to its content\n * (S5 #1392).\n *\n * `createAgentSession` reuses ANY active session for the same\n * agent/profile/tenant unless a `sessionKey` is supplied. The content-editor\n * agent (`content_editor`) opens a distinct conversation per content, so a\n * keyless create would reuse — and the handler would then REWRITE — a session\n * created for a different content, returning the other content's room/threads.\n * Keying the session on the content id makes a new content always create a\n * distinct session/room.\n */\nexport function contentChatSessionKey(contentId: string): string {\n return `content:${contentId}`;\n}\n\n/**\n * Tenant-bound content lookup (S5 #1392).\n *\n * Binds `tenantId` (including the `null` \"global\" scope) into the lookup itself\n * so a content row from another tenant can never be RESOLVED first and only\n * rejected after the fact. The `referenceBelongsToTenant` post-filter at every\n * call site remains as defense-in-depth.\n */\nfunction getContentForTenant(\n contents: ContentChatCollectionLike,\n tenantScope: string | null,\n id: string,\n): Promise<unknown> {\n return contents.get({ id, tenantId: tenantScope });\n}\n\nasync function assertContentExistsForTenant(\n contents: ContentChatCollectionLike | undefined,\n tenantScope: string | null,\n contentId: string,\n): Promise<void> {\n if (!contents) {\n return;\n }\n\n const content = await getContentForTenant(contents, tenantScope, contentId);\n if (!content || !referenceBelongsToTenant(content, tenantScope)) {\n throw new Error('Content not found');\n }\n}\n\nexport function contentChatSessionIsAuthorized(\n session: unknown,\n input: {\n profileId: string;\n contentId: string;\n },\n): boolean {\n const candidate = session as {\n agentId?: string;\n participantProfileId?: string;\n };\n return (\n Boolean(session) &&\n isActiveSession(session) &&\n candidate.agentId === CONTENT_EDITOR_AGENT_ID &&\n candidate.participantProfileId === input.profileId &&\n contentChatSessionMatchesContent(session, input.contentId)\n );\n}\n\nexport function contentChatMessageToJSON(\n message: unknown,\n): Record<string, unknown> {\n return typeof (message as { toJSON?: () => unknown })?.toJSON === 'function'\n ? ((message as { toJSON: () => unknown }).toJSON() as Record<\n string,\n unknown\n >)\n : (message as Record<string, unknown>);\n}\n\nexport function serializeContentChatMessageForUI(\n message: unknown,\n): Record<string, unknown> {\n const json = contentChatMessageToJSON(message);\n return {\n ...json,\n senderName: json.role === 'user' ? 'You' : 'AI Assistant',\n senderAvatarUrl: json.senderAvatarUrl || '',\n };\n}\n\nexport async function getOrCreateContentEditorChatSession(\n input: ContentChatSessionInput,\n) {\n const tenantScope = resolveTenantScope(input.tenantId);\n const tenantId = resolveChatTenantId(input.tenantId);\n const profileId = resolveProfileId(input.profileId);\n const chatService = await ChatService.create({ tenantId, db: input.db });\n await chatService.initialize();\n await assertContentExistsForTenant(\n input.contents,\n tenantScope,\n input.contentId,\n );\n\n let activeSession = null;\n try {\n // Tenant-bound session list via the ChatService facade (S5 #1392): the raw\n // `agentSessions.list({ where })` reach-in was tenant-UNBOUND and could\n // surface a session from another tenant before authorization.\n const agentSessions = await chatService.findActiveAgentSessions({\n tenantId,\n agentId: CONTENT_EDITOR_AGENT_ID,\n participantProfileId: profileId,\n });\n activeSession =\n agentSessions.find((session: unknown) =>\n contentChatSessionMatchesContent(session, input.contentId),\n ) ?? null;\n } catch (error) {\n if (!isSchemaMissingError(error)) {\n throw error;\n }\n // Chat tables may not exist yet. createAgentSession will surface the\n // concrete schema error if the runtime cannot create/use them.\n }\n\n if (!activeSession) {\n const resolvedPrompt = await (input.resolvePromptFn ?? resolvePrompt)(\n contentEditorSessionPrompt.key,\n {\n db: input.db,\n tenantId,\n },\n );\n // Scope the session to this content (S5 #1392) so a new contentId can never\n // reuse — and have its context rewritten over — a session/room created for a\n // different content. The handler-side `contentChatSessionMatchesContent`\n // filter above already narrows reads; this binds the create/reuse path too.\n const { session } = await chatService.createAgentSession({\n tenantId,\n agentId: CONTENT_EDITOR_AGENT_ID,\n actorProfileId: profileId,\n systemPrompt: resolvedPrompt.text,\n sessionKey: contentChatSessionKey(input.contentId),\n });\n await session.updateSessionContext(\n buildContentEditorSessionContext(input.contentId, resolvedPrompt),\n );\n activeSession = session;\n }\n\n let threads: unknown[] = [];\n try {\n const chatRoomId = (activeSession as { chatRoomId?: string | null })\n .chatRoomId;\n if (!chatRoomId) {\n throw new Error('Agent session is missing a chat room');\n }\n // Membership- and tenant-gated thread list via the facade (S5 #1392); the\n // editor (profileId) is the room owner, so the membership check passes.\n threads = await chatService.listRoomThreads({\n roomId: chatRoomId,\n actorProfileId: profileId,\n tenantId,\n });\n } catch {\n // Threads table may not exist yet.\n }\n\n return {\n chatService,\n session: activeSession,\n threads,\n };\n}\n\nexport async function createContentEditorChatThread(\n input: CreateContentChatThreadInput,\n) {\n await input.initializeChatCollections?.();\n\n const tenantScope = resolveTenantScope(input.tenantId);\n const tenantId = resolveChatTenantId(input.tenantId);\n const profileId = resolveProfileId(input.profileId);\n const chatService = await ChatService.create({ tenantId, db: input.db });\n await assertContentExistsForTenant(\n input.contents,\n tenantScope,\n input.contentId,\n );\n // Tenant-bound session lookup via the facade (S5 #1392): the raw\n // `agentSessions.get(id)` was tenant-UNBOUND and could select a session from\n // another tenant before the ownership/context check below.\n const session = await chatService.getAgentSession({\n agentSessionId: input.sessionId,\n tenantId,\n });\n\n if (\n !contentChatSessionIsAuthorized(session, {\n profileId,\n contentId: input.contentId,\n })\n ) {\n throw new Error('Active session not found');\n }\n\n const chatRoomId = (session as { chatRoomId?: string | null }).chatRoomId;\n if (!chatRoomId) {\n throw new Error('Active session is missing a chat room');\n }\n\n let updates: Record<string, string | null> | null = null;\n if (input.model) {\n const ctx = getSessionContext(session);\n const allowedModels = Array.isArray(ctx.allowedModels)\n ? ctx.allowedModels\n : [];\n if (allowedModels.length > 0 && !allowedModels.includes(input.model)) {\n throw new Error('AI model is not allowed for content chat');\n }\n updates = buildContentChatModelUpdates(ctx, input.model, input.model);\n }\n\n // Member-checked thread creation via the facade (S5 #1392): the editor owns\n // the agent room, so the membership check passes. Replaces the raw\n // `threads.create(...)` that skipped the facade entirely.\n const thread = await chatService.startThread({\n tenantId,\n roomId: chatRoomId,\n actorProfileId: profileId,\n title: input.title,\n });\n\n if (updates) {\n const ctx = getSessionContext(session);\n if (Object.entries(updates).some(([key, value]) => ctx[key] !== value)) {\n await (\n session as { updateSessionContext: (value: unknown) => Promise<void> }\n ).updateSessionContext(updates);\n }\n }\n\n return { chatService, session, thread };\n}\n\nexport async function listContentEditorChatThreadMessages(\n input: ListContentChatThreadMessagesInput,\n) {\n const tenantScope = resolveTenantScope(input.tenantId);\n const tenantId = resolveChatTenantId(input.tenantId);\n const profileId = resolveProfileId(input.profileId);\n const chatService = await ChatService.create({ tenantId, db: input.db });\n await assertContentExistsForTenant(\n input.contents,\n tenantScope,\n input.contentId,\n );\n // Tenant-bound thread + session lookups via the facade (S5 #1392): the raw\n // `threads.get(id)` / `agentSessions.list({ where })` reach-ins were\n // tenant-UNBOUND and could select cross-tenant chat state before authZ.\n const thread = await chatService.getThread({\n threadId: input.threadId,\n tenantId,\n });\n if (!thread) {\n throw new Error('Thread not found');\n }\n\n const roomId = (thread as { roomId?: string }).roomId;\n const activeSessions = await chatService.findActiveAgentSessions({\n tenantId,\n agentId: CONTENT_EDITOR_AGENT_ID,\n participantProfileId: profileId,\n });\n const authorizedSession = activeSessions.find(\n (session: unknown) =>\n (session as { chatRoomId?: string }).chatRoomId === roomId &&\n contentChatSessionMatchesContent(session, input.contentId),\n );\n if (!authorizedSession) {\n throw new Error('Thread not found');\n }\n\n // Membership- + tenant-gated thread message read via the facade (S5 #1392);\n // the editor owns the room, so the membership check passes. Returned\n // chronological (oldest-first) by the facade.\n const messages = await chatService.getThreadMessages({\n threadId: input.threadId,\n actorProfileId: profileId,\n tenantId,\n limit: 100,\n });\n\n return {\n chatService,\n thread,\n messages,\n };\n}\n\nfunction getEnvApiKey(\n provider: string,\n env: Record<string, string | undefined>,\n): string | undefined {\n return provider === 'anthropic'\n ? env.ANTHROPIC_API_KEY\n : provider === 'gemini'\n ? env.GEMINI_API_KEY\n : env.OPENAI_API_KEY;\n}\n\nfunction defaultEnv(): Record<string, string | undefined> {\n return typeof process !== 'undefined' ? process.env : {};\n}\n\nexport function resolveDefaultContentChatAI(\n input: ResolveContentChatAIInput,\n): ResolvedContentChatAI {\n const storedSelection = getContentChatAISelection(input.sessionContext);\n const fallbackModel =\n storedSelection.model ??\n input.aiConfig.model ??\n input.aiConfig.defaultModel ??\n input.requestedModel ??\n null;\n if (!fallbackModel) {\n throw new Error('AI model is not configured for content chat');\n }\n\n const { model, provider } = resolveContentChatModelSelection(\n input.sessionContext,\n input.requestedModel,\n fallbackModel,\n );\n const allowedModels =\n input.aiConfig.allowedModels ??\n input.interactionPrompt.ai.allowedModels ??\n storedSelection.allowedModels;\n if (\n Array.isArray(allowedModels) &&\n allowedModels.length > 0 &&\n !allowedModels.includes(model)\n ) {\n throw new Error('AI model is not allowed for content chat');\n }\n const apiKey = input.aiConfig.apiKey || getEnvApiKey(provider, input.env);\n\n return {\n clientOptions: {\n ...input.aiConfig,\n provider,\n apiKey,\n model,\n },\n model,\n provider,\n temperature: storedSelection.temperature ?? 0.7,\n maxTokens: storedSelection.maxTokens ?? 2000,\n };\n}\n\nfunction sanitizeReferenceText(value: unknown, maxLength: number): string {\n const text = stripHtml(sanitizeHtml(String(value ?? '')))\n .replace(/<<<(?:END_)?SMRT_[^>]*>>>/g, '')\n .replace(/<{2,}/g, '< <')\n .replace(/>{2,}/g, '> >')\n .replace(/\\s+/g, ' ')\n .trim();\n\n return text.length > maxLength ? `${text.slice(0, maxLength)}...` : text;\n}\n\nfunction sanitizeReferenceId(value: string): string {\n return value.replace(/[^\\w:.-]/g, '_').slice(0, 128);\n}\n\nfunction getReferenceTenantId(value: unknown): string | null {\n const candidate = value as {\n tenantId?: unknown;\n tenant_id?: unknown;\n } | null;\n return (\n asNonEmptyString(candidate?.tenantId) ??\n asNonEmptyString(candidate?.tenant_id)\n );\n}\n\nfunction referenceBelongsToTenant(\n ref: unknown,\n tenantScope: string | null,\n): boolean {\n const refTenantId = getReferenceTenantId(ref);\n if (tenantScope === null) {\n return !refTenantId;\n }\n return refTenantId === tenantScope;\n}\n\nasync function buildReferenceContext(\n contents: ContentChatCollectionLike,\n tenantScope: string | null,\n referenceIds: unknown,\n): Promise<string> {\n if (!Array.isArray(referenceIds) || referenceIds.length === 0) {\n return '';\n }\n\n const refTexts: string[] = [];\n const ids = [\n ...new Set(\n referenceIds\n .map(asNonEmptyString)\n .filter((id): id is string => Boolean(id)),\n ),\n ].slice(0, MAX_REFERENCE_IDS);\n for (const id of ids) {\n // Tenant-bound lookup (S5 #1392): bind tenantId into the query rather than\n // a cross-tenant get(id) + post-filter. The post-filter below stays as\n // defense-in-depth.\n const ref = (await getContentForTenant(contents, tenantScope, id)) as {\n title?: string;\n body?: string;\n tenantId?: string;\n tenant_id?: string;\n } | null;\n if (ref && referenceBelongsToTenant(ref, tenantScope)) {\n const safeTitle = sanitizeReferenceText(\n ref.title,\n REFERENCE_TITLE_MAX_LENGTH,\n );\n const safeBody = sanitizeReferenceText(\n ref.body,\n REFERENCE_BODY_MAX_LENGTH,\n );\n refTexts.push(\n `<<<SMRT_REFERENCE id=${sanitizeReferenceId(id)}>>>\\nTitle: ${safeTitle}\\nBody: ${safeBody}\\n<<<END_SMRT_REFERENCE>>>`,\n );\n }\n }\n\n return refTexts.length > 0\n ? `\\n\\nATTACHED REFERENCE MATERIALS:\\n${refTexts.join('\\n\\n')}\\n`\n : '';\n}\n\nexport async function sendContentEditorChatThreadMessage(\n input: SendContentChatThreadMessageInput,\n) {\n const tenantScope = resolveTenantScope(input.tenantId);\n const tenantId = resolveChatTenantId(input.tenantId);\n const profileId = resolveProfileId(input.profileId);\n const chatService = await ChatService.create({\n tenantId,\n db: input.contents.db,\n });\n await assertContentExistsForTenant(\n input.contents,\n tenantScope,\n input.contentId,\n );\n\n // Tenant-bound session + thread lookups via the facade (S5 #1392): the raw\n // `agentSessions.get(id)` / `threads.get(id)` reach-ins were tenant-UNBOUND\n // and could select cross-tenant chat state before the authorization below.\n const session = await chatService.getAgentSession({\n agentSessionId: input.sessionId,\n tenantId,\n });\n if (\n !contentChatSessionIsAuthorized(session, {\n profileId,\n contentId: input.contentId,\n })\n ) {\n throw new Error('Active session not found');\n }\n\n const thread = await chatService.getThread({\n threadId: input.threadId,\n tenantId,\n });\n if (\n !thread ||\n (thread as { roomId?: string }).roomId !==\n (session as { chatRoomId?: string | null }).chatRoomId\n ) {\n throw new Error('Thread not found');\n }\n\n const chatRoomId = (session as { chatRoomId?: string | null }).chatRoomId;\n if (!chatRoomId) {\n throw new Error('Active session is missing a chat room');\n }\n\n const references = await buildReferenceContext(\n input.contents,\n tenantScope,\n input.referenceIds,\n );\n const interactionPrompt = await (input.resolvePromptFn ?? resolvePrompt)(\n contentEditorInteractionPrompt.key,\n {\n db: input.contents.db,\n tenantId,\n variables: buildContentEditorInteractionVariables({\n fields: input.formFields,\n currentEditorState: input.currentEditorState,\n references,\n }),\n },\n );\n\n const sessionContext = getSessionContext(session);\n const aiConfig = input.aiConfig ?? {};\n const resolvedAI =\n (await input.resolveAI?.({\n aiConfig,\n env: input.env ?? defaultEnv(),\n sessionContext,\n requestedModel: input.model,\n interactionPrompt,\n })) ??\n resolveDefaultContentChatAI({\n aiConfig,\n env: input.env ?? defaultEnv(),\n sessionContext,\n requestedModel: input.model,\n interactionPrompt,\n });\n\n const userMessage = await chatService.sendMessage({\n tenantId,\n roomId: chatRoomId,\n threadId: (thread as { id: string }).id,\n actorProfileId: profileId,\n content: input.content,\n agentSessionId: (session as { id: string }).id,\n });\n\n // Membership- + tenant-gated thread history via the facade (S5 #1392); the\n // editor owns the room. Returned chronological (oldest-first) by the facade.\n const history = await chatService.getThreadMessages({\n threadId: (thread as { id: string }).id,\n actorProfileId: profileId,\n tenantId,\n limit: 10,\n });\n\n const conversation = history.map((message: unknown) => {\n const json = contentChatMessageToJSON(message);\n return {\n role: json.role as 'user' | 'assistant' | 'system',\n content: String(json.content ?? ''),\n };\n });\n\n const ai = await (input.getAIClient ?? getAI)(resolvedAI.clientOptions);\n\n const fullSystemPrompt =\n typeof (session as { systemPrompt?: unknown }).systemPrompt === 'string'\n ? (session as { systemPrompt: string }).systemPrompt\n : '';\n conversation.unshift({\n role: 'system',\n content: `${fullSystemPrompt}\\n\\n${interactionPrompt.text}`,\n });\n\n const response = await ai.chat(conversation, {\n model: resolvedAI.model,\n temperature: resolvedAI.temperature ?? 0.7,\n maxTokens: resolvedAI.maxTokens ?? 2000,\n });\n\n // Author the assistant reply AS the session's agent via the trusted internal\n // agent-runtime surface — never via the public sendMessage (which would force\n // the message to the caller's identity with role 'user') (S5 #1392).\n const agentMessage = await sendAgentReply(chatService, {\n tenantId,\n agentSessionId: (session as { id: string }).id,\n threadId: (thread as { id: string }).id,\n content: response.content,\n kind: 'assistant',\n });\n\n const updates = buildContentChatModelUpdates(\n sessionContext,\n input.model,\n resolvedAI.model,\n );\n updates.model = resolvedAI.model;\n if (resolvedAI.provider !== undefined) {\n updates.provider = resolvedAI.provider;\n }\n if (resolvedAI.profile !== undefined) {\n updates.profile = resolvedAI.profile;\n }\n if (\n Object.entries(updates).some(\n ([key, value]) => sessionContext[key] !== value,\n )\n ) {\n await (\n session as { updateSessionContext: (value: unknown) => Promise<void> }\n ).updateSessionContext(updates);\n }\n\n return {\n chatService,\n session,\n thread,\n userMessage,\n agentMessage,\n };\n}\n","import {\n getCurrentTenant,\n isSuperAdminBypass,\n isSystemContext,\n isTenancyEnabled,\n} from '@happyvertical/smrt-tenancy';\nimport type { DatabaseInterface } from '@happyvertical/sql';\n\nexport type ContentContributionChannel = 'web' | 'email';\n\nexport type ContentContributionStatus =\n | 'submitted'\n | 'quarantined'\n | 'needs_changes'\n | 'approved'\n | 'promoted'\n | 'rejected'\n | 'withdrawn';\n\nexport type ContentContributionIntakeDecision =\n | 'accepted'\n | 'quarantined'\n | 'rejected';\n\nexport type ContentContributorTrustLevel = 'standard' | 'trusted' | 'blocked';\n\nexport interface ContentContributionIntakeRules {\n maxFiles?: number | null;\n maxTotalBytes?: number | null;\n allowedMimePatterns?: string[];\n blockedMimePatterns?: string[];\n quarantineMimePatterns?: string[];\n blockedTextPatterns?: string[];\n quarantineTextPatterns?: string[];\n trustedOnly?: boolean;\n}\n\nexport interface ContentContributionPromotionMapping {\n targetContentType?: string | null;\n targetContentVariant?: string | null;\n targetContentStatus?: 'draft' | 'review';\n autoPromoteTrusted?: boolean;\n createAssets?: boolean;\n assetRelationship?: string;\n}\n\nexport interface ContentContributionTypeDefinition {\n key: string;\n label: string;\n enabled?: boolean;\n allowedChannels?: ContentContributionChannel[];\n allowText?: boolean;\n allowFiles?: boolean;\n allowEmptyText?: boolean;\n intakeRules?: ContentContributionIntakeRules;\n promotion?: ContentContributionPromotionMapping;\n metadata?: Record<string, unknown>;\n}\n\nexport interface PersistedContentContributionTypeRecord\n extends ContentContributionTypeDefinition {\n id?: string;\n tenantId?: string | null;\n createdAt?: string | null;\n updatedAt?: string | null;\n}\n\nexport interface ContentContributionConfig {\n types: ContentContributionTypeDefinition[];\n}\n\nexport interface ResolvedContentContributionType\n extends ContentContributionTypeDefinition {}\n\nexport interface ContentContributionTypeConfigState {\n effective: ContentContributionTypeDefinition[];\n persisted: PersistedContentContributionTypeRecord[];\n}\n\nexport interface ContentContributionConfigReadOptions {\n db?: DatabaseInterface | null;\n tenantId?: string | null;\n}\n\nexport interface EvaluateContributionIntakeOptions {\n contributionType: ContentContributionTypeDefinition;\n trustLevel?: ContentContributorTrustLevel;\n channel: ContentContributionChannel;\n title?: string | null;\n description?: string | null;\n body?: string | null;\n attachments?: Array<{\n mimeType?: string | null;\n size?: number | null;\n }>;\n}\n\nexport interface EvaluateContributionIntakeResult {\n decision: ContentContributionIntakeDecision;\n reasons: string[];\n}\n\nconst DEFAULT_CONTENT_CONTRIBUTION_CONFIG: ContentContributionConfig = {\n types: [],\n};\n\nlet contributionConfig = cloneContributionConfig(\n DEFAULT_CONTENT_CONTRIBUTION_CONFIG,\n);\n\nfunction cloneJSONArray<T>(items: T[] | null | undefined): T[] {\n return Array.isArray(items) ? items.map((item) => structuredClone(item)) : [];\n}\n\nfunction safeParseJSONObject(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction safeParseJSONArray<T>(raw: unknown): T[] {\n if (!raw) {\n return [];\n }\n\n if (Array.isArray(raw)) {\n return raw.map((item) => structuredClone(item)) as T[];\n }\n\n if (typeof raw !== 'string') {\n return [];\n }\n\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? (parsed as T[]) : [];\n } catch {\n return [];\n }\n}\n\nfunction cloneIntakeRules(\n rules: ContentContributionIntakeRules | null | undefined,\n): ContentContributionIntakeRules {\n return {\n maxFiles:\n typeof rules?.maxFiles === 'number' ? Math.max(0, rules.maxFiles) : null,\n maxTotalBytes:\n typeof rules?.maxTotalBytes === 'number'\n ? Math.max(0, rules.maxTotalBytes)\n : null,\n allowedMimePatterns: cloneJSONArray(rules?.allowedMimePatterns),\n blockedMimePatterns: cloneJSONArray(rules?.blockedMimePatterns),\n quarantineMimePatterns: cloneJSONArray(rules?.quarantineMimePatterns),\n blockedTextPatterns: cloneJSONArray(rules?.blockedTextPatterns),\n quarantineTextPatterns: cloneJSONArray(rules?.quarantineTextPatterns),\n trustedOnly: rules?.trustedOnly === true,\n };\n}\n\nfunction clonePromotionMapping(\n promotion: ContentContributionPromotionMapping | null | undefined,\n): ContentContributionPromotionMapping {\n return {\n targetContentType: promotion?.targetContentType || null,\n targetContentVariant: promotion?.targetContentVariant || null,\n targetContentStatus:\n promotion?.targetContentStatus === 'review' ? 'review' : 'draft',\n autoPromoteTrusted: promotion?.autoPromoteTrusted === true,\n createAssets: promotion?.createAssets !== false,\n assetRelationship: promotion?.assetRelationship || 'attachment',\n };\n}\n\nfunction cloneTypeDefinition(\n type: ContentContributionTypeDefinition,\n): ContentContributionTypeDefinition {\n return {\n key: type.key,\n label: type.label,\n enabled: type.enabled !== false,\n allowedChannels:\n type.allowedChannels && type.allowedChannels.length > 0\n ? [...type.allowedChannels]\n : ['web'],\n allowText: type.allowText !== false,\n allowFiles: type.allowFiles === true,\n allowEmptyText: type.allowEmptyText === true,\n intakeRules: cloneIntakeRules(type.intakeRules),\n promotion: clonePromotionMapping(type.promotion),\n metadata: safeParseJSONObject(type.metadata),\n };\n}\n\nfunction clonePersistedTypeRecord(\n type: PersistedContentContributionTypeRecord,\n): PersistedContentContributionTypeRecord {\n return {\n ...cloneTypeDefinition(type),\n id: type.id,\n tenantId: type.tenantId ?? null,\n createdAt: type.createdAt ?? null,\n updatedAt: type.updatedAt ?? null,\n };\n}\n\nfunction cloneContributionConfig(\n config: ContentContributionConfig,\n): ContentContributionConfig {\n return {\n types: config.types.map(cloneTypeDefinition),\n };\n}\n\nfunction mergeByKey<T extends { key: string }>(\n baseItems: T[],\n overrides: T[],\n normalize: (item: T) => T,\n): T[] {\n const merged = new Map<string, T>();\n\n for (const item of baseItems.map(normalize)) {\n merged.set(item.key, item);\n }\n\n for (const item of overrides.map(normalize)) {\n merged.set(item.key, item);\n }\n\n return [...merged.values()];\n}\n\nfunction isMissingContributionTypesTableError(error: unknown): boolean {\n const message = String(\n (error as Error)?.message || error || '',\n ).toLowerCase();\n return (\n message.includes('content_contribution_types') &&\n (message.includes('no such table') ||\n message.includes('does not exist') ||\n message.includes('relation'))\n );\n}\n\nfunction resolveContributionConfigTenantFilter(\n tenantId: string | null | undefined,\n): string | null | undefined {\n if (tenantId !== undefined) {\n return tenantId;\n }\n\n if (isSystemContext() || isSuperAdminBypass()) {\n return undefined;\n }\n\n const currentTenant = getCurrentTenant();\n if (currentTenant?.tenantId) {\n return currentTenant.tenantId;\n }\n\n return isTenancyEnabled() ? null : undefined;\n}\n\nasync function listPersistedContributionTypeRows(\n db: DatabaseInterface,\n tenantId: string | null | undefined,\n): Promise<Record<string, unknown>[]> {\n const byCreatedAt = (\n a: Record<string, unknown>,\n b: Record<string, unknown>,\n ): number =>\n String(a.created_at || a.createdAt || '').localeCompare(\n String(b.created_at || b.createdAt || ''),\n );\n const sortRows = (rows: Record<string, unknown>[]) => rows.sort(byCreatedAt);\n\n if (tenantId === undefined) {\n return sortRows(\n (await db.list('content_contribution_types', {})) as Record<\n string,\n unknown\n >[],\n );\n }\n\n if (tenantId === null) {\n return sortRows(\n (await db.list('content_contribution_types', {\n tenant_id: null,\n })) as Record<string, unknown>[],\n );\n }\n\n const [globalRows, tenantRows] = await Promise.all([\n db.list('content_contribution_types', { tenant_id: null }) as Promise<\n Record<string, unknown>[]\n >,\n db.list('content_contribution_types', { tenant_id: tenantId }) as Promise<\n Record<string, unknown>[]\n >,\n ]);\n\n return [...sortRows(globalRows), ...sortRows(tenantRows)];\n}\n\nfunction mapPersistedTypeRow(\n row: Record<string, unknown>,\n): PersistedContentContributionTypeRecord {\n return {\n // `id` is an optional string column; coerce non-string/empty driver\n // values to undefined so a numeric id can't leak past the `id?: string`\n // shape (mirrors the getRowString pattern used elsewhere).\n id: typeof row.id === 'string' && row.id ? row.id : undefined,\n key: String(row.key || ''),\n label: String(row.label || row.key || ''),\n enabled: row.enabled !== false && row.enabled !== 0,\n allowedChannels: safeParseJSONArray<ContentContributionChannel>(\n row.allowedChannels || row.allowed_channels,\n ),\n allowText:\n row.allowText === undefined && row.allow_text === undefined\n ? true\n : row.allowText !== false && row.allow_text !== 0,\n allowFiles:\n row.allowFiles === true ||\n row.allow_files === true ||\n row.allow_files === 1,\n allowEmptyText:\n row.allowEmptyText === true ||\n row.allow_empty_text === true ||\n row.allow_empty_text === 1,\n // Persisted JSON blobs deserialize to the documented intake/promotion\n // shapes; they are re-normalized structurally by clone* helpers downstream.\n intakeRules: safeParseJSONObject(\n row.intakeRules || row.intake_rules,\n ) as ContentContributionIntakeRules,\n promotion: safeParseJSONObject(\n row.promotion || row.promotion_mapping,\n ) as ContentContributionPromotionMapping,\n metadata: safeParseJSONObject(row.metadata),\n tenantId: (row.tenantId ?? row.tenant_id ?? null) as string | null,\n createdAt: (row.createdAt || row.created_at || null) as string | null,\n updatedAt: (row.updatedAt || row.updated_at || null) as string | null,\n };\n}\n\nexport function getContentContributionConfig(): ContentContributionConfig {\n return cloneContributionConfig(contributionConfig);\n}\n\nexport function configureContentContributions(\n config: Partial<ContentContributionConfig>,\n): ContentContributionConfig {\n contributionConfig = {\n types: config.types\n ? mergeByKey(contributionConfig.types, config.types, cloneTypeDefinition)\n : contributionConfig.types.map(cloneTypeDefinition),\n };\n\n return getContentContributionConfig();\n}\n\nexport function resetContentContributionConfig(): ContentContributionConfig {\n contributionConfig = cloneContributionConfig(\n DEFAULT_CONTENT_CONTRIBUTION_CONFIG,\n );\n return getContentContributionConfig();\n}\n\nexport function hasStaticContentContributionType(key: string): boolean {\n return contributionConfig.types.some((type) => type.key === key);\n}\n\nexport async function loadPersistedContentContributionTypes(\n options: ContentContributionConfigReadOptions = {},\n): Promise<PersistedContentContributionTypeRecord[]> {\n if (!options.db) {\n return [];\n }\n\n try {\n const tenantId = resolveContributionConfigTenantFilter(options.tenantId);\n const rows = await listPersistedContributionTypeRows(options.db, tenantId);\n return rows.map((row) => mapPersistedTypeRow(row));\n } catch (error) {\n if (isMissingContributionTypesTableError(error)) {\n return [];\n }\n\n throw error;\n }\n}\n\nexport async function getEffectiveContentContributionConfig(\n options: ContentContributionConfigReadOptions = {},\n): Promise<ContentContributionConfig> {\n const persisted = await loadPersistedContentContributionTypes({\n db: options.db,\n tenantId: options.tenantId,\n });\n\n return {\n types: mergeByKey(\n contributionConfig.types,\n persisted,\n cloneTypeDefinition as (\n item: ContentContributionTypeDefinition,\n ) => ContentContributionTypeDefinition,\n ),\n };\n}\n\nexport async function getContentContributionTypeConfigState(\n options: ContentContributionConfigReadOptions = {},\n): Promise<ContentContributionTypeConfigState> {\n const [effective, persisted] = await Promise.all([\n getEffectiveContentContributionConfig(options),\n loadPersistedContentContributionTypes(options),\n ]);\n\n return {\n effective: effective.types,\n persisted: persisted.map(clonePersistedTypeRecord),\n };\n}\n\nexport async function resolveEffectiveContentContributionType(\n key: string,\n options: ContentContributionConfigReadOptions = {},\n): Promise<ResolvedContentContributionType | null> {\n const effective = await getEffectiveContentContributionConfig(options);\n const type = effective.types.find((entry) => entry.key === key);\n return type ? cloneTypeDefinition(type) : null;\n}\n\nfunction matchesMimePattern(mimeType: string, pattern: string): boolean {\n if (!pattern) {\n return false;\n }\n\n if (pattern.endsWith('/*')) {\n return mimeType.startsWith(pattern.slice(0, -1));\n }\n\n return mimeType === pattern;\n}\n\nfunction matchesAnyMimePattern(mimeType: string, patterns: string[]): boolean {\n return patterns.some((pattern) => matchesMimePattern(mimeType, pattern));\n}\n\nfunction containsPattern(text: string, patterns: string[]): boolean {\n const lowerText = text.toLowerCase();\n return patterns.some((pattern) => lowerText.includes(pattern.toLowerCase()));\n}\n\nexport function evaluateContributionIntake(\n options: EvaluateContributionIntakeOptions,\n): EvaluateContributionIntakeResult {\n const type = cloneTypeDefinition(options.contributionType);\n const rules = cloneIntakeRules(type.intakeRules);\n const trustLevel = options.trustLevel || 'standard';\n const attachments = options.attachments || [];\n const text = [options.title, options.description, options.body]\n .filter(Boolean)\n .join('\\n')\n .trim();\n const hasText = text.length > 0;\n const hasFiles = attachments.length > 0;\n const totalBytes = attachments.reduce(\n (sum, attachment) => sum + Math.max(0, attachment.size || 0),\n 0,\n );\n const reasons: string[] = [];\n\n if (type.enabled === false) {\n return {\n decision: 'rejected',\n reasons: ['This contribution type is currently disabled.'],\n };\n }\n\n if (\n Array.isArray(type.allowedChannels) &&\n type.allowedChannels.length > 0 &&\n !type.allowedChannels.includes(options.channel)\n ) {\n return {\n decision: 'rejected',\n reasons: [\n `${options.channel} submissions are not allowed for this type.`,\n ],\n };\n }\n\n if (trustLevel === 'blocked') {\n return {\n decision: 'rejected',\n reasons: ['This contributor is blocked from submitting new material.'],\n };\n }\n\n if (rules.trustedOnly && trustLevel !== 'trusted') {\n return {\n decision: 'rejected',\n reasons: ['This contribution type is limited to trusted contributors.'],\n };\n }\n\n if (type.allowText === false && hasText) {\n return {\n decision: 'rejected',\n reasons: ['This contribution type does not accept text submissions.'],\n };\n }\n\n if (type.allowFiles !== true && hasFiles) {\n return {\n decision: 'rejected',\n reasons: ['This contribution type does not accept file attachments.'],\n };\n }\n\n if (\n type.allowText === true &&\n type.allowEmptyText !== true &&\n !hasText &&\n !hasFiles\n ) {\n return {\n decision: 'rejected',\n reasons: ['A text body or at least one attachment is required.'],\n };\n }\n\n if (\n typeof rules.maxFiles === 'number' &&\n attachments.length > rules.maxFiles\n ) {\n return {\n decision: 'rejected',\n reasons: [\n `This contribution type accepts at most ${rules.maxFiles} files.`,\n ],\n };\n }\n\n if (\n typeof rules.maxTotalBytes === 'number' &&\n totalBytes > rules.maxTotalBytes\n ) {\n return {\n decision: 'rejected',\n reasons: [\n 'The attached files exceed the size limit for this contribution type.',\n ],\n };\n }\n\n const mimeTypes = attachments\n .map((attachment) => attachment.mimeType || '')\n .filter(Boolean);\n\n if (\n rules.allowedMimePatterns &&\n rules.allowedMimePatterns.length > 0 &&\n mimeTypes.some(\n (mimeType) =>\n !matchesAnyMimePattern(mimeType, rules.allowedMimePatterns || []),\n )\n ) {\n return {\n decision: 'rejected',\n reasons: ['One or more attachments use a file type that is not allowed.'],\n };\n }\n\n if (\n mimeTypes.some((mimeType) =>\n matchesAnyMimePattern(mimeType, rules.blockedMimePatterns || []),\n )\n ) {\n return {\n decision: 'rejected',\n reasons: ['One or more attachments match blocked file-type rules.'],\n };\n }\n\n if (containsPattern(text, rules.blockedTextPatterns || [])) {\n return {\n decision: 'rejected',\n reasons: ['The submission body matches blocked intake rules.'],\n };\n }\n\n if (\n mimeTypes.some((mimeType) =>\n matchesAnyMimePattern(mimeType, rules.quarantineMimePatterns || []),\n )\n ) {\n reasons.push('One or more attachments triggered quarantine file rules.');\n }\n\n if (containsPattern(text, rules.quarantineTextPatterns || [])) {\n reasons.push('The submission body triggered quarantine text rules.');\n }\n\n if (reasons.length > 0) {\n return {\n decision: 'quarantined',\n reasons,\n };\n }\n\n return {\n decision: 'accepted',\n reasons: [],\n };\n}\n","import {\n AssetCollection,\n AssetStatusCollection,\n AssetTypeCollection,\n} from '@happyvertical/smrt-assets';\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentContributionAttachment } from './content-contribution-attachment';\nimport {\n type ContentContributionChannel,\n type ContentContributionIntakeDecision,\n type ContentContributionStatus,\n type ContentContributionTypeDefinition,\n resolveEffectiveContentContributionType,\n} from './content-contribution-config';\nimport type { ContentContributionRevision } from './content-contribution-revision';\nimport type { ContentContributor } from './content-contributor';\n\nexport interface ContentContributionAttachmentInput {\n filename: string;\n mimeType?: string | null;\n size?: number | null;\n fileKey?: string | null;\n sourceUri?: string | null;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AppendContentContributionRevisionOptions {\n title?: string | null;\n description?: string | null;\n body?: string | null;\n channel?: ContentContributionChannel;\n sourceMessageId?: string | null;\n sourceThreadKey?: string | null;\n attachments?: ContentContributionAttachmentInput[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface ApproveContentContributionOptions {\n editorNote?: string | null;\n promote?: boolean;\n targetStatus?: 'draft' | 'review';\n}\n\nexport interface RejectContentContributionOptions {\n editorNote?: string | null;\n}\n\nexport interface RequestChangesContentContributionOptions {\n editorNote?: string | null;\n}\n\nexport interface WithdrawContentContributionOptions {\n reason?: string | null;\n}\n\nexport interface PromoteContentContributionOptions {\n editorNote?: string | null;\n targetStatus?: 'draft' | 'review';\n}\n\nexport interface ContentContributionOptions extends SmrtObjectOptions {\n contributorId?: string;\n contributionTypeKey?: string;\n status?: ContentContributionStatus;\n intakeDecision?: ContentContributionIntakeDecision;\n channel?: ContentContributionChannel;\n title?: string | null;\n description?: string | null;\n body?: string | null;\n contributorEmail?: string | null;\n contributorName?: string | null;\n threadKey?: string | null;\n sourceMessageId?: string | null;\n editorNotes?: string | null;\n promotedContentId?: string | null;\n revisionCount?: number;\n approvedAt?: Date | null;\n promotedAt?: Date | null;\n rejectedAt?: Date | null;\n withdrawnAt?: Date | null;\n requestedChangesAt?: Date | null;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseMetadata(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object'\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction inferAssetTypeSlug(mimeType: string): string {\n if (mimeType.startsWith('image/')) {\n return 'image';\n }\n if (mimeType.startsWith('video/')) {\n return 'video';\n }\n if (mimeType.startsWith('audio/')) {\n return 'audio';\n }\n return 'document';\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contributions',\n conflictColumns: ['id'],\n api: {\n include: [\n 'list',\n 'get',\n 'create',\n 'update',\n 'delete',\n 'appendRevisionAction',\n 'requestChangesAction',\n 'approveAction',\n 'rejectAction',\n 'withdrawAction',\n 'promoteAction',\n ],\n routes: {\n appendRevisionAction: { method: 'POST', path: 'revisions' },\n requestChangesAction: { method: 'POST', path: 'request-changes' },\n approveAction: { method: 'POST', path: 'approve' },\n rejectAction: { method: 'POST', path: 'reject' },\n withdrawAction: { method: 'POST', path: 'withdraw' },\n promoteAction: { method: 'POST', path: 'promote' },\n },\n },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class ContentContribution extends SmrtObject {\n @foreignKey('ContentContributor', { required: true })\n contributorId = '';\n\n @field({ required: true })\n contributionTypeKey = '';\n\n status: ContentContributionStatus = 'submitted';\n intakeDecision: ContentContributionIntakeDecision = 'accepted';\n channel: ContentContributionChannel = 'web';\n title = '';\n description = '';\n body = '';\n contributorEmail = '';\n contributorName = '';\n threadKey = '';\n\n @crossPackageRef('@happyvertical/smrt-messages:Message')\n sourceMessageId = '';\n\n editorNotes = '';\n\n @foreignKey('Content')\n promotedContentId = '';\n revisionCount = 0;\n approvedAt: Date | null = null;\n promotedAt: Date | null = null;\n rejectedAt: Date | null = null;\n withdrawnAt: Date | null = null;\n requestedChangesAt: Date | null = null;\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributionOptions = {}) {\n super(options);\n if (options.contributorId !== undefined)\n this.contributorId = options.contributorId;\n if (options.contributionTypeKey !== undefined)\n this.contributionTypeKey = options.contributionTypeKey;\n if (options.status !== undefined) this.status = options.status;\n if (options.intakeDecision !== undefined)\n this.intakeDecision = options.intakeDecision;\n if (options.channel !== undefined) this.channel = options.channel;\n if (options.title !== undefined) this.title = options.title || '';\n if (options.description !== undefined)\n this.description = options.description || '';\n if (options.body !== undefined) this.body = options.body || '';\n if (options.contributorEmail !== undefined)\n this.contributorEmail = options.contributorEmail || '';\n if (options.contributorName !== undefined)\n this.contributorName = options.contributorName || '';\n if (options.threadKey !== undefined)\n this.threadKey = options.threadKey || '';\n if (options.sourceMessageId !== undefined)\n this.sourceMessageId = options.sourceMessageId || '';\n if (options.editorNotes !== undefined)\n this.editorNotes = options.editorNotes || '';\n if (options.promotedContentId !== undefined)\n this.promotedContentId = options.promotedContentId || '';\n if (options.revisionCount !== undefined)\n this.revisionCount = options.revisionCount;\n if (options.approvedAt !== undefined) this.approvedAt = options.approvedAt;\n if (options.promotedAt !== undefined) this.promotedAt = options.promotedAt;\n if (options.rejectedAt !== undefined) this.rejectedAt = options.rejectedAt;\n if (options.withdrawnAt !== undefined)\n this.withdrawnAt = options.withdrawnAt;\n if (options.requestedChangesAt !== undefined)\n this.requestedChangesAt = options.requestedChangesAt;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n return parseMetadata(this.metadata);\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = JSON.stringify(metadata || {});\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n description: this.description || null,\n body: this.body || null,\n contributorEmail: this.contributorEmail || null,\n contributorName: this.contributorName || null,\n threadKey: this.threadKey || null,\n sourceMessageId: this.sourceMessageId || null,\n editorNotes: this.editorNotes || null,\n promotedContentId: this.promotedContentId || null,\n metadata: this.getMetadata(),\n };\n }\n\n private async getRevisionCollection() {\n const { ContentContributionRevisionCollection } = await import(\n './content-contribution-revisions'\n );\n return ContentContributionRevisionCollection.create(this.options);\n }\n\n private async getAttachmentCollection() {\n const { ContentContributionAttachmentCollection } = await import(\n './content-contribution-attachments'\n );\n return ContentContributionAttachmentCollection.create(this.options);\n }\n\n private async getContributorCollection() {\n const { ContentContributorCollection } = await import(\n './content-contributors'\n );\n return ContentContributorCollection.create(this.options);\n }\n\n private async getContentsCollection() {\n const { Contents } = await import('./contents');\n return Contents.create({ db: this.db });\n }\n\n private async getAssetCollection() {\n return AssetCollection.create(this.options);\n }\n\n private async getAssetStatusCollection() {\n return AssetStatusCollection.create(this.options);\n }\n\n private async getAssetTypeCollection() {\n return AssetTypeCollection.create(this.options);\n }\n\n async getContributor(): Promise<ContentContributor | null> {\n if (!this.contributorId) {\n return null;\n }\n\n const contributors = await this.getContributorCollection();\n return contributors.get({ id: this.contributorId });\n }\n\n async getRevisions(): Promise<ContentContributionRevision[]> {\n if (!this.id) {\n return [];\n }\n\n const revisions = await this.getRevisionCollection();\n return revisions.listForContribution(this.id);\n }\n\n async getLatestRevision(): Promise<ContentContributionRevision | null> {\n if (!this.id) {\n return null;\n }\n\n const revisions = await this.getRevisionCollection();\n return revisions.getLatestForContribution(this.id);\n }\n\n async getAttachments(): Promise<ContentContributionAttachment[]> {\n if (!this.id) {\n return [];\n }\n\n const attachments = await this.getAttachmentCollection();\n return attachments.listForContribution(this.id);\n }\n\n async resolveContributionType(): Promise<ContentContributionTypeDefinition | null> {\n return resolveEffectiveContentContributionType(this.contributionTypeKey, {\n db: this.db,\n tenantId: this.tenantId ?? null,\n });\n }\n\n private ensureEditable(): void {\n if (\n this.revisionCount > 0 &&\n ['rejected', 'withdrawn', 'promoted'].includes(this.status)\n ) {\n throw new Error(\n `Contribution \"${this.id || 'new'}\" cannot be edited once it is ${this.status}.`,\n );\n }\n }\n\n private async createRevision(\n options: AppendContentContributionRevisionOptions,\n ): Promise<ContentContributionRevision> {\n if (!this.id) {\n throw new Error('Contribution must be saved before creating revisions.');\n }\n\n const revisions = await this.getRevisionCollection();\n const nextRevisionNumber = Math.max(1, this.revisionCount + 1);\n const revision = await revisions.create({\n contributionId: this.id,\n revisionNumber: nextRevisionNumber,\n channel: options.channel || this.channel,\n title: options.title ?? this.title,\n description: options.description ?? this.description,\n body: options.body ?? this.body,\n sourceMessageId: options.sourceMessageId || '',\n sourceThreadKey: options.sourceThreadKey || this.threadKey || '',\n metadata: JSON.stringify(options.metadata || {}),\n tenantId: this.tenantId,\n });\n\n return revision;\n }\n\n private async createHeldAttachments(\n revisionId: string | null,\n attachments: ContentContributionAttachmentInput[],\n channel: ContentContributionChannel,\n ): Promise<ContentContributionAttachment[]> {\n if (!this.id || attachments.length === 0) {\n return [];\n }\n\n const records = await this.getAttachmentCollection();\n const created: ContentContributionAttachment[] = [];\n for (const attachment of attachments) {\n const item = await records.create({\n contributionId: this.id,\n revisionId: revisionId || '',\n filename: attachment.filename,\n mimeType: attachment.mimeType || 'application/octet-stream',\n size: Math.max(0, attachment.size || 0),\n fileKey: attachment.fileKey || '',\n sourceUri: attachment.sourceUri || '',\n channel,\n tenantId: this.tenantId,\n metadata: JSON.stringify(attachment.metadata || {}),\n });\n created.push(item);\n }\n\n return created;\n }\n\n async appendRevisionAction(\n options: AppendContentContributionRevisionOptions = {},\n ) {\n this.ensureEditable();\n\n const revision = await this.createRevision(options);\n await this.createHeldAttachments(\n revision.id || null,\n options.attachments || [],\n options.channel || this.channel,\n );\n\n this.title = options.title ?? this.title;\n this.description = options.description ?? this.description;\n this.body = options.body ?? this.body;\n this.channel = options.channel || this.channel;\n this.sourceMessageId = options.sourceMessageId || this.sourceMessageId;\n this.threadKey = options.sourceThreadKey || this.threadKey;\n this.revisionCount = revision.revisionNumber;\n\n if (this.status === 'needs_changes') {\n this.status = 'submitted';\n this.requestedChangesAt = null;\n }\n\n const metadata = this.getMetadata();\n metadata.lastRevisionId = revision.id || null;\n metadata.lastRevisionAt = revision.createdAt.toISOString();\n this.setMetadata(metadata);\n await this.save();\n\n return {\n contribution: this.toJSON(),\n revision: revision.toJSON(),\n attachments: (await this.getAttachments()).map((item) => item.toJSON()),\n };\n }\n\n async requestChangesAction(\n options: RequestChangesContentContributionOptions = {},\n ) {\n if (['rejected', 'withdrawn', 'promoted'].includes(this.status)) {\n throw new Error(\n `Contribution \"${this.id || 'new'}\" cannot request changes after ${this.status}.`,\n );\n }\n\n this.status = 'needs_changes';\n this.requestedChangesAt = new Date();\n this.editorNotes = options.editorNote || this.editorNotes;\n\n const metadata = this.getMetadata();\n const requestHistory = Array.isArray(metadata.requestChangesHistory)\n ? metadata.requestChangesHistory\n : [];\n requestHistory.push({\n note: options.editorNote || '',\n at: this.requestedChangesAt.toISOString(),\n });\n metadata.requestChangesHistory = requestHistory;\n this.setMetadata(metadata);\n\n await this.save();\n return this.toJSON();\n }\n\n async rejectAction(options: RejectContentContributionOptions = {}) {\n if (this.status === 'promoted') {\n throw new Error('Promoted contributions cannot be rejected.');\n }\n\n this.status = 'rejected';\n this.rejectedAt = new Date();\n this.editorNotes = options.editorNote || this.editorNotes;\n\n const metadata = this.getMetadata();\n metadata.rejection = {\n note: options.editorNote || '',\n at: this.rejectedAt.toISOString(),\n };\n this.setMetadata(metadata);\n\n await this.save();\n return this.toJSON();\n }\n\n async withdrawAction(options: WithdrawContentContributionOptions = {}) {\n if (this.status === 'promoted') {\n throw new Error('Promoted contributions cannot be withdrawn.');\n }\n\n this.status = 'withdrawn';\n this.withdrawnAt = new Date();\n\n const metadata = this.getMetadata();\n metadata.withdrawal = {\n reason: options.reason || '',\n at: this.withdrawnAt.toISOString(),\n };\n this.setMetadata(metadata);\n\n await this.save();\n return this.toJSON();\n }\n\n async approveAction(options: ApproveContentContributionOptions = {}) {\n if (['rejected', 'withdrawn', 'promoted'].includes(this.status)) {\n throw new Error(\n `Contribution \"${this.id || 'new'}\" cannot be approved after ${this.status}.`,\n );\n }\n\n this.status = 'approved';\n this.approvedAt = new Date();\n this.editorNotes = options.editorNote || this.editorNotes;\n await this.save();\n\n if (options.promote === false) {\n return {\n contribution: this.toJSON(),\n content: null,\n assets: [],\n };\n }\n\n return this.promoteAction({\n editorNote: options.editorNote,\n targetStatus: options.targetStatus,\n });\n }\n\n async promoteAction(options: PromoteContentContributionOptions = {}) {\n if (this.promotedContentId) {\n const contents = await this.getContentsCollection();\n const content = await contents.get({ id: this.promotedContentId });\n return {\n contribution: this.toJSON(),\n content: content ? content.toJSON() : null,\n assets: await this.getAttachments().then((items) =>\n items\n .filter((item) => item.promotedAssetId)\n .map((item) => ({\n attachmentId: item.id,\n assetId: item.promotedAssetId,\n })),\n ),\n };\n }\n\n const type = await this.resolveContributionType();\n if (!type) {\n throw new Error(\n `Contribution type \"${this.contributionTypeKey}\" is not configured.`,\n );\n }\n\n const promotion = type.promotion || {};\n if (!promotion.targetContentType) {\n throw new Error(\n `Contribution type \"${type.key}\" is missing promotion.targetContentType.`,\n );\n }\n\n const contributor = await this.getContributor();\n const latestRevision = await this.getLatestRevision();\n const attachments = await this.getAttachments();\n\n const contents = await this.getContentsCollection();\n const content = await contents.create({\n tenantId: this.tenantId,\n type: promotion.targetContentType,\n variant: promotion.targetContentVariant || null,\n source: 'contribution',\n status: options.targetStatus || promotion.targetContentStatus || 'draft',\n title: latestRevision?.title || this.title,\n description: latestRevision?.description || this.description || null,\n body: latestRevision?.body || this.body || '',\n author:\n contributor?.name ||\n this.contributorName ||\n this.contributorEmail ||\n null,\n name:\n latestRevision?.title ||\n this.title ||\n `${type.label || type.key} contribution`,\n metadata: {\n contribution: {\n contributionId: this.id || null,\n contributorId: this.contributorId || null,\n contributorEmail: this.contributorEmail || null,\n contributorName: this.contributorName || null,\n contributionTypeKey: this.contributionTypeKey,\n revisionCount: this.revisionCount,\n sourceMessageId: this.sourceMessageId || null,\n threadKey: this.threadKey || null,\n },\n },\n });\n await content.save();\n\n const promotedAssets: Array<{\n attachmentId: string | null;\n assetId: string | null;\n }> = [];\n\n if (promotion.createAssets !== false && attachments.length > 0) {\n const [assets, statuses, typesCollection] = await Promise.all([\n this.getAssetCollection(),\n this.getAssetStatusCollection(),\n this.getAssetTypeCollection(),\n ]);\n\n await Promise.all([\n statuses.initializeCommonStatuses(),\n typesCollection.initializeCommonTypes(),\n ]);\n\n for (const [index, attachment] of attachments.entries()) {\n const asset = await assets.create({\n tenantId: this.tenantId,\n ownerProfileId: contributor?.profileId || null,\n name: attachment.filename || `Contribution attachment ${index + 1}`,\n description: `Promoted from contribution ${this.id || ''}`.trim(),\n mimeType: attachment.mimeType || 'application/octet-stream',\n sourceUri:\n attachment.sourceUri ||\n attachment.fileKey ||\n `held://content-contribution/${attachment.id || index}`,\n typeSlug: inferAssetTypeSlug(attachment.mimeType || ''),\n statusSlug: 'draft',\n sourceType: 'content-contribution',\n externalId: attachment.id || '',\n });\n await asset.save();\n\n await content.addAsset(\n asset,\n promotion.assetRelationship || 'attachment',\n index,\n );\n\n attachment.promotedAssetId = asset.id || '';\n await attachment.save();\n promotedAssets.push({\n attachmentId: attachment.id || null,\n assetId: asset.id || null,\n });\n }\n }\n\n this.promotedContentId = content.id || '';\n this.promotedAt = new Date();\n this.status = 'promoted';\n this.editorNotes = options.editorNote || this.editorNotes;\n\n const metadata = this.getMetadata();\n metadata.promotion = {\n contentId: content.id || null,\n contentStatus: content.status || null,\n promotedAt: this.promotedAt.toISOString(),\n assetIds: promotedAssets.map((item) => item.assetId).filter(Boolean),\n };\n this.setMetadata(metadata);\n await this.save();\n\n return {\n contribution: this.toJSON(),\n content: content.toJSON(),\n assets: promotedAssets,\n };\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentContributionChannel } from './content-contribution-config';\n\nexport interface ContentContributionAttachmentOptions\n extends SmrtObjectOptions {\n contributionId?: string;\n revisionId?: string | null;\n filename?: string;\n mimeType?: string;\n size?: number;\n fileKey?: string | null;\n sourceUri?: string | null;\n channel?: ContentContributionChannel;\n promotedAssetId?: string | null;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseMetadata(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object'\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contribution_attachments',\n conflictColumns: ['id'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentContributionAttachment extends SmrtObject {\n @foreignKey('ContentContribution', { required: true })\n contributionId = '';\n\n @foreignKey('ContentContributionRevision')\n revisionId = '';\n\n filename = '';\n mimeType = '';\n size = 0;\n fileKey = '';\n sourceUri = '';\n channel: ContentContributionChannel = 'web';\n\n @crossPackageRef('@happyvertical/smrt-assets:Asset')\n promotedAssetId = '';\n\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributionAttachmentOptions = {}) {\n super(options);\n if (options.contributionId !== undefined)\n this.contributionId = options.contributionId;\n if (options.revisionId !== undefined)\n this.revisionId = options.revisionId || '';\n if (options.filename !== undefined) this.filename = options.filename;\n if (options.mimeType !== undefined) this.mimeType = options.mimeType;\n if (options.size !== undefined) this.size = options.size;\n if (options.fileKey !== undefined) this.fileKey = options.fileKey || '';\n if (options.sourceUri !== undefined)\n this.sourceUri = options.sourceUri || '';\n if (options.channel !== undefined) this.channel = options.channel;\n if (options.promotedAssetId !== undefined)\n this.promotedAssetId = options.promotedAssetId || '';\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n return parseMetadata(this.metadata);\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n revisionId: this.revisionId || null,\n fileKey: this.fileKey || null,\n sourceUri: this.sourceUri || null,\n promotedAssetId: this.promotedAssetId || null,\n metadata: this.getMetadata(),\n };\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentContributionAttachment } from './content-contribution-attachment';\n\nexport class ContentContributionAttachmentCollection extends SmrtCollection<ContentContributionAttachment> {\n static readonly _itemClass = ContentContributionAttachment;\n\n async listForContribution(\n contributionId: string,\n ): Promise<ContentContributionAttachment[]> {\n return this.list({\n where: { contributionId },\n orderBy: 'created_at ASC',\n });\n }\n\n async listForRevision(\n revisionId: string,\n ): Promise<ContentContributionAttachment[]> {\n return this.list({\n where: { revisionId },\n orderBy: 'created_at ASC',\n });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentContributionChannel } from './content-contribution-config';\n\nexport interface ContentContributionRevisionOptions extends SmrtObjectOptions {\n contributionId?: string;\n revisionNumber?: number;\n channel?: ContentContributionChannel;\n title?: string;\n description?: string | null;\n body?: string | null;\n sourceMessageId?: string | null;\n sourceThreadKey?: string | null;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseMetadata(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object'\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contribution_revisions',\n conflictColumns: ['contribution_id', 'revision_number'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentContributionRevision extends SmrtObject {\n @foreignKey('ContentContribution', { required: true })\n contributionId = '';\n\n revisionNumber = 1;\n channel: ContentContributionChannel = 'web';\n title = '';\n description = '';\n body = '';\n\n @crossPackageRef('@happyvertical/smrt-messages:Message')\n sourceMessageId = '';\n\n sourceThreadKey = '';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributionRevisionOptions = {}) {\n super(options);\n if (options.contributionId !== undefined)\n this.contributionId = options.contributionId;\n if (options.revisionNumber !== undefined)\n this.revisionNumber = options.revisionNumber;\n if (options.channel !== undefined) this.channel = options.channel;\n if (options.title !== undefined) this.title = options.title;\n if (options.description !== undefined)\n this.description = options.description || '';\n if (options.body !== undefined) this.body = options.body || '';\n if (options.sourceMessageId !== undefined)\n this.sourceMessageId = options.sourceMessageId || '';\n if (options.sourceThreadKey !== undefined)\n this.sourceThreadKey = options.sourceThreadKey || '';\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n return parseMetadata(this.metadata);\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n sourceMessageId: this.sourceMessageId || null,\n sourceThreadKey: this.sourceThreadKey || null,\n metadata: this.getMetadata(),\n };\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentContributionRevision } from './content-contribution-revision';\n\nexport class ContentContributionRevisionCollection extends SmrtCollection<ContentContributionRevision> {\n static readonly _itemClass = ContentContributionRevision;\n\n async listForContribution(\n contributionId: string,\n ): Promise<ContentContributionRevision[]> {\n return this.list({\n where: { contributionId },\n orderBy: 'revision_number ASC',\n });\n }\n\n async getLatestForContribution(\n contributionId: string,\n ): Promise<ContentContributionRevision | null> {\n const revisions = await this.list({\n where: { contributionId },\n orderBy: 'revision_number DESC',\n limit: 1,\n });\n\n return revisions[0] || null;\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type ContentContributionChannel,\n type ContentContributionIntakeRules,\n type ContentContributionPromotionMapping,\n type ContentContributionTypeDefinition,\n hasStaticContentContributionType,\n} from './content-contribution-config';\nimport { getQueryRows, isMissingTableError } from './database-utils';\n\nexport interface ContentContributionTypeOptions extends SmrtObjectOptions {\n key?: string;\n label?: string;\n enabled?: boolean;\n allowedChannels?: ContentContributionChannel[] | string;\n allowText?: boolean;\n allowFiles?: boolean;\n allowEmptyText?: boolean;\n intakeRules?: ContentContributionIntakeRules | string;\n promotion?: ContentContributionPromotionMapping | string;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseJSON<T>(raw: unknown, fallback: T): T {\n if (!raw) {\n return fallback;\n }\n\n if (typeof raw === 'object') {\n return structuredClone(raw as T);\n }\n\n if (typeof raw !== 'string') {\n return fallback;\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? (parsed as T) : fallback;\n } catch {\n return fallback;\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contribution_types',\n conflictColumns: ['key'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentContributionType extends SmrtObject {\n @field({ required: true })\n key = '';\n\n label = '';\n enabled = true;\n allowedChannels = '';\n allowText = true;\n allowFiles = false;\n allowEmptyText = false;\n intakeRules = '';\n promotion = '';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributionTypeOptions = {}) {\n super(options);\n if (options.key !== undefined) this.key = options.key;\n if (options.label !== undefined) this.label = options.label;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.allowText !== undefined) this.allowText = options.allowText;\n if (options.allowFiles !== undefined) this.allowFiles = options.allowFiles;\n if (options.allowEmptyText !== undefined)\n this.allowEmptyText = options.allowEmptyText;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.allowedChannels !== undefined) {\n this.allowedChannels =\n typeof options.allowedChannels === 'string'\n ? options.allowedChannels\n : JSON.stringify(options.allowedChannels);\n }\n\n if (options.intakeRules !== undefined) {\n this.intakeRules =\n typeof options.intakeRules === 'string'\n ? options.intakeRules\n : JSON.stringify(options.intakeRules);\n }\n\n if (options.promotion !== undefined) {\n this.promotion =\n typeof options.promotion === 'string'\n ? options.promotion\n : JSON.stringify(options.promotion);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getAllowedChannels(): ContentContributionChannel[] {\n const channels = parseJSON<ContentContributionChannel[]>(\n this.allowedChannels,\n [],\n );\n return channels.length > 0 ? channels : ['web'];\n }\n\n setAllowedChannels(channels: ContentContributionChannel[]): void {\n this.allowedChannels = JSON.stringify(channels);\n }\n\n getIntakeRules(): ContentContributionIntakeRules {\n return parseJSON<ContentContributionIntakeRules>(this.intakeRules, {});\n }\n\n setIntakeRules(rules: ContentContributionIntakeRules): void {\n this.intakeRules = JSON.stringify(rules || {});\n }\n\n getPromotion(): ContentContributionPromotionMapping {\n return parseJSON<ContentContributionPromotionMapping>(this.promotion, {});\n }\n\n setPromotion(promotion: ContentContributionPromotionMapping): void {\n this.promotion = JSON.stringify(promotion || {});\n }\n\n getMetadata(): Record<string, unknown> {\n return parseJSON<Record<string, unknown>>(this.metadata, {});\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = JSON.stringify(metadata || {});\n }\n\n toDefinition(): ContentContributionTypeDefinition {\n return {\n key: this.key,\n label: this.label || this.key,\n enabled: this.enabled !== false,\n allowedChannels: this.getAllowedChannels(),\n allowText: this.allowText !== false,\n allowFiles: this.allowFiles === true,\n allowEmptyText: this.allowEmptyText === true,\n intakeRules: this.getIntakeRules(),\n promotion: this.getPromotion(),\n metadata: this.getMetadata(),\n };\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n allowedChannels: this.getAllowedChannels(),\n intakeRules: this.getIntakeRules(),\n promotion: this.getPromotion(),\n metadata: this.getMetadata(),\n };\n }\n\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n\n if (!this.key) {\n throw new Error('Contribution type key is required.');\n }\n\n const promotion = this.getPromotion();\n if (!promotion.targetContentType) {\n throw new Error(\n `Contribution type \"${this.key}\" must define promotion.targetContentType.`,\n );\n }\n }\n\n override async delete(): Promise<void> {\n if (!this.db) {\n return super.delete();\n }\n\n const hasFallback = hasStaticContentContributionType(this.key);\n\n if (!hasFallback) {\n try {\n const existing = await this.db.query(\n 'SELECT id FROM content_contributions WHERE contribution_type_key = ? LIMIT 1',\n this.key,\n );\n const rows = getQueryRows(existing);\n\n if (rows.length > 0) {\n throw new Error(\n `Cannot delete contribution type \"${this.key}\" because contributions already reference it.`,\n );\n }\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('already reference it')\n ) {\n throw error;\n }\n if (!isMissingTableError(error, 'content_contributions')) {\n throw error;\n }\n }\n }\n\n return super.delete();\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentContributionType } from './content-contribution-type';\n\nexport class ContentContributionTypeCollection extends SmrtCollection<ContentContributionType> {\n static readonly _itemClass = ContentContributionType;\n\n async getByKey(key: string): Promise<ContentContributionType | null> {\n return this.get({ key });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentContributorTrustLevel } from './content-contribution-config';\n\nexport interface ContentContributorOptions extends SmrtObjectOptions {\n profileId?: string | null;\n email?: string;\n name?: string;\n trustLevel?: ContentContributorTrustLevel;\n metadata?: Record<string, unknown> | string;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\nfunction parseMetadata(raw: unknown): Record<string, unknown> {\n if (!raw) {\n return {};\n }\n\n if (typeof raw === 'object') {\n return { ...(raw as Record<string, unknown>) };\n }\n\n if (typeof raw !== 'string') {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object'\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_contributors',\n conflictColumns: ['email'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentContributor extends SmrtObject {\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n profileId = '';\n\n @field({ required: true })\n email = '';\n\n name = '';\n trustLevel: ContentContributorTrustLevel = 'standard';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentContributorOptions = {}) {\n super(options);\n if (options.profileId !== undefined)\n this.profileId = options.profileId || '';\n if (options.email !== undefined)\n this.email = options.email.toLowerCase().trim();\n if (options.name !== undefined) this.name = options.name;\n if (options.trustLevel !== undefined) this.trustLevel = options.trustLevel;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n return parseMetadata(this.metadata);\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = JSON.stringify(metadata || {});\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n profileId: this.profileId || null,\n metadata: this.getMetadata(),\n };\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n ProfileCollection,\n ProfileTypeCollection,\n} from '@happyvertical/smrt-profiles';\nimport { ContentContributor } from './content-contributor';\n\nexport class ContentContributorCollection extends SmrtCollection<ContentContributor> {\n static readonly _itemClass = ContentContributor;\n\n async getByEmail(email: string): Promise<ContentContributor | null> {\n return this.get({ email: email.toLowerCase().trim() });\n }\n\n async getByProfileId(profileId: string): Promise<ContentContributor | null> {\n return this.get({ profileId });\n }\n\n async findOrCreateByEmail(options: {\n email: string;\n name?: string | null;\n tenantId?: string | null;\n }): Promise<ContentContributor> {\n const normalizedEmail = options.email.toLowerCase().trim();\n const existing = await this.getByEmail(normalizedEmail);\n if (existing) {\n if (options.name && !existing.name) {\n existing.name = options.name;\n await existing.save();\n }\n return existing;\n }\n\n const profileCollection = await ProfileCollection.create(this.options);\n let profile = await profileCollection.findByEmail(normalizedEmail);\n\n if (!profile) {\n const profileTypes = await ProfileTypeCollection.create(this.options);\n const personType = await profileTypes.getOrCreateBySlug('person', {\n name: 'Person',\n description: 'Individual person',\n });\n\n profile = await profileCollection.create({\n typeId: personType.id || undefined,\n email: normalizedEmail,\n name: options.name || normalizedEmail.split('@')[0],\n tenantId: options.tenantId || undefined,\n });\n await profile.save();\n }\n\n const contributor = await this.create({\n email: normalizedEmail,\n name: options.name || profile.name || normalizedEmail.split('@')[0],\n profileId: profile.id || undefined,\n tenantId: options.tenantId || undefined,\n trustLevel: 'standard',\n });\n await contributor.save();\n return contributor;\n }\n}\n","import { SmrtCollection, smrt } from '@happyvertical/smrt-core';\nimport {\n AttachmentCollection,\n type Email,\n EmailCollection,\n} from '@happyvertical/smrt-messages';\nimport type { ContentContributionAttachmentInput } from './content-contribution';\nimport { ContentContribution } from './content-contribution';\nimport type { ContentContributionTypeDefinition } from './content-contribution-config';\nimport {\n evaluateContributionIntake,\n getContentContributionTypeConfigState,\n getEffectiveContentContributionConfig,\n} from './content-contribution-config';\nimport { ContentContributorCollection } from './content-contributors';\n\nexport interface SubmitWebContentContributionOptions {\n typeKey: string;\n contributorEmail: string;\n contributorName?: string | null;\n title?: string | null;\n description?: string | null;\n body?: string | null;\n attachments?: ContentContributionAttachmentInput[];\n metadata?: Record<string, unknown>;\n tenantId?: string | null;\n}\n\nexport interface IngestEmailContentContributionOptions {\n emailId: string;\n typeKey?: string | null;\n tenantId?: string | null;\n}\n\nfunction toContributionStatus(\n decision: ReturnType<typeof evaluateContributionIntake>['decision'],\n) {\n if (decision === 'quarantined') {\n return 'quarantined' as const;\n }\n\n if (decision === 'rejected') {\n return 'rejected' as const;\n }\n\n return 'submitted' as const;\n}\n\nfunction pickDefaultEmailType(\n types: ContentContributionTypeDefinition[],\n): ContentContributionTypeDefinition | null {\n const emailTypes = types.filter(\n (type) =>\n type.enabled !== false && (type.allowedChannels || []).includes('email'),\n );\n\n return emailTypes.length === 1 ? emailTypes[0] : null;\n}\n\n@smrt({\n api: {\n include: [\n 'submitWebContribution',\n 'ingestEmailContribution',\n 'listForContributor',\n 'listInboxAction',\n 'getContributionTypesAction',\n ],\n routes: {\n submitWebContribution: {\n scope: 'collection',\n method: 'POST',\n path: 'submit',\n },\n ingestEmailContribution: {\n scope: 'collection',\n method: 'POST',\n path: 'ingest-email',\n },\n listForContributor: {\n scope: 'collection',\n method: 'GET',\n path: 'by-contributor',\n },\n listInboxAction: {\n scope: 'collection',\n method: 'GET',\n path: 'inbox',\n },\n getContributionTypesAction: {\n scope: 'collection',\n method: 'GET',\n path: 'types',\n },\n },\n },\n mcp: false,\n cli: false,\n})\nexport class ContentContributions extends SmrtCollection<ContentContribution> {\n static readonly _itemClass = ContentContribution;\n\n private async getContributorCollection() {\n return ContentContributorCollection.create(this.options);\n }\n\n private async getEmailCollection() {\n return EmailCollection.create(this.options);\n }\n\n private async getAttachmentCollection() {\n return AttachmentCollection.create(this.options);\n }\n\n private async resolveSubmissionType(\n typeKey: string | null | undefined,\n tenantId?: string | null,\n ): Promise<ContentContributionTypeDefinition> {\n const effective = await getEffectiveContentContributionConfig({\n db: this.db,\n tenantId,\n });\n\n if (typeKey) {\n const match = effective.types.find((type) => type.key === typeKey);\n if (match) {\n return match;\n }\n throw new Error(`Unknown contribution type \"${typeKey}\".`);\n }\n\n const defaultType = pickDefaultEmailType(effective.types);\n if (!defaultType) {\n throw new Error(\n 'A contribution type key is required unless there is exactly one email-enabled type.',\n );\n }\n\n return defaultType;\n }\n\n async getContributionTypesAction(options: { tenantId?: string | null } = {}) {\n return getContentContributionTypeConfigState({\n db: this.db,\n tenantId: options.tenantId,\n });\n }\n\n async listForContributor(\n options: {\n contributorId?: string;\n contributorEmail?: string;\n tenantId?: string | null;\n } = {},\n ) {\n let contributorId = options.contributorId;\n\n if (!contributorId && options.contributorEmail) {\n const contributors = await this.getContributorCollection();\n const contributor = await contributors.getByEmail(\n options.contributorEmail,\n );\n contributorId = contributor?.id || undefined;\n }\n\n if (!contributorId) {\n return [];\n }\n\n const where: { contributorId: string; tenantId?: string | null } = {\n contributorId,\n };\n if (options.tenantId !== undefined) {\n where.tenantId = options.tenantId;\n }\n\n return this.list({\n where,\n orderBy: 'updated_at DESC',\n });\n }\n\n async listInboxAction(\n options: { statuses?: string | string[]; tenantId?: string | null } = {},\n ): Promise<ContentContribution[]> {\n const requested = Array.isArray(options.statuses)\n ? options.statuses\n : typeof options.statuses === 'string'\n ? options.statuses.split(',').map((item) => item.trim())\n : ['submitted', 'quarantined', 'needs_changes', 'approved'];\n\n const contributions = await this.list({\n where:\n options.tenantId !== undefined\n ? { tenantId: options.tenantId }\n : undefined,\n orderBy: 'updated_at DESC',\n });\n\n return contributions.filter((contribution) =>\n requested.includes(contribution.status),\n );\n }\n\n async submitWebContribution(options: SubmitWebContentContributionOptions) {\n const type = await this.resolveSubmissionType(\n options.typeKey,\n options.tenantId,\n );\n const contributors = await this.getContributorCollection();\n const contributor = await contributors.findOrCreateByEmail({\n email: options.contributorEmail,\n name: options.contributorName || null,\n tenantId: options.tenantId ?? null,\n });\n\n const intake = evaluateContributionIntake({\n contributionType: type,\n trustLevel: contributor.trustLevel,\n channel: 'web',\n title: options.title || null,\n description: options.description || null,\n body: options.body || null,\n attachments: options.attachments || [],\n });\n\n const status = toContributionStatus(intake.decision);\n\n const contribution = await this.create({\n contributorId: contributor.id || '',\n contributionTypeKey: type.key,\n status,\n intakeDecision: intake.decision,\n channel: 'web',\n title: options.title || '',\n description: options.description || '',\n body: options.body || '',\n contributorEmail: contributor.email,\n contributorName: contributor.name,\n editorNotes: intake.reasons.join('\\n'),\n tenantId: options.tenantId ?? null,\n metadata: JSON.stringify({\n intake: {\n decision: intake.decision,\n reasons: intake.reasons,\n },\n submission: options.metadata || {},\n }),\n });\n await contribution.save();\n\n await contribution.appendRevisionAction({\n title: options.title,\n description: options.description,\n body: options.body,\n attachments: options.attachments || [],\n channel: 'web',\n metadata: {\n source: 'web',\n },\n });\n\n if (\n contributor.trustLevel === 'trusted' &&\n intake.decision === 'accepted' &&\n type.promotion?.autoPromoteTrusted === true\n ) {\n return contribution.approveAction({\n editorNote: 'Auto-promoted trusted contributor submission.',\n targetStatus: type.promotion?.targetContentStatus || 'draft',\n });\n }\n\n return {\n contribution: contribution.toJSON(),\n intake,\n };\n }\n\n async ingestEmailContribution(\n options: IngestEmailContentContributionOptions,\n ) {\n const emailCollection = await this.getEmailCollection();\n // EmailCollection.get() is typed as the Message base; the rows it returns\n // are Emails, so narrow to Email to read the email-specific fields below.\n const email = (await emailCollection.get({\n id: options.emailId,\n })) as Email | null;\n if (!email) {\n throw new Error(`Email \"${options.emailId}\" not found.`);\n }\n\n const type = await this.resolveSubmissionType(\n options.typeKey,\n options.tenantId,\n );\n const contributors = await this.getContributorCollection();\n const contributor = await contributors.findOrCreateByEmail({\n email: email.fromAddress,\n name: email.fromName || null,\n tenantId: options.tenantId ?? null,\n });\n\n const attachmentCollection = await this.getAttachmentCollection();\n const inboundAttachments = await attachmentCollection.getByMessage(\n email.id || '',\n );\n const normalizedAttachments: ContentContributionAttachmentInput[] =\n inboundAttachments\n .filter((attachment) => !attachment.isInline())\n .map((attachment) => ({\n filename: attachment.filename || 'attachment',\n mimeType: attachment.contentType || 'application/octet-stream',\n size: attachment.size || 0,\n sourceUri: attachment.filePath || attachment.sourceUrl || null,\n metadata: {\n messageId: email.id || null,\n attachmentId: attachment.id || null,\n },\n }));\n\n const threadKey =\n email.threadId || email.messageId || email.inReplyTo || email.id || '';\n\n const intake = evaluateContributionIntake({\n contributionType: type,\n trustLevel: contributor.trustLevel,\n channel: 'email',\n title: email.subject || null,\n body: email.textBody || email.body || null,\n attachments: normalizedAttachments,\n });\n\n const existing =\n threadKey &&\n (await this.get({\n threadKey,\n contributorId: contributor.id || '',\n }));\n\n if (existing) {\n existing.intakeDecision = intake.decision;\n existing.editorNotes = intake.reasons.join('\\n');\n\n const metadata = existing.getMetadata();\n metadata.lastEmailIntake = {\n decision: intake.decision,\n reasons: intake.reasons,\n emailId: email.id || null,\n };\n existing.setMetadata(metadata);\n\n if (intake.decision === 'rejected') {\n existing.status = 'rejected';\n existing.rejectedAt = new Date();\n await existing.save();\n\n return {\n contribution: existing.toJSON(),\n intake,\n revision: null,\n attachments: (await existing.getAttachments()).map((item) =>\n item.toJSON(),\n ),\n };\n }\n\n existing.status = toContributionStatus(intake.decision);\n await existing.save();\n\n const appended = await existing.appendRevisionAction({\n title: email.subject || existing.title,\n body: email.textBody || email.body || '',\n channel: 'email',\n sourceMessageId: email.id || '',\n sourceThreadKey: threadKey,\n attachments: normalizedAttachments,\n metadata: {\n source: 'email-reply',\n emailId: email.id || null,\n },\n });\n\n return {\n ...appended,\n intake,\n };\n }\n\n const status = toContributionStatus(intake.decision);\n\n const contribution = await this.create({\n contributorId: contributor.id || '',\n contributionTypeKey: type.key,\n status,\n intakeDecision: intake.decision,\n channel: 'email',\n title: email.subject || '',\n description: '',\n body: email.textBody || email.body || '',\n contributorEmail: contributor.email,\n contributorName: contributor.name,\n threadKey,\n sourceMessageId: email.id || '',\n editorNotes: intake.reasons.join('\\n'),\n tenantId: options.tenantId ?? null,\n metadata: JSON.stringify({\n intake: {\n decision: intake.decision,\n reasons: intake.reasons,\n },\n email: {\n emailId: email.id || null,\n messageId: email.messageId || null,\n inReplyTo: email.inReplyTo || null,\n threadId: email.threadId || null,\n },\n }),\n });\n await contribution.save();\n\n await contribution.appendRevisionAction({\n title: email.subject || '',\n body: email.textBody || email.body || '',\n channel: 'email',\n sourceMessageId: email.id || null,\n sourceThreadKey: threadKey,\n attachments: normalizedAttachments,\n metadata: {\n source: 'email',\n emailId: email.id || null,\n },\n });\n\n if (\n contributor.trustLevel === 'trusted' &&\n intake.decision === 'accepted' &&\n type.promotion?.autoPromoteTrusted === true\n ) {\n return contribution.approveAction({\n editorNote: 'Auto-promoted trusted contributor email submission.',\n targetStatus: type.promotion?.targetContentStatus || 'draft',\n });\n }\n\n return {\n contribution: contribution.toJSON(),\n intake,\n };\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n ContentCorrectionStatus,\n ContentCorrectionType,\n} from './content-governance';\n\nexport interface ContentCorrectionOptions extends SmrtObjectOptions {\n contentId?: string;\n contentVersionId?: string;\n factId?: string;\n replacementFactId?: string;\n correctionType?: ContentCorrectionType;\n status?: ContentCorrectionStatus;\n summary?: string;\n incorrectText?: string;\n correctedText?: string;\n publicNote?: string;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n publishedAt?: Date | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_corrections',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class ContentCorrection extends SmrtObject {\n @foreignKey('Content', { required: true })\n contentId = '';\n\n @foreignKey('ContentVersion')\n contentVersionId = '';\n\n @crossPackageRef('@happyvertical/smrt-facts:Fact')\n factId = '';\n\n @crossPackageRef('@happyvertical/smrt-facts:Fact')\n replacementFactId = '';\n correctionType: ContentCorrectionType = 'fact';\n status: ContentCorrectionStatus = 'draft';\n summary = '';\n incorrectText = '';\n correctedText = '';\n publicNote = '';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n publishedAt: Date | null = null;\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentCorrectionOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.contentVersionId !== undefined)\n this.contentVersionId = options.contentVersionId;\n if (options.factId !== undefined) this.factId = options.factId;\n if (options.replacementFactId !== undefined)\n this.replacementFactId = options.replacementFactId;\n if (options.correctionType !== undefined)\n this.correctionType = options.correctionType;\n if (options.status !== undefined) this.status = options.status;\n if (options.summary !== undefined) this.summary = options.summary;\n if (options.incorrectText !== undefined)\n this.incorrectText = options.incorrectText;\n if (options.correctedText !== undefined)\n this.correctedText = options.correctedText;\n if (options.publicNote !== undefined) this.publicNote = options.publicNote;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.publishedAt !== undefined)\n this.publishedAt = options.publishedAt;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentCorrection } from './content-correction';\n\nexport class ContentCorrectionCollection extends SmrtCollection<ContentCorrection> {\n static readonly _itemClass = ContentCorrection;\n\n async listForContent(contentId: string): Promise<ContentCorrection[]> {\n return this.list({\n where: { contentId },\n orderBy: 'created_at DESC',\n });\n }\n\n async getPublishedForContent(\n contentId: string,\n ): Promise<ContentCorrection[]> {\n return this.list({\n where: { contentId, status: 'published' },\n orderBy: 'published_at DESC',\n });\n }\n\n async issue(\n options: ConstructorParameters<typeof ContentCorrection>[0],\n ): Promise<ContentCorrection> {\n if (!options) {\n return this.create({});\n }\n\n return this.create({\n ...options,\n metadata:\n typeof options.metadata === 'string'\n ? options.metadata\n : options.metadata\n ? JSON.stringify(options.metadata)\n : undefined,\n });\n }\n}\n","'use strict';\n\nconst nameStartChar = ':A-Za-z_\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD';\nconst nameChar = nameStartChar + '\\\\-.\\\\d\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040';\nexport const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*';\nconst regexName = new RegExp('^' + nameRegexp + '$');\n\nexport function getAllMatches(string, regex) {\n const matches = [];\n let match = regex.exec(string);\n while (match) {\n const allmatches = [];\n allmatches.startIndex = regex.lastIndex - match[0].length;\n const len = match.length;\n for (let index = 0; index < len; index++) {\n allmatches.push(match[index]);\n }\n matches.push(allmatches);\n match = regex.exec(string);\n }\n return matches;\n}\n\nexport const isName = function (string) {\n const match = regexName.exec(string);\n return !(match === null || typeof match === 'undefined');\n}\n\nexport function isExist(v) {\n return typeof v !== 'undefined';\n}\n\nexport function isEmptyObject(obj) {\n return Object.keys(obj).length === 0;\n}\n\nexport function getValue(v) {\n if (exports.isExist(v)) {\n return v;\n } else {\n return '';\n }\n}\n\n/**\n * Dangerous property names that could lead to prototype pollution or security issues\n */\nexport const DANGEROUS_PROPERTY_NAMES = [\n // '__proto__',\n // 'constructor',\n // 'prototype',\n 'hasOwnProperty',\n 'toString',\n 'valueOf',\n '__defineGetter__',\n '__defineSetter__',\n '__lookupGetter__',\n '__lookupSetter__'\n];\n\nexport const criticalProperties = [\"__proto__\", \"constructor\", \"prototype\"];","'use strict';\n\nimport { getAllMatches, isName } from './util.js';\n\nconst defaultOptions = {\n allowBooleanAttributes: false, //A tag can have attributes without any value\n unpairedTags: []\n};\n\n//const tagsPattern = new RegExp(\"<\\\\/?([\\\\w:\\\\-_\\.]+)\\\\s*\\/?>\",\"g\");\nexport function validate(xmlData, options) {\n options = Object.assign({}, defaultOptions, options);\n\n //xmlData = xmlData.replace(/(\\r\\n|\\n|\\r)/gm,\"\");//make it single line\n //xmlData = xmlData.replace(/(^\\s*<\\?xml.*?\\?>)/g,\"\");//Remove XML starting tag\n //xmlData = xmlData.replace(/(<!DOCTYPE[\\s\\w\\\"\\.\\/\\-\\:]+(\\[.*\\])*\\s*>)/g,\"\");//Remove DOCTYPE\n const tags = [];\n let tagFound = false;\n\n //indicates that the root tag has been closed (aka. depth 0 has been reached)\n let reachedRoot = false;\n\n if (xmlData[0] === '\\ufeff') {\n // check for byte order mark (BOM)\n xmlData = xmlData.substr(1);\n }\n\n for (let i = 0; i < xmlData.length; i++) {\n\n if (xmlData[i] === '<' && xmlData[i + 1] === '?') {\n i += 2;\n i = readPI(xmlData, i);\n if (i.err) return i;\n } else if (xmlData[i] === '<') {\n //starting of tag\n //read until you reach to '>' avoiding any '>' in attribute value\n let tagStartPos = i;\n i++;\n\n if (xmlData[i] === '!') {\n i = readCommentAndCDATA(xmlData, i);\n continue;\n } else {\n let closingTag = false;\n if (xmlData[i] === '/') {\n //closing tag\n closingTag = true;\n i++;\n }\n //read tagname\n let tagName = '';\n for (; i < xmlData.length &&\n xmlData[i] !== '>' &&\n xmlData[i] !== ' ' &&\n xmlData[i] !== '\\t' &&\n xmlData[i] !== '\\n' &&\n xmlData[i] !== '\\r'; i++\n ) {\n tagName += xmlData[i];\n }\n tagName = tagName.trim();\n //console.log(tagName);\n\n if (tagName[tagName.length - 1] === '/') {\n //self closing tag without attributes\n tagName = tagName.substring(0, tagName.length - 1);\n //continue;\n i--;\n }\n if (!validateTagName(tagName)) {\n let msg;\n if (tagName.trim().length === 0) {\n msg = \"Invalid space after '<'.\";\n } else {\n msg = \"Tag '\" + tagName + \"' is an invalid name.\";\n }\n return getErrorObject('InvalidTag', msg, getLineNumberForPosition(xmlData, i));\n }\n\n const result = readAttributeStr(xmlData, i);\n if (result === false) {\n return getErrorObject('InvalidAttr', \"Attributes for '\" + tagName + \"' have open quote.\", getLineNumberForPosition(xmlData, i));\n }\n let attrStr = result.value;\n i = result.index;\n\n if (attrStr[attrStr.length - 1] === '/') {\n //self closing tag\n const attrStrStart = i - attrStr.length;\n attrStr = attrStr.substring(0, attrStr.length - 1);\n const isValid = validateAttributeString(attrStr, options);\n if (isValid === true) {\n tagFound = true;\n //continue; //text may presents after self closing tag\n } else {\n //the result from the nested function returns the position of the error within the attribute\n //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute\n //this gives us the absolute index in the entire xml, which we can use to find the line at last\n return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));\n }\n } else if (closingTag) {\n if (!result.tagClosed) {\n return getErrorObject('InvalidTag', \"Closing tag '\" + tagName + \"' doesn't have proper closing.\", getLineNumberForPosition(xmlData, i));\n } else if (attrStr.trim().length > 0) {\n return getErrorObject('InvalidTag', \"Closing tag '\" + tagName + \"' can't have attributes or invalid starting.\", getLineNumberForPosition(xmlData, tagStartPos));\n } else if (tags.length === 0) {\n return getErrorObject('InvalidTag', \"Closing tag '\" + tagName + \"' has not been opened.\", getLineNumberForPosition(xmlData, tagStartPos));\n } else {\n const otg = tags.pop();\n if (tagName !== otg.tagName) {\n let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);\n return getErrorObject('InvalidTag',\n \"Expected closing tag '\" + otg.tagName + \"' (opened in line \" + openPos.line + \", col \" + openPos.col + \") instead of closing tag '\" + tagName + \"'.\",\n getLineNumberForPosition(xmlData, tagStartPos));\n }\n\n //when there are no more tags, we reached the root level.\n if (tags.length == 0) {\n reachedRoot = true;\n }\n }\n } else {\n const isValid = validateAttributeString(attrStr, options);\n if (isValid !== true) {\n //the result from the nested function returns the position of the error within the attribute\n //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute\n //this gives us the absolute index in the entire xml, which we can use to find the line at last\n return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));\n }\n\n //if the root level has been reached before ...\n if (reachedRoot === true) {\n return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));\n } else if (options.unpairedTags.indexOf(tagName) !== -1) {\n //don't push into stack\n } else {\n tags.push({ tagName, tagStartPos });\n }\n tagFound = true;\n }\n\n //skip tag text value\n //It may include comments and CDATA value\n for (i++; i < xmlData.length; i++) {\n if (xmlData[i] === '<') {\n if (xmlData[i + 1] === '!') {\n //comment or CADATA\n i++;\n i = readCommentAndCDATA(xmlData, i);\n continue;\n } else if (xmlData[i + 1] === '?') {\n i = readPI(xmlData, ++i);\n if (i.err) return i;\n } else {\n break;\n }\n } else if (xmlData[i] === '&') {\n const afterAmp = validateAmpersand(xmlData, i);\n if (afterAmp == -1)\n return getErrorObject('InvalidChar', \"char '&' is not expected.\", getLineNumberForPosition(xmlData, i));\n i = afterAmp;\n } else {\n if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {\n return getErrorObject('InvalidXml', \"Extra text at the end\", getLineNumberForPosition(xmlData, i));\n }\n }\n } //end of reading tag text value\n if (xmlData[i] === '<') {\n i--;\n }\n }\n } else {\n if (isWhiteSpace(xmlData[i])) {\n continue;\n }\n return getErrorObject('InvalidChar', \"char '\" + xmlData[i] + \"' is not expected.\", getLineNumberForPosition(xmlData, i));\n }\n }\n\n if (!tagFound) {\n return getErrorObject('InvalidXml', 'Start tag expected.', 1);\n } else if (tags.length == 1) {\n return getErrorObject('InvalidTag', \"Unclosed tag '\" + tags[0].tagName + \"'.\", getLineNumberForPosition(xmlData, tags[0].tagStartPos));\n } else if (tags.length > 0) {\n return getErrorObject('InvalidXml', \"Invalid '\" +\n JSON.stringify(tags.map(t => t.tagName), null, 4).replace(/\\r?\\n/g, '') +\n \"' found.\", { line: 1, col: 1 });\n }\n\n return true;\n};\n\nfunction isWhiteSpace(char) {\n return char === ' ' || char === '\\t' || char === '\\n' || char === '\\r';\n}\n/**\n * Read Processing insstructions and skip\n * @param {*} xmlData\n * @param {*} i\n */\nfunction readPI(xmlData, i) {\n const start = i;\n for (; i < xmlData.length; i++) {\n if (xmlData[i] == '?' || xmlData[i] == ' ') {\n //tagname\n const tagname = xmlData.substr(start, i - start);\n if (i > 5 && tagname === 'xml') {\n return getErrorObject('InvalidXml', 'XML declaration allowed only at the start of the document.', getLineNumberForPosition(xmlData, i));\n } else if (xmlData[i] == '?' && xmlData[i + 1] == '>') {\n //check if valid attribut string\n i++;\n break;\n } else {\n continue;\n }\n }\n }\n return i;\n}\n\nfunction readCommentAndCDATA(xmlData, i) {\n if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {\n //comment\n for (i += 3; i < xmlData.length; i++) {\n if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {\n i += 2;\n break;\n }\n }\n } else if (\n xmlData.length > i + 8 &&\n xmlData[i + 1] === 'D' &&\n xmlData[i + 2] === 'O' &&\n xmlData[i + 3] === 'C' &&\n xmlData[i + 4] === 'T' &&\n xmlData[i + 5] === 'Y' &&\n xmlData[i + 6] === 'P' &&\n xmlData[i + 7] === 'E'\n ) {\n let angleBracketsCount = 1;\n for (i += 8; i < xmlData.length; i++) {\n if (xmlData[i] === '<') {\n angleBracketsCount++;\n } else if (xmlData[i] === '>') {\n angleBracketsCount--;\n if (angleBracketsCount === 0) {\n break;\n }\n }\n }\n } else if (\n xmlData.length > i + 9 &&\n xmlData[i + 1] === '[' &&\n xmlData[i + 2] === 'C' &&\n xmlData[i + 3] === 'D' &&\n xmlData[i + 4] === 'A' &&\n xmlData[i + 5] === 'T' &&\n xmlData[i + 6] === 'A' &&\n xmlData[i + 7] === '['\n ) {\n for (i += 8; i < xmlData.length; i++) {\n if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {\n i += 2;\n break;\n }\n }\n }\n\n return i;\n}\n\nconst doubleQuote = '\"';\nconst singleQuote = \"'\";\n\n/**\n * Keep reading xmlData until '<' is found outside the attribute value.\n * @param {string} xmlData\n * @param {number} i\n */\nfunction readAttributeStr(xmlData, i) {\n let attrStr = '';\n let startChar = '';\n let tagClosed = false;\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {\n if (startChar === '') {\n startChar = xmlData[i];\n } else if (startChar !== xmlData[i]) {\n //if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa\n } else {\n startChar = '';\n }\n } else if (xmlData[i] === '>') {\n if (startChar === '') {\n tagClosed = true;\n break;\n }\n }\n attrStr += xmlData[i];\n }\n if (startChar !== '') {\n return false;\n }\n\n return {\n value: attrStr,\n index: i,\n tagClosed: tagClosed\n };\n}\n\n/**\n * Select all the attributes whether valid or invalid.\n */\nconst validAttrStrRegxp = new RegExp('(\\\\s*)([^\\\\s=]+)(\\\\s*=)?(\\\\s*([\\'\"])(([\\\\s\\\\S])*?)\\\\5)?', 'g');\n\n//attr, =\"sd\", a=\"amit's\", a=\"sd\"b=\"saf\", ab cd=\"\"\n\nfunction validateAttributeString(attrStr, options) {\n //console.log(\"start:\"+attrStr+\":end\");\n\n //if(attrStr.trim().length === 0) return true; //empty string\n\n const matches = getAllMatches(attrStr, validAttrStrRegxp);\n const attrNames = {};\n\n for (let i = 0; i < matches.length; i++) {\n if (matches[i][1].length === 0) {\n //nospace before attribute name: a=\"sd\"b=\"saf\"\n return getErrorObject('InvalidAttr', \"Attribute '\" + matches[i][2] + \"' has no space in starting.\", getPositionFromMatch(matches[i]))\n } else if (matches[i][3] !== undefined && matches[i][4] === undefined) {\n return getErrorObject('InvalidAttr', \"Attribute '\" + matches[i][2] + \"' is without value.\", getPositionFromMatch(matches[i]));\n } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {\n //independent attribute: ab\n return getErrorObject('InvalidAttr', \"boolean attribute '\" + matches[i][2] + \"' is not allowed.\", getPositionFromMatch(matches[i]));\n }\n /* else if(matches[i][6] === undefined){//attribute without value: ab=\n return { err: { code:\"InvalidAttr\",msg:\"attribute \" + matches[i][2] + \" has no value assigned.\"}};\n } */\n const attrName = matches[i][2];\n if (!validateAttrName(attrName)) {\n return getErrorObject('InvalidAttr', \"Attribute '\" + attrName + \"' is an invalid name.\", getPositionFromMatch(matches[i]));\n }\n if (!Object.prototype.hasOwnProperty.call(attrNames, attrName)) {\n //check for duplicate attribute.\n attrNames[attrName] = 1;\n } else {\n return getErrorObject('InvalidAttr', \"Attribute '\" + attrName + \"' is repeated.\", getPositionFromMatch(matches[i]));\n }\n }\n\n return true;\n}\n\nfunction validateNumberAmpersand(xmlData, i) {\n let re = /\\d/;\n if (xmlData[i] === 'x') {\n i++;\n re = /[\\da-fA-F]/;\n }\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === ';')\n return i;\n if (!xmlData[i].match(re))\n break;\n }\n return -1;\n}\n\nfunction validateAmpersand(xmlData, i) {\n // https://www.w3.org/TR/xml/#dt-charref\n i++;\n if (xmlData[i] === ';')\n return -1;\n if (xmlData[i] === '#') {\n i++;\n return validateNumberAmpersand(xmlData, i);\n }\n let count = 0;\n for (; i < xmlData.length; i++, count++) {\n if (xmlData[i].match(/\\w/) && count < 20)\n continue;\n if (xmlData[i] === ';')\n break;\n return -1;\n }\n return i;\n}\n\nfunction getErrorObject(code, message, lineNumber) {\n return {\n err: {\n code: code,\n msg: message,\n line: lineNumber.line || lineNumber,\n col: lineNumber.col,\n },\n };\n}\n\nfunction validateAttrName(attrName) {\n return isName(attrName);\n}\n\n// const startsWithXML = /^xml/i;\n\nfunction validateTagName(tagname) {\n return isName(tagname) /* && !tagname.match(startsWithXML) */;\n}\n\n//this function returns the line number for the character at the given index\nfunction getLineNumberForPosition(xmlData, index) {\n const lines = xmlData.substring(0, index).split(/\\r?\\n/);\n return {\n line: lines.length,\n\n // column number is last line's length + 1, because column numbering starts at 1:\n col: lines[lines.length - 1].length + 1\n };\n}\n\n//this function returns the position of the first character of match within attrStr\nfunction getPositionFromMatch(match) {\n return match.startIndex + match[1].length;\n}\n","// ---------------------------------------------------------------------------\n// Complete HTML5 named entity reference\n// Organized by logical categories for easy maintenance and selective importing\n// ---------------------------------------------------------------------------\n\n/**\n * Basic Latin & Special Characters\n * @type {Record<string, string>}\n */\nexport const BASIC_LATIN = {\n amp: '&',\n AMP: '&',\n lt: '<',\n LT: '<',\n gt: '>',\n GT: '>',\n quot: '\"',\n QUOT: '\"',\n apos: \"'\",\n lsquo: '‘',\n rsquo: '’',\n ldquo: '“',\n rdquo: '”',\n lsquor: '‚',\n rsquor: '’',\n ldquor: '„',\n bdquo: '„',\n comma: ',',\n period: '.',\n colon: ':',\n semi: ';',\n excl: '!',\n quest: '?',\n num: '#',\n dollar: '$',\n percent: '%',\n ast: '*',\n commat: '@',\n lowbar: '_',\n verbar: '|',\n vert: '|',\n sol: '/',\n bsol: '\\\\',\n lbrace: '{',\n rbrace: '}',\n lbrack: '[',\n rbrack: ']',\n lpar: '(',\n rpar: ')',\n nbsp: '\\u00a0',\n iexcl: '¡',\n cent: '¢',\n pound: '£',\n curren: '¤',\n yen: '¥',\n brvbar: '¦',\n sect: '§',\n uml: '¨',\n copy: '©',\n COPY: '©',\n ordf: 'ª',\n laquo: '«',\n not: '¬',\n shy: '\\u00ad',\n reg: '®',\n REG: '®',\n macr: '¯',\n deg: '°',\n plusmn: '±',\n sup2: '²',\n sup3: '³',\n acute: '´',\n micro: 'µ',\n para: '¶',\n middot: '·',\n cedil: '¸',\n sup1: '¹',\n ordm: 'º',\n raquo: '»',\n frac14: '¼',\n frac12: '½',\n half: '½',\n frac34: '¾',\n iquest: '¿',\n times: '×',\n div: '÷',\n divide: '÷',\n};\n\n/**\n * Latin Extended & Accented Letters (A-Z)\n * @type {Record<string, string>}\n */\nexport const LATIN_ACCENTS = {\n Agrave: 'À',\n agrave: 'à',\n Aacute: 'Á',\n aacute: 'á',\n Acirc: 'Â',\n acirc: 'â',\n Atilde: 'Ã',\n atilde: 'ã',\n Auml: 'Ä',\n auml: 'ä',\n Aring: 'Å',\n aring: 'å',\n AElig: 'Æ',\n aelig: 'æ',\n Ccedil: 'Ç',\n ccedil: 'ç',\n Egrave: 'È',\n egrave: 'è',\n Eacute: 'É',\n eacute: 'é',\n Ecirc: 'Ê',\n ecirc: 'ê',\n Euml: 'Ë',\n euml: 'ë',\n Igrave: 'Ì',\n igrave: 'ì',\n Iacute: 'Í',\n iacute: 'í',\n Icirc: 'Î',\n icirc: 'î',\n Iuml: 'Ï',\n iuml: 'ï',\n ETH: 'Ð',\n eth: 'ð',\n Ntilde: 'Ñ',\n ntilde: 'ñ',\n Ograve: 'Ò',\n ograve: 'ò',\n Oacute: 'Ó',\n oacute: 'ó',\n Ocirc: 'Ô',\n ocirc: 'ô',\n Otilde: 'Õ',\n otilde: 'õ',\n Ouml: 'Ö',\n ouml: 'ö',\n Oslash: 'Ø',\n oslash: 'ø',\n Ugrave: 'Ù',\n ugrave: 'ù',\n Uacute: 'Ú',\n uacute: 'ú',\n Ucirc: 'Û',\n ucirc: 'û',\n Uuml: 'Ü',\n uuml: 'ü',\n Yacute: 'Ý',\n yacute: 'ý',\n THORN: 'Þ',\n thorn: 'þ',\n szlig: 'ß',\n yuml: 'ÿ',\n Yuml: 'Ÿ',\n};\n\n/**\n * Latin Extended (Letters with diacritics)\n * @type {Record<string, string>}\n */\nexport const LATIN_EXTENDED = {\n Amacr: 'Ā',\n amacr: 'ā',\n Abreve: 'Ă',\n abreve: 'ă',\n Aogon: 'Ą',\n aogon: 'ą',\n Cacute: 'Ć',\n cacute: 'ć',\n Ccirc: 'Ĉ',\n ccirc: 'ĉ',\n Cdot: 'Ċ',\n cdot: 'ċ',\n Ccaron: 'Č',\n ccaron: 'č',\n Dcaron: 'Ď',\n dcaron: 'ď',\n Dstrok: 'Đ',\n dstrok: 'đ',\n Emacr: 'Ē',\n emacr: 'ē',\n Ecaron: 'Ě',\n ecaron: 'ě',\n Edot: 'Ė',\n edot: 'ė',\n Eogon: 'Ę',\n eogon: 'ę',\n Gcirc: 'Ĝ',\n gcirc: 'ĝ',\n Gbreve: 'Ğ',\n gbreve: 'ğ',\n Gdot: 'Ġ',\n gdot: 'ġ',\n Gcedil: 'Ģ',\n Hcirc: 'Ĥ',\n hcirc: 'ĥ',\n Hstrok: 'Ħ',\n hstrok: 'ħ',\n Itilde: 'Ĩ',\n itilde: 'ĩ',\n Imacr: 'Ī',\n imacr: 'ī',\n Iogon: 'Į',\n iogon: 'į',\n Idot: 'İ',\n IJlig: 'IJ',\n ijlig: 'ij',\n Jcirc: 'Ĵ',\n jcirc: 'ĵ',\n Kcedil: 'Ķ',\n kcedil: 'ķ',\n kgreen: 'ĸ',\n Lacute: 'Ĺ',\n lacute: 'ĺ',\n Lcedil: 'Ļ',\n lcedil: 'ļ',\n Lcaron: 'Ľ',\n lcaron: 'ľ',\n Lmidot: 'Ŀ',\n lmidot: 'ŀ',\n Lstrok: 'Ł',\n lstrok: 'ł',\n Nacute: 'Ń',\n nacute: 'ń',\n Ncaron: 'Ň',\n ncaron: 'ň',\n Ncedil: 'Ņ',\n ncedil: 'ņ',\n ENG: 'Ŋ',\n eng: 'ŋ',\n Omacr: 'Ō',\n omacr: 'ō',\n Odblac: 'Ő',\n odblac: 'ő',\n OElig: 'Œ',\n oelig: 'œ',\n Racute: 'Ŕ',\n racute: 'ŕ',\n Rcaron: 'Ř',\n rcaron: 'ř',\n Rcedil: 'Ŗ',\n rcedil: 'ŗ',\n Sacute: 'Ś',\n sacute: 'ś',\n Scirc: 'Ŝ',\n scirc: 'ŝ',\n Scedil: 'Ş',\n scedil: 'ş',\n Scaron: 'Š',\n scaron: 'š',\n Tcedil: 'Ţ',\n tcedil: 'ţ',\n Tcaron: 'Ť',\n tcaron: 'ť',\n Tstrok: 'Ŧ',\n tstrok: 'ŧ',\n Utilde: 'Ũ',\n utilde: 'ũ',\n Umacr: 'Ū',\n umacr: 'ū',\n Ubreve: 'Ŭ',\n ubreve: 'ŭ',\n Uring: 'Ů',\n uring: 'ů',\n Udblac: 'Ű',\n udblac: 'ű',\n Uogon: 'Ų',\n uogon: 'ų',\n Wcirc: 'Ŵ',\n wcirc: 'ŵ',\n Ycirc: 'Ŷ',\n ycirc: 'ŷ',\n Zacute: 'Ź',\n zacute: 'ź',\n Zdot: 'Ż',\n zdot: 'ż',\n Zcaron: 'Ž',\n zcaron: 'ž',\n};\n\n/**\n * Greek Letters\n * @type {Record<string, string>}\n */\nexport const GREEK = {\n Alpha: 'Α',\n alpha: 'α',\n Beta: 'Β',\n beta: 'β',\n Gamma: 'Γ',\n gamma: 'γ',\n Delta: 'Δ',\n delta: 'δ',\n Epsilon: 'Ε',\n epsilon: 'ε',\n epsiv: 'ϵ',\n varepsilon: 'ϵ',\n Zeta: 'Ζ',\n zeta: 'ζ',\n Eta: 'Η',\n eta: 'η',\n Theta: 'Θ',\n theta: 'θ',\n thetasym: 'ϑ',\n vartheta: 'ϑ',\n Iota: 'Ι',\n iota: 'ι',\n Kappa: 'Κ',\n kappa: 'κ',\n kappav: 'ϰ',\n varkappa: 'ϰ',\n Lambda: 'Λ',\n lambda: 'λ',\n Mu: 'Μ',\n mu: 'μ',\n Nu: 'Ν',\n nu: 'ν',\n Xi: 'Ξ',\n xi: 'ξ',\n Omicron: 'Ο',\n omicron: 'ο',\n Pi: 'Π',\n pi: 'π',\n piv: 'ϖ',\n varpi: 'ϖ',\n Rho: 'Ρ',\n rho: 'ρ',\n rhov: 'ϱ',\n varrho: 'ϱ',\n Sigma: 'Σ',\n sigma: 'σ',\n sigmaf: 'ς',\n sigmav: 'ς',\n varsigma: 'ς',\n Tau: 'Τ',\n tau: 'τ',\n Upsilon: 'Υ',\n upsilon: 'υ',\n upsi: 'υ',\n Upsi: 'ϒ',\n upsih: 'ϒ',\n Phi: 'Φ',\n phi: 'φ',\n phiv: 'ϕ',\n varphi: 'ϕ',\n Chi: 'Χ',\n chi: 'χ',\n Psi: 'Ψ',\n psi: 'ψ',\n Omega: 'Ω',\n omega: 'ω',\n ohm: 'Ω',\n Gammad: 'Ϝ',\n gammad: 'ϝ',\n digamma: 'ϝ',\n};\n\n/**\n * Cyrillic Letters\n * @type {Record<string, string>}\n */\nexport const CYRILLIC = {\n Afr: '𝔄',\n afr: '𝔞',\n Acy: 'А',\n acy: 'а',\n Bcy: 'Б',\n bcy: 'б',\n Vcy: 'В',\n vcy: 'в',\n Gcy: 'Г',\n gcy: 'г',\n Dcy: 'Д',\n dcy: 'д',\n IEcy: 'Е',\n iecy: 'е',\n IOcy: 'Ё',\n iocy: 'ё',\n ZHcy: 'Ж',\n zhcy: 'ж',\n Zcy: 'З',\n zcy: 'з',\n Icy: 'И',\n icy: 'и',\n Jcy: 'Й',\n jcy: 'й',\n Kcy: 'К',\n kcy: 'к',\n Lcy: 'Л',\n lcy: 'л',\n Mcy: 'М',\n mcy: 'м',\n Ncy: 'Н',\n ncy: 'н',\n Ocy: 'О',\n ocy: 'о',\n Pcy: 'П',\n pcy: 'п',\n Rcy: 'Р',\n rcy: 'р',\n Scy: 'С',\n scy: 'с',\n Tcy: 'Т',\n tcy: 'т',\n Ucy: 'У',\n ucy: 'у',\n Fcy: 'Ф',\n fcy: 'ф',\n KHcy: 'Х',\n khcy: 'х',\n TScy: 'Ц',\n tscy: 'ц',\n CHcy: 'Ч',\n chcy: 'ч',\n SHcy: 'Ш',\n shcy: 'ш',\n SHCHcy: 'Щ',\n shchcy: 'щ',\n HARDcy: 'Ъ',\n hardcy: 'ъ',\n Ycy: 'Ы',\n ycy: 'ы',\n SOFTcy: 'Ь',\n softcy: 'ь',\n Ecy: 'Э',\n ecy: 'э',\n YUcy: 'Ю',\n yucy: 'ю',\n YAcy: 'Я',\n yacy: 'я',\n DJcy: 'Ђ',\n djcy: 'ђ',\n GJcy: 'Ѓ',\n gjcy: 'ѓ',\n Jukcy: 'Є',\n jukcy: 'є',\n DScy: 'Ѕ',\n dscy: 'ѕ',\n Iukcy: 'І',\n iukcy: 'і',\n YIcy: 'Ї',\n yicy: 'ї',\n Jsercy: 'Ј',\n jsercy: 'ј',\n LJcy: 'Љ',\n ljcy: 'љ',\n NJcy: 'Њ',\n njcy: 'њ',\n TSHcy: 'Ћ',\n tshcy: 'ћ',\n KJcy: 'Ќ',\n kjcy: 'ќ',\n Ubrcy: 'Ў',\n ubrcy: 'ў',\n DZcy: 'Џ',\n dzcy: 'џ',\n};\n\n/**\n * Mathematical Operators & Relations\n * @type {Record<string, string>}\n */\nexport const MATH = {\n plus: '+',\n pm: '±',\n times: '×',\n div: '÷',\n divide: '÷',\n sdot: '⋅',\n star: '☆',\n starf: '★',\n bigstar: '★',\n lowast: '∗',\n ast: '*',\n midast: '*',\n compfn: '∘',\n smallcircle: '∘',\n bullet: '•',\n bull: '•',\n nbsp: '\\u00a0',\n hellip: '…',\n mldr: '…',\n prime: '′',\n Prime: '″',\n tprime: '‴',\n bprime: '‵',\n backprime: '‵',\n minus: '−',\n minusd: '∸',\n dotminus: '∸',\n plusdo: '∔',\n dotplus: '∔',\n plusmn: '±',\n minusplus: '∓',\n mnplus: '∓',\n mp: '∓',\n setminus: '∖',\n smallsetminus: '∖',\n Backslash: '∖',\n setmn: '∖',\n ssetmn: '∖',\n lowbar: '_',\n verbar: '|',\n vert: '|',\n VerticalLine: '|',\n colon: ':',\n Colon: '∷',\n Proportion: '∷',\n ratio: '∶',\n equals: '=',\n ne: '≠',\n nequiv: '≢',\n equiv: '≡',\n Congruent: '≡',\n sim: '∼',\n thicksim: '∼',\n thksim: '∼',\n sime: '≃',\n simeq: '≃',\n TildeEqual: '≃',\n asymp: '≈',\n approx: '≈',\n thickapprox: '≈',\n thkap: '≈',\n TildeTilde: '≈',\n ncong: '≇',\n cong: '≅',\n TildeFullEqual: '≅',\n asympeq: '≍',\n CupCap: '≍',\n bump: '≎',\n Bumpeq: '≎',\n HumpDownHump: '≎',\n bumpe: '≏',\n bumpeq: '≏',\n HumpEqual: '≏',\n le: '≤',\n LessEqual: '≤',\n ge: '≥',\n GreaterEqual: '≥',\n lesseqgtr: '⋚',\n lesseqqgtr: '⪋',\n greater: '>',\n less: '<',\n};\n\n/**\n * Mathematical Operators (Advanced)\n * @type {Record<string, string>}\n */\nexport const MATH_ADVANCED = {\n alefsym: 'ℵ',\n aleph: 'ℵ',\n beth: 'ℶ',\n gimel: 'ℷ',\n daleth: 'ℸ',\n forall: '∀',\n ForAll: '∀',\n part: '∂',\n PartialD: '∂',\n exist: '∃',\n Exists: '∃',\n nexist: '∄',\n nexists: '∄',\n empty: '∅',\n emptyset: '∅',\n emptyv: '∅',\n varnothing: '∅',\n nabla: '∇',\n Del: '∇',\n isin: '∈',\n isinv: '∈',\n in: '∈',\n Element: '∈',\n notin: '∉',\n notinva: '∉',\n ni: '∋',\n niv: '∋',\n SuchThat: '∋',\n ReverseElement: '∋',\n notni: '∌',\n notniva: '∌',\n prod: '∏',\n Product: '∏',\n coprod: '∐',\n Coproduct: '∐',\n sum: '∑',\n Sum: '∑',\n minus: '−',\n mp: '∓',\n plusdo: '∔',\n dotplus: '∔',\n setminus: '∖',\n lowast: '∗',\n radic: '√',\n Sqrt: '√',\n prop: '∝',\n propto: '∝',\n Proportional: '∝',\n varpropto: '∝',\n infin: '∞',\n infintie: '⧝',\n ang: '∠',\n angle: '∠',\n angmsd: '∡',\n measuredangle: '∡',\n angsph: '∢',\n mid: '∣',\n VerticalBar: '∣',\n nmid: '∤',\n nsmid: '∤',\n npar: '∦',\n parallel: '∥',\n spar: '∥',\n nparallel: '∦',\n nspar: '∦',\n and: '∧',\n wedge: '∧',\n or: '∨',\n vee: '∨',\n cap: '∩',\n cup: '∪',\n int: '∫',\n Integral: '∫',\n conint: '∮',\n ContourIntegral: '∮',\n Conint: '∯',\n DoubleContourIntegral: '∯',\n Cconint: '∰',\n there4: '∴',\n therefore: '∴',\n Therefore: '∴',\n becaus: '∵',\n because: '∵',\n Because: '∵',\n ratio: '∶',\n Proportion: '∷',\n minusd: '∸',\n dotminus: '∸',\n mDDot: '∺',\n homtht: '∻',\n sim: '∼',\n bsimg: '∽',\n backsim: '∽',\n ac: '∾',\n mstpos: '∾',\n acd: '∿',\n VerticalTilde: '≀',\n wr: '≀',\n wreath: '≀',\n nsime: '≄',\n nsimeq: '≄',\n ncong: '≇',\n simne: '≆',\n ncongdot: '⩭̸',\n ngsim: '≵',\n nsim: '≁',\n napprox: '≉',\n nap: '≉',\n ngeq: '≱',\n nge: '≱',\n nleq: '≰',\n nle: '≰',\n ngtr: '≯',\n ngt: '≯',\n nless: '≮',\n nlt: '≮',\n nprec: '⊀',\n npr: '⊀',\n nsucc: '⊁',\n nsc: '⊁',\n};\n\n/**\n * Arrows\n * @type {Record<string, string>}\n */\nexport const ARROWS = {\n larr: '←',\n leftarrow: '←',\n LeftArrow: '←',\n uarr: '↑',\n uparrow: '↑',\n UpArrow: '↑',\n rarr: '→',\n rightarrow: '→',\n RightArrow: '→',\n darr: '↓',\n downarrow: '↓',\n DownArrow: '↓',\n harr: '↔',\n leftrightarrow: '↔',\n LeftRightArrow: '↔',\n varr: '↕',\n updownarrow: '↕',\n UpDownArrow: '↕',\n nwarr: '↖',\n nwarrow: '↖',\n UpperLeftArrow: '↖',\n nearr: '↗',\n nearrow: '↗',\n UpperRightArrow: '↗',\n searr: '↘',\n searrow: '↘',\n LowerRightArrow: '↘',\n swarr: '↙',\n swarrow: '↙',\n LowerLeftArrow: '↙',\n lArr: '⇐',\n Leftarrow: '⇐',\n uArr: '⇑',\n Uparrow: '⇑',\n rArr: '⇒',\n Rightarrow: '⇒',\n dArr: '⇓',\n Downarrow: '⇓',\n hArr: '⇔',\n Leftrightarrow: '⇔',\n iff: '⇔',\n vArr: '⇕',\n Updownarrow: '⇕',\n lAarr: '⇚',\n Lleftarrow: '⇚',\n rAarr: '⇛',\n Rrightarrow: '⇛',\n lrarr: '⇆',\n leftrightarrows: '⇆',\n rlarr: '⇄',\n rightleftarrows: '⇄',\n lrhar: '⇋',\n leftrightharpoons: '⇋',\n ReverseEquilibrium: '⇋',\n rlhar: '⇌',\n rightleftharpoons: '⇌',\n Equilibrium: '⇌',\n udarr: '⇅',\n UpArrowDownArrow: '⇅',\n duarr: '⇵',\n DownArrowUpArrow: '⇵',\n llarr: '⇇',\n leftleftarrows: '⇇',\n rrarr: '⇉',\n rightrightarrows: '⇉',\n ddarr: '⇊',\n downdownarrows: '⇊',\n har: '↽',\n lhard: '↽',\n leftharpoondown: '↽',\n lharu: '↼',\n leftharpoonup: '↼',\n rhard: '⇁',\n rightharpoondown: '⇁',\n rharu: '⇀',\n rightharpoonup: '⇀',\n lsh: '↰',\n Lsh: '↰',\n rsh: '↱',\n Rsh: '↱',\n ldsh: '↲',\n rdsh: '↳',\n hookleftarrow: '↩',\n hookrightarrow: '↪',\n mapstoleft: '↤',\n mapstoup: '↥',\n map: '↦',\n mapsto: '↦',\n mapstodown: '↧',\n crarr: '↵',\n nleftarrow: '↚',\n nleftrightarrow: '↮',\n nrightarrow: '↛',\n nrarr: '↛',\n larrtl: '↢',\n rarrtl: '↣',\n leftarrowtail: '↢',\n rightarrowtail: '↣',\n twoheadleftarrow: '↞',\n twoheadrightarrow: '↠',\n Larr: '↞',\n Rarr: '↠',\n larrhk: '↩',\n rarrhk: '↪',\n larrlp: '↫',\n looparrowleft: '↫',\n rarrlp: '↬',\n looparrowright: '↬',\n harrw: '↭',\n leftrightsquigarrow: '↭',\n nrarrw: '↝̸',\n rarrw: '↝',\n rightsquigarrow: '↝',\n larrbfs: '⤟',\n rarrbfs: '⤠',\n nvHarr: '⤄',\n nvlArr: '⤂',\n nvrArr: '⤃',\n larrfs: '⤝',\n rarrfs: '⤞',\n Map: '⤅',\n larrsim: '⥳',\n rarrsim: '⥴',\n harrcir: '⥈',\n Uarrocir: '⥉',\n lurdshar: '⥊',\n ldrdhar: '⥧',\n ldrushar: '⥋',\n rdldhar: '⥩',\n lrhard: '⥭',\n uharr: '↾',\n uharl: '↿',\n dharr: '⇂',\n dharl: '⇃',\n Uarr: '↟',\n Darr: '↡',\n zigrarr: '⇝',\n nwArr: '⇖',\n neArr: '⇗',\n seArr: '⇘',\n swArr: '⇙',\n nharr: '↮',\n nhArr: '⇎',\n nlarr: '↚',\n nlArr: '⇍',\n nrArr: '⇏',\n larrb: '⇤',\n LeftArrowBar: '⇤',\n rarrb: '⇥',\n RightArrowBar: '⇥',\n};\n\n/**\n * Geometric Shapes\n * @type {Record<string, string>}\n */\nexport const SHAPES = {\n square: '□',\n Square: '□',\n squ: '□',\n squf: '▪',\n squarf: '▪',\n blacksquar: '▪',\n blacksquare: '▪',\n FilledVerySmallSquare: '▪',\n blk34: '▓',\n blk12: '▒',\n blk14: '░',\n block: '█',\n srect: '▭',\n rect: '▭',\n sdot: '⋅',\n sdotb: '⊡',\n dotsquare: '⊡',\n triangle: '▵',\n tri: '▵',\n trine: '▵',\n utri: '▵',\n triangledown: '▿',\n dtri: '▿',\n tridown: '▿',\n triangleleft: '◃',\n ltri: '◃',\n triangleright: '▹',\n rtri: '▹',\n blacktriangle: '▴',\n utrif: '▴',\n blacktriangledown: '▾',\n dtrif: '▾',\n blacktriangleleft: '◂',\n ltrif: '◂',\n blacktriangleright: '▸',\n rtrif: '▸',\n loz: '◊',\n lozenge: '◊',\n blacklozenge: '⧫',\n lozf: '⧫',\n bigcirc: '◯',\n xcirc: '◯',\n circ: 'ˆ',\n Circle: '○',\n cir: '○',\n o: '○',\n bullet: '•',\n bull: '•',\n hellip: '…',\n mldr: '…',\n nldr: '‥',\n boxh: '─',\n HorizontalLine: '─',\n boxv: '│',\n boxdr: '┌',\n boxdl: '┐',\n boxur: '└',\n boxul: '┘',\n boxvr: '├',\n boxvl: '┤',\n boxhd: '┬',\n boxhu: '┴',\n boxvh: '┼',\n boxH: '═',\n boxV: '║',\n boxdR: '╒',\n boxDr: '╓',\n boxDR: '╔',\n boxDl: '╕',\n boxdL: '╖',\n boxDL: '╗',\n boxuR: '╘',\n boxUr: '╙',\n boxUR: '╚',\n boxUl: '╜',\n boxuL: '╛',\n boxUL: '╝',\n boxvR: '╞',\n boxVr: '╟',\n boxVR: '╠',\n boxVl: '╢',\n boxvL: '╡',\n boxVL: '╣',\n boxHd: '╤',\n boxhD: '╥',\n boxHD: '╦',\n boxHu: '╧',\n boxhU: '╨',\n boxHU: '╩',\n boxvH: '╪',\n boxVh: '╫',\n boxVH: '╬',\n};\n\n/**\n * Punctuation & Diacritics\n * @type {Record<string, string>}\n */\nexport const PUNCTUATION = {\n excl: '!',\n iexcl: '¡',\n brvbar: '¦',\n sect: '§',\n uml: '¨',\n copy: '©',\n ordf: 'ª',\n laquo: '«',\n not: '¬',\n shy: '\\u00ad',\n reg: '®',\n macr: '¯',\n deg: '°',\n plusmn: '±',\n sup2: '²',\n sup3: '³',\n acute: '´',\n micro: 'µ',\n para: '¶',\n middot: '·',\n cedil: '¸',\n sup1: '¹',\n ordm: 'º',\n raquo: '»',\n frac14: '¼',\n frac12: '½',\n frac34: '¾',\n iquest: '¿',\n nbsp: '\\u00a0',\n comma: ',',\n period: '.',\n colon: ':',\n semi: ';',\n vert: '|',\n Verbar: '‖',\n verbar: '|',\n dblac: '˝',\n circ: 'ˆ',\n caron: 'ˇ',\n breve: '˘',\n dot: '˙',\n ring: '˚',\n ogon: '˛',\n tilde: '˜',\n DiacriticalGrave: '`',\n DiacriticalAcute: '´',\n DiacriticalTilde: '˜',\n DiacriticalDot: '˙',\n DiacriticalDoubleAcute: '˝',\n grave: '`',\n};\n\n/**\n * Currency Symbols\n * @type {Record<string, string>}\n */\nexport const CURRENCY = {\n cent: '¢',\n pound: '£',\n curren: '¤',\n yen: '¥',\n euro: '€',\n dollar: '$',\n fnof: 'ƒ',\n inr: '₹',\n af: '؋',\n birr: 'ብር',\n peso: '₱',\n rub: '₽',\n won: '₩',\n yuan: '¥',\n cedil: '¸',\n};\n\n/**\n * Fractions\n * @type {Record<string, string>}\n */\nexport const FRACTIONS = {\n frac12: '½',\n half: '½',\n frac13: '⅓',\n frac14: '¼',\n frac15: '⅕',\n frac16: '⅙',\n frac18: '⅛',\n frac23: '⅔',\n frac25: '⅖',\n frac34: '¾',\n frac35: '⅗',\n frac38: '⅜',\n frac45: '⅘',\n frac56: '⅚',\n frac58: '⅝',\n frac78: '⅞',\n frasl: '⁄',\n};\n\n/**\n * Miscellaneous Symbols\n * @type {Record<string, string>}\n */\nexport const MISC_SYMBOLS = {\n trade: '™',\n TRADE: '™',\n telrec: '⌕',\n target: '⌖',\n ulcorn: '⌜',\n ulcorner: '⌜',\n urcorn: '⌝',\n urcorner: '⌝',\n dlcorn: '⌞',\n llcorner: '⌞',\n drcorn: '⌟',\n lrcorner: '⌟',\n intercal: '⊺',\n intcal: '⊺',\n oplus: '⊕',\n CirclePlus: '⊕',\n ominus: '⊖',\n CircleMinus: '⊖',\n otimes: '⊗',\n CircleTimes: '⊗',\n osol: '⊘',\n odot: '⊙',\n CircleDot: '⊙',\n oast: '⊛',\n circledast: '⊛',\n odash: '⊝',\n circleddash: '⊝',\n ocirc: '⊚',\n circledcirc: '⊚',\n boxplus: '⊞',\n plusb: '⊞',\n boxminus: '⊟',\n minusb: '⊟',\n boxtimes: '⊠',\n timesb: '⊠',\n boxdot: '⊡',\n sdotb: '⊡',\n veebar: '⊻',\n vee: '∨',\n barvee: '⊽',\n and: '∧',\n wedge: '∧',\n Cap: '⋒',\n Cup: '⋓',\n Fork: '⋔',\n pitchfork: '⋔',\n epar: '⋕',\n ltlarr: '⥶',\n nvap: '≍⃒',\n nvsim: '∼⃒',\n nvge: '≥⃒',\n nvle: '≤⃒',\n nvlt: '<⃒',\n nvgt: '>⃒',\n nvltrie: '⊴⃒',\n nvrtrie: '⊵⃒',\n Vdash: '⊩',\n dashv: '⊣',\n vDash: '⊨',\n Vvdash: '⊪',\n nvdash: '⊬',\n nvDash: '⊭',\n nVdash: '⊮',\n nVDash: '⊯',\n};\n\nexport const XML = {\n amp: \"&\",\n apos: \"'\",\n gt: \">\",\n lt: \"<\",\n quot: \"\\\"\"\n}\nexport const COMMON_HTML = {\n nbsp: '\\u00a0',\n copy: '\\u00a9',\n reg: '\\u00ae',\n trade: '\\u2122',\n mdash: '\\u2014',\n ndash: '\\u2013',\n hellip: '\\u2026',\n laquo: '\\u00ab',\n raquo: '\\u00bb',\n lsquo: '\\u2018',\n rsquo: '\\u2019',\n ldquo: '\\u201c',\n rdquo: '\\u201d',\n bull: '\\u2022',\n para: '\\u00b6',\n sect: '\\u00a7',\n deg: '\\u00b0',\n frac12: '\\u00bd',\n frac14: '\\u00bc',\n frac34: '\\u00be',\n}\n// ---------------------------------------------------------------------------\n// Note: NUMERIC_ENTITIES (&#NNN; / &#xHH;) are handled by the scanner directly\n// via String.fromCodePoint() without any map lookup.\n// ---------------------------------------------------------------------------","// ---------------------------------------------------------------------------\n// Built-in named entity map (name → replacement string)\n// No regex, no {regex,val} objects — just flat key/value pairs.\n// ---------------------------------------------------------------------------\n\nimport { XML as DEFAULT_XML_ENTITIES } from \"./entities.js\"\n\n// ---------------------------------------------------------------------------\n// Entity hook action constants\n// ---------------------------------------------------------------------------\n\n/**\n * Action constants for `onExternalEntity` and `onInputEntity` hooks.\n *\n * Use these instead of raw strings to avoid typos:\n *\n * @example\n * import EntityDecoder, { ENTITY_ACTION } from './EntityDecoder.js';\n * const dec = new EntityDecoder({\n * onInputEntity: (name, value) => ENTITY_ACTION.BLOCK,\n * });\n */\nexport const ENTITY_ACTION = Object.freeze({\n /** Resolve and expand the entity normally. */\n ALLOW: 'allow',\n /** Silently skip this entity — it will not be registered. */\n BLOCK: 'block',\n /** Throw an error, aborting entity registration entirely. */\n THROW: 'throw',\n});\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst SPECIAL_CHARS = new Set('!?\\\\\\\\/[]$%{}^&*()<>|+');\n\n/**\n * Validate that an entity name contains no dangerous characters.\n * @param {string} name\n * @returns {string} the name, unchanged\n * @throws {Error} on invalid characters\n */\nfunction validateEntityName(name) {\n if (name[0] === '#') {\n throw new Error(`[EntityReplacer] Invalid character '#' in entity name: \"${name}\"`);\n }\n for (const ch of name) {\n if (SPECIAL_CHARS.has(ch)) {\n throw new Error(`[EntityReplacer] Invalid character '${ch}' in entity name: \"${name}\"`);\n }\n }\n return name;\n}\n\n/**\n * Merge one or more entity maps into a flat name→string map.\n * Accepts either:\n * - plain string values: { amp: '&' }\n * - legacy {regex,val} / {regx,val}: { lt: { regex: /.../, val: '<' } }\n *\n * Values containing '&' are skipped (recursive expansion risk).\n *\n * @param {...object} maps\n * @returns {Record<string, string>}\n */\nfunction mergeEntityMaps(...maps) {\n const out = Object.create(null);\n for (const map of maps) {\n if (!map) continue;\n for (const key of Object.keys(map)) {\n const raw = map[key];\n if (typeof raw === 'string') {\n out[key] = raw;\n } else if (raw && typeof raw === 'object' && raw.val !== undefined) {\n // Legacy {regex,val} or {regx,val} — extract the string val only\n const val = raw.val;\n if (typeof val === 'string') {\n out[key] = val;\n }\n // function vals are not supported in the scanner — skip\n }\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// applyLimitsTo helpers\n// ---------------------------------------------------------------------------\n\nconst LIMIT_TIER_EXTERNAL = 'external'; // input/runtime + persistent external maps\nconst LIMIT_TIER_BASE = 'base'; // DEFAULT_XML_ENTITIES + namedEntities (system) maps\nconst LIMIT_TIER_ALL = 'all'; // every entity regardless of tier\n\n/**\n * Resolve `applyLimitsTo` option into a normalised Set of tier strings.\n * Accepted values: 'external' | 'base' | 'all' | string[]\n * Default: 'external' (only untrusted injected entities are counted).\n * @param {string|string[]|undefined} raw\n * @returns {Set<string>}\n */\nfunction parseLimitTiers(raw) {\n if (!raw || raw === LIMIT_TIER_EXTERNAL) return new Set([LIMIT_TIER_EXTERNAL]);\n if (raw === LIMIT_TIER_ALL) return new Set([LIMIT_TIER_ALL]);\n if (raw === LIMIT_TIER_BASE) return new Set([LIMIT_TIER_BASE]);\n if (Array.isArray(raw)) return new Set(raw);\n return new Set([LIMIT_TIER_EXTERNAL]); // safe default for unrecognised values\n}\n\n// ---------------------------------------------------------------------------\n// NCR (Numeric Character Reference) classification\n// ---------------------------------------------------------------------------\n\n// Severity order — higher number = stricter action.\n// Used to enforce minimum action levels for specific codepoint ranges.\nconst NCR_LEVEL = Object.freeze({ allow: 0, leave: 1, remove: 2, throw: 3 });\n\n// XML 1.0 §2.2: allowed chars are #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]\n// Restricted C0: U+0001–U+001F excluding U+0009, U+000A, U+000D\nconst XML10_ALLOWED_C0 = new Set([0x09, 0x0A, 0x0D]);\n\n/**\n * Parse the `ncr` constructor option into flat, hot-path-friendly fields.\n * @param {object|undefined} ncr\n * @returns {{ xmlVersion: number, onLevel: number, nullLevel: number }}\n */\nfunction parseNCRConfig(ncr) {\n if (!ncr) {\n return { xmlVersion: 1.0, onLevel: NCR_LEVEL.allow, nullLevel: NCR_LEVEL.remove };\n }\n const xmlVersion = ncr.xmlVersion === 1.1 ? 1.1 : 1.0;\n const onLevel = NCR_LEVEL[ncr.onNCR] ?? NCR_LEVEL.allow;\n const nullLevel = NCR_LEVEL[ncr.nullNCR] ?? NCR_LEVEL.remove;\n // 'allow' is not meaningful for null — clamp to at least 'remove'\n const clampedNull = Math.max(nullLevel, NCR_LEVEL.remove);\n return { xmlVersion, onLevel, nullLevel: clampedNull };\n}\n\n// ---------------------------------------------------------------------------\n// EntityReplacer\n// ---------------------------------------------------------------------------\n\n/**\n * Single-pass, zero-regex entity replacer for XML/HTML content.\n *\n * Algorithm: scan the string once for '&', read to ';', resolve via map\n * or direct codepoint conversion, build output chunks, join once at the end.\n *\n * Entity lookup priority (highest → lowest):\n * 1. input / runtime (DOCTYPE entities for current document)\n * 2. persistent external (survive across documents)\n * 3. base named map (DEFAULT_XML_ENTITIES + user-supplied namedEntities)\n *\n * Both input and external resolve as the 'external' tier for limit purposes.\n * Base map entities resolve as the 'base' tier.\n *\n * Numeric / hex references (&#NNN; / &#xHH;) are resolved directly via\n * String.fromCodePoint() — no map needed. They count as 'base' tier.\n *\n * @example\n * const replacer = new EntityReplacer({ namedEntities: COMMON_HTML });\n * replacer.setExternalEntities({ brand: 'Acme' });\n *\n * const instance = replacer.reset();\n * instance.addInputEntities({ version: '1.0' });\n * instance.encode('&brand; v&version; <'); // 'Acme v1.0 <'\n */\nexport default class EntityDecoder {\n /**\n * @param {object} [options]\n * @param {object|null} [options.namedEntities] — extra named entities merged into base map\n * @param {object} [options.limit] — security limits\n * @param {number} [options.limit.maxTotalExpansions=0] — 0 = unlimited\n * @param {number} [options.limit.maxExpandedLength=0] — 0 = unlimited\n * @param {'external'|'base'|'all'|string[]} [options.limit.applyLimitsTo='external']\n * Which entity tiers count against the security limits:\n * - 'external' (default) — only input/runtime + persistent external entities\n * - 'base' — only DEFAULT_XML_ENTITIES + namedEntities\n * - 'all' — every entity regardless of tier\n * - string[] — explicit combination, e.g. ['external', 'base']\n * @param {((resolved: string, original: string) => string)|null} [options.postCheck=null]\n * @param {string[]} [options.remove=[]] — entity names (e.g. ['nbsp', '#13']) to delete (replace with empty string)\n * @param {string[]} [options.leave=[]] — entity names to keep as literal (unchanged in output)\n * @param {object} [options.ncr] — Numeric Character Reference controls\n * @param {1.0|1.1} [options.ncr.xmlVersion=1.0]\n * XML version governing which codepoint ranges are restricted:\n * - 1.0 — C0 controls U+0001–U+001F (except U+0009/000A/000D) are prohibited\n * - 1.1 — C0 controls are allowed when written as NCRs; C1 (U+007F–U+009F) decoded as-is\n * @param {'allow'|'leave'|'remove'|'throw'} [options.ncr.onNCR='allow']\n * Base action for numeric references. Severity order: allow < leave < remove < throw.\n * For codepoint ranges that carry a minimum level (surrogates → remove, XML 1.0 C0 → remove),\n * the effective action is max(onNCR, rangeMinimum).\n * @param {'remove'|'throw'} [options.ncr.nullNCR='remove']\n * Action for U+0000 (null). 'allow' and 'leave' are clamped to 'remove' since null is never safe.\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onExternalEntity=null]\n * Hook called when an external entity is registered via `setExternalEntities()` or\n * `addExternalEntity()`. Return `ENTITY_ACTION.ALLOW` to accept the entity,\n * `ENTITY_ACTION.BLOCK` to silently skip it, or `ENTITY_ACTION.THROW` to abort with an error.\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} [options.onInputEntity=null]\n * Hook called when an input entity is registered via `addInputEntities()`. Return\n * `ENTITY_ACTION.ALLOW` to accept, `ENTITY_ACTION.BLOCK` to silently skip, or\n * `ENTITY_ACTION.THROW` to abort with an error.\n */\n constructor(options = {}) {\n this._limit = options.limit || {};\n this._maxTotalExpansions = this._limit.maxTotalExpansions || 0;\n this._maxExpandedLength = this._limit.maxExpandedLength || 0;\n this._postCheck = typeof options.postCheck === 'function' ? options.postCheck : r => r;\n this._limitTiers = parseLimitTiers(this._limit.applyLimitsTo ?? LIMIT_TIER_EXTERNAL);\n this._numericAllowed = options.numericAllowed ?? true;\n // Base map: DEFAULT_XML_ENTITIES + user-supplied extras. Immutable after construction.\n this._baseMap = mergeEntityMaps(DEFAULT_XML_ENTITIES, options.namedEntities || null);\n\n // Persistent external entities — survive across documents.\n // Stored as a separate map so reset() never touches them.\n /** @type {Record<string, string>} */\n this._externalMap = Object.create(null);\n\n // Input / runtime entities — current document only, wiped on reset().\n /** @type {Record<string, string>} */\n this._inputMap = Object.create(null);\n\n // Per-document counters\n this._totalExpansions = 0;\n this._expandedLength = 0;\n\n // --- New: remove / leave sets ---\n /** @type {Set<string>} */\n this._removeSet = new Set(options.remove && Array.isArray(options.remove) ? options.remove : []);\n /** @type {Set<string>} */\n this._leaveSet = new Set(options.leave && Array.isArray(options.leave) ? options.leave : []);\n\n // --- NCR config (parsed into flat fields for hot-path speed) ---\n const ncrCfg = parseNCRConfig(options.ncr);\n this._ncrXmlVersion = ncrCfg.xmlVersion;\n this._ncrOnLevel = ncrCfg.onLevel;\n this._ncrNullLevel = ncrCfg.nullLevel;\n\n // --- Registration hooks ---\n /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */\n this._onExternalEntity = typeof options.onExternalEntity === 'function'\n ? options.onExternalEntity\n : null;\n /** @type {((name: string, value: string) => 'allow'|'block'|'throw')|null} */\n this._onInputEntity = typeof options.onInputEntity === 'function'\n ? options.onInputEntity\n : null;\n }\n\n // -------------------------------------------------------------------------\n // Private: registration hook dispatch\n // -------------------------------------------------------------------------\n\n /**\n * Invoke a registration hook for a single entity name/value pair.\n * Returns true when the entity should be accepted, false when it should be\n * silently skipped (BLOCK), and throws when the hook returns THROW.\n *\n * @param {((name: string, value: string) => 'allow'|'block'|'throw')|null} hook\n * @param {string} name\n * @param {string} value\n * @param {string} context — used in error messages ('external' | 'input')\n * @returns {boolean} true = accept, false = skip\n */\n _applyRegistrationHook(hook, name, value, context) {\n if (!hook) return true; // no hook → always accept\n const action = hook(name, value);\n if (action === ENTITY_ACTION.BLOCK) return false;\n if (action === ENTITY_ACTION.THROW) {\n throw new Error(\n `[EntityDecoder] Registration of ${context} entity \"&${name};\" was rejected by hook`\n );\n }\n return true; // ALLOW or any unknown return value → accept\n }\n\n // -------------------------------------------------------------------------\n // Persistent external entity registration\n // -------------------------------------------------------------------------\n\n /**\n * Replace the full set of persistent external entities.\n * All keys are validated — throws on invalid characters.\n * If `onExternalEntity` is set, it is called once per entry; entries that\n * return `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW`\n * aborts the whole call.\n * @param {Record<string, string | { regex?: RegExp, val: string }>} map\n */\n setExternalEntities(map) {\n if (map) {\n for (const key of Object.keys(map)) {\n validateEntityName(key);\n }\n }\n if (!this._onExternalEntity) {\n this._externalMap = mergeEntityMaps(map);\n return;\n }\n // Hook present — resolve values first, then filter\n const flat = mergeEntityMaps(map);\n const filtered = Object.create(null);\n for (const [name, value] of Object.entries(flat)) {\n if (this._applyRegistrationHook(this._onExternalEntity, name, value, 'external')) {\n filtered[name] = value;\n }\n }\n this._externalMap = filtered;\n }\n\n /**\n * Add a single persistent external entity.\n * If `onExternalEntity` is set it is called before the entity is stored;\n * `ENTITY_ACTION.BLOCK` silently skips storage, `ENTITY_ACTION.THROW` raises.\n * @param {string} key\n * @param {string} value\n */\n addExternalEntity(key, value) {\n validateEntityName(key);\n if (typeof value === 'string' && value.indexOf('&') === -1) {\n if (this._applyRegistrationHook(this._onExternalEntity, key, value, 'external')) {\n this._externalMap[key] = value;\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Input / runtime entity registration (per document)\n // -------------------------------------------------------------------------\n\n /**\n * Inject DOCTYPE entities for the current document.\n * Also resets per-document expansion counters.\n * If `onInputEntity` is set it is called once per entry; entries returning\n * `ENTITY_ACTION.BLOCK` are silently omitted, `ENTITY_ACTION.THROW` aborts.\n * @param {Record<string, string | { regx?: RegExp, regex?: RegExp, val: string }>} map\n */\n addInputEntities(map) {\n this._totalExpansions = 0;\n this._expandedLength = 0;\n if (!this._onInputEntity) {\n this._inputMap = mergeEntityMaps(map);\n return;\n }\n const flat = mergeEntityMaps(map);\n const filtered = Object.create(null);\n for (const [name, value] of Object.entries(flat)) {\n if (this._applyRegistrationHook(this._onInputEntity, name, value, 'input')) {\n filtered[name] = value;\n }\n }\n this._inputMap = filtered;\n }\n\n // -------------------------------------------------------------------------\n // Per-document reset\n // -------------------------------------------------------------------------\n\n /**\n * Wipe input/runtime entities and reset counters.\n * Call this before processing each new document.\n * @returns {this}\n */\n reset() {\n this._inputMap = Object.create(null);\n this._totalExpansions = 0;\n this._expandedLength = 0;\n return this;\n }\n\n // -------------------------------------------------------------------------\n // XML version (can be set after construction, e.g. once parser reads <?xml?>)\n // -------------------------------------------------------------------------\n\n /**\n * Update the XML version used for NCR classification.\n * Call this as soon as the document's `<?xml version=\"...\">` declaration is parsed.\n * @param {1.0|1.1|number} version\n */\n setXmlVersion(version) {\n this._ncrXmlVersion = version === 1.1 ? 1.1 : 1.0;\n }\n\n // -------------------------------------------------------------------------\n // Primary API\n // -------------------------------------------------------------------------\n\n /**\n * Replace all entity references in `str` in a single pass.\n *\n * @param {string} str\n * @returns {string}\n */\n decode(str) {\n if (typeof str !== 'string' || str.length === 0) return str;\n //TODO: check if needed\n if (str.indexOf('&') === -1) return str; // fast path — no entities at all\n\n const original = str;\n const chunks = [];\n const len = str.length;\n let last = 0; // start of next unprocessed literal chunk\n let i = 0;\n\n const limitExpansions = this._maxTotalExpansions > 0;\n const limitLength = this._maxExpandedLength > 0;\n const checkLimits = limitExpansions || limitLength;\n\n while (i < len) {\n // Scan forward to next '&'\n if (str.charCodeAt(i) !== 38 /* '&' */) { i++; continue; }\n\n // --- Found '&' at position i ---\n\n // Scan forward to ';'\n let j = i + 1;\n while (j < len && str.charCodeAt(j) !== 59 /* ';' */ && (j - i) <= 32) j++;\n\n if (j >= len || str.charCodeAt(j) !== 59) {\n // No closing ';' within window — treat '&' as literal\n i++;\n continue;\n }\n\n // Raw token between '&' and ';' (exclusive)\n const token = str.slice(i + 1, j);\n if (token.length === 0) { i++; continue; }\n\n let replacement;\n let tier; // which limit tier this entity belongs to\n\n if (this._removeSet.has(token)) {\n // Remove entity: replace with empty string\n replacement = '';\n // If entity was unknown (replacement undefined), we still need a tier for limits.\n // Treat as external tier because it's user-directed removal of an unknown reference.\n if (tier === undefined) {\n tier = LIMIT_TIER_EXTERNAL;\n }\n } else if (this._leaveSet.has(token)) {\n // Do not replace — keep original &token; as literal\n i++;\n continue;\n } else if (token.charCodeAt(0) === 35 /* '#' */) {\n // ---- Numeric / NCR reference ----\n // NCR classification always runs first — prohibited codepoints must be\n // caught regardless of numericAllowed.\n const ncrResult = this._resolveNCR(token);\n if (ncrResult === undefined) {\n // 'leave' action — keep original &token; as-is\n i++;\n continue;\n }\n replacement = ncrResult; // '' for remove, char string for allow\n tier = LIMIT_TIER_BASE;\n } else {\n // ---- Named reference ----\n const resolved = this._resolveName(token);\n replacement = resolved?.value;\n tier = resolved?.tier;\n }\n\n if (replacement === undefined) {\n // Unknown entity — leave as-is, advance past '&' only\n i++;\n continue;\n }\n\n // Flush literal chunk before this entity\n if (i > last) chunks.push(str.slice(last, i));\n chunks.push(replacement);\n last = j + 1; // skip past ';'\n i = last;\n\n // Apply expansion limits only if this tier is being tracked\n if (checkLimits && this._tierCounts(tier)) {\n if (limitExpansions) {\n this._totalExpansions++;\n if (this._totalExpansions > this._maxTotalExpansions) {\n throw new Error(\n `[EntityReplacer] Entity expansion count limit exceeded: ` +\n `${this._totalExpansions} > ${this._maxTotalExpansions}`\n );\n }\n }\n if (limitLength) {\n // delta: replacement.length minus the raw &token; length (token.length + 2 for '&' and ';')\n const delta = replacement.length - (token.length + 2);\n if (delta > 0) {\n this._expandedLength += delta;\n if (this._expandedLength > this._maxExpandedLength) {\n throw new Error(\n `[EntityReplacer] Expanded content length limit exceeded: ` +\n `${this._expandedLength} > ${this._maxExpandedLength}`\n );\n }\n }\n }\n }\n }\n\n // Flush trailing literal\n if (last < len) chunks.push(str.slice(last));\n\n // If nothing was replaced, chunks is empty — return original\n const result = chunks.length === 0 ? str : chunks.join('');\n\n return this._postCheck(result, original);\n }\n\n // -------------------------------------------------------------------------\n // Private: limit tier check\n // -------------------------------------------------------------------------\n\n /**\n * Returns true if a resolved entity of the given tier should count\n * against the expansion/length limits.\n * @param {string} tier — LIMIT_TIER_EXTERNAL | LIMIT_TIER_BASE\n * @returns {boolean}\n */\n _tierCounts(tier) {\n if (this._limitTiers.has(LIMIT_TIER_ALL)) return true;\n return this._limitTiers.has(tier);\n }\n\n // -------------------------------------------------------------------------\n // Private: entity resolution\n // -------------------------------------------------------------------------\n\n /**\n * Resolve a named entity token (without & and ;).\n * Priority: inputMap > externalMap > baseMap\n * Returns the resolved value tagged with its limit tier.\n *\n * @param {string} name\n * @returns {{ value: string, tier: string }|undefined}\n */\n _resolveName(name) {\n // input and external both count as 'external' tier for limit purposes —\n // they are injected at runtime and are the untrusted surface.\n if (name in this._inputMap) return { value: this._inputMap[name], tier: LIMIT_TIER_EXTERNAL };\n if (name in this._externalMap) return { value: this._externalMap[name], tier: LIMIT_TIER_EXTERNAL };\n if (name in this._baseMap) return { value: this._baseMap[name], tier: LIMIT_TIER_BASE };\n return undefined;\n }\n\n /**\n * Classify a codepoint and return the minimum action level that must be applied.\n * Returns -1 when no minimum is imposed (normal allow path).\n *\n * Ranges checked (in priority order):\n * 1. U+0000 — null, governed by nullNCR (always ≥ remove)\n * 2. U+D800–U+DFFF — surrogates, always prohibited (min: remove)\n * 3. U+0001–U+001F \\ {0x09,0x0A,0x0D} — XML 1.0 restricted C0 (min: remove)\n * (skipped in XML 1.1 — C0 controls are allowed when written as NCRs)\n *\n * @param {number} cp — codepoint\n * @returns {number} — minimum NCR_LEVEL value, or -1 for no restriction\n */\n _classifyNCR(cp) {\n // 1. Null\n if (cp === 0) return this._ncrNullLevel;\n\n // 2. Surrogates — always prohibited, minimum 'remove'\n if (cp >= 0xD800 && cp <= 0xDFFF) return NCR_LEVEL.remove;\n\n // 3. XML 1.0 restricted C0 controls\n if (this._ncrXmlVersion === 1.0) {\n if (cp >= 0x01 && cp <= 0x1F && !XML10_ALLOWED_C0.has(cp)) return NCR_LEVEL.remove;\n }\n\n return -1; // no restriction\n }\n\n /**\n * Execute a resolved NCR action.\n *\n * @param {number} action — NCR_LEVEL value\n * @param {string} token — raw token (e.g. '#38') for error messages\n * @param {number} cp — codepoint, used only for error messages\n * @returns {string|undefined}\n * - decoded character string → 'allow'\n * - '' → 'remove'\n * - undefined → 'leave' (caller must skip past '&' only)\n * - throws Error → 'throw'\n */\n _applyNCRAction(action, token, cp) {\n switch (action) {\n case NCR_LEVEL.allow: return String.fromCodePoint(cp);\n case NCR_LEVEL.remove: return '';\n case NCR_LEVEL.leave: return undefined; // signal: keep literal\n case NCR_LEVEL.throw:\n throw new Error(\n `[EntityDecoder] Prohibited numeric character reference ` +\n `&${token}; (U+${cp.toString(16).toUpperCase().padStart(4, '0')})`\n );\n default: return String.fromCodePoint(cp);\n }\n }\n\n /**\n * Full NCR resolution pipeline for a numeric token.\n *\n * Steps:\n * 1. Parse the codepoint (decimal or hex).\n * 2. Validate the raw codepoint range (NaN, <0, >0x10FFFF).\n * 3. If numericAllowed is false and no minimum restriction applies → leave as-is.\n * 4. Classify the codepoint to find the minimum required action level.\n * 5. Resolve effective action = max(onNCR, minimum).\n * 6. Apply and return.\n *\n * @param {string} token — e.g. '#38', '#x26', '#X26'\n * @returns {string|undefined}\n * - string (incl. '') — replacement ('' = remove)\n * - undefined — leave original &token; as-is\n */\n _resolveNCR(token) {\n // Step 1: parse codepoint\n const second = token.charCodeAt(1);\n let cp;\n if (second === 120 /* x */ || second === 88 /* X */) {\n cp = parseInt(token.slice(2), 16);\n } else {\n cp = parseInt(token.slice(1), 10);\n }\n\n // Step 2: out-of-range → leave as-is unconditionally\n if (Number.isNaN(cp) || cp < 0 || cp > 0x10FFFF) return undefined;\n\n // Step 3: classify to get minimum action level\n const minimum = this._classifyNCR(cp);\n\n // Step 4: if numericAllowed is false and no hard minimum → leave\n if (!this._numericAllowed && minimum < NCR_LEVEL.remove) return undefined;\n\n // Step 5: effective action = max(configured onNCR, range minimum)\n const effective = minimum === -1\n ? this._ncrOnLevel\n : Math.max(this._ncrOnLevel, minimum);\n\n // Step 6: apply\n return this._applyNCRAction(effective, token, cp);\n }\n}","import { DANGEROUS_PROPERTY_NAMES, criticalProperties } from \"../util.js\";\nimport { COMMON_HTML, CURRENCY } from '@nodable/entities';\n\nconst defaultOnDangerousProperty = (name) => {\n if (DANGEROUS_PROPERTY_NAMES.includes(name)) {\n return \"__\" + name;\n }\n return name;\n};\n\n\nexport const defaultOptions = {\n preserveOrder: false,\n attributeNamePrefix: '@_',\n attributesGroupName: false,\n textNodeName: '#text',\n ignoreAttributes: true,\n removeNSPrefix: false, // remove NS from tag name or attribute name if true\n allowBooleanAttributes: false, //a tag can have attributes without any value\n //ignoreRootElement : false,\n parseTagValue: true,\n parseAttributeValue: false,\n trimValues: true, //Trim string values of tag and attributes\n cdataPropName: false,\n numberParseOptions: {\n hex: true,\n leadingZeros: true,\n eNotation: true,\n unicode: false\n },\n tagValueProcessor: function (tagName, val) {\n return val;\n },\n attributeValueProcessor: function (attrName, val) {\n return val;\n },\n stopNodes: [], //nested tags will not be parsed even for errors\n alwaysCreateTextNode: false,\n isArray: () => false,\n commentPropName: false,\n unpairedTags: [],\n processEntities: true,\n htmlEntities: false,\n entityDecoder: null,\n ignoreDeclaration: false,\n ignorePiTags: false,\n transformTagName: false,\n transformAttributeName: false,\n updateTag: function (tagName, jPath, attrs) {\n return tagName\n },\n // skipEmptyListItem: false\n captureMetaData: false,\n maxNestedTags: 100,\n strictReservedNames: true,\n jPath: true, // if true, pass jPath string to callbacks; if false, pass matcher instance\n onDangerousProperty: defaultOnDangerousProperty\n};\n\n\n/**\n * Validates that a property name is safe to use\n * @param {string} propertyName - The property name to validate\n * @param {string} optionName - The option field name (for error message)\n * @throws {Error} If property name is dangerous\n */\nfunction validatePropertyName(propertyName, optionName) {\n if (typeof propertyName !== 'string') {\n return; // Only validate string property names\n }\n\n const normalized = propertyName.toLowerCase();\n if (DANGEROUS_PROPERTY_NAMES.some(dangerous => normalized === dangerous.toLowerCase())) {\n throw new Error(\n `[SECURITY] Invalid ${optionName}: \"${propertyName}\" is a reserved JavaScript keyword that could cause prototype pollution`\n );\n }\n\n if (criticalProperties.some(dangerous => normalized === dangerous.toLowerCase())) {\n throw new Error(\n `[SECURITY] Invalid ${optionName}: \"${propertyName}\" is a reserved JavaScript keyword that could cause prototype pollution`\n );\n }\n}\n\n/**\n * Normalizes processEntities option for backward compatibility\n * @param {boolean|object} value \n * @returns {object} Always returns normalized object\n */\nfunction normalizeProcessEntities(value, htmlEntities) {\n // Boolean backward compatibility\n if (typeof value === 'boolean') {\n return {\n enabled: value, // true or false\n maxEntitySize: 10000,\n maxExpansionDepth: 10000,\n maxTotalExpansions: Infinity,\n maxExpandedLength: 100000,\n maxEntityCount: 1000,\n allowedTags: null,\n tagFilter: null,\n appliesTo: \"all\",\n };\n }\n\n // Object config - merge with defaults\n if (typeof value === 'object' && value !== null) {\n return {\n enabled: value.enabled !== false,\n maxEntitySize: Math.max(1, value.maxEntitySize ?? 10000),\n maxExpansionDepth: Math.max(1, value.maxExpansionDepth ?? 10000),\n maxTotalExpansions: Math.max(1, value.maxTotalExpansions ?? Infinity),\n maxExpandedLength: Math.max(1, value.maxExpandedLength ?? 100000),\n maxEntityCount: Math.max(1, value.maxEntityCount ?? 1000),\n allowedTags: value.allowedTags ?? null,\n tagFilter: value.tagFilter ?? null,\n appliesTo: value.appliesTo ?? \"all\",\n };\n }\n\n // Default to enabled with limits\n return normalizeProcessEntities(true);\n}\n\nexport const buildOptions = function (options) {\n const built = Object.assign({}, defaultOptions, options);\n\n // Validate property names to prevent prototype pollution\n const propertyNameOptions = [\n { value: built.attributeNamePrefix, name: 'attributeNamePrefix' },\n { value: built.attributesGroupName, name: 'attributesGroupName' },\n { value: built.textNodeName, name: 'textNodeName' },\n { value: built.cdataPropName, name: 'cdataPropName' },\n { value: built.commentPropName, name: 'commentPropName' }\n ];\n\n for (const { value, name } of propertyNameOptions) {\n if (value) {\n validatePropertyName(value, name);\n }\n }\n\n if (built.onDangerousProperty === null) {\n built.onDangerousProperty = defaultOnDangerousProperty;\n }\n\n // Always normalize processEntities for backward compatibility and validation\n built.processEntities = normalizeProcessEntities(built.processEntities, built.htmlEntities);\n built.unpairedTagsSet = new Set(built.unpairedTags);\n // Convert old-style stopNodes for backward compatibility\n if (built.stopNodes && Array.isArray(built.stopNodes)) {\n built.stopNodes = built.stopNodes.map(node => {\n if (typeof node === 'string' && node.startsWith('*.')) {\n // Old syntax: *.tagname meant \"tagname anywhere\"\n // Convert to new syntax: ..tagname\n return '..' + node.substring(2);\n }\n return node;\n });\n }\n //console.debug(built.processEntities)\n return built;\n};","'use strict';\n\nlet METADATA_SYMBOL;\n\nif (typeof Symbol !== \"function\") {\n METADATA_SYMBOL = \"@@xmlMetadata\";\n} else {\n METADATA_SYMBOL = Symbol(\"XML Node Metadata\");\n}\n\nexport default class XmlNode {\n constructor(tagname) {\n this.tagname = tagname;\n this.child = []; //nested tags, text, cdata, comments in order\n this[\":@\"] = Object.create(null); //attributes map\n }\n add(key, val) {\n // this.child.push( {name : key, val: val, isCdata: isCdata });\n if (key === \"__proto__\") key = \"#__proto__\";\n this.child.push({ [key]: val });\n }\n addChild(node, startIndex) {\n if (node.tagname === \"__proto__\") node.tagname = \"#__proto__\";\n if (node[\":@\"] && Object.keys(node[\":@\"]).length > 0) {\n this.child.push({ [node.tagname]: node.child, [\":@\"]: node[\":@\"] });\n } else {\n this.child.push({ [node.tagname]: node.child });\n }\n // if requested, add the startIndex\n if (startIndex !== undefined) {\n // Note: for now we just overwrite the metadata. If we had more complex metadata,\n // we might need to do an object append here: metadata = { ...metadata, startIndex }\n this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex };\n }\n }\n /** symbol used for metadata */\n static getMetaDataSymbol() {\n return METADATA_SYMBOL;\n }\n}\n","/**\n * xml-naming\n * Validates XML Name productions as defined in the XML 1.0 and 1.1 specifications.\n * Covers: Name, NCName, QName, NMToken, NMTokens\n *\n * XML 1.0 spec: https://www.w3.org/TR/xml/#NT-Name\n * XML 1.1 spec: https://www.w3.org/TR/xml11/#NT-NameStartChar\n * XML NS spec: https://www.w3.org/TR/xml-names/#NT-NCName\n */\n\n// ---------------------------------------------------------------------------\n// Character class strings — XML 1.0\n//\n// NameStartChar ::= \":\" | [A-Z] | \"_\" | [a-z]\n// | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF]\n// | [#x370-#x37D] | [#x37F-#x1FFF] <- split to exclude #x0487\n// | [#x200C-#x200D]\n// | [#x2070-#x218F] | [#x2C00-#x2FEF]\n// | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD]\n//\n// NameChar ::= NameStartChar | \"-\" | \".\" | [0-9]\n// | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]\n//\n// Note: \\u0487 (Combining Cyrillic Millions Sign) was added in Unicode 4.0,\n// after XML 1.0 was defined against Unicode 2.0. It falls inside the range\n// \\u037F-\\u1FFF but must be excluded. We split that range into\n// \\u037F-\\u0486 and \\u0488-\\u1FFF to exclude it explicitly.\n// ---------------------------------------------------------------------------\n\nconst nameStartChar10 =\n ':A-Za-z_' +\n '\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF' +\n '\\u0370-\\u037D' +\n '\\u037F-\\u0486\\u0488-\\u1FFF' + // split to exclude \\u0487\n '\\u200C-\\u200D' +\n '\\u2070-\\u218F' +\n '\\u2C00-\\u2FEF' +\n '\\u3001-\\uD7FF' +\n '\\uF900-\\uFDCF' +\n '\\uFDF0-\\uFFFD';\n\nconst nameChar10 =\n nameStartChar10 +\n '\\\\-\\\\.\\\\d' +\n '\\u00B7' +\n '\\u0300-\\u036F' +\n '\\u203F-\\u2040';\n\n// ---------------------------------------------------------------------------\n// Character class strings — XML 1.1\n//\n// Differences from XML 1.0:\n//\n// NameStartChar:\n// 1.0 has split ranges: \\u00C0-\\u00D6, \\u00D8-\\u00F6, \\u00F8-\\u02FF\n// 1.1 merges them into: \\u00C0-\\u02FF\n// (\\u00D7 x and \\u00F7 / are division symbols, excluded in both versions)\n//\n// 1.0 tops out at \\uFFFD (BMP only)\n// 1.1 adds \\u{10000}-\\u{EFFFF} (supplementary planes)\n// These require the /u flag on the RegExp — see buildRegexes below.\n//\n// NameChar:\n// 1.1 adds \\u0487 (Combining Cyrillic Millions Sign, added in Unicode 4.0)\n// ---------------------------------------------------------------------------\n\nconst nameStartChar11 =\n ':A-Za-z_' +\n '\\u00C0-\\u02FF' + // merged — 1.0 had three split ranges here\n '\\u0370-\\u037D' +\n '\\u037F-\\u0486\\u0488-\\u1FFF' + // split to exclude \\u0487 (combining mark, never a NameStartChar)\n '\\u200C-\\u200D' +\n '\\u2070-\\u218F' +\n '\\u2C00-\\u2FEF' +\n '\\u3001-\\uD7FF' +\n '\\uF900-\\uFDCF' +\n '\\uFDF0-\\uFFFD' +\n '\\u{10000}-\\u{EFFFF}'; // supplementary planes — REQUIRES /u flag on RegExp\n\nconst nameChar11 =\n nameStartChar11 +\n '\\\\-\\\\.\\\\d' +\n '\\u00B7' +\n '\\u0300-\\u036F' +\n '\\u0487' + // Combining Cyrillic Millions Sign — valid in 1.1, not 1.0\n '\\u203F-\\u2040';\n\n// ---------------------------------------------------------------------------\n// Regex builders\n//\n// XML 1.0 regexes: no flags — BMP only, standard JS regex behaviour.\n// XML 1.1 regexes: /u flag — required for \\u{10000}-\\u{EFFFF} to match actual\n// supplementary code points rather than lone surrogates (which are illegal XML).\n// ---------------------------------------------------------------------------\n\nconst buildRegexes = (startChar, char, flags = '') => {\n const ncStart = startChar.replace(':', '');\n const ncChar = char.replace(':', '');\n const ncNamePat = `[${ncStart}][${ncChar}]*`;\n\n return {\n name: new RegExp(`^[${startChar}][${char}]*$`, flags),\n ncName: new RegExp(`^${ncNamePat}$`, flags),\n qName: new RegExp(`^${ncNamePat}(?::${ncNamePat})?$`, flags),\n nmToken: new RegExp(`^[${char}]+$`, flags),\n nmTokens: new RegExp(`^[${char}]+(?:\\\\s+[${char}]+)*$`, flags),\n };\n};\n\nconst regexes10 = buildRegexes(nameStartChar10, nameChar10); // no /u — BMP only\nconst regexes11 = buildRegexes(nameStartChar11, nameChar11, 'u'); // /u — enables \\u{10000}-\\u{EFFFF}\n\n// ---------------------------------------------------------------------------\n// ASCII-only fast path (opt-in, off by default)\n//\n// The XML 1.0 vs 1.1 NameStartChar/NameChar productions differ *only* in\n// their non-ASCII ranges (merged vs split Latin-1 ranges, \\u0487, and\n// supplementary planes). Restricted to ASCII, both versions collapse to the\n// same character classes, so a single regex pair covers both xmlVersion\n// values — no /u flag needed.\n//\n// Rationale: unicode-aware regexes (the /u flag, required for XML 1.1's\n// supplementary-plane range) are measurably slower in V8 than plain\n// non-unicode regexes on the same input, even when the input is pure ASCII.\n// For the common case — HTML/SVG ids, XML tags — names are ASCII, so callers\n// who know this can opt in to skip the unicode-aware matching path entirely.\n// This is a real but *conditional* win: mainly for XML 1.1 input (avoids /u),\n// or at scale where the larger unicode character classes add engine\n// overhead. It also changes behaviour (rejects legitimate non-ASCII XML\n// 1.0/1.1 names), so it must never be silently enabled — hence off by\n// default.\n// ---------------------------------------------------------------------------\n\nconst nameStartCharAscii = ':A-Za-z_';\nconst nameCharAscii = nameStartCharAscii + '\\\\-\\\\.\\\\d';\n\nconst regexesAscii = buildRegexes(nameStartCharAscii, nameCharAscii); // no /u — ASCII only\n\nconst getRegexes = (xmlVersion = '1.0', asciiOnly = false) => {\n if (asciiOnly) return regexesAscii;\n return xmlVersion === '1.1' ? regexes11 : regexes10;\n};\n\n// ---------------------------------------------------------------------------\n// Boolean validators\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true if the string is a valid XML Name.\n * Colons are allowed anywhere (Name production).\n * Used for: DOCTYPE entity names, notation names, DTD element declarations.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const name = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).name.test(str);\n\n/**\n * Returns true if the string is a valid NCName (Non-Colonized Name).\n * Colons are not permitted.\n * Used for: namespace prefixes, local names, SVG id attributes.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const ncName = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).ncName.test(str);\n\n/**\n * Returns true if the string is a valid QName (Qualified Name).\n * Allows exactly one colon as a prefix separator: prefix:localName.\n * Used for: element and attribute names in namespace-aware XML/SVG.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const qName = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).qName.test(str);\n\n/**\n * Returns true if the string is a valid NMToken.\n * Like Name but no restriction on the first character.\n * Used for: DTD NMTOKEN attribute values.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const nmToken = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).nmToken.test(str);\n\n/**\n * Returns true if the string is a valid NMTokens value.\n * A whitespace-separated list of NMToken values.\n * Used for: DTD NMTOKENS attribute values.\n *\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * asciiOnly: skip unicode-aware matching, ASCII names only (default false).\n */\nexport const nmTokens = (str, { xmlVersion = '1.0', asciiOnly = false } = {}) =>\n getRegexes(xmlVersion, asciiOnly).nmTokens.test(str);\n\n// ---------------------------------------------------------------------------\n// Memoized validator factory\n//\n// Real documents reuse a small vocabulary of tag/attribute names across many\n// siblings (e.g. `id`, `class`, `href` repeated across hundreds of elements).\n// The plain boolean validators above re-run the regex on every call\n// regardless of repeats. `createValidator` returns a closure with a private\n// string -> boolean cache, so repeated names after the first become O(1)\n// lookups instead of regex tests.\n//\n// - opts (xmlVersion, asciiOnly) are fixed at creation time, so the regex is\n// resolved once, not on every call.\n// - The cache is private to the returned closure — no shared/global state,\n// no cross-caller pollution.\n// - `maxCacheSize` bounds memory: once the cache reaches this many entries,\n// it stops accepting new ones (existing entries keep serving hits; new\n// misses just fall through to the regex, uncached). This avoids unbounded\n// growth against adversarial/high-cardinality input (e.g. validating\n// attacker-supplied names with no repeats) without the cost/complexity of\n// a full LRU, and without the perf cliff of reset-and-refill thrashing.\n// - Call `.reset()` on the returned function to clear the cache manually\n// (e.g. between unrelated parse calls).\n// ---------------------------------------------------------------------------\n\nconst PRODUCTIONS = ['name', 'ncName', 'qName', 'nmToken', 'nmTokens'];\n\n/**\n * Returns a memoized boolean validator function for a single production,\n * with opts fixed at creation time.\n *\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean, maxCacheSize?: number }} [opts]\n * maxCacheSize: max number of distinct strings to cache (default 2048).\n * Once reached, new strings are validated but not cached; existing cached\n * entries keep being served.\n * @returns {((str: string) => boolean) & { reset: () => void }}\n */\nexport const createValidator = (production, { xmlVersion = '1.0', asciiOnly = false, maxCacheSize = 2048 } = {}) => {\n if (!PRODUCTIONS.includes(production)) {\n throw new TypeError(\n `Unknown production \"${production}\". Must be one of: ${PRODUCTIONS.join(', ')}`\n );\n }\n\n const regex = getRegexes(xmlVersion, asciiOnly)[production];\n let cache = new Map();\n\n const validator = (str) => {\n const cached = cache.get(str);\n if (cached !== undefined) return cached;\n\n const result = regex.test(str);\n if (cache.size < maxCacheSize) cache.set(str, result);\n return result;\n };\n\n validator.reset = () => { cache = new Map(); };\n\n return validator;\n};\n\n// ---------------------------------------------------------------------------\n// Diagnostic validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validates a string against a named production and returns a detailed result.\n *\n * @param {string} str\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * @returns {{ valid: boolean, production: string, input: string, reason?: string, position?: number }}\n */\nexport const validate = (str, production, { xmlVersion = '1.0', asciiOnly = false } = {}) => {\n if (!PRODUCTIONS.includes(production)) {\n throw new TypeError(\n `Unknown production \"${production}\". Must be one of: ${PRODUCTIONS.join(', ')}`\n );\n }\n\n const validators = { name, ncName, qName, nmToken, nmTokens };\n const isValid = validators[production](str, { xmlVersion, asciiOnly });\n\n if (isValid) return { valid: true, production, input: str };\n\n let reason = 'Does not match the production rules';\n let position;\n\n // Diagnostic fallback char checks must mirror the same character set the\n // boolean validator above used, or the reported reason/position could\n // contradict the `valid: false` result (e.g. flagging a char as illegal\n // that the unicode-aware check would have accepted).\n const startCharPattern = asciiOnly ? /^[:A-Za-z_]/ : /^[:A-Za-z_\\u00C0-\\uFFFD]/;\n const namePattern = asciiOnly ? /[\\w\\-\\\\.:]/ : /[\\w\\-\\\\.:\\u00B7\\u00C0-\\uFFFD]/;\n\n if (str.length === 0) {\n reason = 'Input is empty';\n } else if (production === 'ncName' && str.includes(':')) {\n position = str.indexOf(':');\n reason = 'Colon is not allowed in NCName';\n } else if (production === 'qName' && str.startsWith(':')) {\n reason = 'QName cannot start with a colon';\n position = 0;\n } else if (production === 'qName' && str.endsWith(':')) {\n reason = 'QName cannot end with a colon';\n position = str.length - 1;\n } else if (production === 'qName' && (str.match(/:/g) || []).length > 1) {\n reason = 'QName can have at most one colon';\n position = str.lastIndexOf(':');\n } else if (\n ['name', 'ncName', 'qName'].includes(production) &&\n !startCharPattern.test(str[0])\n ) {\n reason = `First character \"${str[0]}\" is not a valid NameStartChar`;\n position = 0;\n } else {\n for (let i = 0; i < str.length; i++) {\n if (!namePattern.test(str[i])) {\n reason = `Character \"${str[i]}\" at position ${i} is not a valid NameChar`;\n position = i;\n break;\n }\n }\n }\n\n return { valid: false, production, input: str, reason, position };\n};\n\n// ---------------------------------------------------------------------------\n// Batch validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validates an array of strings against a named production.\n *\n * @param {string[]} strings\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ xmlVersion?: '1.0'|'1.1', asciiOnly?: boolean }} [opts]\n * @returns {Array<{ valid: boolean, production: string, input: string, reason?: string, position?: number }>}\n */\nexport const validateAll = (strings, production, opts = {}) =>\n strings.map(str => validate(str, production, opts));\n\n// ---------------------------------------------------------------------------\n// Sanitizer\n// ---------------------------------------------------------------------------\n\n/**\n * Transforms an invalid string into the nearest valid XML name for the given production.\n *\n * @param {string} str\n * @param {'name'|'ncName'|'qName'|'nmToken'|'nmTokens'} production\n * @param {{ replacement?: string, asciiOnly?: boolean }} [opts]\n * asciiOnly: also replace any non-ASCII character, not just XML-illegal\n * ones (default false).\n * @returns {string}\n */\nexport const sanitize = (str, production = 'name', { replacement = '_', asciiOnly = false } = {}) => {\n if (!str) return replacement;\n\n let result = str;\n\n // Strip colons for NCName\n if (production === 'ncName') {\n result = result.replace(/:/g, '');\n }\n\n // Replace illegal characters\n const allowedCharPattern = asciiOnly ? /[^\\w\\-\\.:]/g : /[^\\w\\-\\.:\\u00B7\\u00C0-\\uFFFD]/g;\n result = result.replace(allowedCharPattern, replacement);\n\n // Fix invalid start character for Name / NCName / QName\n if (production !== 'nmToken' && production !== 'nmTokens') {\n if (/^[\\-\\.\\d]/.test(result)) {\n result = replacement + result;\n }\n }\n\n return result || replacement;\n};","import { qName as isName } from 'xml-naming';\n\nexport default class DocTypeReader {\n constructor(options, xmlVersion) {\n this.suppressValidationErr = !options;\n this.options = options;\n this.xmlVersion = xmlVersion || 1.0;\n }\n\n setXmlVersion(xmlVersion = 1.0) {\n this.xmlVersion = xmlVersion;\n }\n readDocType(xmlData, i) {\n const entities = Object.create(null);\n let entityCount = 0;\n\n if (xmlData[i + 3] === 'O' &&\n xmlData[i + 4] === 'C' &&\n xmlData[i + 5] === 'T' &&\n xmlData[i + 6] === 'Y' &&\n xmlData[i + 7] === 'P' &&\n xmlData[i + 8] === 'E') {\n i = i + 9;\n let angleBracketsCount = 1;\n let hasBody = false, comment = false;\n let exp = \"\";\n for (; i < xmlData.length; i++) {\n if (xmlData[i] === '<' && !comment) { //Determine the tag type\n if (hasBody && hasSeq(xmlData, \"!ENTITY\", i)) {\n i += 7;\n let entityName, val;\n [entityName, val, i] = this.readEntityExp(xmlData, i + 1, this.suppressValidationErr);\n if (val.indexOf(\"&\") === -1) { //Parameter entities are not supported\n if (this.options.enabled !== false &&\n this.options.maxEntityCount != null &&\n entityCount >= this.options.maxEntityCount) {\n throw new Error(\n `Entity count (${entityCount + 1}) exceeds maximum allowed (${this.options.maxEntityCount})`\n );\n }\n //const escaped = entityName.replace(/[.\\-+*:]/g, '\\\\.');\n //const escaped = entityName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n entities[entityName] = val;\n entityCount++;\n }\n }\n else if (hasBody && hasSeq(xmlData, \"!ELEMENT\", i)) {\n i += 8;//Not supported\n const { index } = this.readElementExp(xmlData, i + 1);\n i = index;\n } else if (hasBody && hasSeq(xmlData, \"!ATTLIST\", i)) {\n i += 8;//Not supported\n // const {index} = this.readAttlistExp(xmlData,i+1);\n // i = index;\n } else if (hasBody && hasSeq(xmlData, \"!NOTATION\", i)) {\n i += 9;//Not supported\n const { index } = this.readNotationExp(xmlData, i + 1, this.suppressValidationErr);\n i = index;\n } else if (hasSeq(xmlData, \"!--\", i)) comment = true;\n else throw new Error(`Invalid DOCTYPE`);\n\n angleBracketsCount++;\n exp = \"\";\n } else if (xmlData[i] === '>') { //Read tag content\n if (comment) {\n if (xmlData[i - 1] === \"-\" && xmlData[i - 2] === \"-\") {\n comment = false;\n angleBracketsCount--;\n }\n } else {\n angleBracketsCount--;\n }\n if (angleBracketsCount === 0) {\n break;\n }\n } else if (xmlData[i] === '[') {\n hasBody = true;\n } else {\n exp += xmlData[i];\n }\n }\n if (angleBracketsCount !== 0) {\n throw new Error(`Unclosed DOCTYPE`);\n }\n } else {\n throw new Error(`Invalid Tag instead of DOCTYPE`);\n }\n return { entities, i };\n }\n readEntityExp(xmlData, i) {\n //External entities are not supported\n // <!ENTITY ext SYSTEM \"http://normal-website.com\" >\n\n //Parameter entities are not supported\n // <!ENTITY entityname \"&anotherElement;\">\n\n //Internal entities are supported\n // <!ENTITY entityname \"replacement text\">\n\n // Skip leading whitespace after <!ENTITY\n i = skipWhitespace(xmlData, i);\n\n // Read entity name\n const startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i]) && xmlData[i] !== '\"' && xmlData[i] !== \"'\") {\n i++;\n }\n let entityName = xmlData.substring(startIndex, i);\n\n validateEntityName(entityName, { xmlVersion: this.xmlVersion });\n\n // Skip whitespace after entity name\n i = skipWhitespace(xmlData, i);\n\n // Check for unsupported constructs (external entities or parameter entities)\n if (!this.suppressValidationErr) {\n if (xmlData.substring(i, i + 6).toUpperCase() === \"SYSTEM\") {\n throw new Error(\"External entities are not supported\");\n } else if (xmlData[i] === \"%\") {\n throw new Error(\"Parameter entities are not supported\");\n }\n }\n\n // Read entity value (internal entity)\n let entityValue = \"\";\n [i, entityValue] = this.readIdentifierVal(xmlData, i, \"entity\");\n\n // Validate entity size\n if (this.options.enabled !== false &&\n this.options.maxEntitySize != null &&\n entityValue.length > this.options.maxEntitySize) {\n throw new Error(\n `Entity \"${entityName}\" size (${entityValue.length}) exceeds maximum allowed size (${this.options.maxEntitySize})`\n );\n }\n\n i--;\n return [entityName, entityValue, i];\n }\n\n readNotationExp(xmlData, i) {\n // Skip leading whitespace after <!NOTATION\n i = skipWhitespace(xmlData, i);\n\n // Read notation name\n\n const startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n let notationName = xmlData.substring(startIndex, i);\n\n !this.suppressValidationErr && validateEntityName(notationName, { xmlVersion: this.xmlVersion });\n\n // Skip whitespace after notation name\n i = skipWhitespace(xmlData, i);\n\n // Check identifier type (SYSTEM or PUBLIC)\n const identifierType = xmlData.substring(i, i + 6).toUpperCase();\n if (!this.suppressValidationErr && identifierType !== \"SYSTEM\" && identifierType !== \"PUBLIC\") {\n throw new Error(`Expected SYSTEM or PUBLIC, found \"${identifierType}\"`);\n }\n i += identifierType.length;\n\n // Skip whitespace after identifier type\n i = skipWhitespace(xmlData, i);\n\n // Read public identifier (if PUBLIC)\n let publicIdentifier = null;\n let systemIdentifier = null;\n\n if (identifierType === \"PUBLIC\") {\n [i, publicIdentifier] = this.readIdentifierVal(xmlData, i, \"publicIdentifier\");\n\n // Skip whitespace after public identifier\n i = skipWhitespace(xmlData, i);\n\n // Optionally read system identifier\n if (xmlData[i] === '\"' || xmlData[i] === \"'\") {\n [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, \"systemIdentifier\");\n }\n } else if (identifierType === \"SYSTEM\") {\n // Read system identifier (mandatory for SYSTEM)\n [i, systemIdentifier] = this.readIdentifierVal(xmlData, i, \"systemIdentifier\");\n\n if (!this.suppressValidationErr && !systemIdentifier) {\n throw new Error(\"Missing mandatory system identifier for SYSTEM notation\");\n }\n }\n\n return { notationName, publicIdentifier, systemIdentifier, index: --i };\n }\n\n readIdentifierVal(xmlData, i, type) {\n let identifierVal = \"\";\n const startChar = xmlData[i];\n if (startChar !== '\"' && startChar !== \"'\") {\n throw new Error(`Expected quoted string, found \"${startChar}\"`);\n }\n i++;\n\n const startIndex = i;\n while (i < xmlData.length && xmlData[i] !== startChar) {\n i++;\n }\n identifierVal = xmlData.substring(startIndex, i);\n\n if (xmlData[i] !== startChar) {\n throw new Error(`Unterminated ${type} value`);\n }\n i++;\n return [i, identifierVal];\n }\n\n readElementExp(xmlData, i) {\n // <!ELEMENT br EMPTY>\n // <!ELEMENT div ANY>\n // <!ELEMENT title (#PCDATA)>\n // <!ELEMENT book (title, author+)>\n // <!ELEMENT name (content-model)>\n\n // Skip leading whitespace after <!ELEMENT\n i = skipWhitespace(xmlData, i);\n\n // Read element name\n const startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n let elementName = xmlData.substring(startIndex, i);\n\n // Validate element name\n if (!this.suppressValidationErr && !isName(elementName, { xmlVersion: this.xmlVersion })) {\n throw new Error(`Invalid element name: \"${elementName}\"`);\n }\n\n // Skip whitespace after element name\n i = skipWhitespace(xmlData, i);\n let contentModel = \"\";\n // Expect '(' to start content model\n if (xmlData[i] === \"E\" && hasSeq(xmlData, \"MPTY\", i)) i += 4;\n else if (xmlData[i] === \"A\" && hasSeq(xmlData, \"NY\", i)) i += 2;\n else if (xmlData[i] === \"(\") {\n i++; // Move past '('\n\n // Read content model\n const startIndex = i;\n while (i < xmlData.length && xmlData[i] !== \")\") {\n i++;\n }\n contentModel = xmlData.substring(startIndex, i);\n\n if (xmlData[i] !== \")\") {\n throw new Error(\"Unterminated content model\");\n }\n\n } else if (!this.suppressValidationErr) {\n throw new Error(`Invalid Element Expression, found \"${xmlData[i]}\"`);\n }\n\n return {\n elementName,\n contentModel: contentModel.trim(),\n index: i\n };\n }\n\n readAttlistExp(xmlData, i) {\n // Skip leading whitespace after <!ATTLIST\n i = skipWhitespace(xmlData, i);\n\n // Read element name\n let startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n let elementName = xmlData.substring(startIndex, i);\n\n // Validate element name\n validateEntityName(elementName, { xmlVersion: this.xmlVersion })\n\n // Skip whitespace after element name\n i = skipWhitespace(xmlData, i);\n\n // Read attribute name\n startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n let attributeName = xmlData.substring(startIndex, i);\n\n // Validate attribute name\n if (!validateEntityName(attributeName, { xmlVersion: this.xmlVersion })) {\n throw new Error(`Invalid attribute name: \"${attributeName}\"`);\n }\n\n // Skip whitespace after attribute name\n i = skipWhitespace(xmlData, i);\n\n // Read attribute type\n let attributeType = \"\";\n if (xmlData.substring(i, i + 8).toUpperCase() === \"NOTATION\") {\n attributeType = \"NOTATION\";\n i += 8; // Move past \"NOTATION\"\n\n // Skip whitespace after \"NOTATION\"\n i = skipWhitespace(xmlData, i);\n\n // Expect '(' to start the list of notations\n if (xmlData[i] !== \"(\") {\n throw new Error(`Expected '(', found \"${xmlData[i]}\"`);\n }\n i++; // Move past '('\n\n // Read the list of allowed notations\n let allowedNotations = [];\n while (i < xmlData.length && xmlData[i] !== \")\") {\n\n\n const startIndex = i;\n while (i < xmlData.length && xmlData[i] !== \"|\" && xmlData[i] !== \")\") {\n i++;\n }\n let notation = xmlData.substring(startIndex, i);\n\n // Validate notation name\n notation = notation.trim();\n if (!validateEntityName(notation, { xmlVersion: this.xmlVersion })) {\n throw new Error(`Invalid notation name: \"${notation}\"`);\n }\n\n allowedNotations.push(notation);\n\n // Skip '|' separator or exit loop\n if (xmlData[i] === \"|\") {\n i++; // Move past '|'\n i = skipWhitespace(xmlData, i); // Skip optional whitespace after '|'\n }\n }\n\n if (xmlData[i] !== \")\") {\n throw new Error(\"Unterminated list of notations\");\n }\n i++; // Move past ')'\n\n // Store the allowed notations as part of the attribute type\n attributeType += \" (\" + allowedNotations.join(\"|\") + \")\";\n } else {\n // Handle simple types (e.g., CDATA, ID, IDREF, etc.)\n const startIndex = i;\n while (i < xmlData.length && !/\\s/.test(xmlData[i])) {\n i++;\n }\n attributeType += xmlData.substring(startIndex, i);\n\n // Validate simple attribute type\n const validTypes = [\"CDATA\", \"ID\", \"IDREF\", \"IDREFS\", \"ENTITY\", \"ENTITIES\", \"NMTOKEN\", \"NMTOKENS\"];\n if (!this.suppressValidationErr && !validTypes.includes(attributeType.toUpperCase())) {\n throw new Error(`Invalid attribute type: \"${attributeType}\"`);\n }\n }\n\n // Skip whitespace after attribute type\n i = skipWhitespace(xmlData, i);\n\n // Read default value\n let defaultValue = \"\";\n if (xmlData.substring(i, i + 8).toUpperCase() === \"#REQUIRED\") {\n defaultValue = \"#REQUIRED\";\n i += 8;\n } else if (xmlData.substring(i, i + 7).toUpperCase() === \"#IMPLIED\") {\n defaultValue = \"#IMPLIED\";\n i += 7;\n } else {\n [i, defaultValue] = this.readIdentifierVal(xmlData, i, \"ATTLIST\");\n }\n\n return {\n elementName,\n attributeName,\n attributeType,\n defaultValue,\n index: i\n }\n }\n}\n\n\n\nconst skipWhitespace = (data, index) => {\n while (index < data.length && /\\s/.test(data[index])) {\n index++;\n }\n return index;\n};\n\n\n\nfunction hasSeq(data, seq, i) {\n for (let j = 0; j < seq.length; j++) {\n if (seq[j] !== data[i + j + 1]) return false;\n }\n return true;\n}\n\nfunction validateEntityName(name, xmlVersion) {\n if (isName(name, { xmlVersion: xmlVersion }))\n return name;\n else\n throw new Error(`Invalid entity name ${name}`);\n}","/**\n * Flat lookup table: maps Unicode code point → ASCII digit (0-9).\n * Only decimal digit characters (Unicode category Nd) are included.\n *\n * Strategy: Int32Array of size (maxCodePoint - minCodePoint + 1).\n * Value 0xFF means \"not a digit\". Value 0-9 is the ASCII digit value.\n * This gives O(1) lookup with no branching, no bisect, no loop.\n *\n * Memory: range is 0x0660 to 0x1FBF0 → ~129,936 entries × 1 byte = ~127 KB.\n * Acceptable for a one-time init; lookup is a single array index.\n */\n\n// All known Unicode Nd (decimal digit) script zero code points.\n// Each script has exactly 10 consecutive digits: zero+0 .. zero+9.\nconst SCRIPT_ZEROS = [\n // Basic Latin (ASCII) — included for completeness / pass-through\n 0x0030, // 0-9\n\n // Arabic scripts\n 0x0660, // Arabic-Indic ٠١٢٣٤٥٦٧٨٩\n 0x06F0, // Extended Arabic-Indic (Urdu/Persian/Sindhi) ۰۱۲۳\n\n // Indic scripts\n 0x0966, // Devanagari ०१२३४५६७८९\n 0x09E6, // Bengali ০১২৩৪৫৬৭৮৯\n 0x0A66, // Gurmukhi ੦੧੨੩੪੫੬੭੮੯\n 0x0AE6, // Gujarati ૦૧૨૩૪૫૬૭૮૯\n 0x0B66, // Odia ୦୧୨୩୪୫୬୭୮୯\n 0x0BE6, // Tamil ௦௧௨௩௪௫௬௭௮௯\n 0x0C66, // Telugu ౦౧౨౩౪౫౬౭౮౯\n 0x0CE6, // Kannada ೦೧೨೩೪೫೬೭೮೯\n 0x0D66, // Malayalam ൦൧൨൩൪൫൬൭൮൯\n 0x0DE6, // Sinhala Archaic ෦෧෨෩෪෫෬෭෮෯\n\n // Southeast Asian scripts\n 0x0E50, // Thai ๐๑๒๓๔๕๖๗๘๙\n 0x0ED0, // Lao ໐໑໒໓໔໕໖໗໘໙\n 0x0F20, // Tibetan ༠༡༢༣༤༥༦༧༨༩\n 0x1040, // Myanmar ၀၁၂၃၄၅၆၇၈၉\n 0x1090, // Myanmar Shan ႐႑႒႓႔႕႖႗႘႙\n 0x17E0, // Khmer ០១២៣៤៥៦៧៨៩\n 0x1810, // Mongolian ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙\n 0x1946, // Limbu ᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏\n 0x19D0, // New Tai Lue ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙\n 0x1A80, // Tai Tham Hora ᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉\n 0x1A90, // Tai Tham Tham ᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙\n 0x1B50, // Balinese ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙\n 0x1BB0, // Sundanese ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹\n 0x1C40, // Lepcha ᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉\n 0x1C50, // Ol Chiki ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙\n\n // Fullwidth (CJK context)\n 0xFF10, // Fullwidth 0123456789\n\n // Mathematical digit variants (Unicode math block)\n 0x1D7CE, // Mathematical Bold\n 0x1D7D8, // Mathematical Double-Struck\n 0x1D7E2, // Mathematical Sans-Serif\n 0x1D7EC, // Mathematical Sans-Serif Bold\n 0x1D7F6, // Mathematical Monospace\n\n // Other scripts\n 0x104A0, // Osmanya 𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩\n 0x10D30, // Hanifi Rohingya 𐴰𐴱𐴲𐴳𐴴𐴵𐴶𐴷𐴸𐴹\n 0x11066, // Brahmi 𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯\n 0x110F0, // Sora Sompeng 𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹\n 0x11136, // Chakma 𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿\n 0x111D0, // Sharada 𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙\n 0x112F0, // Khudawadi 𑋰𑋱𑋲𑋳𑋴𑋵𑋶𑋷𑋸𑋹\n 0x11450, // Newa 𑑐𑑑𑑒𑑓𑑔𑑕𑑖𑑗𑑘𑑙\n 0x114D0, // Tirhuta 𑓐𑓑𑓒𑓓𑓔𑓕𑓖𑓗𑓘𑓙\n 0x11650, // Modi 𑙐𑙑𑙒𑙓𑙔𑙕𑙖𑙗𑙘𑙙\n 0x116C0, // Takri 𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉\n 0x11730, // Ahom 𑜰𑜱𑜲𑜳𑜴𑜵𑜶𑜷𑜸𑜹\n 0x118E0, // Warang Citi 𑣠𑣡𑣢𑣣𑣤𑣥𑣦𑣧𑣨𑣩\n 0x11950, // Dives Akuru 𑥐𑥑𑥒𑥓𑥔𑥕𑥖𑥗𑥘𑥙\n 0x11BF0, // Khitan Small Script \n 0x11C50, // Bhaiksuki 𑱐𑱑𑱒𑱓𑱔𑱕𑱖𑱗𑱘𑱙\n 0x11D50, // Masaram Gondi 𑵐𑵑𑵒𑵓𑵔𑵕𑵖𑵗𑵘𑵙\n 0x11DA0, // Gunjala Gondi 𑶠𑶡𑶢𑶣𑶤𑶥𑶦𑶧𑶨𑶩\n 0x11F50, // Kawi 𑽐𑽑𑽒𑽓𑽔𑽕𑽖𑽗𑽘𑽙\n 0x16A60, // Mro 𖩠𖩡𖩢𖩣𖩤𖩥𖩦𖩧𖩨𖩩\n 0x16AC0, // Tangsa 𖫀𖫁𖫂𖫃𖫄𖫅𖫆𖫇𖫈𖫉\n 0x16B50, // Pahawh Hmong 𖭐𖭑𖭒𖭓𖭔𖭕𖭖𖭗𖭘𖭙\n 0x1E140, // Nyiakeng Puachue Hmong 𞅀𞅁𞅂𞅃𞅄𞅅𞅆𞅇𞅈𞅉\n 0x1E2F0, // Wancho 𞋰𞋱𞋲𞋳𞋴𞋵𞋶𞋷𞋸𞋹\n 0x1E4F0, // Nag Mundari 𞓰𞓱𞓲𞓳𞓴𞓵𞓶𞓷𞓸𞓹\n 0x1E950, // Adlam 𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙\n 0x1FBF0, // Segmented digit symbols 🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹\n];\n\n// Build a sparse Map for scripts above 0xFFFF (surrogate-pair range).\n// These can't go into a flat Uint8Array indexed by code point efficiently.\nconst NOT_DIGIT = 0xFF;\nconst HIGH_MAP = new Map(); // codePoint → digit value (0-9)\n\nconst LOW_MAX = 0xFFFF;\nconst LOW_MIN = 0x0660; // first non-ASCII digit script\n\n// Flat Uint8Array covering 0x0660 .. 0xFFFF\nconst TABLE_OFFSET = LOW_MIN;\nconst TABLE_SIZE = LOW_MAX - LOW_MIN + 1;\nconst TABLE = new Uint8Array(TABLE_SIZE).fill(NOT_DIGIT);\n\nfor (const zero of SCRIPT_ZEROS) {\n for (let d = 0; d < 10; d++) {\n const cp = zero + d;\n if (cp <= LOW_MAX) {\n TABLE[cp - TABLE_OFFSET] = d;\n } else {\n HIGH_MAP.set(cp, d);\n }\n }\n}\n\nexport { TABLE, TABLE_OFFSET, HIGH_MAP, NOT_DIGIT };\n","'use strict';\n\nimport { TABLE, TABLE_OFFSET, HIGH_MAP, NOT_DIGIT } from './digitTable.js';\n\nconst CHAR_0 = 48; // '0'.charCodeAt(0)\nconst CHAR_9 = 57; // '9'.charCodeAt(0)\nconst CHAR_MINUS = 45; // '-'.charCodeAt(0)\n\n// Unicode minus/hyphen variants worth normalizing to ASCII '-' in numeric context:\n// U+2212 MINUS SIGN − (mathematically correct minus)\n// U+FF0D FULLWIDTH HYPHEN-MINUS - (Japanese fullwidth context)\n// U+FE63 SMALL HYPHEN-MINUS ﹣ (small form variant)\n//\n// NOT normalized (deliberate):\n// U+2013 EN DASH – (punctuation, not a numeric sign)\n// U+2014 EM DASH — (punctuation)\n// U+2010 HYPHEN ‐ (typographic hyphen)\n//\n// Rationale: only characters a human or locale formatter would plausibly use\n// as a numeric minus sign are normalized. Dashes used for punctuation are left\n// alone to avoid mangling non-numeric strings.\nconst MINUS_SET = new Set([0x2212, 0xFF0D, 0xFE63]);\n\n/**\n * Normalize all Unicode decimal digit characters in a string to ASCII (0-9),\n * and normalize Unicode minus variants to ASCII '-' (U+002D).\n *\n * Non-digit, non-minus characters are passed through unchanged.\n *\n * Performance design:\n * - Fast path: if the string has no convertible characters, return it unchanged\n * (zero allocation).\n * - BMP digits (0x0660..0xFFFF excl. surrogates): flat Uint8Array lookup (O(1)).\n * - Supplementary plane digits (> 0xFFFF, encoded as surrogate pairs): Map lookup.\n * - Minus variants: checked inline with a small fixed Set.\n *\n * @param {string} str\n * @returns {string}\n */\nfunction anynum(str) {\n if (typeof str !== 'string') return str;\n\n const len = str.length;\n if (len === 0) return str;\n\n // Scan for first character needing conversion.\n // If none found, return original string (zero allocation).\n let firstHit = -1;\n\n for (let i = 0; i < len; i++) {\n const cc = str.charCodeAt(i);\n\n // ASCII digit or ASCII minus — already normalized, skip fast\n if ((cc >= CHAR_0 && cc <= CHAR_9) || cc === CHAR_MINUS) continue;\n\n // Below first unicode digit script — check minus variants only\n if (cc < TABLE_OFFSET) {\n if (MINUS_SET.has(cc)) { firstHit = i; break; }\n continue;\n }\n\n // Surrogate pairs live in BMP range 0xD800-0xDFFF — check before TABLE\n if (cc >= 0xD800 && cc <= 0xDBFF) {\n if (i + 1 < len) {\n const low = str.charCodeAt(i + 1);\n if (low >= 0xDC00 && low <= 0xDFFF) {\n const cp = 0x10000 + ((cc - 0xD800) << 10) + (low - 0xDC00);\n if (HIGH_MAP.has(cp)) { firstHit = i; break; }\n }\n }\n continue;\n }\n\n // BMP non-surrogate: flat table lookup; also check minus variants in this range\n if (TABLE[cc - TABLE_OFFSET] !== NOT_DIGIT || MINUS_SET.has(cc)) {\n firstHit = i;\n break;\n }\n }\n\n // Nothing to replace — return original, zero allocation\n if (firstHit === -1) return str;\n\n // Build result: copy unchanged prefix, then convert from firstHit onward\n const chars = [];\n\n if (firstHit > 0) chars.push(str.slice(0, firstHit));\n\n for (let i = firstHit; i < len; i++) {\n const cc = str.charCodeAt(i);\n\n // ASCII digit or ASCII minus — pass through\n if ((cc >= CHAR_0 && cc <= CHAR_9) || cc === CHAR_MINUS) {\n chars.push(str[i]);\n continue;\n }\n\n // Below TABLE_OFFSET — check minus variants, else pass through\n if (cc < TABLE_OFFSET) {\n chars.push(MINUS_SET.has(cc) ? '-' : str[i]);\n continue;\n }\n\n // Surrogate pairs\n if (cc >= 0xD800 && cc <= 0xDBFF) {\n if (i + 1 < len) {\n const low = str.charCodeAt(i + 1);\n if (low >= 0xDC00 && low <= 0xDFFF) {\n const cp = 0x10000 + ((cc - 0xD800) << 10) + (low - 0xDC00);\n const d = HIGH_MAP.get(cp);\n if (d !== undefined) {\n chars.push(String.fromCharCode(d + 48));\n i++; // consume low surrogate\n continue;\n }\n }\n }\n chars.push(str[i]);\n continue;\n }\n\n // BMP non-surrogate: flat table lookup + minus variants\n if (MINUS_SET.has(cc)) {\n chars.push('-');\n continue;\n }\n const d = TABLE[cc - TABLE_OFFSET];\n chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) : str[i]);\n }\n\n return chars.join('');\n}\n\nexport { anynum };\nexport default anynum;","const hexRegex = /^[-+]?0x[a-fA-F0-9]+$/;\nconst binRegex = /^0b[01]+$/;\nconst octRegex = /^0o[0-7]+$/;\nconst numRegex = /^([\\-\\+])?(0*)([0-9]*(\\.[0-9]*)?)$/;\n\nimport anynum from \"anynum\";\n\nconst consider = {\n hex: true,\n binary: false,\n octal: false,\n leadingZeros: true,\n decimalPoint: \"\\.\",\n eNotation: true,\n //skipLike: /regex/,\n infinity: \"original\", // \"null\", \"infinity\" (Infinity type), \"string\" (\"Infinity\" (the string literal))\n unicode: false,\n};\n\nexport default function toNumber(str, options = {}) {\n options = Object.assign({}, consider, options);\n if (!str || typeof str !== \"string\") return str;\n\n let trimmedStr = str.trim();\n\n if (trimmedStr.length === 0) return str;\n else if (options.skipLike !== undefined && options.skipLike.test(trimmedStr)) return str;\n else if (trimmedStr === \"0\") return 0;\n\n if (options.unicode) {\n trimmedStr = anynum(trimmedStr);\n if (trimmedStr === \"0\") return 0; // re-check after normalization\n }\n if (options.hex && hexRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 16);\n } else if (options.binary && binRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 2);\n } else if (options.octal && octRegex.test(trimmedStr)) {\n return parse_int(trimmedStr, 8);\n } else if (!isFinite(trimmedStr)) { //Infinity\n return handleInfinity(str, Number(trimmedStr), options);\n } else if (trimmedStr.includes('e') || trimmedStr.includes('E')) { //eNotation\n return resolveEnotation(str, trimmedStr, options);\n } else {\n //separate negative sign, leading zeros, and rest number\n const match = numRegex.exec(trimmedStr);\n // +00.123 => [ , '+', '00', '.123', ..\n if (match) {\n const sign = match[1] || \"\";\n const leadingZeros = match[2];\n let numTrimmedByZeros = trimZeros(match[3]); //complete num without leading zeros\n const decimalAdjacentToLeadingZeros = sign ? // 0., -00., 000.\n str[leadingZeros.length + 1] === \".\"\n : str[leadingZeros.length] === \".\";\n\n //trim ending zeros for floating number\n if (!options.leadingZeros //leading zeros are not allowed\n && (leadingZeros.length > 1\n || (leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros))) {\n // 00, 00.3, +03.24, 03, 03.24\n return str;\n }\n else {//no leading zeros or leading zeros are allowed\n const num = Number(trimmedStr);\n const parsedStr = String(num);\n\n if (num === 0) return num;\n if (parsedStr.search(/[eE]/) !== -1) { //given number is long and parsed to eNotation\n if (options.eNotation) return num;\n else return str;\n } else if (trimmedStr.indexOf(\".\") !== -1) { //floating number\n if (parsedStr === \"0\") return num; //0.0\n else if (parsedStr === numTrimmedByZeros) return num; //0.456. 0.79000\n else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num;\n else return str;\n }\n\n let n = leadingZeros ? numTrimmedByZeros : trimmedStr;\n if (leadingZeros) {\n // -009 => -9\n return (n === parsedStr) || (sign + n === parsedStr) ? num : str\n } else {\n // +9\n return (n === parsedStr) || (n === sign + parsedStr) ? num : str\n }\n }\n } else { //non-numeric string\n return str;\n }\n }\n}\n\nconst eNotationRegx = /^([-+])?(0*)(\\d*(\\.\\d*)?[eE][-\\+]?\\d+)$/;\nfunction resolveEnotation(str, trimmedStr, options) {\n if (!options.eNotation) return str;\n const notation = trimmedStr.match(eNotationRegx);\n if (notation) {\n let sign = notation[1] || \"\";\n const eChar = notation[3].indexOf(\"e\") === -1 ? \"E\" : \"e\";\n const leadingZeros = notation[2];\n const eAdjacentToLeadingZeros = sign ? // 0E.\n str[leadingZeros.length + 1] === eChar\n : str[leadingZeros.length] === eChar;\n\n if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) return str;\n else if (leadingZeros.length === 1\n && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) {\n return Number(trimmedStr);\n } else if (leadingZeros.length > 0) {\n // Has leading zeros — only accept if leadingZeros option allows it\n if (options.leadingZeros && !eAdjacentToLeadingZeros) {\n trimmedStr = (notation[1] || \"\") + notation[3];\n return Number(trimmedStr);\n } else return str;\n } else {\n // No leading zeros — always valid e-notation, parse it\n return Number(trimmedStr);\n }\n } else {\n return str;\n }\n}\n\n/**\n * \n * @param {string} numStr without leading zeros\n * @returns \n */\nfunction trimZeros(numStr) {\n if (numStr && numStr.indexOf(\".\") !== -1) {//float\n numStr = numStr.replace(/0+$/, \"\"); //remove ending zeros\n if (numStr === \".\") numStr = \"0\";\n else if (numStr[0] === \".\") numStr = \"0\" + numStr;\n else if (numStr[numStr.length - 1] === \".\") numStr = numStr.substring(0, numStr.length - 1);\n return numStr;\n }\n return numStr;\n}\n\nfunction parse_int(numStr, base) {\n const str = numStr.trim();\n if (base === 2 || base === 8) numStr = str.substring(2);\n\n if (parseInt) return parseInt(numStr, base);\n else if (Number.parseInt) return Number.parseInt(numStr, base);\n else if (window && window.parseInt) return window.parseInt(numStr, base);\n else throw new Error(\"parseInt, Number.parseInt, window.parseInt are not supported\");\n}\n\n/**\n * Handle infinite values based on user option\n * @param {string} str - original input string\n * @param {number} num - parsed number (Infinity or -Infinity)\n * @param {object} options - user options\n * @returns {string|number|null} based on infinity option\n */\nfunction handleInfinity(str, num, options) {\n const isPositive = num === Infinity;\n\n switch (options.infinity.toLowerCase()) {\n case \"null\":\n return null;\n case \"infinity\":\n return num; // Return Infinity or -Infinity\n case \"string\":\n return isPositive ? \"Infinity\" : \"-Infinity\";\n case \"original\":\n default:\n return str; // Return original string like \"1e1000\"\n }\n}","export default function getIgnoreAttributesFn(ignoreAttributes) {\n if (typeof ignoreAttributes === 'function') {\n return ignoreAttributes\n }\n if (Array.isArray(ignoreAttributes)) {\n return (attrName) => {\n for (const pattern of ignoreAttributes) {\n if (typeof pattern === 'string' && attrName === pattern) {\n return true\n }\n if (pattern instanceof RegExp && pattern.test(attrName)) {\n return true\n }\n }\n }\n }\n return () => false\n}","/**\n * Expression - Parses and stores a tag pattern expression\n * \n * Patterns are parsed once and stored in an optimized structure for fast matching.\n * \n * @example\n * const expr = new Expression(\"root.users.user\");\n * const expr2 = new Expression(\"..user[id]:first\");\n * const expr3 = new Expression(\"root/users/user\", { separator: '/' });\n */\nexport default class Expression {\n /**\n * Create a new Expression\n * @param {string} pattern - Pattern string (e.g., \"root.users.user\", \"..user[id]\")\n * @param {Object} options - Configuration options\n * @param {string} options.separator - Path separator (default: '.')\n */\n constructor(pattern, options = {}, data) {\n this.pattern = pattern;\n this.separator = options.separator || '.';\n this.segments = this._parse(pattern);\n this.data = data;\n // Cache expensive checks for performance (O(1) instead of O(n))\n this._hasDeepWildcard = this.segments.some(seg => seg.type === 'deep-wildcard');\n this._hasAttributeCondition = this.segments.some(seg => seg.attrName !== undefined);\n this._hasPositionSelector = this.segments.some(seg => seg.position !== undefined);\n }\n\n /**\n * Parse pattern string into segments\n * @private\n * @param {string} pattern - Pattern to parse\n * @returns {Array} Array of segment objects\n */\n _parse(pattern) {\n const segments = [];\n\n // Split by separator but handle \"..\" specially\n let i = 0;\n let currentPart = '';\n\n while (i < pattern.length) {\n if (pattern[i] === this.separator) {\n // Check if next char is also separator (deep wildcard)\n if (i + 1 < pattern.length && pattern[i + 1] === this.separator) {\n // Flush current part if any\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n currentPart = '';\n }\n // Add deep wildcard\n segments.push({ type: 'deep-wildcard' });\n i += 2; // Skip both separators\n } else {\n // Regular separator\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n currentPart = '';\n i++;\n }\n } else {\n currentPart += pattern[i];\n i++;\n }\n }\n\n // Flush remaining part\n if (currentPart.trim()) {\n segments.push(this._parseSegment(currentPart.trim()));\n }\n\n return segments;\n }\n\n /**\n * Parse a single segment\n * @private\n * @param {string} part - Segment string (e.g., \"user\", \"ns::user\", \"user[id]\", \"ns::user:first\")\n * @returns {Object} Segment object\n */\n _parseSegment(part) {\n const segment = { type: 'tag' };\n\n // NEW NAMESPACE SYNTAX (v2.0):\n // ============================\n // Namespace uses DOUBLE colon (::)\n // Position uses SINGLE colon (:)\n // \n // Examples:\n // \"user\" → tag\n // \"user:first\" → tag + position\n // \"user[id]\" → tag + attribute\n // \"user[id]:first\" → tag + attribute + position\n // \"ns::user\" → namespace + tag\n // \"ns::user:first\" → namespace + tag + position\n // \"ns::user[id]\" → namespace + tag + attribute\n // \"ns::user[id]:first\" → namespace + tag + attribute + position\n // \"ns::first\" → namespace + tag named \"first\" (NO ambiguity!)\n //\n // This eliminates all ambiguity:\n // :: = namespace separator\n // : = position selector\n // [] = attributes\n\n // Step 1: Extract brackets [attr] or [attr=value]\n let bracketContent = null;\n let withoutBrackets = part;\n\n const bracketMatch = part.match(/^([^\\[]+)(\\[[^\\]]*\\])(.*)$/);\n if (bracketMatch) {\n withoutBrackets = bracketMatch[1] + bracketMatch[3];\n if (bracketMatch[2]) {\n const content = bracketMatch[2].slice(1, -1);\n if (content) {\n bracketContent = content;\n }\n }\n }\n\n // Step 2: Check for namespace (double colon ::)\n let namespace = undefined;\n let tagAndPosition = withoutBrackets;\n\n if (withoutBrackets.includes('::')) {\n const nsIndex = withoutBrackets.indexOf('::');\n namespace = withoutBrackets.substring(0, nsIndex).trim();\n tagAndPosition = withoutBrackets.substring(nsIndex + 2).trim(); // Skip ::\n\n if (!namespace) {\n throw new Error(`Invalid namespace in pattern: ${part}`);\n }\n }\n\n // Step 3: Parse tag and position (single colon :)\n let tag = undefined;\n let positionMatch = null;\n\n if (tagAndPosition.includes(':')) {\n const colonIndex = tagAndPosition.lastIndexOf(':'); // Use last colon for position\n const tagPart = tagAndPosition.substring(0, colonIndex).trim();\n const posPart = tagAndPosition.substring(colonIndex + 1).trim();\n\n // Verify position is a valid keyword\n const isPositionKeyword = ['first', 'last', 'odd', 'even'].includes(posPart) ||\n /^nth\\(\\d+\\)$/.test(posPart);\n\n if (isPositionKeyword) {\n tag = tagPart;\n positionMatch = posPart;\n } else {\n // Not a valid position keyword, treat whole thing as tag\n tag = tagAndPosition;\n }\n } else {\n tag = tagAndPosition;\n }\n\n if (!tag) {\n throw new Error(`Invalid segment pattern: ${part}`);\n }\n\n segment.tag = tag;\n if (namespace) {\n segment.namespace = namespace;\n }\n\n // Step 4: Parse attributes\n if (bracketContent) {\n if (bracketContent.includes('=')) {\n const eqIndex = bracketContent.indexOf('=');\n segment.attrName = bracketContent.substring(0, eqIndex).trim();\n segment.attrValue = bracketContent.substring(eqIndex + 1).trim();\n } else {\n segment.attrName = bracketContent.trim();\n }\n }\n\n // Step 5: Parse position selector\n if (positionMatch) {\n const nthMatch = positionMatch.match(/^nth\\((\\d+)\\)$/);\n if (nthMatch) {\n segment.position = 'nth';\n segment.positionValue = parseInt(nthMatch[1], 10);\n } else {\n segment.position = positionMatch;\n }\n }\n\n return segment;\n }\n\n /**\n * Get the number of segments\n * @returns {number}\n */\n get length() {\n return this.segments.length;\n }\n\n /**\n * Check if expression contains deep wildcard\n * @returns {boolean}\n */\n hasDeepWildcard() {\n return this._hasDeepWildcard;\n }\n\n /**\n * Check if expression has attribute conditions\n * @returns {boolean}\n */\n hasAttributeCondition() {\n return this._hasAttributeCondition;\n }\n\n /**\n * Check if expression has position selectors\n * @returns {boolean}\n */\n hasPositionSelector() {\n return this._hasPositionSelector;\n }\n\n /**\n * Get string representation\n * @returns {string}\n */\n toString() {\n return this.pattern;\n }\n}","/**\n * ExpressionSet - An indexed collection of Expressions for efficient bulk matching\n *\n * Instead of iterating all expressions on every tag, ExpressionSet pre-indexes\n * them at insertion time by depth and terminal tag name. At match time, only\n * the relevant bucket is evaluated — typically reducing checks from O(E) to O(1)\n * lookup plus O(small bucket) matches.\n *\n * Three buckets are maintained:\n * - `_byDepthAndTag` — exact depth + exact tag name (tightest, used first)\n * - `_wildcardByDepth` — exact depth + wildcard tag `*` (depth-matched only)\n * - `_deepWildcards` — expressions containing `..` (cannot be depth-indexed)\n *\n * @example\n * import { Expression, ExpressionSet } from 'fast-xml-tagger';\n *\n * // Build once at config time\n * const stopNodes = new ExpressionSet();\n * stopNodes.add(new Expression('root.users.user'));\n * stopNodes.add(new Expression('root.config.setting'));\n * stopNodes.add(new Expression('..script'));\n *\n * // Query on every tag — hot path\n * if (stopNodes.matchesAny(matcher)) { ... }\n */\nexport default class ExpressionSet {\n constructor() {\n /** @type {Map<string, import('./Expression.js').default[]>} depth:tag → expressions */\n this._byDepthAndTag = new Map();\n\n /** @type {Map<number, import('./Expression.js').default[]>} depth → wildcard-tag expressions */\n this._wildcardByDepth = new Map();\n\n /** @type {import('./Expression.js').default[]} expressions containing deep wildcard (..) */\n this._deepWildcards = [];\n\n /** @type {Map<string, import('./Expression.js').default[]>} terminalTag → deep wildcard expressions */\n this._deepByTerminalTag = new Map();\n\n /** @type {Set<string>} pattern strings already added — used for deduplication */\n this._patterns = new Set();\n\n /** @type {boolean} whether the set is sealed against further additions */\n this._sealed = false;\n }\n\n /**\n * Add an Expression to the set.\n * Duplicate patterns (same pattern string) are silently ignored.\n *\n * @param {import('./Expression.js').default} expression - A pre-constructed Expression instance\n * @returns {this} for chaining\n * @throws {TypeError} if called after seal()\n *\n * @example\n * set.add(new Expression('root.users.user'));\n * set.add(new Expression('..script'));\n */\n add(expression) {\n if (this._sealed) {\n throw new TypeError(\n 'ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.'\n );\n }\n\n // Deduplicate by pattern string\n if (this._patterns.has(expression.pattern)) return this;\n this._patterns.add(expression.pattern);\n\n if (expression.hasDeepWildcard()) {\n const lastSeg = expression.segments[expression.segments.length - 1];\n if (lastSeg && lastSeg.type !== 'deep-wildcard' && lastSeg.tag !== '*') {\n const tag = lastSeg.tag;\n if (!this._deepByTerminalTag.has(tag)) this._deepByTerminalTag.set(tag, []);\n this._deepByTerminalTag.get(tag).push(expression);\n } else {\n this._deepWildcards.push(expression);\n }\n return this;\n }\n\n const depth = expression.length;\n const lastSeg = expression.segments[expression.segments.length - 1];\n const tag = lastSeg?.tag;\n\n if (!tag || tag === '*') {\n // Can index by depth but not by tag\n if (!this._wildcardByDepth.has(depth)) this._wildcardByDepth.set(depth, []);\n this._wildcardByDepth.get(depth).push(expression);\n } else {\n // Tightest bucket: depth + tag\n const key = `${depth}:${tag}`;\n if (!this._byDepthAndTag.has(key)) this._byDepthAndTag.set(key, []);\n this._byDepthAndTag.get(key).push(expression);\n }\n\n return this;\n }\n\n /**\n * Add multiple expressions at once.\n *\n * @param {import('./Expression.js').default[]} expressions - Array of Expression instances\n * @returns {this} for chaining\n *\n * @example\n * set.addAll([\n * new Expression('root.users.user'),\n * new Expression('root.config.setting'),\n * ]);\n */\n addAll(expressions) {\n for (const expr of expressions) this.add(expr);\n return this;\n }\n\n /**\n * Check whether a pattern string is already present in the set.\n *\n * @param {import('./Expression.js').default} expression\n * @returns {boolean}\n */\n has(expression) {\n return this._patterns.has(expression.pattern);\n }\n\n /**\n * Number of expressions in the set.\n * @type {number}\n */\n get size() {\n return this._patterns.size;\n }\n\n /**\n * Seal the set against further modifications.\n * Useful to prevent accidental mutations after config is built.\n * Calling add() or addAll() on a sealed set throws a TypeError.\n *\n * @returns {this}\n */\n seal() {\n this._sealed = true;\n return this;\n }\n\n /**\n * Whether the set has been sealed.\n * @type {boolean}\n */\n get isSealed() {\n return this._sealed;\n }\n\n /**\n * Test whether the matcher's current path matches any expression in the set.\n *\n * Evaluation order (cheapest → most expensive):\n * 1. Exact depth + tag bucket — O(1) lookup, typically 0–2 expressions\n * 2. Depth-only wildcard bucket — O(1) lookup, rare\n * 3. Deep-wildcard list — always checked, but usually small\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {boolean} true if any expression matches the current path\n *\n * @example\n * if (stopNodes.matchesAny(matcher)) {\n * // handle stop node\n * }\n */\n matchesAny(matcher) {\n return this.findMatch(matcher) !== null;\n }\n /**\n * Find and return the first Expression that matches the matcher's current path.\n *\n * Uses the same evaluation order as matchesAny (cheapest → most expensive):\n * 1. Exact depth + tag bucket\n * 2. Depth-only wildcard bucket\n * 3. Deep-wildcard list\n *\n * @param {import('./Matcher.js').default} matcher - Matcher instance (or readOnly view)\n * @returns {import('./Expression.js').default | null} the first matching Expression, or null\n *\n * @example\n * const expr = stopNodes.findMatch(matcher);\n * if (expr) {\n * // access expr.config, expr.pattern, etc.\n * }\n */\n findMatch(matcher) {\n const depth = matcher.getDepth();\n const tag = matcher.getCurrentTag();\n\n // 1. Tightest bucket — most expressions live here\n const exactKey = `${depth}:${tag}`;\n const exactBucket = this._byDepthAndTag.get(exactKey);\n if (exactBucket) {\n for (let i = 0; i < exactBucket.length; i++) {\n if (matcher.matches(exactBucket[i])) return exactBucket[i];\n }\n }\n\n // 2. Depth-matched wildcard-tag expressions\n const wildcardBucket = this._wildcardByDepth.get(depth);\n if (wildcardBucket) {\n for (let i = 0; i < wildcardBucket.length; i++) {\n if (matcher.matches(wildcardBucket[i])) return wildcardBucket[i];\n }\n }\n\n // 3. Deep wildcards — indexed by terminal tag, then unindexed fallback\n const deepBucket = this._deepByTerminalTag.get(tag);\n if (deepBucket) {\n for (let i = 0; i < deepBucket.length; i++) {\n if (matcher.matches(deepBucket[i])) return deepBucket[i];\n }\n }\n for (let i = 0; i < this._deepWildcards.length; i++) {\n if (matcher.matches(this._deepWildcards[i])) return this._deepWildcards[i];\n }\n\n return null;\n }\n}\n","import ExpressionSet from \"./ExpressionSet.js\";\n\n/**\n * MatcherView - A lightweight read-only view over a Matcher's internal state.\n *\n * Created once by Matcher and reused across all callbacks. Holds a direct\n * reference to the parent Matcher so it always reflects current parser state\n * with zero copying or freezing overhead.\n *\n * Users receive this via {@link Matcher#readOnly} or directly from parser\n * callbacks. It exposes all query and matching methods but has no mutation\n * methods — misuse is caught at the TypeScript level rather than at runtime.\n *\n * @example\n * const matcher = new Matcher();\n * const view = matcher.readOnly();\n *\n * matcher.push(\"root\", {});\n * view.getCurrentTag(); // \"root\"\n * view.getDepth(); // 1\n */\nexport class MatcherView {\n /**\n * @param {Matcher} matcher - The parent Matcher instance to read from.\n */\n constructor(matcher) {\n this._matcher = matcher;\n }\n\n /**\n * Get the path separator used by the parent matcher.\n * @returns {string}\n */\n get separator() {\n return this._matcher.separator;\n }\n\n /**\n * Get current tag name.\n * @returns {string|undefined}\n */\n getCurrentTag() {\n const path = this._matcher.path;\n return path.length > 0 ? path[path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace.\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n const path = this._matcher.path;\n return path.length > 0 ? path[path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value.\n * @param {string} attrName\n * @returns {*}\n */\n getAttrValue(attrName) {\n const path = this._matcher.path;\n if (path.length === 0) return undefined;\n return path[path.length - 1].values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAttr(attrName) {\n const path = this._matcher.path;\n if (path.length === 0) return false;\n const current = path[path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get the value of a \"kept\" attribute from the nearest ancestor (or\n * current node) that declared it via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {*}\n */\n getAnyParentAttr(attrName) {\n return this._matcher.getAnyParentAttr(attrName);\n }\n\n /**\n * Check whether any ancestor (or the current node) kept the given\n * attribute via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAnyParentAttr(attrName) {\n return this._matcher.hasAnyParentAttr(attrName);\n }\n\n /**\n * Get current node's sibling position (child index in parent).\n * @returns {number}\n */\n getPosition() {\n const path = this._matcher.path;\n if (path.length === 0) return -1;\n return path[path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name).\n * @returns {number}\n */\n getCounter() {\n const path = this._matcher.path;\n if (path.length === 0) return -1;\n return path[path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition).\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth.\n * @returns {number}\n */\n getDepth() {\n return this._matcher.path.length;\n }\n\n /**\n * Get path as string.\n * @param {string} [separator] - Optional separator (uses default if not provided)\n * @param {boolean} [includeNamespace=true]\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n return this._matcher.toString(separator, includeNamespace);\n }\n\n /**\n * Get path as array of tag names.\n * @returns {string[]}\n */\n toArray() {\n return this._matcher.path.map(n => n.tag);\n }\n\n /**\n * Match current path against an Expression.\n * @param {Expression} expression\n * @returns {boolean}\n */\n matches(expression) {\n return this._matcher.matches(expression);\n }\n\n /**\n * Match any expression in the given set against the current path.\n * @param {ExpressionSet} exprSet\n * @returns {boolean}\n */\n matchesAny(exprSet) {\n return exprSet.matchesAny(this._matcher);\n }\n}\n\n/**\n * Matcher - Tracks current path in XML/JSON tree and matches against Expressions.\n *\n * The matcher maintains a stack of nodes representing the current path from root to\n * current tag. It only stores attribute values for the current (top) node to minimize\n * memory usage. Sibling tracking is used to auto-calculate position and counter.\n *\n * Use {@link Matcher#readOnly} to obtain a {@link MatcherView} safe to pass to\n * user callbacks — it always reflects current state with no Proxy overhead.\n *\n * @example\n * const matcher = new Matcher();\n * matcher.push(\"root\", {});\n * matcher.push(\"users\", {});\n * matcher.push(\"user\", { id: \"123\", type: \"admin\" });\n *\n * const expr = new Expression(\"root.users.user\");\n * matcher.matches(expr); // true\n */\nexport default class Matcher {\n /**\n * Create a new Matcher.\n * @param {Object} [options={}]\n * @param {string} [options.separator='.'] - Default path separator\n */\n constructor(options = {}) {\n this.separator = options.separator || '.';\n this.path = [];\n this.siblingStacks = [];\n // Each path node: { tag, values, position, counter, namespace? }\n // values only present for current (last) node\n // Each siblingStacks entry: Map<tagName, count> tracking occurrences at each level\n this._pathStringCache = null;\n this._view = new MatcherView(this);\n\n // Kept-attribute stack: only populated when push() is called with options.keep.\n this._keptAttrs = [];\n }\n\n /**\n * Push a new tag onto the path.\n * @param {string} tagName\n * @param {Object|null} [attrValues=null]\n * @param {string|null} [namespace=null]\n * @param {Object|null} [options=null]\n * @param {string[]} [options.keep] - Names of attributes (from attrValues)\n */\n push(tagName, attrValues = null, namespace = null, options = null) {\n this._pathStringCache = null;\n\n // Remove values from previous current node (now becoming ancestor)\n if (this.path.length > 0) {\n this.path[this.path.length - 1].values = undefined;\n }\n\n // Get or create sibling tracking for current level\n const currentLevel = this.path.length;\n let level = this.siblingStacks[currentLevel];\n if (!level) {\n // `counts` tells same-name siblings apart (the \"counter\" — nth <item>\n // among other <item>s). `total` is every child seen at this level so\n // far, kept as a running number instead of re-added from `counts` on\n // every push — a parent with many differently-named children would\n // otherwise cost more per child the more distinct names it has.\n level = { counts: new Map(), total: 0 };\n this.siblingStacks[currentLevel] = level;\n }\n\n // Create a unique key for sibling tracking that includes namespace\n const siblingKey = namespace ? `${namespace}:${tagName}` : tagName;\n\n // Calculate counter (how many times this tag appeared at this level)\n const counter = level.counts.get(siblingKey) || 0;\n\n // Position = total children at this level seen before this one.\n const position = level.total;\n\n // Update sibling count for this tag, and the level's running total.\n level.counts.set(siblingKey, counter + 1);\n level.total++;\n\n // Create new node\n const node = {\n tag: tagName,\n position: position,\n counter: counter\n };\n\n if (namespace !== null && namespace !== undefined) {\n node.namespace = namespace;\n }\n\n if (attrValues !== null && attrValues !== undefined) {\n node.values = attrValues;\n }\n\n this.path.push(node);\n\n // Depth of the node we just pushed (1-based, matches this.path.length)\n const depth = this.path.length;\n\n // Copy only the requested attributes into the kept-attrs stack. This is\n // the one part of push() whose cost scales with input (O(keep.length))\n // rather than being O(1) — by design, since the caller is explicitly\n // opting in for specific attribute names. No options/keep => zero added\n // cost beyond the two property reads below.\n const keep = options !== null ? options.keep : null;\n if (keep !== null && keep !== undefined && keep.length > 0 && attrValues) {\n for (let i = 0; i < keep.length; i++) {\n const name = keep[i];\n if (attrValues[name] !== undefined) {\n this._keptAttrs.push({ depth, name, value: attrValues[name] });\n }\n }\n }\n }\n\n /**\n * Pop the last tag from the path.\n * @returns {Object|undefined} The popped node\n */\n pop() {\n if (this.path.length === 0) return undefined;\n this._pathStringCache = null;\n\n const node = this.path.pop();\n\n if (this.siblingStacks.length > this.path.length + 1) {\n this.siblingStacks.length = this.path.length + 1;\n }\n\n // Drop any kept attributes that belonged to the popped node (or deeper).\n // _keptAttrs is depth-ordered (push only ever appends increasing depths),\n // so this is a backward scan that stops at the first surviving entry —\n // typically O(1) since kept attrs are rare by design.\n const poppedDepth = this.path.length + 1;\n while (\n this._keptAttrs.length > 0 &&\n this._keptAttrs[this._keptAttrs.length - 1].depth >= poppedDepth\n ) {\n this._keptAttrs.pop();\n }\n\n return node;\n }\n\n /**\n * Update current node's attribute values.\n * Useful when attributes are parsed after push.\n * @param {Object} attrValues\n */\n updateCurrent(attrValues) {\n if (this.path.length > 0) {\n const current = this.path[this.path.length - 1];\n if (attrValues !== null && attrValues !== undefined) {\n current.values = attrValues;\n }\n }\n }\n\n /**\n * Get current tag name.\n * @returns {string|undefined}\n */\n getCurrentTag() {\n return this.path.length > 0 ? this.path[this.path.length - 1].tag : undefined;\n }\n\n /**\n * Get current namespace.\n * @returns {string|undefined}\n */\n getCurrentNamespace() {\n return this.path.length > 0 ? this.path[this.path.length - 1].namespace : undefined;\n }\n\n /**\n * Get current node's attribute value.\n * @param {string} attrName\n * @returns {*}\n */\n getAttrValue(attrName) {\n if (this.path.length === 0) return undefined;\n return this.path[this.path.length - 1].values?.[attrName];\n }\n\n /**\n * Check if current node has an attribute.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAttr(attrName) {\n if (this.path.length === 0) return false;\n const current = this.path[this.path.length - 1];\n return current.values !== undefined && attrName in current.values;\n }\n\n /**\n * Get the value of a \"kept\" attribute from the nearest ancestor (or\n * current node) that declared it via `push(tag, attrs, ns, { keep: [...] })`.\n * Unlike getAttrValue(), this works regardless of how deep the path has\n * gone since the attribute was pushed — but only for attribute names that\n * were explicitly marked with `keep` at push time. Cost is proportional to\n * the number of currently-kept attributes (typically 0-3), not path depth.\n * @param {string} attrName\n * @returns {*} the value, or undefined if no ancestor kept this attribute\n */\n getAnyParentAttr(attrName) {\n const kept = this._keptAttrs;\n for (let i = kept.length - 1; i >= 0; i--) {\n if (kept[i].name === attrName) return kept[i].value;\n }\n return undefined;\n }\n\n /**\n * Check whether any ancestor (or the current node) kept the given\n * attribute via `push(tag, attrs, ns, { keep: [...] })`.\n * @param {string} attrName\n * @returns {boolean}\n */\n hasAnyParentAttr(attrName) {\n const kept = this._keptAttrs;\n for (let i = kept.length - 1; i >= 0; i--) {\n if (kept[i].name === attrName) return true;\n }\n return false;\n }\n\n /**\n * Get current node's sibling position (child index in parent).\n * @returns {number}\n */\n getPosition() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].position ?? 0;\n }\n\n /**\n * Get current node's repeat counter (occurrence count of this tag name).\n * @returns {number}\n */\n getCounter() {\n if (this.path.length === 0) return -1;\n return this.path[this.path.length - 1].counter ?? 0;\n }\n\n /**\n * Get current node's sibling index (alias for getPosition).\n * @returns {number}\n * @deprecated Use getPosition() or getCounter() instead\n */\n getIndex() {\n return this.getPosition();\n }\n\n /**\n * Get current path depth.\n * @returns {number}\n */\n getDepth() {\n return this.path.length;\n }\n\n /**\n * Get path as string.\n * @param {string} [separator] - Optional separator (uses default if not provided)\n * @param {boolean} [includeNamespace=true]\n * @returns {string}\n */\n toString(separator, includeNamespace = true) {\n const sep = separator || this.separator;\n const isDefault = (sep === this.separator && includeNamespace === true);\n\n if (isDefault) {\n if (this._pathStringCache !== null) {\n return this._pathStringCache;\n }\n const result = this.path.map(n =>\n (n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n this._pathStringCache = result;\n return result;\n }\n\n return this.path.map(n =>\n (includeNamespace && n.namespace) ? `${n.namespace}:${n.tag}` : n.tag\n ).join(sep);\n }\n\n /**\n * Get path as array of tag names.\n * @returns {string[]}\n */\n toArray() {\n return this.path.map(n => n.tag);\n }\n\n /**\n * Reset the path to empty.\n */\n reset() {\n this._pathStringCache = null;\n this.path = [];\n this.siblingStacks = [];\n this._keptAttrs = [];\n }\n\n /**\n * Match current path against an Expression.\n * @param {Expression} expression\n * @returns {boolean}\n */\n matches(expression) {\n const segments = expression.segments;\n\n if (segments.length === 0) {\n return false;\n }\n\n if (expression.hasDeepWildcard()) {\n return this._matchWithDeepWildcard(segments);\n }\n\n return this._matchSimple(segments);\n }\n\n /**\n * @private\n */\n _matchSimple(segments) {\n if (this.path.length !== segments.length) {\n return false;\n }\n\n for (let i = 0; i < segments.length; i++) {\n if (!this._matchSegment(segments[i], this.path[i], i === this.path.length - 1)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * @private\n */\n _matchWithDeepWildcard(segments) {\n let pathIdx = this.path.length - 1;\n let segIdx = segments.length - 1;\n\n while (segIdx >= 0 && pathIdx >= 0) {\n const segment = segments[segIdx];\n\n if (segment.type === 'deep-wildcard') {\n segIdx--;\n\n if (segIdx < 0) {\n return true;\n }\n\n const nextSeg = segments[segIdx];\n let found = false;\n\n for (let i = pathIdx; i >= 0; i--) {\n if (this._matchSegment(nextSeg, this.path[i], i === this.path.length - 1)) {\n pathIdx = i - 1;\n segIdx--;\n found = true;\n break;\n }\n }\n\n if (!found) {\n return false;\n }\n } else {\n if (!this._matchSegment(segment, this.path[pathIdx], pathIdx === this.path.length - 1)) {\n return false;\n }\n pathIdx--;\n segIdx--;\n }\n }\n\n return segIdx < 0;\n }\n\n /**\n * @private\n */\n _matchSegment(segment, node, isCurrentNode) {\n if (segment.tag !== '*' && segment.tag !== node.tag) {\n return false;\n }\n\n if (segment.namespace !== undefined) {\n if (segment.namespace !== '*' && segment.namespace !== node.namespace) {\n return false;\n }\n }\n\n if (segment.attrName !== undefined) {\n if (!isCurrentNode) {\n return false;\n }\n\n if (!node.values || !(segment.attrName in node.values)) {\n return false;\n }\n\n if (segment.attrValue !== undefined) {\n if (String(node.values[segment.attrName]) !== String(segment.attrValue)) {\n return false;\n }\n }\n }\n\n if (segment.position !== undefined) {\n if (!isCurrentNode) {\n return false;\n }\n\n const counter = node.counter ?? 0;\n\n if (segment.position === 'first' && counter !== 0) {\n return false;\n } else if (segment.position === 'odd' && counter % 2 !== 1) {\n return false;\n } else if (segment.position === 'even' && counter % 2 !== 0) {\n return false;\n } else if (segment.position === 'nth' && counter !== segment.positionValue) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Match any expression in the given set against the current path.\n * @param {ExpressionSet} exprSet\n * @returns {boolean}\n */\n matchesAny(exprSet) {\n return exprSet.matchesAny(this);\n }\n\n /**\n * Create a snapshot of current state.\n * @returns {Object}\n */\n snapshot() {\n return {\n path: this.path.map(node => ({ ...node })),\n siblingStacks: this.siblingStacks.map(level => level ? { counts: new Map(level.counts), total: level.total } : level),\n keptAttrs: this._keptAttrs.map(entry => ({ ...entry }))\n };\n }\n\n /**\n * Restore state from snapshot.\n * @param {Object} snapshot\n */\n restore(snapshot) {\n this._pathStringCache = null;\n this.path = snapshot.path.map(node => ({ ...node }));\n this.siblingStacks = snapshot.siblingStacks.map(level => level ? { counts: new Map(level.counts), total: level.total } : level);\n this._keptAttrs = (snapshot.keptAttrs || []).map(entry => ({ ...entry }));\n }\n\n /**\n * Return the read-only {@link MatcherView} for this matcher.\n *\n * The same instance is returned on every call — no allocation occurs.\n * It always reflects the current parser state and is safe to pass to\n * user callbacks without risk of accidental mutation.\n *\n * @returns {MatcherView}\n *\n * @example\n * const view = matcher.readOnly();\n * // pass view to callbacks — it stays in sync automatically\n * view.matches(expr); // ✓\n * view.getCurrentTag(); // ✓\n * // view.push(...) // ✗ method does not exist — caught by TypeScript\n */\n readOnly() {\n return this._view;\n }\n}\n","/**\n * HTML context patterns.\n *\n * Detects XSS vectors that are dangerous when a string ends up rendered as HTML.\n * All patterns use bounded quantifiers to ensure linear-time matching (ReDoS-safe).\n *\n * Each entry is { pattern: RegExp, id: string, description: string }\n * so callers can inspect which rule fired if they need to.\n */\n\nconst HTML_PATTERNS = [\n {\n id: 'html-script-open',\n description: '<script opening tag',\n pattern: /<script[\\s>/]/i,\n },\n {\n id: 'html-script-close',\n description: '</script closing tag',\n pattern: /<\\/script[\\s>]/i,\n },\n {\n id: 'html-javascript-protocol',\n description: 'javascript: URI scheme (with optional whitespace/encoding)',\n // Handles javascript:, j\\u0061vascript:, and whitespace variants\n pattern: /j[\\t\\n\\r ]*a[\\t\\n\\r ]*v[\\t\\n\\r ]*a[\\t\\n\\r ]*s[\\t\\n\\r ]*c[\\t\\n\\r ]*r[\\t\\n\\r ]*i[\\t\\n\\r ]*p[\\t\\n\\r ]*t[\\t\\n\\r ]*:/i,\n },\n {\n id: 'html-vbscript-protocol',\n description: 'vbscript: URI scheme',\n pattern: /vbscript[\\t\\n\\r ]*:/i,\n },\n {\n id: 'html-data-html',\n description: 'data:text/html URI — can execute scripts in browsers',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*text\\/html/i,\n },\n {\n id: 'html-data-xhtml',\n description: 'data:application/xhtml+xml URI',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*application\\/xhtml/i,\n },\n {\n id: 'html-data-svg',\n description: 'data:image/svg+xml URI — can execute scripts',\n pattern: /data[\\t\\n\\r ]*:[\\t\\n\\r ]*image\\/svg\\+xml/i,\n },\n {\n id: 'html-inline-event-handler',\n description: 'Inline event handler attributes: onclick=, onerror=, onload=, etc.',\n // \\bon ensures we match a word boundary so \"phonetic=\" is not caught\n pattern: /\\bon\\w{1,30}\\s*=/i,\n },\n {\n id: 'html-entity-obfuscated-script',\n description: 'HTML-entity-encoded <script (e.g. <script or <script)',\n // Entities include optional trailing semicolon: < or < (both valid in HTML5)\n pattern: /(?:�*3[Cc];?|�*60;?|<)\\s*script/i,\n },\n {\n id: 'html-entity-obfuscated-javascript',\n description: 'HTML-entity-encoded javascript: (partial — catches common j or j for \"j\")',\n pattern: /(?:�*6[Aa];?|�*106;?)\\s*(?:�*61;?|a)[\\s\\S]{0,80}script\\s*:/i,\n },\n {\n id: 'html-style-expression',\n description: 'CSS expression() — IE-era code execution in style attributes',\n pattern: /style[\\s\\S]{0,20}expression\\s*\\(/i,\n },\n {\n id: 'html-object-embed',\n description: '<object or <embed tags that can load active content',\n pattern: /<(?:object|embed)[\\s>/]/i,\n },\n {\n id: 'html-base-tag',\n description: '<base href= — can hijack all relative URLs on a page',\n pattern: /<base[\\s>]/i,\n },\n {\n id: 'html-meta-refresh',\n description: '<meta http-equiv=\"refresh\" — can redirect users',\n pattern: /<meta[\\s\\S]{0,40}http-equiv[\\s\\S]{0,20}refresh/i,\n },\n {\n id: 'html-srcdoc',\n description: 'srcdoc= attribute on iframes — embeds HTML that can run scripts',\n pattern: /srcdoc\\s*=/i,\n },\n {\n id: 'html-iframe',\n description: '<iframe tag',\n pattern: /<iframe[\\s>/]/i,\n },\n {\n id: 'html-form',\n description: '<form tag — can be used for phishing / credential harvesting injection',\n pattern: /<form[\\s>/]/i,\n },\n];\n\nexport default HTML_PATTERNS;\n","/**\n * XML context patterns.\n *\n * Detects injection vectors that are specifically dangerous when a string\n * is inserted into an XML document (not HTML rendering context).\n *\n * Key distinction from HTML: these patterns target parser-level attacks —\n * things that can confuse or subvert an XML parser, trigger external entity\n * resolution, or inject DTD content. HTML rendering concerns (XSS) belong\n * in the HTML context.\n */\n\nconst XML_PATTERNS = [\n {\n id: 'xml-cdata-injection',\n description: 'CDATA section injection: <![CDATA[ breaks out of text node context',\n pattern: /<!\\[CDATA\\[/i,\n },\n {\n id: 'xml-cdata-close',\n description: 'CDATA close sequence: ]]> can terminate an enclosing CDATA section',\n pattern: /\\]\\]>/,\n },\n {\n id: 'xml-processing-instruction',\n description: 'XML processing instruction: <?xml-stylesheet or <?php etc.',\n pattern: /<\\?(?:xml[\\- ]|php|asp)/i,\n },\n {\n id: 'xml-doctype-injection',\n description: 'DOCTYPE declaration embedded in content — can define entities',\n // Match <!DOCTYPE followed by end-of-string, whitespace, or [ (internal subset)\n pattern: /<!DOCTYPE(?:[\\s[]|$)/i,\n },\n {\n id: 'xml-entity-system',\n description: 'SYSTEM keyword — used in external entity declarations (XXE)',\n pattern: /\\bSYSTEM\\s+[\"']/i,\n },\n {\n id: 'xml-entity-public',\n description: 'PUBLIC keyword — used in external entity declarations (XXE)',\n pattern: /\\bPUBLIC\\s+[\"']/i,\n },\n {\n id: 'xml-entity-declaration',\n description: '<!ENTITY declaration — defines entities, potential XXE or entity expansion',\n pattern: /<!ENTITY[\\s%]/i,\n },\n {\n id: 'xml-billion-laughs',\n description: 'Entity reference chaining / billion laughs: repeated &eX; style references',\n // Heuristic: 3+ consecutive entity refs suggests expansion attack\n pattern: /(?:&\\w{1,20};){3,}/,\n },\n {\n id: 'xml-namespace-confusion',\n description: 'xmlns: attribute injection — can redefine namespaces to confuse parsers',\n pattern: /\\bxmlns\\s*(?::\\w{1,40})?\\s*=/i,\n },\n {\n id: 'xml-comment-injection',\n description: '<!-- comment injection — can hide content from some parsers',\n pattern: /<!--/,\n },\n {\n id: 'xml-comment-close',\n description: '--> closes an enclosing XML comment',\n pattern: /-->/,\n },\n {\n id: 'xml-pi-close',\n description: '?> closes an enclosing processing instruction',\n pattern: /\\?>/,\n },\n];\n\nexport default XML_PATTERNS;\n","/**\n * SVG context patterns.\n *\n * SVG is XML-based but renders in browsers, giving it a unique attack surface\n * that combines XML parser behaviour with browser rendering and JavaScript execution.\n *\n * Many of these vectors bypass HTML sanitizers that don't understand SVG semantics\n * (DOMPurify has documented bypass vulnerabilities specifically in SVG/XML context).\n */\n\nconst SVG_PATTERNS = [\n {\n id: 'svg-script-element',\n description: '<script element inside SVG executes JavaScript',\n pattern: /<script[\\s>/]/i,\n },\n {\n id: 'svg-xlink-href-javascript',\n description: 'xlink:href with javascript: — classic SVG XSS via <a> or <use>',\n pattern: /xlink\\s*:\\s*href\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-href-javascript',\n description: 'href= with javascript: in SVG context (<a>, <animate>, etc.)',\n pattern: /href\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-foreignobject',\n description: '<foreignObject embeds HTML inside SVG — can execute scripts',\n pattern: /<foreignObject[\\s>/]/i,\n },\n {\n id: 'svg-use-external',\n description: '<use xlink:href or href pointing to external resource (non-fragment URL)',\n // Match <use with href= where the value starts with a non-# character (external URL)\n // [\\\"'][^#] catches quoted values not starting with #; [^\\\"'#\\s>] catches unquoted\n pattern: /<use[\\s\\S]{0,60}(?:xlink\\s*:\\s*)?href\\s*=\\s*(?:[\"'][^#]|[^\"'#\\s>])/i,\n },\n {\n id: 'svg-animate-href',\n description: '<animate attributeName=\"href\" — can dynamically change href to javascript:',\n pattern: /<animate[\\s\\S]{0,80}attributeName\\s*=\\s*[\"'][\\s]*href[\"']/i,\n },\n {\n id: 'svg-animate-xlinkhref',\n description: '<animate attributeName=\"xlink:href\"',\n pattern: /<animate[\\s\\S]{0,80}attributeName\\s*=\\s*[\"'][\\s]*xlink\\s*:\\s*href[\"']/i,\n },\n {\n id: 'svg-set-javascript',\n description: '<set to=\"javascript:...\" — sets an attribute to a javascript: URI',\n pattern: /<set[\\s\\S]{0,80}to\\s*=\\s*[\"']?\\s*javascript\\s*:/i,\n },\n {\n id: 'svg-event-handler',\n description: 'SVG-specific event handler attributes: onload=, onerror=, onactivate=, etc.',\n pattern: /\\bon(?:load|error|activate|begin|end|repeat|focus|blur|click|mouse\\w{1,20}|key\\w{1,20})\\s*=/i,\n },\n {\n id: 'svg-handler-generic',\n description: 'Generic on* handler catch-all for SVG attributes',\n pattern: /\\bon\\w{1,30}\\s*=/i,\n },\n {\n id: 'svg-filter-feimage',\n description: '<feImage href= — filter primitive that can load external resources',\n pattern: /<feImage[\\s\\S]{0,80}(?:xlink\\s*:\\s*)?href\\s*=/i,\n },\n {\n id: 'svg-image-external',\n description: '<image xlink:href with http/https or javascript protocol',\n pattern: /<image[\\s\\S]{0,80}(?:xlink\\s*:\\s*)?href\\s*=\\s*[\"']?\\s*(?:https?|javascript)\\s*:/i,\n },\n {\n id: 'svg-style-javascript',\n description: 'style= attribute containing javascript: (e.g. background:url(javascript:...))',\n pattern: /style\\s*=[\\s\\S]{0,60}javascript\\s*:/i,\n },\n];\n\nexport default SVG_PATTERNS;\n","/**\n * SQL context patterns — high-precision rules only.\n *\n * These rules have very low false-positive risk and are safe to apply to\n * general user text (names, descriptions, search queries, etc.).\n * All patterns are ReDoS-safe — unlike the `sql-injection` npm package\n * which has an active CVE on its own detection regexes.\n *\n * For exhaustive coverage including noisier heuristics (comment sequences,\n * hex literals, stacked queries with semicolons), use 'SQL-STRICT' instead.\n * Apply 'SQL-STRICT' only to strings that are specifically SQL fragments,\n * not to general free-text fields.\n */\n\nconst SQL_PATTERNS = [\n {\n id: 'sql-block-comment-open',\n description: 'SQL block comment open: /* ... */ — unusual in legitimate user text',\n pattern: /\\/\\*/,\n },\n {\n id: 'sql-union-select',\n description: 'UNION SELECT — most common SQL injection aggregation attack',\n pattern: /\\bUNION\\s{1,20}(?:ALL\\s{1,20})?SELECT\\b/i,\n },\n {\n id: 'sql-drop-table',\n description: 'DROP TABLE — destructive DDL injection',\n pattern: /\\bDROP\\s{1,20}TABLE\\b/i,\n },\n {\n id: 'sql-drop-database',\n description: 'DROP DATABASE — destructive DDL injection',\n pattern: /\\bDROP\\s{1,20}DATABASE\\b/i,\n },\n {\n id: 'sql-insert-into',\n description: 'INSERT INTO — data injection',\n pattern: /\\bINSERT\\s{1,20}INTO\\b/i,\n },\n {\n id: 'sql-delete-from',\n description: 'DELETE FROM — data deletion injection',\n pattern: /\\bDELETE\\s{1,20}FROM\\b/i,\n },\n {\n id: 'sql-update-set',\n description: 'UPDATE ... SET — data modification injection',\n // Allows arbitrary content between UPDATE and SET (table name, alias, etc.)\n pattern: /\\bUPDATE\\b[\\s\\S]{1,60}\\bSET\\b/i,\n },\n {\n id: 'sql-exec-xp',\n description: 'EXEC xp_ — MSSQL extended stored procedure execution',\n pattern: /\\bEXEC(?:UTE)?\\s{1,20}xp_/i,\n },\n {\n id: 'sql-tautology-string',\n description: \"Classic string tautology: ' OR '1'='1 or \\\" OR \\\"1\\\"=\\\"1\\\"\",\n // Last quote is optional — injection may truncate it: ' OR '1'='1--\n pattern: /'\\s{0,10}OR\\s{0,10}'[^']{0,20}'\\s*=\\s*'[^']{0,20}/i,\n },\n {\n id: 'sql-tautology-numeric',\n description: 'Numeric tautology: OR 1=1',\n pattern: /\\bOR\\s{1,10}1\\s*=\\s*1\\b/i,\n },\n {\n id: 'sql-always-true-zero',\n description: 'Numeric tautology: OR 0=0',\n pattern: /\\bOR\\s{1,10}0\\s*=\\s*0\\b/i,\n },\n {\n id: 'sql-sleep-benchmark',\n description: 'Time-based blind injection: SLEEP() or BENCHMARK()',\n pattern: /\\b(?:SLEEP|BENCHMARK)\\s*\\(/i,\n },\n {\n id: 'sql-waitfor-delay',\n description: 'MSSQL time-based blind injection: WAITFOR DELAY',\n pattern: /\\bWAITFOR\\s{1,20}DELAY\\b/i,\n },\n {\n id: 'sql-char-function',\n description: 'CHAR() function — used to obfuscate injected strings',\n pattern: /\\bCHAR\\s*\\(\\s*\\d{1,3}/i,\n },\n {\n id: 'sql-information-schema',\n description: 'INFORMATION_SCHEMA — reconnaissance query for table/column enumeration',\n pattern: /\\bINFORMATION_SCHEMA\\b/i,\n },\n];\n\nexport default SQL_PATTERNS;\n","/**\n * SHELL context patterns.\n *\n * Detects shell injection vectors and path traversal patterns.\n * Designed for use when a string will be passed to a shell command,\n * used as a file path, or interpolated into OS-level operations.\n */\n\nconst SHELL_PATTERNS = [\n {\n id: 'shell-path-traversal-unix',\n description: 'Unix path traversal: ../ — climbing the directory tree',\n pattern: /\\.\\.\\//,\n },\n {\n id: 'shell-path-traversal-windows',\n description: 'Windows path traversal: ..\\\\ — climbing the directory tree',\n pattern: /\\.\\.\\\\/,\n },\n {\n id: 'shell-path-traversal-encoded',\n description: 'URL-encoded path traversal: %2e%2e or %2f variants',\n pattern: /%2e%2e|%2f\\.\\.|\\.\\.%2f/i,\n },\n {\n id: 'shell-null-byte',\n description: 'Null byte injection: \\\\x00 or %00 — truncates strings in C-backed functions',\n pattern: /\\x00|%00/,\n },\n {\n id: 'shell-semicolon',\n description: 'Semicolon command separator: cmd1; cmd2',\n pattern: /;/,\n },\n {\n id: 'shell-pipe',\n description: 'Pipe operator: cmd1 | cmd2',\n pattern: /\\|/,\n },\n {\n id: 'shell-and-operator',\n description: 'AND operator: cmd1 && cmd2',\n pattern: /&&/,\n },\n {\n id: 'shell-or-operator',\n description: 'OR operator: cmd1 || cmd2',\n pattern: /\\|\\|/,\n },\n {\n id: 'shell-backtick',\n description: 'Backtick command substitution: `cmd`',\n pattern: /`/,\n },\n {\n id: 'shell-dollar-paren',\n description: 'Dollar-paren command substitution: $(cmd)',\n pattern: /\\$\\(/,\n },\n {\n id: 'shell-dollar-brace',\n description: 'Dollar-brace variable expansion: ${var} — can be abused for injection',\n pattern: /\\$\\{/,\n },\n {\n id: 'shell-redirect-out',\n description: 'Output redirection: cmd > file or cmd >> file',\n pattern: />{1,2}/,\n },\n {\n id: 'shell-redirect-in',\n description: 'Input redirection: cmd < file',\n pattern: /</,\n },\n {\n id: 'shell-newline-injection',\n description: 'Newline injection: \\\\n or \\\\r — can inject new shell commands',\n pattern: /[\\n\\r]/,\n },\n {\n id: 'shell-glob-star',\n description: 'Glob expansion: * or ? — can expand to unintended files',\n // Only flag when combined with path separators to reduce false positives\n pattern: /[/\\\\][*?]/,\n },\n {\n id: 'shell-absolute-root',\n description: 'Absolute root path injection: string starting with / or \\\\ (Windows UNC)',\n pattern: /^(?:\\/|\\\\\\\\)/,\n },\n {\n id: 'shell-windows-drive',\n description: 'Windows drive letter path injection: C:\\\\ or D:/',\n pattern: /^[a-zA-Z]:[/\\\\]/,\n },\n {\n id: 'shell-curl-wget',\n description: 'curl/wget with URL or flags — can exfiltrate data or download payloads',\n // Require a URL scheme (http/https/ftp) or a flag (-) to reduce false positives\n // \"curl is a tool\" won't match; \"curl http://...\" or \"curl -s ...\" will\n pattern: /\\b(?:curl|wget)\\s+(?:https?:\\/\\/|ftp:\\/\\/|-)/i,\n },\n];\n\nexport default SHELL_PATTERNS;\n","/**\n * REDOS context patterns.\n *\n * Detects strings that, if used as regular expressions, could cause\n * catastrophic backtracking (ReDoS — Regular Expression Denial of Service).\n *\n * These patterns detect the structural forms that lead to exponential or\n * polynomial backtracking in NFA-based regex engines (V8, PCRE, Java, etc.).\n *\n * Use this context when user-supplied strings will be compiled into RegExp objects.\n */\n\nconst REDOS_PATTERNS = [\n {\n id: 'redos-nested-quantifier-plus',\n description: 'Nested + quantifier inside a group with outer quantifier: (a+)+, (.+b)*, etc.',\n // Matches any group containing a + quantifier, with an outer * or + — catches (a+)+, (.+b)*, etc.\n pattern: /\\([^)]*\\+[^)]*\\)[+*]/,\n },\n {\n id: 'redos-nested-quantifier-star',\n description: 'Nested * quantifier: (a*)* or (a*)+ — catastrophic backtracking',\n pattern: /\\([^)]*\\*[^)]*\\)[*+]/,\n },\n {\n id: 'redos-nested-groups',\n description: 'Doubly nested quantified groups: ((a+)+) — guaranteed catastrophic',\n pattern: /\\(\\([^)]{0,40}\\)[+*]\\)[+*]/,\n },\n {\n id: 'redos-alternation-overlap',\n description: 'Overlapping alternation under quantifier: (a|a)+ — ambiguous NFA paths',\n // Detect repeated identical alternatives under a quantifier\n pattern: /\\(([^|()]{1,20})\\|(?:\\1)(?:\\|[^|()]{1,20}){0,5}\\)[+*?]{1,2}/,\n },\n {\n id: 'redos-star-plus-concat',\n description: '(x*x)+ pattern — triggers super-linear backtracking',\n pattern: /\\([^)]{0,10}\\*[^)]{0,10}\\)[+*]/,\n },\n {\n id: 'redos-dot-star-greedy',\n description: '(.*){n,} or (.+){n,} — repeated greedy dot quantifiers',\n pattern: /\\(\\.[*+]\\)\\{?\\d/,\n },\n {\n id: 'redos-large-repetition',\n description: 'Very large fixed or range repetition count {1000,} or {1000,n} — denial of service via backtracking',\n // Matches { followed by 4+ digits (≥1000), then optional ,digits }\n pattern: /\\{\\d{4,}(?:,\\d*)?\\}/,\n },\n {\n id: 'redos-catastrophic-alternation',\n description: 'Long alternation with many similar branches — polynomial backtracking risk',\n // Heuristic: 10+ pipe-separated alternatives in a single group\n pattern: /\\([^)]{0,200}(?:\\|[^|)]{0,50}){9,}\\)/,\n },\n];\n\nexport default REDOS_PATTERNS;\n","/**\n * NOSQL context patterns.\n *\n * Detects injection vectors specific to NoSQL databases (primarily MongoDB)\n * and JavaScript-evaluated queries.\n *\n * Attack categories:\n * 1. MongoDB query operator injection: $where, $ne, $gt, $regex, $or, $and, etc.\n * These operators, when injected into a JSON query object, can bypass\n * authentication or exfiltrate data without knowing passwords.\n *\n * 2. JavaScript execution: $where clauses execute arbitrary JS server-side.\n *\n * 3. Prototype pollution: __proto__, constructor.prototype — can corrupt\n * the prototype chain of all objects in the Node.js process.\n *\n * Pattern note: MongoDB operators appear as JSON keys. In JSON, keys are\n * quoted: {\"$where\": ...} so the pattern must allow an optional closing\n * quote between the operator name and the colon: /\\$where[\"'\\s]*:/\n */\n\n// Shared suffix: optional closing quote/whitespace before the colon\n// Handles: $op: (bare), \"$op\": (JSON), '$op': (single-quoted)\nconst SEP = /[\"'\\s]*:/;\nconst sep = '[\"\\'\\\\s]*:';\n\nconst NOSQL_PATTERNS = [\n // ─── MongoDB $ operator injection ────────────────────────────────────────\n {\n id: 'nosql-where-operator',\n description: '$where — executes arbitrary JavaScript server-side in MongoDB',\n pattern: new RegExp(`\\\\$where${sep}`, 'i'),\n },\n {\n id: 'nosql-ne-operator',\n description: '$ne — \"not equal\" operator used to bypass equality checks',\n pattern: new RegExp(`\\\\$ne${sep}`, 'i'),\n },\n {\n id: 'nosql-gt-operator',\n description: '$gt — \"greater than\" used to bypass password/value checks',\n pattern: new RegExp(`\\\\$gte?${sep}`, 'i'),\n },\n {\n id: 'nosql-lt-operator',\n description: '$lt / $lte — \"less than\" bypass variants',\n pattern: new RegExp(`\\\\$lte?${sep}`, 'i'),\n },\n {\n id: 'nosql-regex-operator',\n description: '$regex — can be used to extract data character by character (blind injection)',\n pattern: new RegExp(`\\\\$regex${sep}`, 'i'),\n },\n {\n id: 'nosql-or-operator',\n description: '$or — logical OR; used to create always-true conditions',\n pattern: new RegExp(`\\\\$or${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-and-operator',\n description: '$and — logical AND operator injection',\n pattern: new RegExp(`\\\\$and${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-nor-operator',\n description: '$nor — logical NOR operator injection',\n pattern: new RegExp(`\\\\$nor${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-exists-operator',\n description: '$exists — can enumerate fields to determine schema',\n pattern: new RegExp(`\\\\$exists${sep}`, 'i'),\n },\n {\n id: 'nosql-in-operator',\n description: '$in — matches any value in a list; can enumerate values',\n pattern: new RegExp(`\\\\$in${sep}\\\\s*\\\\[`, 'i'),\n },\n {\n id: 'nosql-expr-operator',\n description: '$expr — allows aggregation expressions in queries (MongoDB 3.6+)',\n pattern: new RegExp(`\\\\$expr${sep}`, 'i'),\n },\n {\n id: 'nosql-function-operator',\n description: '$function — executes arbitrary JavaScript in MongoDB 4.4+',\n pattern: new RegExp(`\\\\$function${sep}`, 'i'),\n },\n {\n id: 'nosql-accumulator-operator',\n description: '$accumulator — custom aggregation with arbitrary JS execution',\n pattern: new RegExp(`\\\\$accumulator${sep}`, 'i'),\n },\n // ─── Prototype pollution ─────────────────────────────────────────────────\n {\n id: 'nosql-proto-pollution',\n description: '__proto__ — prototype pollution via object key injection',\n pattern: /__proto__/,\n },\n {\n id: 'nosql-constructor-prototype',\n description: 'constructor.prototype — alternative prototype pollution vector (dot notation or JSON key)',\n // Matches dot-notation (obj.constructor.prototype) and JSON key adjacency\n // (\"constructor\": {\"prototype\": ...})\n pattern: /constructor[\\s\"':.,{\\[]*prototype/i,\n },\n {\n id: 'nosql-proto-bracket',\n description: '[\"__proto__\"] — bracket-notation prototype pollution',\n pattern: /\\[[\"']__proto__[\"']\\]/,\n },\n];\n\nexport default NOSQL_PATTERNS;\n","/**\n * LOG context patterns.\n *\n * Detects injection vectors that are dangerous when a string is written\n * to a log file, passed to a logging framework, or interpolated into\n * a log message that will be parsed or displayed.\n *\n * Attack categories:\n * 1. CRLF injection — injects fake log lines by embedding newlines\n * 2. Log4Shell (CVE-2021-44228) — ${jndi:...} triggers JNDI lookup in Log4j\n * 3. SSTI in log templates — {{...}}, #{...} trigger template evaluation\n * if the log message is passed through a template engine\n * 4. Null byte injection — truncates log entries in some implementations\n * 5. ANSI escape injection — manipulates terminal output when logs are\n * tailed in a terminal (colour codes, cursor movement, etc.)\n *\n * Note: Newline characters (\\n, \\r) will produce false positives for\n * multi-line legitimate values. Use this context only for single-line\n * log field values (usernames, IDs, request parameters, etc.).\n */\n\nconst LOG_PATTERNS = [\n // ─── CRLF / newline injection ─────────────────────────────────────────────\n {\n id: 'log-crlf-injection',\n description: 'CRLF injection: literal \\\\r or \\\\n embeds fake log lines',\n pattern: /[\\r\\n]/,\n },\n {\n id: 'log-url-encoded-crlf',\n description: 'URL-encoded CRLF: %0d, %0a, %0D, %0A — decoded by some log parsers',\n pattern: /%0[dDaA]/,\n },\n {\n id: 'log-unicode-newline',\n description: 'Unicode newline variants: U+2028 (line separator), U+2029 (paragraph separator)',\n pattern: /[\\u2028\\u2029]/,\n },\n\n // ─── Log4Shell / JNDI injection (CVE-2021-44228) ─────────────────────────\n {\n id: 'log-log4shell-jndi',\n description: 'Log4Shell: ${jndi:...} triggers remote code execution in Apache Log4j',\n pattern: /\\$\\{jndi\\s*:/i,\n },\n {\n id: 'log-log4shell-obfuscated',\n description: 'Obfuscated Log4Shell: ${::-j}... lookup-bypass prefix used to evade WAF detection',\n // ${::- is the Log4j lookup-bypass escape sequence; presence alone is suspicious\n pattern: /\\$\\{::-/,\n },\n {\n id: 'log-log4j-lookup',\n description: 'Log4j lookup syntax: ${env:...}, ${sys:...}, ${ctx:...} — data exfiltration',\n pattern: /\\$\\{(?:env|sys|ctx|main|map|sd|web|docker|k8s|spring)\\s*:/i,\n },\n\n // ─── Server-Side Template Injection (SSTI) in log messages ───────────────\n {\n id: 'log-ssti-double-brace',\n description: 'SSTI double-brace: {{expression}} — Jinja2, Twig, Handlebars, etc.',\n pattern: /\\{\\{[\\s\\S]{0,80}\\}\\}/,\n },\n {\n id: 'log-ssti-hash-brace',\n description: 'SSTI hash-brace: #{expression} — Thymeleaf, Velocity, Ruby ERB',\n pattern: /#\\{[\\s\\S]{0,80}\\}/,\n },\n {\n id: 'log-ssti-dollar-brace',\n description: 'SSTI/EL injection: ${expression with operators or method calls} — JSP EL, Freemarker, SpEL',\n // Require that the ${...} content looks like an expression, not a plain variable name.\n // Flags if the content contains: . ( * + operators, or known SSTI keywords.\n // This avoids flagging ${PATH}, ${HOME} etc. (plain shell variables).\n pattern: /\\$\\{[^}]*(?:\\.|\\(|\\*|\\+|\\bclass\\b|\\bruntime\\b|\\bprocess\\b|\\bexec\\b)[^}]{0,80}\\}/i,\n },\n {\n id: 'log-ssti-percent-tag',\n description: 'SSTI ERB/ASP tag: <%= expression %> — Ruby ERB, ASP',\n pattern: /<%=[\\s\\S]{0,80}%>/,\n },\n\n // ─── Null byte ────────────────────────────────────────────────────────────\n {\n id: 'log-null-byte',\n description: 'Null byte: \\\\x00 or %00 — can truncate log entries in C-backed loggers',\n pattern: /\\x00|%00/,\n },\n\n // ─── ANSI escape injection ────────────────────────────────────────────────\n {\n id: 'log-ansi-escape',\n description: 'ANSI escape sequence: ESC[ — can manipulate terminal output when logs are tailed',\n pattern: /\\x1b\\[/,\n },\n];\n\nexport default LOG_PATTERNS;\n","/**\n * SQL-STRICT context patterns.\n *\n * Extends the base 'SQL' context with three additional rules that are\n * effective at detecting real injections but carry a higher false-positive\n * risk on general free-text input.\n *\n * Use 'SQL-STRICT' when:\n * - The string is specifically a SQL fragment or database identifier\n * - You control the input domain (e.g. a dedicated SQL search field)\n * - You can tolerate occasional false positives in exchange for broader coverage\n *\n * Use 'SQL' (not STRICT) when:\n * - The field is general user text (names, descriptions, comments)\n * - False positives would block legitimate content (e.g. \"see note -- above\")\n *\n * Rules moved here from 'SQL' due to false-positive risk:\n *\n * sql-line-comment — \"--\" fires on \"see note -- above\", \"value--\", CSS var(--primary)\n * sql-stacked-query — \"; SELECT\" fires on legitimate prose with semicolons + SQL words\n * sql-hex-encoding — \"0xDEAD\" fires on hex values in technical docs and log output\n */\n\nimport SQL_PATTERNS from './sql.js';\n\nconst SQL_STRICT_EXTRA = [\n {\n id: 'sql-line-comment',\n description: 'SQL line comment: -- followed by whitespace or end of string',\n pattern: /--(?:\\s|$)/,\n },\n {\n id: 'sql-stacked-query',\n description: 'Stacked queries: semicolon immediately followed by a SQL keyword',\n pattern: /;\\s{0,10}(?:SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC)\\b/i,\n },\n {\n id: 'sql-hex-encoding',\n description: 'Hex-encoded string injection: 0x41414141 style (MySQL)',\n pattern: /\\b0x[0-9a-f]{4,}/i,\n },\n];\n\n// SQL-STRICT = all base SQL rules + the three noisy extras\nconst SQL_STRICT_PATTERNS = [...SQL_PATTERNS, ...SQL_STRICT_EXTRA];\n\nexport default SQL_STRICT_PATTERNS;\n","/**\n * is-unsafe v2\n *\n * Zero-dependency, DOM-free, pure predicate for detecting unsafe strings\n * across HTML, XML, SVG, SQL, SQL-STRICT, SHELL, REDOS, NOSQL, and LOG contexts.\n *\n * v2 change: contexts are imported as named pattern arrays rather than resolved\n * via a string-keyed registry. This makes each context independently\n * tree-shakeable — bundlers can drop any context you never import.\n *\n * @module is-unsafe\n */\n\n// ─── Context pattern arrays (named exports) ────────────────────────────────\n// Import only the ones you need. Each is independently tree-shakeable.\n\nexport { default as HTML } from './contexts/html.js';\nexport { default as XML } from './contexts/xml.js';\nexport { default as SVG } from './contexts/svg.js';\nexport { default as SQL } from './contexts/sql.js';\nexport { default as SHELL } from './contexts/shell.js';\nexport { default as REDOS } from './contexts/redos.js';\nexport { default as NOSQL } from './contexts/nosql.js';\nexport { default as LOG } from './contexts/log.js';\n\n// SQL-STRICT needs a quoted identifier because of the hyphen\nimport SQL_STRICT from './contexts/sql-strict.js';\nexport { SQL_STRICT };\n\n// ─── VALID_CONTEXTS convenience re-export ─────────────────────────────────\n// Importing this pulls in ALL contexts. Use it only when you need all of them\n// (e.g. for validation UI, tooling, or exhaustive audits).\n// If you only need a subset, import the named contexts directly instead.\n\nimport HTML from './contexts/html.js';\nimport XML from './contexts/xml.js';\nimport SVG from './contexts/svg.js';\nimport SQL from './contexts/sql.js';\nimport SHELL from './contexts/shell.js';\nimport REDOS from './contexts/redos.js';\nimport NOSQL from './contexts/nosql.js';\nimport LOG from './contexts/log.js';\n\n// ─── Attach labels to named contexts ──────────────────────────────────────\n// Each built-in PatternList carries its canonical name so matchList can read\n// list.label directly — no registry lookup needed at match time.\n// Custom PatternLists default to 'CUSTOM' unless the caller sets list.label.\n\nHTML.label = 'HTML';\nXML.label = 'XML';\nSVG.label = 'SVG';\nSQL.label = 'SQL';\nSQL_STRICT.label = 'SQL-STRICT';\nSHELL.label = 'SHELL';\nREDOS.label = 'REDOS';\nNOSQL.label = 'NOSQL';\nLOG.label = 'LOG';\n\nexport const VALID_CONTEXTS = Object.freeze({\n HTML,\n XML,\n SVG,\n SQL,\n 'SQL-STRICT': SQL_STRICT,\n SHELL,\n REDOS,\n NOSQL,\n LOG,\n});\n\n// ─── Types ────────────────────────────────────────────────────────────────\n\n/**\n * @typedef {{ id: string, description: string, pattern: RegExp }} Rule\n */\n\n/**\n * @typedef {Rule[]} PatternList\n */\n\n/**\n * @typedef {Object} MatchResult\n * @property {string} context - Label identifying which context matched ('HTML', 'CUSTOM', etc.)\n * @property {string} id - Rule identifier\n * @property {string} description - Human-readable description of what was matched\n * @property {RegExp} pattern - The pattern that matched\n */\n\n// ─── Internal helpers ──────────────────────────────────────────────────────\n\n/**\n * @param {unknown} value\n */\nfunction assertString(value) {\n if (typeof value !== 'string') {\n throw new TypeError(\n `is-unsafe: first argument must be a string, got ${typeof value}`\n );\n }\n}\n\n/**\n * @param {unknown} context\n */\nfunction assertContext(context) {\n if (context instanceof RegExp) return;\n\n if (Array.isArray(context)) {\n if (context.length === 0) {\n throw new TypeError('is-unsafe: context must not be an empty array');\n }\n // Detect array-of-arrays vs flat pattern list\n if (Array.isArray(context[0])) {\n // Array of PatternLists\n for (const list of context) {\n if (!Array.isArray(list) || list.length === 0) {\n throw new TypeError(\n 'is-unsafe: each context in the array must be a non-empty pattern array (PatternList)'\n );\n }\n }\n }\n // else: flat PatternList — trust it, no deep validation needed\n return;\n }\n\n throw new TypeError(\n `is-unsafe: second argument must be a PatternList (e.g. HTML), ` +\n `an array of PatternLists (e.g. [HTML, XML]), or a RegExp. Got: ${typeof context}`\n );\n}\n\n/**\n * Normalise any valid context arg into an array of PatternLists.\n *\n * @param {Rule[]|Rule[][]|RegExp} context\n * @returns {{ lists: Rule[][]|null, regex: RegExp|null }}\n */\nfunction normalise(context) {\n if (context instanceof RegExp) return { lists: null, regex: context };\n // Distinguish PatternList (array of rule objects) from array of PatternLists\n if (Array.isArray(context[0])) return { lists: context, regex: null };\n return { lists: [context], regex: null };\n}\n\n/**\n * Test value against a single PatternList. Returns the first MatchResult or null.\n *\n * @param {string} value\n * @param {Rule[]} list\n * @returns {MatchResult|null}\n */\nfunction matchList(value, list) {\n const label = list.label ?? 'CUSTOM';\n for (const rule of list) {\n if (rule.pattern.test(value)) {\n return { context: label, id: rule.id, description: rule.description, pattern: rule.pattern };\n }\n }\n return null;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────\n\n/**\n * Returns `true` if `value` is unsafe in the given context(s), `false` otherwise.\n *\n * @param {string} value - The string to test\n * @param {PatternList | PatternList[] | RegExp} context\n * - A PatternList imported from is-unsafe (e.g. `HTML`, `XML`)\n * - An array of PatternLists — returns true if unsafe in **any** of them\n * - A custom RegExp — returns true if the pattern matches\n * @returns {boolean}\n *\n * @example\n * import { isUnsafe, HTML, SQL } from 'is-unsafe';\n *\n * isUnsafe('<script>alert(1)</script>', HTML) // true\n * isUnsafe('hello world', HTML) // false\n * isUnsafe('value', [HTML, SQL]) // false\n * isUnsafe('value', /my-pattern/i) // false\n */\nfunction isUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n const { lists, regex } = normalise(context);\n\n if (regex) return regex.test(value);\n\n for (const list of lists) {\n if (matchList(value, list) !== null) return true;\n }\n return false;\n}\n\n/**\n * Like `isUnsafe`, but returns the first `MatchResult` describing **why**\n * the value was flagged, or `null` if it is safe.\n *\n * @param {string} value\n * @param {PatternList | PatternList[] | RegExp} context\n * @returns {MatchResult|null}\n *\n * @example\n * import { whyUnsafe, HTML } from 'is-unsafe';\n *\n * whyUnsafe('<script>alert(1)</script>', HTML)\n * // { context: 'HTML', id: 'html-script-open', description: '...', pattern: /.../ }\n */\nfunction whyUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n const { lists, regex } = normalise(context);\n\n if (regex) {\n return regex.test(value)\n ? { context: 'CUSTOM', id: 'custom-regex', description: 'Matched caller-supplied pattern', pattern: regex }\n : null;\n }\n\n for (const list of lists) {\n const result = matchList(value, list);\n if (result !== null) return result;\n }\n return null;\n}\n\n/**\n * Returns **all** matching rules across the given context(s), or an empty\n * array if the value is safe. Useful for comprehensive auditing.\n *\n * @param {string} value\n * @param {PatternList | PatternList[] | RegExp} context\n * @returns {MatchResult[]}\n */\nfunction allUnsafe(value, context) {\n assertString(value);\n assertContext(context);\n\n const { lists, regex } = normalise(context);\n const results = [];\n\n if (regex) {\n if (regex.test(value)) {\n results.push({ context: 'CUSTOM', id: 'custom-regex', description: 'Matched caller-supplied pattern', pattern: regex });\n }\n return results;\n }\n\n for (const list of lists) {\n const label = list.label ?? 'CUSTOM';\n for (const rule of list) {\n if (rule.pattern.test(value)) {\n results.push({ context: label, id: rule.id, description: rule.description, pattern: rule.pattern });\n }\n }\n }\n\n return results;\n}\n\nexport { isUnsafe, whyUnsafe, allUnsafe };\nexport default isUnsafe;\n","'use strict';\n///@ts-check\n\nimport { getAllMatches, isExist, DANGEROUS_PROPERTY_NAMES, criticalProperties } from '../util.js';\nimport xmlNode from './xmlNode.js';\nimport DocTypeReader from './DocTypeReader.js';\nimport toNumber from \"strnum\";\nimport getIgnoreAttributesFn from \"../ignoreAttributes.js\";\nimport { Expression, Matcher } from 'path-expression-matcher';\nimport { ExpressionSet } from 'path-expression-matcher';\nimport { EntityDecoder, XML, CURRENCY, COMMON_HTML, ENTITY_ACTION } from '@nodable/entities';\nimport { isUnsafe, HTML as HTML_CONTEXT, XML as XML_CONTEXT } from \"is-unsafe\"\n\n\n// const regx =\n// '<((!\\\\[CDATA\\\\[([\\\\s\\\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\\\/)(NAME)\\\\s*>))([^<]*)'\n// .replace(/NAME/g, util.nameRegexp);\n\n//const tagsRegx = new RegExp(\"<(\\\\/?[\\\\w:\\\\-\\._]+)([^>]*)>(\\\\s*\"+cdataRegx+\")*([^<]+)?\",\"g\");\n//const tagsRegx = new RegExp(\"<(\\\\/?)((\\\\w*:)?([\\\\w:\\\\-\\._]+))([^>]*)>([^<]*)(\"+cdataRegx+\"([^<]*))*([^<]+)?\",\"g\");\n\n// Helper functions for attribute and namespace handling\n\n/**\n * Extract raw attributes (without prefix) from prefixed attribute map\n * @param {object} prefixedAttrs - Attributes with prefix from buildAttributesMap\n * @param {object} options - Parser options containing attributeNamePrefix\n * @returns {object} Raw attributes for matcher\n */\nfunction extractRawAttributes(prefixedAttrs, options) {\n if (!prefixedAttrs) return {};\n\n // Handle attributesGroupName option\n const attrs = options.attributesGroupName\n ? prefixedAttrs[options.attributesGroupName]\n : prefixedAttrs;\n\n if (!attrs) return {};\n\n const rawAttrs = {};\n for (const key in attrs) {\n // Remove the attribute prefix to get raw name\n if (key.startsWith(options.attributeNamePrefix)) {\n const rawName = key.substring(options.attributeNamePrefix.length);\n rawAttrs[rawName] = attrs[key];\n } else {\n // Attribute without prefix (shouldn't normally happen, but be safe)\n rawAttrs[key] = attrs[key];\n }\n }\n return rawAttrs;\n}\n\n/**\n * Extract namespace from raw tag name\n * @param {string} rawTagName - Tag name possibly with namespace (e.g., \"soap:Envelope\")\n * @returns {string|undefined} Namespace or undefined\n */\nfunction extractNamespace(rawTagName) {\n if (!rawTagName || typeof rawTagName !== 'string') return undefined;\n\n const colonIndex = rawTagName.indexOf(':');\n if (colonIndex !== -1 && colonIndex > 0) {\n const ns = rawTagName.substring(0, colonIndex);\n // Don't treat xmlns as a namespace\n if (ns !== 'xmlns') {\n return ns;\n }\n }\n return undefined;\n}\n\nexport default class OrderedObjParser {\n constructor(options, externalEntities) {\n this.options = options;\n this.currentNode = null;\n this.tagsNodeStack = [];\n this.parseXml = parseXml;\n this.parseTextData = parseTextData;\n this.resolveNameSpace = resolveNameSpace;\n this.buildAttributesMap = buildAttributesMap;\n this.isItStopNode = isItStopNode;\n this.replaceEntitiesValue = replaceEntitiesValue;\n this.readStopNodeData = readStopNodeData;\n this.saveTextToParentTag = saveTextToParentTag;\n this.addChild = addChild;\n this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)\n this.entityExpansionCount = 0;\n this.currentExpandedLength = 0;\n this.doctypefound = false;\n let namedEntities = { ...XML };\n if (this.options.entityDecoder) {\n this.entityDecoder = this.options.entityDecoder\n } else {\n if (typeof this.options.htmlEntities === \"object\") namedEntities = this.options.htmlEntities;\n else if (this.options.htmlEntities === true) namedEntities = { ...COMMON_HTML, ...CURRENCY };\n this.entityDecoder = new EntityDecoder({\n namedEntities: { ...namedEntities, ...externalEntities },\n numericAllowed: this.options.htmlEntities,\n limit: {\n maxTotalExpansions: this.options.processEntities.maxTotalExpansions,\n maxExpandedLength: this.options.processEntities.maxExpandedLength,\n applyLimitsTo: this.options.processEntities.appliesTo,\n },\n // onExternalEntity: (name, value) => isUnsafe(value) ? 'block' : 'allow',\n onInputEntity: (name, value) =>\n //TODO: VALID_CONTEXTS.HTML should be set only if this.options.htmlEntities\n isUnsafe(value, [HTML_CONTEXT, XML_CONTEXT]) ? ENTITY_ACTION.BLOCK : ENTITY_ACTION.ALLOW,\n\n //postCheck: resolved => resolved\n });\n }\n\n // Initialize path matcher for path-expression-matcher\n this.matcher = new Matcher();\n this.readonlyMatcher = this.matcher.readOnly();\n\n // Flag to track if current node is a stop node (optimization)\n this.isCurrentNodeStopNode = false;\n\n // Pre-compile stopNodes expressions\n this.stopNodeExpressionsSet = new ExpressionSet();\n const stopNodesOpts = this.options.stopNodes;\n if (stopNodesOpts && stopNodesOpts.length > 0) {\n for (let i = 0; i < stopNodesOpts.length; i++) {\n const stopNodeExp = stopNodesOpts[i];\n if (typeof stopNodeExp === 'string') {\n // Convert string to Expression object\n this.stopNodeExpressionsSet.add(new Expression(stopNodeExp));\n } else if (stopNodeExp instanceof Expression) {\n // Already an Expression object\n this.stopNodeExpressionsSet.add(stopNodeExp);\n }\n }\n this.stopNodeExpressionsSet.seal();\n }\n }\n\n}\n\n\n/**\n * @param {string} val\n * @param {string} tagName\n * @param {string|Matcher} jPath - jPath string or Matcher instance based on options.jPath\n * @param {boolean} dontTrim\n * @param {boolean} hasAttributes\n * @param {boolean} isLeafNode\n * @param {boolean} escapeEntities\n */\nfunction parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {\n const options = this.options;\n if (val !== undefined) {\n if (options.trimValues && !dontTrim) {\n val = val.trim();\n }\n if (val.length > 0) {\n if (!escapeEntities) val = this.replaceEntitiesValue(val, tagName, jPath);\n\n // Pass jPath string or matcher based on options.jPath setting\n const jPathOrMatcher = options.jPath ? jPath.toString() : jPath;\n const newval = options.tagValueProcessor(tagName, val, jPathOrMatcher, hasAttributes, isLeafNode);\n if (newval === null || newval === undefined) {\n //don't parse\n return val;\n } else if (typeof newval !== typeof val || newval !== val) {\n //overwrite\n return newval;\n } else if (options.trimValues) {\n return parseValue(val, options.parseTagValue, options.numberParseOptions);\n } else {\n const trimmedVal = val.trim();\n if (trimmedVal === val) {\n return parseValue(val, options.parseTagValue, options.numberParseOptions);\n } else {\n return val;\n }\n }\n }\n }\n}\n\nfunction resolveNameSpace(tagname) {\n if (this.options.removeNSPrefix) {\n const tags = tagname.split(':');\n const prefix = tagname.charAt(0) === '/' ? '/' : '';\n if (tags[0] === 'xmlns') {\n return '';\n }\n if (tags.length === 2) {\n tagname = prefix + tags[1];\n }\n }\n return tagname;\n}\n\n//TODO: change regex to capture NS\n//const attrsRegx = new RegExp(\"([\\\\w\\\\-\\\\.\\\\:]+)\\\\s*=\\\\s*(['\\\"])((.|\\n)*?)\\\\2\",\"gm\");\nconst attrsRegx = new RegExp('([^\\\\s=]+)\\\\s*(=\\\\s*([\\'\"])([\\\\s\\\\S]*?)\\\\3)?', 'gm');\n\nfunction buildAttributesMap(attrStr, jPath, tagName, force = false) {\n const options = this.options;\n if (force === true || (options.ignoreAttributes !== true && typeof attrStr === 'string')) {\n // attrStr = attrStr.replace(/\\r?\\n/g, ' ');\n //attrStr = attrStr || attrStr.trim();\n\n const matches = getAllMatches(attrStr, attrsRegx);\n const len = matches.length; //don't make it inline\n const attrs = {};\n\n // Pre-process values once: trim + entity replacement\n // Reused in both matcher update and second pass\n const processedVals = new Array(len);\n let hasRawAttrs = false;\n const rawAttrsForMatcher = {};\n\n for (let i = 0; i < len; i++) {\n const attrName = this.resolveNameSpace(matches[i][1]);\n const oldVal = matches[i][4];\n\n if (attrName.length && oldVal !== undefined) {\n let val = oldVal;\n if (options.trimValues) val = val.trim();\n val = this.replaceEntitiesValue(val, tagName, this.readonlyMatcher);\n processedVals[i] = val;\n\n rawAttrsForMatcher[attrName] = val;\n hasRawAttrs = true;\n }\n }\n\n // Update matcher ONCE before second pass, if applicable\n if (hasRawAttrs && typeof jPath === 'object' && jPath.updateCurrent) {\n jPath.updateCurrent(rawAttrsForMatcher);\n }\n\n // Hoist toString() once — path doesn't change during attribute processing\n const jPathStr = options.jPath ? jPath.toString() : this.readonlyMatcher;\n\n // Second pass: apply processors, build final attrs\n let hasAttrs = false;\n for (let i = 0; i < len; i++) {\n const attrName = this.resolveNameSpace(matches[i][1]);\n\n if (this.ignoreAttributesFn(attrName, jPathStr)) continue;\n\n let aName = options.attributeNamePrefix + attrName;\n\n if (attrName.length) {\n if (options.transformAttributeName) {\n aName = options.transformAttributeName(aName);\n }\n aName = sanitizeName(aName, options);\n\n if (matches[i][4] !== undefined) {\n // Reuse already-processed value — no double entity replacement\n const oldVal = processedVals[i];\n\n const newVal = options.attributeValueProcessor(attrName, oldVal, jPathStr);\n if (newVal === null || newVal === undefined) {\n attrs[aName] = oldVal;\n } else if (typeof newVal !== typeof oldVal || newVal !== oldVal) {\n attrs[aName] = newVal;\n } else {\n attrs[aName] = parseValue(oldVal, options.parseAttributeValue, options.numberParseOptions);\n }\n hasAttrs = true;\n } else if (options.allowBooleanAttributes) {\n attrs[aName] = true;\n hasAttrs = true;\n }\n }\n }\n\n if (!hasAttrs) return;\n\n if (options.attributesGroupName && !options.preserveOrder) {\n const attrCollection = {};\n attrCollection[options.attributesGroupName] = attrs;\n return attrCollection;\n }\n return attrs;\n }\n}\nconst parseXml = function (xmlData) {\n xmlData = xmlData.replace(/\\r\\n?/g, \"\\n\"); //TODO: remove this line\n const xmlObj = new xmlNode('!xml');\n let currentNode = xmlObj;\n let textData = \"\";\n\n // Reset matcher for new document\n this.matcher.reset();\n this.entityDecoder.reset();\n\n // Reset entity expansion counters for this document\n this.entityExpansionCount = 0;\n this.currentExpandedLength = 0;\n this.doctypefound = false;\n const options = this.options;\n const docTypeReader = new DocTypeReader(options.processEntities);\n const xmlLen = xmlData.length;\n for (let i = 0; i < xmlLen; i++) {//for each char in XML data\n const ch = xmlData[i];\n if (ch === '<') {\n // const nextIndex = i+1;\n // const _2ndChar = xmlData[nextIndex];\n const c1 = xmlData.charCodeAt(i + 1);\n if (c1 === 47) {//Closing Tag '/'\n const closeIndex = findClosingIndex(xmlData, \">\", i, \"Closing Tag is not closed.\")\n let tagName = xmlData.substring(i + 2, closeIndex).trim();\n\n if (options.removeNSPrefix) {\n const colonIndex = tagName.indexOf(\":\");\n if (colonIndex !== -1) {\n tagName = tagName.substr(colonIndex + 1);\n }\n }\n\n tagName = transformTagName(options.transformTagName, tagName, \"\", options).tagName;\n\n if (currentNode) {\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);\n }\n\n //check if last tag of nested tag was unpaired tag\n const lastTagName = this.matcher.getCurrentTag();\n if (tagName && options.unpairedTagsSet.has(tagName)) {\n throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);\n }\n if (lastTagName && options.unpairedTagsSet.has(lastTagName)) {\n // Pop the unpaired tag\n this.matcher.pop();\n this.tagsNodeStack.pop();\n }\n // Pop the closing tag\n this.matcher.pop();\n this.isCurrentNodeStopNode = false; // Reset flag when closing tag\n\n currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope\n textData = \"\";\n i = closeIndex;\n } else if (c1 === 63) { //'?'\n\n let tagData = readTagExp(xmlData, i, false, \"?>\");\n if (!tagData) throw new Error(\"Pi Tag is not closed.\");\n\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);\n const attsMap = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName, true);\n if (attsMap) {\n const ver = attsMap[this.options.attributeNamePrefix + \"version\"];\n this.entityDecoder.setXmlVersion(Number(ver) || 1.0);\n docTypeReader.setXmlVersion(Number(ver) || 1.0);\n }\n if ((options.ignoreDeclaration && tagData.tagName === \"?xml\") || options.ignorePiTags) {\n //do nothing\n } else {\n\n const childNode = new xmlNode(tagData.tagName);\n childNode.add(options.textNodeName, \"\");\n\n if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent && options.ignoreAttributes !== true) {\n childNode[\":@\"] = attsMap\n }\n this.addChild(currentNode, childNode, this.readonlyMatcher, i);\n }\n\n\n i = tagData.closeIndex + 1;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 45\n && xmlData.charCodeAt(i + 3) === 45) { //'!--'\n const endIndex = findClosingIndex(xmlData, \"-->\", i + 4, \"Comment is not closed.\")\n if (options.commentPropName) {\n const comment = xmlData.substring(i + 4, endIndex - 2);\n\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);\n\n currentNode.add(options.commentPropName, [{ [options.textNodeName]: comment }]);\n }\n i = endIndex;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 68) { //'!D'\n if (this.doctypefound) throw new Error(\"Multiple DOCTYPE declarations found.\");\n this.doctypefound = true;\n const result = docTypeReader.readDocType(xmlData, i);\n this.entityDecoder.addInputEntities(result.entities);\n i = result.i;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 91) { // '!['\n const closeIndex = findClosingIndex(xmlData, \"]]>\", i, \"CDATA is not closed.\") - 2;\n const tagExp = xmlData.substring(i + 9, closeIndex);\n\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);\n\n let val = this.parseTextData(tagExp, currentNode.tagname, this.readonlyMatcher, true, false, true, true);\n if (val == undefined) val = \"\";\n\n //cdata should be set even if it is 0 length string\n if (options.cdataPropName) {\n currentNode.add(options.cdataPropName, [{ [options.textNodeName]: tagExp }]);\n } else {\n currentNode.add(options.textNodeName, val);\n }\n\n i = closeIndex + 2;\n } else {//Opening tag\n let result = readTagExp(xmlData, i, options.removeNSPrefix);\n\n // Safety check: readTagExp can return undefined\n if (!result) {\n // Log context for debugging\n const context = xmlData.substring(Math.max(0, i - 50), Math.min(xmlLen, i + 50));\n throw new Error(`readTagExp returned undefined at position ${i}. Context: \"${context}\"`);\n }\n\n let tagName = result.tagName;\n const rawTagName = result.rawTagName;\n let tagExp = result.tagExp;\n let attrExpPresent = result.attrExpPresent;\n let closeIndex = result.closeIndex;\n\n ({ tagName, tagExp } = transformTagName(options.transformTagName, tagName, tagExp, options));\n\n if (options.strictReservedNames &&\n (tagName === options.commentPropName\n || tagName === options.cdataPropName\n || tagName === options.textNodeName\n || tagName === options.attributesGroupName\n )) {\n throw new Error(`Invalid tag name: ${tagName}`);\n }\n\n //save text as child node\n if (currentNode && textData) {\n if (currentNode.tagname !== '!xml') {\n //when nested tag is found\n textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher, false);\n }\n }\n\n //check if last tag was unpaired tag\n const lastTag = currentNode;\n if (lastTag && options.unpairedTagsSet.has(lastTag.tagname)) {\n currentNode = this.tagsNodeStack.pop();\n this.matcher.pop();\n }\n\n // Clean up self-closing syntax BEFORE processing attributes\n // This is where tagExp gets the trailing / removed\n let isSelfClosing = false;\n if (tagExp.length > 0 && tagExp.lastIndexOf(\"/\") === tagExp.length - 1) {\n isSelfClosing = true;\n if (tagName[tagName.length - 1] === \"/\") {\n tagName = tagName.substr(0, tagName.length - 1);\n tagExp = tagName;\n } else {\n tagExp = tagExp.substr(0, tagExp.length - 1);\n }\n\n // Re-check attrExpPresent after cleaning\n attrExpPresent = (tagName !== tagExp);\n }\n\n // Now process attributes with CLEAN tagExp (no trailing /)\n let prefixedAttrs = null;\n let rawAttrs = {};\n let namespace = undefined;\n\n // Extract namespace from rawTagName\n namespace = extractNamespace(rawTagName);\n\n // Push tag to matcher FIRST (with empty attrs for now) so callbacks see correct path\n if (tagName !== xmlObj.tagname) {\n this.matcher.push(tagName, {}, namespace);\n }\n\n // Now build attributes - callbacks will see correct matcher state\n if (tagName !== tagExp && attrExpPresent) {\n // Build attributes (returns prefixed attributes for the tree)\n // Note: buildAttributesMap now internally updates the matcher with raw attributes\n prefixedAttrs = this.buildAttributesMap(tagExp, this.matcher, tagName);\n\n if (prefixedAttrs) {\n // Extract raw attributes (without prefix) for our use\n //TODO: seems a performance overhead\n rawAttrs = extractRawAttributes(prefixedAttrs, options);\n }\n }\n\n // Now check if this is a stop node (after attributes are set)\n if (tagName !== xmlObj.tagname) {\n this.isCurrentNodeStopNode = this.isItStopNode();\n }\n\n const startIndex = i;\n if (this.isCurrentNodeStopNode) {\n let tagContent = \"\";\n\n // For self-closing tags, content is empty\n if (isSelfClosing) {\n i = result.closeIndex;\n }\n //unpaired tag\n else if (options.unpairedTagsSet.has(tagName)) {\n i = result.closeIndex;\n }\n //normal tag\n else {\n //read until closing tag is found\n const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);\n if (!result) throw new Error(`Unexpected end of ${rawTagName}`);\n i = result.i;\n tagContent = result.tagContent;\n }\n\n const childNode = new xmlNode(tagName);\n\n if (prefixedAttrs) {\n childNode[\":@\"] = prefixedAttrs;\n }\n\n // For stop nodes, store raw content as-is without any processing\n childNode.add(options.textNodeName, tagContent);\n\n this.matcher.pop(); // Pop the stop node tag\n this.isCurrentNodeStopNode = false; // Reset flag\n\n this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);\n } else {\n //selfClosing tag\n if (isSelfClosing) {\n ({ tagName, tagExp } = transformTagName(options.transformTagName, tagName, tagExp, options));\n\n const childNode = new xmlNode(tagName);\n if (prefixedAttrs) {\n childNode[\":@\"] = prefixedAttrs;\n }\n this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);\n this.matcher.pop(); // Pop self-closing tag\n this.isCurrentNodeStopNode = false; // Reset flag\n }\n else if (options.unpairedTagsSet.has(tagName)) {//unpaired tag\n const childNode = new xmlNode(tagName);\n if (prefixedAttrs) {\n childNode[\":@\"] = prefixedAttrs;\n }\n this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);\n this.matcher.pop(); // Pop unpaired tag\n this.isCurrentNodeStopNode = false; // Reset flag\n i = result.closeIndex;\n // Continue to next iteration without changing currentNode\n continue;\n }\n //opening tag\n else {\n const childNode = new xmlNode(tagName);\n if (this.tagsNodeStack.length > options.maxNestedTags) {\n throw new Error(\"Maximum nested tags exceeded\");\n }\n this.tagsNodeStack.push(currentNode);\n\n if (prefixedAttrs) {\n childNode[\":@\"] = prefixedAttrs;\n }\n this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);\n currentNode = childNode;\n }\n textData = \"\";\n i = closeIndex;\n }\n }\n } else {\n textData += xmlData[i];\n }\n }\n return xmlObj.child;\n}\n\nfunction addChild(currentNode, childNode, matcher, startIndex) {\n // unset startIndex if not requested\n if (!this.options.captureMetaData) startIndex = undefined;\n\n // Pass jPath string or matcher based on options.jPath setting\n const jPathOrMatcher = this.options.jPath ? matcher.toString() : matcher;\n const result = this.options.updateTag(childNode.tagname, jPathOrMatcher, childNode[\":@\"])\n if (result === false) {\n //do nothing\n } else if (typeof result === \"string\") {\n childNode.tagname = result\n currentNode.addChild(childNode, startIndex);\n } else {\n currentNode.addChild(childNode, startIndex);\n }\n}\n\n/**\n * @param {object} val - Entity object with regex and val properties\n * @param {string} tagName - Tag name\n * @param {string|Matcher} jPath - jPath string or Matcher instance based on options.jPath\n */\nfunction replaceEntitiesValue(val, tagName, jPath) {\n const entityConfig = this.options.processEntities;\n\n if (!entityConfig || !entityConfig.enabled) {\n return val;\n }\n\n // Check if tag is allowed to contain entities\n if (entityConfig.allowedTags) {\n const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;\n const allowed = Array.isArray(entityConfig.allowedTags)\n ? entityConfig.allowedTags.includes(tagName)\n : entityConfig.allowedTags(tagName, jPathOrMatcher);\n\n if (!allowed) {\n return val;\n }\n }\n\n // Apply custom tag filter if provided\n if (entityConfig.tagFilter) {\n const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;\n if (!entityConfig.tagFilter(tagName, jPathOrMatcher)) {\n return val; // Skip based on custom filter\n }\n }\n\n return this.entityDecoder.decode(val);\n}\n\n\nfunction saveTextToParentTag(textData, parentNode, matcher, isLeafNode) {\n if (textData) { //store previously collected data as textNode\n if (isLeafNode === undefined) isLeafNode = parentNode.child.length === 0\n\n textData = this.parseTextData(textData,\n parentNode.tagname,\n matcher,\n false,\n parentNode[\":@\"] ? Object.keys(parentNode[\":@\"]).length !== 0 : false,\n isLeafNode);\n\n if (textData !== undefined && textData !== \"\")\n parentNode.add(this.options.textNodeName, textData);\n textData = \"\";\n }\n return textData;\n}\n\n/**\n * @param {Array<Expression>} stopNodeExpressions - Array of compiled Expression objects\n * @param {Matcher} matcher - Current path matcher\n */\nfunction isItStopNode() {\n if (this.stopNodeExpressionsSet.size === 0) return false;\n\n return this.matcher.matchesAny(this.stopNodeExpressionsSet);\n}\n\n/**\n * Returns the tag Expression and where it is ending handling single-double quotes situation\n * @param {string} xmlData \n * @param {number} i starting index\n * @returns \n */\nfunction tagExpWithClosingIndex(xmlData, i, closingChar = \">\") {\n //TODO: ignore boolean attributes in tag expression\n //TODO: if ignore attributes, dont read full attribute expression but the end. But read for xml declaration\n let attrBoundary = 0;\n const len = xmlData.length;\n const closeCode0 = closingChar.charCodeAt(0);\n const closeCode1 = closingChar.length > 1 ? closingChar.charCodeAt(1) : -1;\n\n let result = '';\n let segmentStart = i;\n\n for (let index = i; index < len; index++) {\n const code = xmlData.charCodeAt(index);\n\n if (attrBoundary) {\n if (code === attrBoundary) attrBoundary = 0;\n } else if (code === 34 || code === 39) { // \" or '\n attrBoundary = code;\n } else if (code === closeCode0) {\n if (closeCode1 !== -1) {\n if (xmlData.charCodeAt(index + 1) === closeCode1) {\n result += xmlData.substring(segmentStart, index);\n return { data: result, index };\n }\n } else {\n result += xmlData.substring(segmentStart, index);\n return { data: result, index };\n }\n } else if (code === 9 && !attrBoundary) { // \\t - only replace with space outside attribute values\n // Flush accumulated segment, add space, start new segment\n result += xmlData.substring(segmentStart, index) + ' ';\n segmentStart = index + 1;\n }\n }\n}\n\nfunction findClosingIndex(xmlData, str, i, errMsg) {\n const closingIndex = xmlData.indexOf(str, i);\n if (closingIndex === -1) {\n throw new Error(errMsg)\n } else {\n return closingIndex + str.length - 1;\n }\n}\n\nfunction findClosingChar(xmlData, char, i, errMsg) {\n const closingIndex = xmlData.indexOf(char, i);\n if (closingIndex === -1) throw new Error(errMsg);\n return closingIndex; // no offset needed\n}\n\nfunction readTagExp(xmlData, i, removeNSPrefix, closingChar = \">\") {\n const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar);\n if (!result) return;\n let tagExp = result.data;\n const closeIndex = result.index;\n const separatorIndex = tagExp.search(/\\s/);\n let tagName = tagExp;\n let attrExpPresent = true;\n if (separatorIndex !== -1) {//separate tag name and attributes expression\n tagName = tagExp.substring(0, separatorIndex);\n tagExp = tagExp.substring(separatorIndex + 1).trimStart();\n }\n\n const rawTagName = tagName;\n if (removeNSPrefix) {\n const colonIndex = tagName.indexOf(\":\");\n if (colonIndex !== -1) {\n tagName = tagName.substr(colonIndex + 1);\n attrExpPresent = tagName !== result.data.substr(colonIndex + 1);\n }\n }\n\n return {\n tagName: tagName,\n tagExp: tagExp,\n closeIndex: closeIndex,\n attrExpPresent: attrExpPresent,\n rawTagName: rawTagName,\n }\n}\n/**\n * find paired tag for a stop node\n * @param {string} xmlData \n * @param {string} tagName \n * @param {number} i \n */\nfunction readStopNodeData(xmlData, tagName, i) {\n const startIndex = i;\n // Starting at 1 since we already have an open tag\n let openTagCount = 1;\n\n const xmllen = xmlData.length;\n for (; i < xmllen; i++) {\n if (xmlData[i] === \"<\") {\n const c1 = xmlData.charCodeAt(i + 1);\n if (c1 === 47) {//close tag '/'\n const closeIndex = findClosingChar(xmlData, \">\", i, `${tagName} is not closed`);\n let closeTagName = xmlData.substring(i + 2, closeIndex).trim();\n if (closeTagName === tagName) {\n openTagCount--;\n if (openTagCount === 0) {\n return {\n tagContent: xmlData.substring(startIndex, i),\n i: closeIndex\n }\n }\n }\n i = closeIndex;\n } else if (c1 === 63) { //?\n const closeIndex = findClosingIndex(xmlData, \"?>\", i + 1, \"StopNode is not closed.\")\n i = closeIndex;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 45\n && xmlData.charCodeAt(i + 3) === 45) { // '!--'\n const closeIndex = findClosingIndex(xmlData, \"-->\", i + 3, \"StopNode is not closed.\")\n i = closeIndex;\n } else if (c1 === 33\n && xmlData.charCodeAt(i + 2) === 91) { // '!['\n const closeIndex = findClosingIndex(xmlData, \"]]>\", i, \"StopNode is not closed.\") - 2;\n i = closeIndex;\n } else {\n const tagData = readTagExp(xmlData, i, false)\n\n if (tagData) {\n const openTagName = tagData && tagData.tagName;\n if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length - 1] !== \"/\") {\n openTagCount++;\n }\n i = tagData.closeIndex;\n }\n }\n }\n }//end for loop\n}\n\nfunction parseValue(val, shouldParse, options) {\n if (shouldParse && typeof val === 'string') {\n //console.log(options)\n const newval = val.trim();\n if (newval === 'true') return true;\n else if (newval === 'false') return false;\n else return toNumber(val, options);\n } else {\n if (isExist(val)) {\n return val;\n } else {\n return '';\n }\n }\n}\n\nfunction fromCodePoint(str, base, prefix) {\n const codePoint = Number.parseInt(str, base);\n\n if (codePoint >= 0 && codePoint <= 0x10FFFF) {\n return String.fromCodePoint(codePoint);\n } else {\n return prefix + str + \";\";\n }\n}\n\nfunction transformTagName(fn, tagName, tagExp, options) {\n if (fn) {\n const newTagName = fn(tagName);\n if (tagExp === tagName) {\n tagExp = newTagName\n }\n tagName = newTagName;\n }\n tagName = sanitizeName(tagName, options);\n return { tagName, tagExp };\n}\n\n\n\nfunction sanitizeName(name, options) {\n if (criticalProperties.includes(name)) {\n throw new Error(`[SECURITY] Invalid name: \"${name}\" is a reserved JavaScript keyword that could cause prototype pollution`);\n } else if (DANGEROUS_PROPERTY_NAMES.includes(name)) {\n return options.onDangerousProperty(name);\n }\n return name;\n}","'use strict';\n\nimport XmlNode from './xmlNode.js';\nimport { Matcher } from 'path-expression-matcher';\n\nconst METADATA_SYMBOL = XmlNode.getMetaDataSymbol();\n\n/**\n * Helper function to strip attribute prefix from attribute map\n * @param {object} attrs - Attributes with prefix (e.g., {\"@_class\": \"code\"})\n * @param {string} prefix - Attribute prefix to remove (e.g., \"@_\")\n * @returns {object} Attributes without prefix (e.g., {\"class\": \"code\"})\n */\nfunction stripAttributePrefix(attrs, prefix) {\n if (!attrs || typeof attrs !== 'object') return {};\n if (!prefix) return attrs;\n\n const rawAttrs = {};\n for (const key in attrs) {\n if (key.startsWith(prefix)) {\n const rawName = key.substring(prefix.length);\n rawAttrs[rawName] = attrs[key];\n } else {\n // Attribute without prefix (shouldn't normally happen, but be safe)\n rawAttrs[key] = attrs[key];\n }\n }\n return rawAttrs;\n}\n\n/**\n * \n * @param {array} node \n * @param {any} options \n * @param {Matcher} matcher - Path matcher instance\n * @returns \n */\nexport default function prettify(node, options, matcher, readonlyMatcher) {\n return compress(node, options, matcher, readonlyMatcher);\n}\n\n/**\n * @param {array} arr \n * @param {object} options \n * @param {Matcher} matcher - Path matcher instance\n * @returns object\n */\nfunction compress(arr, options, matcher, readonlyMatcher) {\n let text;\n const compressedObj = {}; //This is intended to be a plain object\n for (let i = 0; i < arr.length; i++) {\n const tagObj = arr[i];\n const property = propName(tagObj);\n\n // Push current property to matcher WITH RAW ATTRIBUTES (no prefix)\n if (property !== undefined && property !== options.textNodeName) {\n const rawAttrs = stripAttributePrefix(\n tagObj[\":@\"] || {},\n options.attributeNamePrefix\n );\n matcher.push(property, rawAttrs);\n }\n\n if (property === options.textNodeName) {\n if (text === undefined) text = tagObj[property];\n else text += \"\" + tagObj[property];\n } else if (property === undefined) {\n continue;\n } else if (tagObj[property]) {\n\n let val = compress(tagObj[property], options, matcher, readonlyMatcher);\n const isLeaf = isLeafTag(val, options);\n\n if (Object.keys(val).length === 0 && options.alwaysCreateTextNode) {\n val[options.textNodeName] = \"\";\n }\n\n if (tagObj[\":@\"]) {\n assignAttributes(val, tagObj[\":@\"], readonlyMatcher, options);\n } else if (Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode) {\n val = val[options.textNodeName];\n } else if (Object.keys(val).length === 0) {\n if (options.alwaysCreateTextNode) val[options.textNodeName] = \"\";\n else val = \"\";\n }\n\n if (tagObj[METADATA_SYMBOL] !== undefined && typeof val === \"object\" && val !== null) {\n val[METADATA_SYMBOL] = tagObj[METADATA_SYMBOL]; // copy over metadata\n }\n\n\n if (compressedObj[property] !== undefined && Object.prototype.hasOwnProperty.call(compressedObj, property)) {\n if (!Array.isArray(compressedObj[property])) {\n compressedObj[property] = [compressedObj[property]];\n }\n compressedObj[property].push(val);\n } else {\n //TODO: if a node is not an array, then check if it should be an array\n //also determine if it is a leaf node\n\n // Pass jPath string or readonlyMatcher based on options.jPath setting\n const jPathOrMatcher = options.jPath ? readonlyMatcher.toString() : readonlyMatcher;\n if (options.isArray(property, jPathOrMatcher, isLeaf)) {\n compressedObj[property] = [val];\n } else {\n compressedObj[property] = val;\n }\n }\n\n // Pop property from matcher after processing\n if (property !== undefined && property !== options.textNodeName) {\n matcher.pop();\n }\n }\n\n }\n // if(text && text.length > 0) compressedObj[options.textNodeName] = text;\n if (typeof text === \"string\") {\n if (text.length > 0) compressedObj[options.textNodeName] = text;\n } else if (text !== undefined) compressedObj[options.textNodeName] = text;\n\n\n return compressedObj;\n}\n\nfunction propName(obj) {\n const keys = Object.keys(obj);\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n if (key !== \":@\") return key;\n }\n}\n\nfunction assignAttributes(obj, attrMap, readonlyMatcher, options) {\n if (attrMap) {\n const keys = Object.keys(attrMap);\n const len = keys.length; //don't make it inline\n for (let i = 0; i < len; i++) {\n const atrrName = keys[i]; // This is the PREFIXED name (e.g., \"@_class\")\n\n // Strip prefix for matcher path (for isArray callback)\n const rawAttrName = atrrName.startsWith(options.attributeNamePrefix)\n ? atrrName.substring(options.attributeNamePrefix.length)\n : atrrName;\n\n // For attributes, we need to create a temporary path\n // Pass jPath string or matcher based on options.jPath setting\n const jPathOrMatcher = options.jPath\n ? readonlyMatcher.toString() + \".\" + rawAttrName\n : readonlyMatcher;\n\n if (options.isArray(atrrName, jPathOrMatcher, true, true)) {\n obj[atrrName] = [attrMap[atrrName]];\n } else {\n obj[atrrName] = attrMap[atrrName];\n }\n }\n }\n}\n\nfunction isLeafTag(obj, options) {\n const { textNodeName } = options;\n const propCount = Object.keys(obj).length;\n\n if (propCount === 0) {\n return true;\n }\n\n if (\n propCount === 1 &&\n (obj[textNodeName] || typeof obj[textNodeName] === \"boolean\" || obj[textNodeName] === 0)\n ) {\n return true;\n }\n\n return false;\n}","import { buildOptions } from './OptionsBuilder.js';\nimport OrderedObjParser from './OrderedObjParser.js';\nimport prettify from './node2json.js';\nimport { validate } from \"../validator.js\";\nimport XmlNode from './xmlNode.js';\n\nexport default class XMLParser {\n\n constructor(options) {\n this.externalEntities = {};\n this.options = buildOptions(options);\n\n }\n /**\n * Parse XML dats to JS object \n * @param {string|Uint8Array} xmlData \n * @param {boolean|Object} validationOption \n */\n parse(xmlData, validationOption) {\n if (typeof xmlData !== \"string\" && xmlData.toString) {\n xmlData = xmlData.toString();\n } else if (typeof xmlData !== \"string\") {\n throw new Error(\"XML data is accepted in String or Bytes[] form.\")\n }\n\n if (validationOption) {\n if (validationOption === true) validationOption = {}; //validate with default options\n\n const result = validate(xmlData, validationOption);\n if (result !== true) {\n throw Error(`${result.err.msg}:${result.err.line}:${result.err.col}`)\n }\n }\n const orderedObjParser = new OrderedObjParser(this.options, this.externalEntities);\n // orderedObjParser.entityDecoder.setExternalEntities(this.externalEntities);\n const orderedResult = orderedObjParser.parseXml(xmlData);\n if (this.options.preserveOrder || orderedResult === undefined) return orderedResult;\n else return prettify(orderedResult, this.options, orderedObjParser.matcher, orderedObjParser.readonlyMatcher);\n }\n\n /**\n * Add Entity which is not by default supported by this library\n * @param {string} key \n * @param {string} value \n */\n addEntity(key, value) {\n if (value.indexOf(\"&\") !== -1) {\n throw new Error(\"Entity value can't have '&'\")\n } else if (key.indexOf(\"&\") !== -1 || key.indexOf(\";\") !== -1) {\n throw new Error(\"An entity must be set without '&' and ';'. Eg. use '#xD' for '
'\")\n } else if (value === \"&\") {\n throw new Error(\"An entity with value '&' is not permitted\");\n } else {\n this.externalEntities[key] = value;\n }\n }\n\n /**\n * Returns a Symbol that can be used to access the metadata\n * property on a node.\n * \n * If Symbol is not available in the environment, an ordinary property is used\n * and the name of the property is here returned.\n * \n * The XMLMetaData property is only present when `captureMetaData`\n * is true in the options.\n */\n static getMetaDataSymbol() {\n return XmlNode.getMetaDataSymbol();\n }\n}","import { XMLParser } from 'fast-xml-parser';\n\nexport type ContentFeedFormat = 'rss' | 'atom';\n\nexport interface ParsedContentFeedItem {\n title: string;\n url: string;\n guid: string | null;\n author: string | null;\n publishedAt: Date | null;\n updatedAt: Date | null;\n summary: string | null;\n categories: string[];\n}\n\nexport interface ParsedContentFeed {\n format: ContentFeedFormat;\n title: string | null;\n homepageUrl: string | null;\n items: ParsedContentFeedItem[];\n}\n\nfunction asArray<T>(value: T | T[] | null | undefined): T[] {\n if (value === null || value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\nfunction textFromNode(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string' || typeof value === 'number') {\n return String(value).trim();\n }\n if (typeof value !== 'object') return '';\n\n const record = value as Record<string, unknown>;\n return String(record['#text'] ?? record.__cdata ?? record.href ?? '').trim();\n}\n\nfunction firstText(...values: unknown[]): string {\n for (const value of values) {\n const text = textFromNode(value);\n if (text) return text;\n }\n return '';\n}\n\nfunction parseDate(value: unknown): Date | null {\n const text = textFromNode(value);\n if (!text) return null;\n const parsed = new Date(text);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\nfunction stripHtml(value: string): string {\n return value\n .replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n .replace(/<style\\b[^<]*(?:(?!<\\/style>)<[^<]*)*<\\/style>/gi, '')\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/'/g, \"'\")\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction excerpt(value: unknown): string | null {\n const text = stripHtml(textFromNode(value));\n if (!text) return null;\n return text.length > 500 ? `${text.slice(0, 497).trim()}...` : text;\n}\n\nfunction parseCategories(value: unknown): string[] {\n return Array.from(\n new Set(\n asArray(value)\n .map((entry) => {\n if (entry && typeof entry === 'object') {\n return firstText(entry, (entry as Record<string, unknown>).term);\n }\n return textFromNode(entry);\n })\n .map((entry) => entry.trim())\n .filter(Boolean),\n ),\n );\n}\n\nfunction parseAuthor(value: unknown): string | null {\n if (Array.isArray(value)) return parseAuthor(value[0]);\n if (value && typeof value === 'object') {\n return firstText((value as Record<string, unknown>).name, value) || null;\n }\n return textFromNode(value) || null;\n}\n\nfunction resolveUrl(value: string, baseUrl?: string | null): string {\n if (!value) return '';\n if (!baseUrl) return value;\n\n try {\n return new URL(value, baseUrl).toString();\n } catch {\n return value;\n }\n}\n\nfunction parseRssLink(value: unknown, baseUrl?: string | null): string {\n if (value && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n return resolveUrl(\n firstText(record.href, record['#text'], record.__cdata),\n baseUrl,\n );\n }\n return resolveUrl(textFromNode(value), baseUrl);\n}\n\nfunction parseAtomLink(value: unknown, baseUrl?: string | null): string {\n const links = asArray(value);\n const alternate =\n links.find((entry) => {\n if (!entry || typeof entry !== 'object') return false;\n return (\n String((entry as Record<string, unknown>).rel ?? 'alternate') ===\n 'alternate'\n );\n }) ?? links[0];\n\n if (alternate && typeof alternate === 'object') {\n return resolveUrl(\n firstText((alternate as Record<string, unknown>).href, alternate),\n baseUrl,\n );\n }\n return resolveUrl(textFromNode(alternate), baseUrl);\n}\n\nfunction dedupeItems(items: ParsedContentFeedItem[]): ParsedContentFeedItem[] {\n const seen = new Set<string>();\n return items.filter((item) => {\n const key = item.guid || item.url;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nexport function parseContentFeed(\n xml: string,\n baseUrl?: string | null,\n): ParsedContentFeed {\n const parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '',\n cdataPropName: '__cdata',\n textNodeName: '#text',\n removeNSPrefix: true,\n trimValues: true,\n });\n // Parsed XML is an untyped node tree; every field is read through the\n // unknown-tolerant helpers above, so a loose record shape is sufficient.\n type FeedNode = Record<string, unknown>;\n const doc = parser.parse(xml) as {\n rss?: { channel?: FeedNode };\n channel?: FeedNode;\n feed?: FeedNode;\n };\n\n if (doc.rss?.channel || doc.channel) {\n const channel = (doc.rss?.channel ?? doc.channel) as FeedNode;\n return {\n format: 'rss',\n title: firstText(channel.title) || null,\n homepageUrl: parseRssLink(channel.link, baseUrl) || null,\n items: dedupeItems(\n asArray<FeedNode>(channel.item as FeedNode | FeedNode[] | undefined)\n .map(\n (item): ParsedContentFeedItem => ({\n title: firstText(item.title),\n url:\n parseRssLink(item.link, baseUrl) ||\n resolveUrl(firstText(item.guid), baseUrl),\n guid: firstText(item.guid, item.id) || null,\n author: parseAuthor(item.author ?? item.creator),\n publishedAt: parseDate(\n item.pubDate ?? item.published ?? item.date,\n ),\n updatedAt: parseDate(item.updated),\n summary: excerpt(\n item.description ?? item.summary ?? item.content,\n ),\n categories: parseCategories(item.category),\n }),\n )\n .filter((item) => item.title && item.url),\n ),\n };\n }\n\n if (doc.feed) {\n const feed = doc.feed;\n return {\n format: 'atom',\n title: firstText(feed.title) || null,\n homepageUrl: parseAtomLink(feed.link, baseUrl) || null,\n items: dedupeItems(\n asArray<FeedNode>(feed.entry as FeedNode | FeedNode[] | undefined)\n .map(\n (entry): ParsedContentFeedItem => ({\n title: firstText(entry.title),\n url: parseAtomLink(entry.link, baseUrl),\n guid: firstText(entry.id) || null,\n author: parseAuthor(entry.author),\n publishedAt: parseDate(entry.published ?? entry.updated),\n updatedAt: parseDate(entry.updated),\n summary: excerpt(entry.summary ?? entry.content),\n categories: parseCategories(entry.category),\n }),\n )\n .filter((item) => item.title && item.url),\n ),\n };\n }\n\n throw new Error('Unsupported feed format');\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentFeedFormat } from './content-feed-parser';\n\nexport type ContentFeedSourceStatus =\n | 'active'\n | 'paused'\n | 'error'\n | 'archived';\n\nexport interface ContentFeedSourceOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n name?: string;\n feedUrl?: string;\n homepageUrl?: string | null;\n format?: ContentFeedFormat | null;\n status?: ContentFeedSourceStatus;\n defaultCategory?: string | null;\n sourceGroup?: string | null;\n pollIntervalMinutes?: number;\n etag?: string | null;\n lastModified?: string | null;\n lastFetchedAt?: Date | string | null;\n lastSuccessAt?: Date | string | null;\n lastError?: string | null;\n metadata?: Record<string, unknown> | string;\n createdAt?: Date | string;\n updatedAt?: Date | string;\n}\n\nconst SOURCE_STATUSES = new Set<ContentFeedSourceStatus>([\n 'active',\n 'paused',\n 'error',\n 'archived',\n]);\n\nfunction parseDate(value: Date | string | null | undefined): Date | null {\n if (!value) return null;\n if (value instanceof Date) return value;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\nfunction parseMetadata(\n value: Record<string, unknown> | string | undefined,\n): Record<string, unknown> {\n if (!value) return {};\n if (typeof value === 'object') return structuredClone(value);\n\n try {\n const parsed = JSON.parse(value);\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction normalizePollIntervalMinutes(value: unknown): number {\n const parsed = Number(value);\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 15;\n}\n\nfunction normalizeStatus(value: unknown): ContentFeedSourceStatus {\n return typeof value === 'string' &&\n SOURCE_STATUSES.has(value as ContentFeedSourceStatus)\n ? (value as ContentFeedSourceStatus)\n : 'active';\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_feed_sources',\n conflictColumns: ['tenant_id', 'feed_url'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentFeedSource extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @field({ required: true })\n name = '';\n\n @field({ required: true })\n feedUrl = '';\n\n homepageUrl: string | null = null;\n format: ContentFeedFormat | null = null;\n @field({ type: 'text', required: true, default: 'active' })\n status: ContentFeedSourceStatus = 'active';\n defaultCategory: string | null = null;\n sourceGroup: string | null = null;\n pollIntervalMinutes = 15;\n etag: string | null = null;\n lastModified: string | null = null;\n lastFetchedAt: Date | null = null;\n lastSuccessAt: Date | null = null;\n lastError: string | null = null;\n metadata: Record<string, unknown> = {};\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentFeedSourceOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.name !== undefined) this.name = options.name;\n if (options.feedUrl !== undefined) this.feedUrl = options.feedUrl;\n if (options.homepageUrl !== undefined)\n this.homepageUrl = options.homepageUrl;\n if (options.format !== undefined) this.format = options.format;\n if (options.status !== undefined)\n this.status = normalizeStatus(options.status);\n if (options.defaultCategory !== undefined)\n this.defaultCategory = options.defaultCategory;\n if (options.sourceGroup !== undefined)\n this.sourceGroup = options.sourceGroup;\n if (options.pollIntervalMinutes !== undefined) {\n this.pollIntervalMinutes = normalizePollIntervalMinutes(\n options.pollIntervalMinutes,\n );\n }\n if (options.etag !== undefined) this.etag = options.etag;\n if (options.lastModified !== undefined)\n this.lastModified = options.lastModified;\n if (options.lastFetchedAt !== undefined)\n this.lastFetchedAt = parseDate(options.lastFetchedAt);\n if (options.lastSuccessAt !== undefined)\n this.lastSuccessAt = parseDate(options.lastSuccessAt);\n if (options.lastError !== undefined) this.lastError = options.lastError;\n if (options.metadata !== undefined)\n this.metadata = parseMetadata(options.metadata);\n if (options.createdAt !== undefined)\n this.createdAt = parseDate(options.createdAt) ?? new Date();\n if (options.updatedAt !== undefined)\n this.updatedAt = parseDate(options.updatedAt) ?? new Date();\n }\n\n getMetadata(): Record<string, unknown> {\n return structuredClone(this.metadata);\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = structuredClone(metadata);\n this.updatedAt = new Date();\n }\n\n markFetchStarted(at = new Date()): void {\n this.lastFetchedAt = at;\n this.updatedAt = at;\n }\n\n markFetchSucceeded(\n at = new Date(),\n headers: { etag?: string | null; lastModified?: string | null } = {},\n ): void {\n this.status = 'active';\n this.lastFetchedAt = at;\n this.lastSuccessAt = at;\n this.lastError = null;\n if (headers.etag !== undefined) this.etag = headers.etag;\n if (headers.lastModified !== undefined)\n this.lastModified = headers.lastModified;\n this.updatedAt = at;\n }\n\n markFetchFailed(error: unknown, at = new Date()): void {\n this.status = 'error';\n this.lastFetchedAt = at;\n this.lastError = error instanceof Error ? error.message : String(error);\n this.updatedAt = at;\n }\n\n protected override transformJSON(\n json: Record<string, unknown>,\n ): Record<string, unknown> {\n return {\n ...json,\n tenantId: this.tenantId,\n name: this.name,\n feedUrl: this.feedUrl,\n homepageUrl: this.homepageUrl,\n format: this.format,\n status: this.status,\n defaultCategory: this.defaultCategory,\n sourceGroup: this.sourceGroup,\n pollIntervalMinutes: this.pollIntervalMinutes,\n etag: this.etag,\n lastModified: this.lastModified,\n lastFetchedAt: this.lastFetchedAt?.toISOString() ?? null,\n lastSuccessAt: this.lastSuccessAt?.toISOString() ?? null,\n lastError: this.lastError,\n metadata: this.metadata,\n createdAt: this.createdAt.toISOString(),\n updatedAt: this.updatedAt.toISOString(),\n };\n }\n}\n","import type { SmrtCollectionOptions } from '@happyvertical/smrt-core';\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n ContentFeedSource,\n type ContentFeedSourceStatus,\n} from './content-feed-source';\n\nexport interface ContentFeedSourceCollectionOptions\n extends SmrtCollectionOptions {}\n\nexport class ContentFeedSourceCollection extends SmrtCollection<ContentFeedSource> {\n static readonly _itemClass = ContentFeedSource;\n\n async findActive(tenantId?: string | null): Promise<ContentFeedSource[]> {\n const where: Record<string, unknown> = { status: 'active' };\n if (tenantId !== undefined) where.tenantId = tenantId;\n return this.list({ where, orderBy: 'name ASC' });\n }\n\n async findByStatus(\n status: ContentFeedSourceStatus,\n tenantId?: string | null,\n ): Promise<ContentFeedSource[]> {\n const where: Record<string, unknown> = { status };\n if (tenantId !== undefined) where.tenantId = tenantId;\n return this.list({ where, orderBy: 'name ASC' });\n }\n\n async findByFeedUrl(\n feedUrl: string,\n tenantId?: string | null,\n ): Promise<ContentFeedSource | null> {\n const where: Record<string, unknown> = { feedUrl };\n if (tenantId !== undefined) where.tenantId = tenantId;\n const rows = await this.list({ where, limit: 1 });\n return rows[0] ?? null;\n }\n}\n","/**\n * Content subclasses for STI (Single Table Inheritance)\n *\n * These classes inherit from Content and automatically use the shared\n * contents table with _meta_type discriminator.\n */\n\nimport { foreignKey, smrt } from '@happyvertical/smrt-core';\nimport { Content, type ContentOptions } from './content';\n\n/**\n * Article content type\n *\n * Represents editorial content like blog posts, news articles, and written pieces.\n */\n@smrt({\n tableStrategy: 'sti',\n api: false,\n mcp: false,\n cli: false,\n})\nexport class Article extends Content {\n constructor(options: ContentOptions = {}) {\n super(options);\n // Article-specific initialization can go here\n }\n}\n\n/**\n * ContentDocument content type\n *\n * Represents structured documents like PDFs, reports, and technical documentation.\n */\n@smrt({\n tableStrategy: 'sti',\n api: false,\n mcp: false,\n cli: false,\n})\nexport class ContentDocument extends Content {\n constructor(options: ContentOptions = {}) {\n super(options);\n // ContentDocument-specific initialization can go here\n }\n}\n\nexport interface MirrorOptions extends ContentOptions {\n feedSourceId?: string | null;\n sourceGuid?: string | null;\n sourceName?: string | null;\n sourceHomepageUrl?: string | null;\n externalUrl?: string | null;\n originalPublishedAt?: Date | string | null;\n dedupeKey?: string | null;\n}\n\nfunction parseOptionalDate(\n value: Date | string | null | undefined,\n): Date | null {\n if (!value) return null;\n if (value instanceof Date) return value;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\n/**\n * Mirror content type\n *\n * Represents mirrored/cached content from external feed or web sources.\n */\n@smrt({\n tableStrategy: 'sti',\n api: false,\n mcp: false,\n cli: false,\n})\nexport class Mirror extends Content {\n @foreignKey('ContentFeedSource')\n feedSourceId: string | null = null;\n sourceGuid: string | null = null;\n sourceName: string | null = null;\n sourceHomepageUrl: string | null = null;\n externalUrl: string | null = null;\n originalPublishedAt: Date | null = null;\n dedupeKey: string | null = null;\n\n constructor(options: MirrorOptions = {}) {\n super(options);\n if (options.feedSourceId !== undefined)\n this.feedSourceId = options.feedSourceId;\n if (options.sourceGuid !== undefined) this.sourceGuid = options.sourceGuid;\n if (options.sourceName !== undefined) this.sourceName = options.sourceName;\n if (options.sourceHomepageUrl !== undefined)\n this.sourceHomepageUrl = options.sourceHomepageUrl;\n if (options.externalUrl !== undefined)\n this.externalUrl = options.externalUrl;\n if (options.originalPublishedAt !== undefined) {\n this.originalPublishedAt = parseOptionalDate(options.originalPublishedAt);\n }\n if (options.dedupeKey !== undefined) this.dedupeKey = options.dedupeKey;\n }\n\n protected override transformJSON(json: Record<string, unknown>) {\n return {\n ...json,\n feedSourceId: this.feedSourceId,\n sourceGuid: this.sourceGuid,\n sourceName: this.sourceName,\n sourceHomepageUrl: this.sourceHomepageUrl,\n externalUrl: this.externalUrl,\n originalPublishedAt: this.originalPublishedAt?.toISOString() ?? null,\n dedupeKey: this.dedupeKey,\n };\n }\n}\n","/**\n * Bounded, tenant-safe content data queries (#2452) over the canonical\n * transport-neutral query protocol (#2444).\n *\n * `ContentList` (and any other consumer) sends a `DataQueryRequest`; this\n * module normalizes it against a schema derived from the registered `Content`\n * field metadata, executes it through `SmrtCollection.list/count/facets` — never\n * raw SQL, never a full collection hydration — and returns a validated\n * `DataQueryResult`.\n *\n * Three independent boundaries protect a read:\n *\n * 1. **Schema** — `buildContentQuerySchema()` declares the only field ids a\n * caller may name. `sensitive`, `readPermission`-gated, transient,\n * non-column-backed, tenant, and internal (`_`-prefixed) fields are never\n * declared, so `normalizeDataQueryRequest()` rejects them outright.\n * 2. **Collection** — every projection, order term, and predicate still passes\n * through `SmrtCollection`, which independently refuses sensitive and\n * permission-gated fields. A schema bug alone cannot expose a field.\n * 3. **Scope** — trusted, server-derived conditions are ANDed into every branch\n * of the caller's filter, so a request can only ever narrow the read.\n */\n\nimport {\n createDataQueryFingerprint,\n DataQueryValidationError,\n normalizeDataQueryRequest,\n normalizeDataQueryResult,\n normalizeDataQuerySchema,\n ObjectRegistry,\n} from '@happyvertical/smrt-core';\nimport {\n getCurrentTenant,\n isSuperAdminBypass,\n isSystemContext,\n isTenancyEnabled,\n} from '@happyvertical/smrt-tenancy';\nimport type {\n DataQueryFacetResult,\n DataQueryFieldDescriptor,\n DataQueryFilter,\n DataQueryFilterOperator,\n DataQueryRequest,\n DataQueryResult,\n DataQueryRow,\n DataQuerySchema,\n DataQuerySort,\n} from '@happyvertical/smrt-types';\n\n/** Registered qualified name of the STI base every content query reads. */\nexport const CONTENT_QUERY_CLASS_NAME = '@happyvertical/smrt-content:Content';\n\n/** Row identity for every content query. Never a page or display index. */\nexport const CONTENT_QUERY_IDENTITY_FIELD = 'id';\n\n/**\n * Deterministic default ordering: most recently updated first, tie-broken by\n * id. `updated_at` is chosen over `publish_date` deliberately — it is always\n * populated, so ordering never depends on engine-specific NULL placement, and\n * it matches the ordering the rest of the `Contents` collection already uses.\n */\nexport const CONTENT_QUERY_DEFAULT_SORT: DataQuerySort[] = [\n { field: 'updated_at', direction: 'desc' },\n { field: CONTENT_QUERY_IDENTITY_FIELD, direction: 'asc' },\n];\n\n/** Page bounds. Content rows can carry long text, so the ceiling is modest. */\nexport const CONTENT_QUERY_DEFAULT_PAGE_LIMIT = 50;\nexport const CONTENT_QUERY_MAX_PAGE_LIMIT = 200;\nexport const CONTENT_QUERY_MAX_RESULT_BYTES = 1_000_000;\n\n/**\n * Fields the content query never declares even though they are column-backed.\n *\n * `body` is a document, not list data: the canonical envelope caps a scalar at\n * {@link DATA_QUERY_MAX_STRING_LENGTH} characters, so a real body could only\n * ever be returned mangled. Read a body through the item route\n * (`GET /api/v1/contents/{id}`), which serializes it in full.\n */\nexport const CONTENT_QUERY_EXCLUDED_FIELD_IDS: readonly string[] = ['body'];\n\n/**\n * The protocol's hard scalar cap (`dataQueryScalar` in\n * `@happyvertical/smrt-core`): a string value longer than this makes the whole\n * result invalid, so long values are truncated and flagged instead.\n */\nexport const DATA_QUERY_MAX_STRING_LENGTH = 4_096;\n\n/**\n * The protocol's limits for a `json` field, mirrored from\n * `canonicalJson` in `@happyvertical/smrt-core`. Exceeding any of them makes\n * the whole result invalid rather than the one value, so the adapter bounds a\n * JSON document itself (see `boundJsonValue`).\n */\nexport const DATA_QUERY_MAX_JSON_STRING_LENGTH = 65_536;\nexport const DATA_QUERY_MAX_JSON_CONTAINER_ITEMS = 1_000;\nexport const DATA_QUERY_MAX_JSON_DEPTH = 16;\n\n/**\n * Keys `plainObject` refuses outright (`FORBIDDEN_DATA_QUERY`), mirrored from\n * `@happyvertical/smrt-core`. This is a *validity* rule rather than a size\n * limit, and it is the reachable one: `metadata` is the documented extension\n * point, it is writable through the generated REST API and through\n * `Content.mirror()` ingestion, and `JSON.parse` of the stored column creates\n * an own `__proto__` property. One row carrying such a key would otherwise make\n * every query projecting that field return 400 for the whole page, with no way\n * to page past it.\n */\nexport const DATA_QUERY_FORBIDDEN_JSON_KEYS: ReadonlySet<string> = new Set([\n '__proto__',\n 'constructor',\n 'prototype',\n]);\n\n/**\n * Bytes held back from the row budget for the envelope itself (request id,\n * fingerprint, page, total, freshness, warnings). The normalizer re-checks the\n * complete serialized envelope against `maxResultBytes`, so the row budget must\n * leave room for it.\n */\nexport const RESULT_ENVELOPE_RESERVE_BYTES = 4_096;\n\n/**\n * Smallest row allowance a page can be given and still answer with anything.\n * Comfortably holds one identity-only row plus a few projected scalars.\n */\nconst MIN_RESULT_ROW_BYTES = 512;\n\n/**\n * The smallest `maxResultBytes` a schema may declare.\n *\n * The row budget is `maxResultBytes` minus the envelope reserve, so a schema\n * below the reserve leaves nothing for rows: every query would answer with an\n * empty page flagged `truncated`, forever, and a page small enough that the\n * metadata alone overruns the budget would fail `normalizeDataQueryResult`\n * outright. Both look like \"no content matched\" from the outside.\n *\n * `schema` is trusted adapter configuration, never caller input, so a budget\n * this small is a deployment mistake rather than a bad request — and it is\n * refused loudly at the boundary rather than degrading every read.\n */\nexport const CONTENT_QUERY_MIN_RESULT_BYTES =\n RESULT_ENVELOPE_RESERVE_BYTES + MIN_RESULT_ROW_BYTES;\n\n/**\n * Refuse a result budget too small to carry an envelope and a row.\n *\n * Throws a plain `Error`, not a `DataQueryValidationError`: a validation error\n * becomes a 400 and tells the caller they asked for something wrong, when the\n * fault is in the host's own schema. This mirrors how an unusable `scope` is\n * refused.\n *\n * Applied uniformly across query modes, including `count`, which returns no\n * rows and would technically work. A schema too small to serve its own row\n * mode is misconfigured whatever this particular request asked for, and letting\n * `count` succeed would hide that until the first rows query.\n */\nfunction assertUsableResultBudget(schema: DataQuerySchema): void {\n const budget = schema.maxResultBytes ?? CONTENT_QUERY_MAX_RESULT_BYTES;\n if (budget >= CONTENT_QUERY_MIN_RESULT_BYTES) return;\n throw new Error(\n `Content query schema maxResultBytes must be at least ${CONTENT_QUERY_MIN_RESULT_BYTES} ` +\n `(${RESULT_ENVELOPE_RESERVE_BYTES} reserved for the result envelope, ` +\n `${MIN_RESULT_ROW_BYTES} for rows); received ${budget}.`,\n );\n}\n\n/**\n * Upper bound on OR branches handed to the collection query builder.\n *\n * Exported so a client can mirror it: the null-safe `ne`/`notIn` lowering below\n * turns one predicate into two branches, and an `all` of them multiplies, so a\n * caller has to be able to stop short of the ceiling rather than be refused.\n */\nexport const MAX_CONTENT_QUERY_OR_BRANCHES = 128;\n\nconst encoder = new TextEncoder();\n\n/** One AND-ed group of SMRT `where` conditions. */\ntype WhereCondition = Record<string, unknown>;\n\n/** Bounded disjunctive-normal-form `where`: outer OR of inner AND groups. */\ntype WhereDnf = WhereCondition[][];\n\n/**\n * The subset of `SmrtCollection` a content query needs. Structural so this\n * module never imports `Contents` (which imports this one) and so a host can\n * supply an application-owned collection that preserves the same boundary.\n */\nexport interface ContentQueryCollection {\n list(options: {\n select?: readonly string[];\n where?: WhereCondition | WhereDnf;\n offset?: number;\n limit?: number;\n orderBy?: string | string[];\n }): Promise<Record<string, unknown>[]>;\n count(options?: { where?: WhereCondition | WhereDnf }): Promise<number>;\n facets(options: {\n fields: readonly { field: string; limit?: number }[];\n where?: WhereCondition | WhereDnf;\n }): Promise<{ field: string; values: { value: unknown; count: number }[] }[]>;\n}\n\n/**\n * Trusted, server-derived narrowing conditions.\n *\n * Each entry is a plain SMRT `where` condition object (`{ status: 'published' }`,\n * `{ 'publish_date <=': someInstant }`, `{ category: ['news', 'sport'] }`).\n * Every condition is ANDed into **every** OR branch of the caller's filter.\n */\nexport type ContentQueryScope = WhereCondition | readonly WhereCondition[];\n\nexport interface ContentQueryOptions {\n /**\n * Application-supplied narrowing conditions derived from the authenticated\n * server-side context — never from the request body.\n *\n * This is how an application expresses site, organization, workspace, or\n * ownership scoping. The framework deliberately does not model site or\n * organization: `Content` carries tenancy plus a freeform `metadata` blob and\n * nothing else, so the host that knows what \"site\" means for its deployment\n * passes the conditions that mean it here (a subclass column, a denormalized\n * id column, a pre-resolved id list, and so on).\n *\n * SECURITY INVARIANT: scope may only come from trusted server-side context. A\n * `DataQueryRequest` has no way to supply, replace, widen, or remove a scope\n * condition — scope conditions are ANDed into every branch of the caller's\n * filter, including inside `any`/`not` branches, so a request can only ever\n * narrow the result set. Never derive `scope` from client input.\n *\n * `undefined` and `[]` mean OPPOSITE things. Omit the option to apply no\n * application scope; pass an empty array to say the principal is permitted\n * nothing, which matches no rows. A host deriving the scope from an\n * allowed-resource list gets the safe answer by construction: an empty list\n * denies instead of unlocking the whole tenant.\n */\n scope?: ContentQueryScope;\n /**\n * Trusted adapter policy override. Defaults to the memoized `Content` schema.\n * This is adapter configuration, never caller input; it exists so a host (or\n * a test) can execute the same bounded protocol against another registered\n * class. Supplying a schema does NOT relax the collection-level field checks.\n */\n schema?: DataQuerySchema;\n}\n\n/**\n * Validate a host-supplied schema before anything depends on it.\n *\n * `executeContentQuery` performs the same checks on every request, so a\n * misconfigured schema can never actually serve a query — but a request-path\n * failure reaches the caller as an opaque 500 through the generated route, with\n * the message that names the minimum only in the server log. Call this once\n * where the schema is configured, so the failure lands next to the mistake:\n *\n * ```ts\n * const schema = buildAdminContentSchema();\n * assertContentQuerySchema(schema); // at startup, not on the first request\n * ```\n *\n * Covers core's own schema rules as well as this adapter's, so one call is\n * enough.\n */\nexport function assertContentQuerySchema(schema: DataQuerySchema): void {\n normalizeDataQuerySchema(schema);\n assertUsableResultBudget(schema);\n}\n\n/**\n * Resolve the fail-closed tenant read scope for a content query.\n *\n * Mirrors the generated route helpers `tenantReadScope()` /\n * `tenantReadOptionsScope()`: with tenancy enabled and no active tenant\n * context, reads are restricted to NULL-tenant (global) rows rather than\n * passing through unfiltered, because `Content` is\n * `@TenantScoped({ mode: 'optional' })` and the interceptor alone would not\n * filter an anonymous read. `withSystemContext()` and super-admin bypass remain\n * the explicit, deliberate cross-tenant paths.\n */\nexport function resolveContentTenantReadScope():\n | { tenantId: string | null }\n | undefined {\n if (!isTenancyEnabled()) return undefined;\n if (isSuperAdminBypass() || isSystemContext()) return undefined;\n return { tenantId: getCurrentTenant()?.tenantId ?? null };\n}\n\nfunction queryFail(message: string, code = 'INVALID_DATA_QUERY'): never {\n throw new DataQueryValidationError(message, code);\n}\n\nfunction isPlainRecord(value: unknown): value is WhereCondition {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction queryFieldType(\n type: unknown,\n): DataQueryFieldDescriptor['type'] | undefined {\n switch (type) {\n case 'text':\n case 'foreignKey':\n case 'crossPackageRef':\n return 'string';\n case 'integer':\n case 'decimal':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'datetime':\n return 'datetime';\n case 'json':\n return 'json';\n // `meta`, `oneToMany`, `manyToMany`, and anything a future scanner adds are\n // not column-backed scalars. Fail closed by leaving them undeclared.\n default:\n return undefined;\n }\n}\n\nfunction filterOperatorsFor(\n type: DataQueryFieldDescriptor['type'],\n): DataQueryFilterOperator[] | undefined {\n switch (type) {\n case 'string':\n return ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn', 'like'];\n case 'number':\n case 'datetime':\n return ['eq', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn'];\n case 'boolean':\n return ['eq', 'ne', 'in', 'notIn'];\n // JSON columns store serialized documents; there is no portable predicate\n // for them at the collection boundary, so they stay unfilterable.\n case 'json':\n return undefined;\n }\n}\n\ninterface RegistryFieldLike {\n type?: unknown;\n sensitive?: unknown;\n readPermission?: unknown;\n transient?: unknown;\n _meta?: Record<string, unknown>;\n __tenancy?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nfunction meta(field: RegistryFieldLike): Record<string, unknown> {\n return isPlainRecord(field._meta) ? field._meta : {};\n}\n\n/** `sensitive`/`readPermission` may be declared top-level or under `_meta`. */\nfunction isRestrictedField(field: RegistryFieldLike): boolean {\n const fieldMeta = meta(field);\n return (\n field.sensitive === true ||\n fieldMeta.sensitive === true ||\n typeof field.readPermission === 'string' ||\n typeof fieldMeta.readPermission === 'string'\n );\n}\n\nfunction isTransientField(field: RegistryFieldLike): boolean {\n return field.transient === true || meta(field).transient === true;\n}\n\nfunction isTenantField(name: string, field: RegistryFieldLike): boolean {\n const fieldMeta = meta(field);\n const tenancy = isPlainRecord(field.__tenancy)\n ? field.__tenancy\n : isPlainRecord(fieldMeta.__tenancy)\n ? fieldMeta.__tenancy\n : undefined;\n return (\n tenancy?.isTenantIdField === true ||\n name === 'tenantId' ||\n name === 'tenant_id'\n );\n}\n\n/**\n * Build a `DataQuerySchema` from registered field metadata.\n *\n * Excluded, and therefore un-nameable by any caller:\n * - `sensitive` and `readPermission`-gated fields (exposure boundary);\n * - transient and non-column-backed fields (`meta`, `oneToMany`, `manyToMany`);\n * - the tenant field — tenancy is enforced by the executor, and a caller must\n * never be able to filter, sort, project, or facet on it;\n * - internal `_`-prefixed fields such as the STI discriminator.\n */\nasync function buildQuerySchemaForClass(\n qualifiedName: string,\n excluded: ReadonlySet<string>,\n): Promise<DataQuerySchema> {\n const registered = (await ObjectRegistry.getAllFields(qualifiedName)) as Map<\n string,\n RegistryFieldLike\n >;\n const fields: DataQueryFieldDescriptor[] = [];\n for (const [name, field] of registered) {\n if (name.startsWith('_')) continue;\n if (excluded.has(name)) continue;\n if (isRestrictedField(field)) continue;\n if (isTransientField(field)) continue;\n if (isTenantField(name, field)) continue;\n const type = queryFieldType(field.type);\n if (!type) continue;\n const filterOperators = filterOperatorsFor(type);\n fields.push({\n id: name,\n type,\n projectable: true,\n // JSON documents have no portable ordering at the SQL layer.\n sortable: type !== 'json',\n // Facets group by the stored column: only bounded scalar domains are\n // useful, and the identity field is unique by definition.\n facetable:\n name !== CONTENT_QUERY_IDENTITY_FIELD &&\n (type === 'string' || type === 'boolean' || type === 'number'),\n ...(filterOperators ? { filterOperators } : {}),\n });\n }\n\n const identity = fields.find(\n (field) => field.id === CONTENT_QUERY_IDENTITY_FIELD,\n );\n if (!identity) {\n throw new Error(\n `${qualifiedName} does not declare a queryable '${CONTENT_QUERY_IDENTITY_FIELD}' field`,\n );\n }\n\n const declared = new Set(fields.map((field) => field.id));\n const defaultSort = CONTENT_QUERY_DEFAULT_SORT.filter((term) =>\n declared.has(term.field),\n );\n\n return {\n version: 1,\n identityField: CONTENT_QUERY_IDENTITY_FIELD,\n fields,\n defaultPageLimit: CONTENT_QUERY_DEFAULT_PAGE_LIMIT,\n maxPageLimit: CONTENT_QUERY_MAX_PAGE_LIMIT,\n maxResultBytes: CONTENT_QUERY_MAX_RESULT_BYTES,\n ...(defaultSort.length > 0 ? { defaultSort } : {}),\n supports: {\n // Offset paging only: cursor paging would need an opaque, query-bound\n // cursor the collection read path does not issue today.\n cursorPagination: false,\n // Live table reads; no snapshot or as-of capability.\n consistency: false,\n facets: true,\n },\n };\n}\n\nconst schemaCache = new Map<string, Promise<DataQuerySchema>>();\n\n/**\n * Memoized query schema for one registered class (keyed by qualified name).\n * The schema is derived from immutable registration metadata, so it is built\n * once per process rather than per request.\n */\nexport function buildDataQuerySchemaForClass(\n qualifiedName: string,\n options: { exclude?: readonly string[] } = {},\n): Promise<DataQuerySchema> {\n const excluded = [...new Set(options.exclude ?? [])].sort();\n const key = `${qualifiedName}::${excluded.join(',')}`;\n const cached = schemaCache.get(key);\n if (cached) return cached;\n const pending = buildQuerySchemaForClass(\n qualifiedName,\n new Set(excluded),\n ).catch((cause) => {\n schemaCache.delete(key);\n throw cause;\n });\n schemaCache.set(key, pending);\n return pending;\n}\n\n/** Memoized bounded query schema for `Content`. */\nexport function buildContentQuerySchema(): Promise<DataQuerySchema> {\n return buildDataQuerySchemaForClass(CONTENT_QUERY_CLASS_NAME, {\n exclude: CONTENT_QUERY_EXCLUDED_FIELD_IDS,\n });\n}\n\n/** Testing seam: drop memoized schemas so a rebuild re-reads the registry. */\nexport function clearContentQuerySchemaCache(): void {\n schemaCache.clear();\n}\n\nfunction inverseOperator(\n operator: DataQueryFilterOperator,\n): DataQueryFilterOperator {\n switch (operator) {\n case 'eq':\n return 'ne';\n case 'ne':\n return 'eq';\n case 'gt':\n return 'lte';\n case 'gte':\n return 'lt';\n case 'lt':\n return 'gte';\n case 'lte':\n return 'gt';\n case 'in':\n return 'notIn';\n case 'notIn':\n return 'in';\n case 'like':\n return queryFail(\n 'Content queries cannot negate a like predicate',\n 'DATA_QUERY_UNSUPPORTED',\n );\n }\n}\n\n/**\n * Lower one condition to bounded DNF.\n *\n * `negated` says the condition was reached through an odd number of `not`s and\n * its operator has already been inverted by {@link inverseOperator}. It matters\n * only for the ordered comparisons: `lte` asked for directly must exclude a\n * NULL row, exactly as SQL and the local evaluator both do, while the `lte`\n * that `not(gt)` produces must INCLUDE it or the predicate and its negation are\n * not complements and a row with no value falls through both.\n */\nfunction conditionToDnf(\n field: string,\n operator: DataQueryFilterOperator,\n value: unknown,\n negated = false,\n): WhereDnf {\n const key = (suffix: string) => (suffix ? `${field} ${suffix}` : field);\n const single = (whereKey: string, whereValue: unknown): WhereDnf => [\n [{ [whereKey]: whereValue }],\n ];\n\n if (operator === 'in') {\n const values = (value as unknown[]) ?? [];\n const nonNull = values.filter((entry) => entry !== null);\n if (nonNull.length === 0) return single(field, null);\n if (nonNull.length === values.length) return single(key('in'), nonNull);\n // SQL `IN` never matches NULL; model the caller-visible union explicitly.\n return [[{ [field]: null }], [{ [key('in')]: nonNull }]];\n }\n\n if (operator === 'notIn') {\n const values = (value as unknown[]) ?? [];\n if (values.length === 0) {\n // The normalizer refuses an empty list, so this is unreachable; failing\n // is still the only safe answer, because \"excludes nothing\" would have to\n // be an unbounded OR branch.\n return queryFail(\n 'Content query notIn requires at least one value',\n 'DATA_QUERY_UNSUPPORTED',\n );\n }\n // `buildWhere()` has no NOT IN primitive. A bounded AND of inequalities has\n // the same null-safe semantics and stays fully validated by the collection.\n const inequalities = values\n .filter((entry) => entry !== null)\n .map((entry) => ({ [key('!=')]: entry }));\n if (values.some((entry) => entry === null)) {\n // A listed `null` says \"rows with no value are excluded too\", so the\n // null-safe union below must NOT be added — it would return exactly the\n // rows the caller asked to exclude, and would make `in [x, null]` and its\n // negation overlap. `{ field '!=': null }` is `IS NOT NULL`.\n return [[...inequalities, { [key('!=')]: null }]];\n }\n // No `null` was listed. SQL's `<>` is UNKNOWN for NULL, so a bare AND of\n // inequalities silently excludes rows with no value at all — while the\n // caller-visible meaning of \"not one of these\" includes them, and the local\n // evaluator agrees. Model that union explicitly, exactly as `in` does\n // above, so the same shared link returns the same rows whether the list is\n // server-backed or not.\n return [[{ [field]: null }], inequalities];\n }\n\n if (operator === 'ne' && value !== null) {\n // Same reasoning as `notIn`. A `ne null` is left alone: it is the\n // `isNotNull` predicate, and unioning IS NULL into it would match every row.\n return [[{ [field]: null }], [{ [key('!=')]: value }]];\n }\n\n const suffixes: Record<\n Exclude<DataQueryFilterOperator, 'in' | 'notIn'>,\n string\n > = {\n eq: '',\n ne: '!=',\n gt: '>',\n gte: '>=',\n lt: '<',\n lte: '<=',\n like: 'like',\n };\n\n if (\n negated &&\n (operator === 'gt' ||\n operator === 'gte' ||\n operator === 'lt' ||\n operator === 'lte')\n ) {\n // The complement of an ordered comparison. SQL's `>`/`<` are UNKNOWN for\n // NULL, so `not(gt v)` lowered as a bare `<= v` leaves a row with no value\n // matching NEITHER side — the same gap `ne`/`notIn` close above. `eq`\n // reached by negating `ne` gets no union: the complement of\n // \"IS NULL OR <> v\" is \"= v\", which excludes NULL by construction.\n return [[{ [field]: null }], [{ [key(suffixes[operator])]: value }]];\n }\n\n return single(key(suffixes[operator]), value);\n}\n\nfunction crossProduct(left: WhereDnf, right: WhereDnf): WhereDnf {\n if (left.length * right.length > MAX_CONTENT_QUERY_OR_BRANCHES) {\n return queryFail(\n `Content query filter expands beyond ${MAX_CONTENT_QUERY_OR_BRANCHES} OR branches`,\n 'DATA_QUERY_UNSUPPORTED',\n );\n }\n return left.flatMap((leftGroup) =>\n right.map((rightGroup) => [...leftGroup, ...rightGroup]),\n );\n}\n\nfunction filterToDnf(\n filter: DataQueryFilter,\n declared: ReadonlySet<string>,\n negate = false,\n): WhereDnf {\n if (filter.kind === 'condition') {\n // The normalizer already rejected undeclared fields; re-check so a schema\n // and an executor can never disagree about what is queryable.\n if (!declared.has(filter.field)) {\n return queryFail(\n `Content query filter field is not declared: ${filter.field}`,\n 'DATA_QUERY_FILTER_NOT_ALLOWED',\n );\n }\n return conditionToDnf(\n filter.field,\n negate ? inverseOperator(filter.operator) : filter.operator,\n filter.value,\n negate,\n );\n }\n\n if (filter.kind === 'not') {\n return filterToDnf(filter.filter, declared, !negate);\n }\n\n // De Morgan: a negated `any` behaves as an `all` of negated children.\n const combineWithAnd =\n (filter.kind === 'all' && !negate) || (filter.kind === 'any' && negate);\n if (combineWithAnd) {\n return filter.filters.reduce<WhereDnf>(\n (combined, child) =>\n crossProduct(combined, filterToDnf(child, declared, negate)),\n [[]],\n );\n }\n\n const branches = filter.filters.flatMap((child) =>\n filterToDnf(child, declared, negate),\n );\n if (branches.length > MAX_CONTENT_QUERY_OR_BRANCHES) {\n return queryFail(\n `Content query filter expands beyond ${MAX_CONTENT_QUERY_OR_BRANCHES} OR branches`,\n 'DATA_QUERY_UNSUPPORTED',\n );\n }\n return branches;\n}\n\nfunction normalizeScopeConditions(\n scope: ContentQueryScope | undefined,\n): WhereCondition[] {\n if (scope === undefined) return [];\n const candidates = Array.isArray(scope)\n ? (scope as readonly unknown[])\n : [scope];\n return candidates.map((candidate) => {\n if (!isPlainRecord(candidate) || Object.keys(candidate).length === 0) {\n throw new Error(\n 'Content query scope conditions must be non-empty plain objects',\n );\n }\n return { ...candidate };\n });\n}\n\n/**\n * A scope condition no row can satisfy.\n *\n * `id` is the primary key, so it is never NULL and `id IS NULL` is false for\n * every row on every dialect — the portable way to say \"permit nothing\" through\n * a `where` clause rather than by special-casing the read path.\n */\nconst DENY_ALL_SCOPE_CONDITION: WhereCondition = Object.freeze({\n [CONTENT_QUERY_IDENTITY_FIELD]: null,\n});\n\n/**\n * Normalize the APPLICATION scope, where an explicitly empty set denies.\n *\n * The two absences are not the same thing, and conflating them is an\n * authorization fail-open:\n *\n * - `undefined` means \"this deployment applies no application scope\" — read\n * the tenant, subject to tenancy alone.\n * - `[]` means \"the set of conditions this principal is permitted is EMPTY\".\n * A host builds a scope from an allowed-resource list — the sites,\n * workspaces, or organizations this principal may see — and that list is\n * empty exactly when the principal may see nothing. Treating it as \"no\n * scope\" turns *access to zero sites* into *access to every row in the\n * tenant*, which is the failure the two-layer scope design exists to\n * prevent.\n *\n * An empty set is a legitimate authorization state, not a programming error, so\n * it lowers to a predicate that matches nothing rather than throwing — a throw\n * would answer a correct \"you may see nothing\" with a 500.\n */\nfunction normalizeApplicationScope(\n scope: ContentQueryScope | undefined,\n): WhereCondition[] {\n if (scope === undefined) return [];\n const conditions = normalizeScopeConditions(scope);\n return conditions.length > 0 ? conditions : [{ ...DENY_ALL_SCOPE_CONDITION }];\n}\n\n/**\n * AND every trusted scope condition into every OR branch of the caller's\n * filter.\n *\n * This is the whole widening story: a branch can only ever gain conditions, and\n * conjunction is monotonically narrowing, so no filter shape — including\n * `any` (OR) and `not` (negation) — can produce a branch that escapes the base\n * scope. A caller predicate on a scoped field can contradict the scope (and\n * return nothing); it can never replace it.\n *\n * Returns `undefined` only when there is neither a scope nor a filter, so the\n * collection sees a plain unfiltered read rather than an empty DNF branch.\n *\n * An explicitly EMPTY scope denies rather than passing through; see\n * {@link normalizeApplicationScope}. Pass `undefined`, not `[]`, to mean \"no\n * application scope\".\n */\nexport function mergeContentQueryScope(\n scope: ContentQueryScope | undefined,\n callerWhere: WhereDnf | undefined,\n): WhereDnf | undefined {\n const scopeConditions = normalizeApplicationScope(scope);\n const branches: WhereDnf =\n callerWhere && callerWhere.length > 0 ? callerWhere : [[]];\n const merged = branches.map((branch) => [...scopeConditions, ...branch]);\n if (merged.length === 1 && merged[0].length === 0) return undefined;\n if (merged.some((branch) => branch.length === 0)) {\n // An empty OR branch matches every row, which would widen the read.\n return queryFail(\n 'Content query filter produced an unbounded OR branch',\n 'DATA_QUERY_UNSUPPORTED',\n );\n }\n return merged;\n}\n\n/**\n * The result normalizer's warning rule: at most 100 entries, each a non-empty\n * string of at most 512 characters. A warning that names a field list derived\n * from a host-supplied schema could otherwise exceed it and fail the result\n * this warning exists to explain.\n */\nexport const DATA_QUERY_MAX_WARNINGS = 100;\nexport const DATA_QUERY_MAX_WARNING_LENGTH = 512;\n\n/** Appends a warning bounded to what `normalizeDataQueryResult` accepts. */\nfunction pushWarning(warnings: string[], message: string): void {\n if (warnings.length >= DATA_QUERY_MAX_WARNINGS) return;\n const text =\n message.length > DATA_QUERY_MAX_WARNING_LENGTH\n ? `${message.slice(0, DATA_QUERY_MAX_WARNING_LENGTH - 1)}\\u2026`\n : message;\n if (text.length > 0) warnings.push(text);\n}\n\n/** Records values the adapter had to shorten so the caller is told. */\nexport interface TruncationLog {\n fields: Set<string>;\n}\n\n/**\n * Cut an over-long string to the protocol's scalar cap without leaving a lone\n * surrogate behind.\n */\nfunction capString(\n value: string,\n limit = DATA_QUERY_MAX_STRING_LENGTH,\n): string {\n if (value.length <= limit) return value;\n const cut = value.slice(0, limit);\n const last = cut.charCodeAt(cut.length - 1);\n return last >= 0xd800 && last <= 0xdbff ? cut.slice(0, -1) : cut;\n}\n\n/**\n * Bound one JSON document the same way {@link capString} bounds a scalar.\n *\n * `normalizeDataQueryResult` validates a `json` field with `canonicalJson`,\n * which *rejects the whole result* — not just the offending value — when a\n * nested string exceeds {@link DATA_QUERY_MAX_JSON_STRING_LENGTH}, a container\n * exceeds {@link DATA_QUERY_MAX_JSON_CONTAINER_ITEMS}, nesting passes\n * {@link DATA_QUERY_MAX_JSON_DEPTH}, a number is non-finite, a value is not a\n * plain JSON type, or the document contains a cycle. One row with a large\n * `metadata` blob would therefore fail an otherwise valid page.\n *\n * Every one of those is bounded here instead, and the field is flagged so the\n * caller sees `truncated` plus a warning naming it.\n */\nfunction boundJsonValue(\n value: unknown,\n descriptor: DataQueryFieldDescriptor,\n truncation: TruncationLog | undefined,\n depth = 0,\n ancestors = new Set<object>(),\n): unknown {\n const flag = (): void => {\n truncation?.fields.add(descriptor.id);\n };\n if (value === null || value === undefined) return null;\n if (typeof value === 'boolean') return value;\n if (typeof value === 'string') {\n if (value.length > DATA_QUERY_MAX_JSON_STRING_LENGTH) {\n flag();\n return capString(value, DATA_QUERY_MAX_JSON_STRING_LENGTH);\n }\n return value;\n }\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) {\n flag();\n return null;\n }\n return value;\n }\n if (typeof value === 'bigint') {\n const safe =\n value <= BigInt(Number.MAX_SAFE_INTEGER) &&\n value >= BigInt(Number.MIN_SAFE_INTEGER);\n if (!safe) {\n flag();\n return null;\n }\n return Number(value);\n }\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? null : value.toISOString();\n }\n // Deeper than the validator accepts: keep the row, drop the sub-document.\n if (depth >= DATA_QUERY_MAX_JSON_DEPTH) {\n flag();\n return null;\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value)) {\n flag();\n return null;\n }\n ancestors.add(value);\n try {\n let entries = value;\n if (entries.length > DATA_QUERY_MAX_JSON_CONTAINER_ITEMS) {\n flag();\n entries = entries.slice(0, DATA_QUERY_MAX_JSON_CONTAINER_ITEMS);\n }\n return entries.map((entry) =>\n boundJsonValue(entry, descriptor, truncation, depth + 1, ancestors),\n );\n } finally {\n ancestors.delete(value);\n }\n }\n if (!isPlainRecord(value)) {\n // A class instance, function, or symbol would fail `plainObject` outright.\n flag();\n return null;\n }\n if (ancestors.has(value)) {\n flag();\n return null;\n }\n ancestors.add(value);\n try {\n let keys = Object.keys(value);\n if (keys.length > DATA_QUERY_MAX_JSON_CONTAINER_ITEMS) {\n flag();\n keys = keys.slice(0, DATA_QUERY_MAX_JSON_CONTAINER_ITEMS);\n }\n // A null prototype, so writing a key named `__proto__` stores an own\n // property instead of invoking the inherited setter — which would silently\n // drop the key AND change this object's prototype, failing `plainObject`'s\n // prototype check on the way out. `Object.prototype` and `null` are the two\n // prototypes the validator accepts.\n const bounded = Object.create(null) as Record<string, unknown>;\n for (const key of keys) {\n if (key.length > DATA_QUERY_MAX_JSON_STRING_LENGTH) {\n flag();\n continue;\n }\n // `plainObject` REJECTS the whole result for one of these keys, and\n // `JSON.parse` of a stored `metadata` column creates an own `__proto__`\n // property, so a single row could otherwise brick every query that\n // projects the field. Dropping the key keeps the row readable.\n if (DATA_QUERY_FORBIDDEN_JSON_KEYS.has(key)) {\n flag();\n continue;\n }\n Object.defineProperty(bounded, key, {\n value: boundJsonValue(\n value[key],\n descriptor,\n truncation,\n depth + 1,\n ancestors,\n ),\n enumerable: true,\n configurable: true,\n writable: true,\n });\n }\n return bounded;\n } finally {\n ancestors.delete(value);\n }\n}\n\nfunction toDeclaredValue(\n value: unknown,\n descriptor: DataQueryFieldDescriptor,\n truncation?: TruncationLog,\n): unknown {\n if (value === undefined) return null;\n if (value instanceof Date) {\n return Number.isNaN(value.getTime()) ? null : value.toISOString();\n }\n // A json field is validated as a document, not a scalar: it has its own,\n // larger limits, and the scalar cap would corrupt a serialized payload.\n if (descriptor.type === 'json') {\n return boundJsonValue(value, descriptor, truncation);\n }\n if (\n typeof value === 'string' &&\n value.length > DATA_QUERY_MAX_STRING_LENGTH\n ) {\n // The envelope rejects any scalar longer than the cap, which would turn one\n // long row into a failed query. Shorten it and say so instead.\n truncation?.fields.add(descriptor.id);\n return capString(value);\n }\n if (typeof value === 'bigint') {\n if (\n value > BigInt(Number.MAX_SAFE_INTEGER) ||\n value < BigInt(Number.MIN_SAFE_INTEGER)\n ) {\n return queryFail(\n `Content query value for ${descriptor.id} exceeds the safe integer range`,\n 'DATA_QUERY_RESULT_INVALID',\n );\n }\n return Number(value);\n }\n if (descriptor.type === 'boolean' && typeof value === 'number') {\n // SQLite/DuckDB surface booleans as 0/1.\n return value !== 0;\n }\n return value;\n}\n\nfunction jsonByteLength(value: unknown): number {\n return encoder.encode(JSON.stringify(value) ?? 'null').byteLength;\n}\n\nexport interface BoundedRows {\n rows: DataQueryRow[];\n truncated: boolean;\n}\n\n/** Cut a string to a BYTE budget without splitting a code point. */\nfunction capStringBytes(value: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n const totalBytes = encoder.encode(value).byteLength;\n if (totalBytes <= maxBytes) return value;\n // Fast path: an all-ASCII value has one byte per code unit, so the cut is a\n // slice. Encoding character by character allocates a typed array PER\n // CHARACTER, which dominates the whole bounding pass on a large page.\n if (totalBytes === value.length) return value.slice(0, maxBytes);\n let used = 0;\n let end = 0;\n for (const character of value) {\n const point = character.codePointAt(0) ?? 0;\n const cost = point < 0x80 ? 1 : point < 0x800 ? 2 : point < 0x10000 ? 3 : 4;\n if (used + cost > maxBytes) break;\n used += cost;\n end += character.length;\n }\n return value.slice(0, end);\n}\n\n/**\n * How a field's value may give way when a row has to get smaller.\n *\n * The distinction that matters is FORMAT. A `string` is free text, so a prefix\n * of it is still a valid string. A `datetime` is an RFC 3339 instant and NO\n * prefix of one is valid — truncating it makes the adapter emit a value that\n * violates the field type it declared, and the result normalizer then rejects\n * the whole page with `must be an RFC 3339 instant`, blaming the caller for a\n * shape the adapter produced. `json` is a document with no incremental\n * shortening. Both of those are all-or-nothing.\n *\n * Numbers and booleans are already minimal, and the identity field is the row's\n * address, so neither is reducible at all.\n */\ntype FieldReduction = 'truncate' | 'null' | 'none';\n\nfunction reductionFor(\n field: string,\n value: unknown,\n identityField: string,\n descriptors: Map<string, DataQueryFieldDescriptor>,\n): FieldReduction {\n if (field === identityField || value === null) return 'none';\n const type = descriptors.get(field)?.type;\n if (type === 'json') return 'null';\n // Format-constrained: reduce to null or not at all, never to a prefix.\n if (type === 'datetime') return 'null';\n if (typeof value === 'string' && value.length > 0) return 'truncate';\n return 'none';\n}\n\n/** The JSON byte cost of a value once it has given way completely. */\nconst REDUCED_VALUE_BYTES: Record<FieldReduction, number> = {\n truncate: 2, // `\"\"`\n null: 4, // `null`\n none: 0, // replaced by the value's own size\n};\n\ninterface MeasuredField {\n field: string;\n /** `\"key\":` — fixed for the life of the row. */\n keyBytes: number;\n /** Current JSON byte cost of the value. */\n valueBytes: number;\n reduction: FieldReduction;\n /** Value bytes once fully reduced. */\n floorBytes: number;\n}\n\ninterface MeasuredRow {\n fields: MeasuredField[];\n /** Braces, commas, and the array separator — independent of the values. */\n structural: number;\n /** Current serialized cost, equal to `jsonByteLength(row) + 1`. */\n cost: number;\n /** The smallest this row can ever be: every reducible value given way. */\n floor: number;\n}\n\n/**\n * Measure a row ONCE, so every later decision is arithmetic.\n *\n * The previous shrink re-serialized the whole row on every iteration AND every\n * field on every iteration, turning an ordinary page — 200 rows of a wide\n * projection at the default 1 MB budget — into seconds of blocked event loop.\n * The serialized size of an object is exactly its structural bytes plus, per\n * field, the key, a colon, and the value; so it can be recomputed from a single\n * measurement as values change, with no further stringification.\n */\nfunction measureRow(\n row: DataQueryRow,\n descriptors: Map<string, DataQueryFieldDescriptor>,\n identityField: string,\n): MeasuredRow {\n const entries = Object.entries(row);\n const fields = entries.map(([field, value]) => {\n const declared = reductionFor(field, value, identityField, descriptors);\n const valueBytes = jsonByteLength(value);\n // A reduction that would not SHRINK the field is not a reduction. `{}` and\n // `[]` serialize to two bytes, so nulling them costs four and makes the row\n // bigger; taking the reduced size as the floor unconditionally also\n // overstates the floor, which refuses pages that would have fitted.\n const reduction: FieldReduction =\n declared === 'none' || REDUCED_VALUE_BYTES[declared] < valueBytes\n ? declared\n : 'none';\n return {\n field,\n keyBytes: jsonByteLength(field),\n valueBytes,\n reduction,\n floorBytes:\n reduction === 'none' ? valueBytes : REDUCED_VALUE_BYTES[reduction],\n };\n });\n // `{`, `}`, one comma between fields, and the separator this row costs inside\n // the rows array.\n const structural = entries.length === 0 ? 3 : entries.length + 2;\n const overhead = fields.reduce(\n (sum, entry) => sum + entry.keyBytes + 1,\n structural,\n );\n return {\n fields,\n structural,\n cost: fields.reduce((sum, entry) => sum + entry.valueBytes, overhead),\n floor: fields.reduce((sum, entry) => sum + entry.floorBytes, overhead),\n };\n}\n\n/**\n * The largest per-value byte cap that keeps a set of truncatable sizes within\n * `available`, or `undefined` when even the floor does not fit.\n *\n * Classic max-min water-filling: sizes at or below the cap keep their real\n * cost, and everything above it is levelled to the cap. Solved by walking the\n * sorted sizes once rather than by repeated halving and re-measurement.\n */\nfunction waterFillCap(\n sizes: readonly number[],\n available: number,\n floorBytes: number,\n): number | undefined {\n if (sizes.length === 0)\n return available >= 0 ? Number.MAX_SAFE_INTEGER : undefined;\n if (available < sizes.length * floorBytes) return undefined;\n const sorted = [...sizes].sort((left, right) => left - right);\n let prefix = 0;\n for (let index = 0; index < sorted.length; index += 1) {\n const remaining = sorted.length - index;\n // Everything from `index` on levelled to `sorted[index]`.\n if (prefix + remaining * sorted[index] > available) {\n return Math.max(floorBytes, Math.floor((available - prefix) / remaining));\n }\n prefix += sorted[index];\n }\n return Number.MAX_SAFE_INTEGER;\n}\n\n/**\n * Shrink ONE row so its serialized form fits `allowance` bytes.\n *\n * HOW a field gives way depends on its declared type (see {@link reductionFor}):\n * a string is levelled to a shared cap, while a `json` document or a `datetime`\n * is all-or-nothing, dropped to `null`, because no prefix of either is valid.\n *\n * **Strings give way FIRST.** An all-or-nothing field loses its whole value to\n * save its bytes, so it is dropped only when the row cannot fit with it kept —\n * that is, only while it is larger than the cap the strings could otherwise\n * level down to. At a 50 KB allowance a 30 KB `metadata` blob beside a 100 KB\n * `title` therefore SURVIVES: levelling the title alone is enough. The same\n * blob beside a 20-character title does not, because no amount of levelling\n * gets there. Reaching for the largest field first would instead empty the blob\n * whenever it happened to be the biggest, discarding a whole document to save\n * bytes the strings had to spare.\n *\n * Returns `undefined` only when the row's floor exceeds the allowance. Callers\n * allocate at least each row's floor, so in practice this never fires.\n */\nfunction shrinkRowToBytes(\n row: DataQueryRow,\n allowance: number,\n measured: MeasuredRow,\n truncation: TruncationLog,\n): DataQueryRow | undefined {\n if (measured.floor > allowance) return undefined;\n const nulled = new Set<string>();\n let cap: number | undefined;\n\n // Give way on the all-or-nothing fields only while one of them is a bigger\n // contributor than any string would be after levelling.\n for (;;) {\n const kept = measured.fields.filter(\n (entry) => entry.reduction !== 'truncate' && !nulled.has(entry.field),\n );\n const truncatable = measured.fields.filter(\n (entry) => entry.reduction === 'truncate',\n );\n const fixed = kept.reduce((sum, entry) => sum + entry.valueBytes, 0);\n const nulledBytes = REDUCED_VALUE_BYTES.null * nulled.size;\n const overhead = measured.fields.reduce(\n (sum, entry) => sum + entry.keyBytes + 1,\n measured.structural,\n );\n const available = allowance - overhead - fixed - nulledBytes;\n cap = waterFillCap(\n truncatable.map((entry) => entry.valueBytes),\n available,\n REDUCED_VALUE_BYTES.truncate,\n );\n // An all-or-nothing field gives way only when the row cannot fit with it\n // KEPT. Dropping one loses a whole value to save a few bytes, so it is a\n // last resort rather than a race with the strings: a 200 KB `metadata`\n // blob makes the row infeasible and goes immediately, while a 26-byte\n // `updated_at` survives whenever the strings can absorb the difference.\n if (cap !== undefined) break;\n const biggestNullable = measured.fields\n .filter((entry) => entry.reduction === 'null' && !nulled.has(entry.field))\n .sort((left, right) => right.valueBytes - left.valueBytes)[0];\n if (biggestNullable === undefined) return undefined;\n nulled.add(biggestNullable.field);\n }\n if (cap === undefined) return undefined;\n\n const shrunk: DataQueryRow = { ...row };\n for (const entry of measured.fields) {\n if (nulled.has(entry.field)) {\n shrunk[entry.field] = null;\n truncation.fields.add(entry.field);\n continue;\n }\n if (entry.reduction !== 'truncate' || entry.valueBytes <= cap) continue;\n const original = shrunk[entry.field] as string;\n // The cap is a budget for the SERIALIZED value, so the content budget is\n // two bytes smaller; JSON escaping can inflate the rest, so the value is\n // measured once and trimmed again on the rare occasion it overshoots.\n let content = capStringBytes(original, cap - 2);\n let guard = 0;\n while (jsonByteLength(content) > cap && content.length > 0 && guard < 8) {\n guard += 1;\n const overshoot = jsonByteLength(content) - cap;\n content = capStringBytes(\n content,\n Math.max(0, encoder.encode(content).byteLength - overshoot),\n );\n }\n shrunk[entry.field] = content;\n truncation.fields.add(entry.field);\n }\n return shrunk;\n}\n\n/**\n * Share a byte budget across rows: every row keeps its irreducible floor, and\n * the surplus is divided max-min fair over what each row could still use.\n *\n * Allocating max-min fair over the rows' CURRENT costs — without seating the\n * floors first — can declare a feasible page impossible. A small row is handed\n * its whole cost while a large, mostly-irreducible row is left below its own\n * floor, so the request fails even though the floors fit the budget with room\n * to spare. Seating the floors first makes the guarantee unconditional: if\n * `sum(floors) <= budget` then every row is allocated at least its floor,\n * whatever the shape of the page.\n *\n * Exported because that guarantee is a property of the ARITHMETIC, not of any\n * page the content schema can actually produce — a row's floor is dominated by\n * the projection's key bytes, which are identical across rows, so the disparity\n * that breaks cost-first ordering is not reachable through `executeContentQuery`.\n * The property is real and worth holding; this is the level it can be held at.\n *\n * PRECONDITION: `floors[i] <= costs[i]` for every row — a floor is a size the\n * row can actually be reduced TO, so it can never exceed the size it already\n * is. {@link measureRow} guarantees this by declining any reduction that would\n * not shrink the field. The appetite below is clamped at zero so that a\n * violation still yields at least the floor rather than silently starving a row.\n *\n * @param floors - Per-row irreducible byte cost; at most the row's cost.\n * @param costs - Per-row current byte cost; always at least the floor.\n * @param budget - Bytes available for the rows themselves.\n * @returns Per-row byte allowance, each at least the row's floor.\n */\nexport function allocateRowBytes(\n floors: readonly number[],\n costs: readonly number[],\n budget: number,\n): number[] {\n const allowances = [...floors];\n const seated = floors.reduce((sum, floor) => sum + floor, 0);\n if (seated > budget) return allowances;\n // What each row could still use ON TOP of its floor. Ordering by appetite\n // rather than by cost is what keeps a row that needs nothing from consuming\n // another row's floor.\n const appetites = costs.map((cost, index) =>\n Math.max(0, cost - floors[index]),\n );\n const order = floors\n .map((_, index) => index)\n .sort((left, right) => appetites[left] - appetites[right]);\n let surplus = budget - seated;\n let left = floors.length;\n for (const index of order) {\n const granted = Math.min(appetites[index], Math.floor(surplus / left));\n allowances[index] += granted;\n surplus -= granted;\n left -= 1;\n }\n return allowances;\n}\n\n/**\n * Keep the returned rows inside the schema byte budget WITHOUT dropping any.\n *\n * The normalizer rejects an oversized result rather than trimming it, and one\n * content row can carry megabytes of `metadata`, so the adapter has to bound\n * the payload itself. It used to do that by dropping trailing rows — which is\n * silent, permanent data loss: offset paging advances by the requested LIMIT,\n * not by the number of rows actually returned, so the next page starts past the\n * dropped rows and they are skipped on every page, forever. `DataQueryResult`'s\n * offset page is `{ kind, offset, limit, hasMore }` with no next-offset slot,\n * and the normalizer refuses a `nextCursor` on an offset page, so a\n * continuation offset cannot be expressed to say \"resume at 170\" either.\n *\n * So a row is never dropped for size — only shortened, which is a state the\n * result already reports through `truncated` and its warning, and which leaves\n * offset paging exact.\n *\n * Allocation is floor-first, then max-min fair. Every row is given its\n * irreducible floor before any surplus is shared, so a page whose floors fit is\n * always produced — ordering rows by their CURRENT cost could otherwise hand a\n * small row more than it needed and starve a large one below its floor,\n * declaring a feasible page impossible.\n *\n * Throws only when the floors themselves exceed the budget, which is the one\n * case no amount of shortening can reach. Failing loudly beats answering with a\n * page that silently omits rows.\n */\nexport function boundRowBytes(\n rows: DataQueryRow[],\n maxResultBytes: number,\n descriptors: Map<string, DataQueryFieldDescriptor>,\n identityField: string,\n truncation: TruncationLog,\n): BoundedRows {\n const budget = Math.max(\n 0,\n (maxResultBytes || CONTENT_QUERY_MAX_RESULT_BYTES) -\n RESULT_ENVELOPE_RESERVE_BYTES,\n );\n // `[` and `]`. Each row's `structural` charges the separator that FOLLOWS it,\n // but N rows need N-1, so a non-empty page has one separator too many; refund\n // it here rather than special-casing the last row. Without the refund a page\n // whose true size exactly equals the budget is needlessly shortened — or, if\n // it is irreducible, refused.\n const framing = rows.length > 0 ? 1 : 2;\n const measured = rows.map((row) =>\n measureRow(row, descriptors, identityField),\n );\n const total = measured.reduce((sum, row) => sum + row.cost, framing);\n if (total <= budget) return { rows, truncated: false };\n\n const floors = measured.reduce((sum, row) => sum + row.floor, framing);\n if (floors > budget) {\n return queryFail(\n 'Content query cannot fit its rows inside the maximum result bytes; request fewer fields or a smaller page.',\n 'DATA_QUERY_RESULT_TOO_LARGE',\n );\n }\n\n const allowances = allocateRowBytes(\n measured.map((row) => row.floor),\n measured.map((row) => row.cost),\n budget - framing,\n );\n\n const bounded = rows.map((row, index) => {\n if (measured[index].cost <= allowances[index]) return row;\n const shrunk = shrinkRowToBytes(\n row,\n allowances[index],\n measured[index],\n truncation,\n );\n if (shrunk === undefined) {\n return queryFail(\n 'Content query cannot fit a row inside its maximum result bytes; request fewer fields or a smaller page.',\n 'DATA_QUERY_RESULT_TOO_LARGE',\n );\n }\n return shrunk;\n });\n return { rows: bounded, truncated: true };\n}\n\nfunction orderByTerms(sort: DataQuerySort[] | undefined): string[] | undefined {\n if (!sort || sort.length === 0) return undefined;\n return sort.map((term) => `${term.field} ${term.direction.toUpperCase()}`);\n}\n\n/**\n * Execute one bounded content query.\n *\n * Every read goes through `SmrtCollection.list({ select, where, orderBy,\n * offset, limit })`, `count()`, and `facets()` — the collection remains the\n * authorization, tenancy-interception, and SQL boundary. The full collection is\n * never hydrated to filter or page in memory.\n *\n * Tenancy is applied by this function itself (see\n * {@link resolveContentTenantReadScope}) in addition to any application\n * `scope`; a caller cannot opt out of it.\n *\n * @param collection Content collection to read through.\n * @param rawRequest Untrusted `DataQueryRequest` (typically an HTTP body).\n * @param options Trusted adapter configuration — never derived from the caller.\n */\nexport async function executeContentQuery(\n collection: ContentQueryCollection,\n rawRequest: unknown,\n options: ContentQueryOptions = {},\n): Promise<DataQueryResult> {\n const schema = options.schema ?? (await buildContentQuerySchema());\n // Configuration first: a budget too small to hold an envelope plus a row\n // would otherwise answer every query with an empty page rather than fail.\n assertUsableResultBudget(schema);\n const request: DataQueryRequest = normalizeDataQueryRequest(\n rawRequest,\n schema,\n );\n const queryFingerprint = createDataQueryFingerprint(request, schema);\n const descriptors = new Map(schema.fields.map((field) => [field.id, field]));\n const declared = new Set(descriptors.keys());\n\n const callerWhere = request.filter\n ? filterToDnf(request.filter, declared)\n : undefined;\n // Tenancy first, then the application scope: both are trusted, both narrow.\n // An empty application scope contributes its deny-all condition here, so it\n // survives the merge alongside tenancy rather than being mistaken for\n // \"unscoped\" — an empty app scope plus a tenant scope must still deny.\n const scopeConditions = [\n ...normalizeScopeConditions(resolveContentTenantReadScope()),\n ...normalizeApplicationScope(options.scope),\n ];\n // `undefined`, never `[]`: the aggregate is empty only when there genuinely\n // is no scope, and `[]` now means the opposite.\n const where = mergeContentQueryScope(\n scopeConditions.length > 0 ? scopeConditions : undefined,\n callerWhere,\n );\n const countOptions = where === undefined ? undefined : { where };\n\n const warnings: string[] = [];\n let truncated = false;\n let facets: DataQueryFacetResult[] | undefined;\n\n if (request.mode === 'rows') {\n const projection = request.projection ?? [schema.identityField];\n const offset = request.page?.kind === 'offset' ? request.page.offset : 0;\n const limit =\n request.page?.limit ??\n schema.defaultPageLimit ??\n CONTENT_QUERY_DEFAULT_PAGE_LIMIT;\n const orderBy = orderByTerms(request.sort);\n const listed = await collection.list({\n select: projection,\n offset,\n limit,\n ...(orderBy\n ? { orderBy: orderBy.length === 1 ? orderBy[0] : orderBy }\n : {}),\n ...(where === undefined ? {} : { where }),\n });\n const truncation: TruncationLog = { fields: new Set() };\n const mapped = listed.map((row) => {\n const out: DataQueryRow = {};\n for (const field of projection) {\n const descriptor = descriptors.get(field);\n if (!descriptor) {\n return queryFail(\n `Content query returned an undeclared field: ${field}`,\n 'DATA_QUERY_RESULT_NOT_ALLOWED',\n );\n }\n out[field] = toDeclaredValue(row[field], descriptor, truncation);\n }\n return out;\n });\n const bounded = boundRowBytes(\n mapped,\n schema.maxResultBytes ?? CONTENT_QUERY_MAX_RESULT_BYTES,\n descriptors,\n schema.identityField,\n truncation,\n );\n const rows: DataQueryRow[] = bounded.rows;\n truncated = bounded.truncated || truncation.fields.size > 0;\n if (bounded.truncated) {\n pushWarning(\n warnings,\n 'Content query shortened values to fit its maximum result bytes; request fewer fields or a smaller page.',\n );\n }\n if (truncation.fields.size > 0) {\n pushWarning(\n warnings,\n `Content query shortened over-long values in: ${[...truncation.fields].sort().join(', ')}.`,\n );\n }\n const total = await collection.count(countOptions);\n const page: DataQueryResult['page'] = {\n kind: 'offset',\n offset,\n limit,\n // No row is ever dropped for size any more, so the page is exactly the\n // rows the offset asked for and `hasMore` is a plain positional fact.\n hasMore: offset + rows.length < total,\n };\n return normalizeDataQueryResult(\n {\n version: 1 as const,\n requestId: request.requestId,\n queryFingerprint,\n identityField: schema.identityField,\n rows,\n page,\n total: { kind: 'exact' as const, value: total },\n freshness: { state: 'fresh' as const, asOf: new Date().toISOString() },\n warnings,\n truncated,\n },\n request,\n schema,\n );\n }\n\n const total = await collection.count(countOptions);\n\n if (request.mode === 'facets') {\n const requested = request.facets ?? [];\n const sourceFacets = await collection.facets({\n fields: requested.map((facet) => ({\n field: facet.field,\n limit: facet.limit,\n })),\n ...(where === undefined ? {} : { where }),\n });\n const byField = new Map(sourceFacets.map((facet) => [facet.field, facet]));\n const facetTruncation: TruncationLog = { fields: new Set() };\n // Facet values go through the SAME shared byte budget the rows do: two text\n // facets of 200 distinct 4096-character values are inside every per-value\n // cap and still over the 1 MB result limit, which would make the normalizer\n // reject an otherwise valid response.\n let facetBudget = Math.max(\n 0,\n (schema.maxResultBytes ?? CONTENT_QUERY_MAX_RESULT_BYTES) -\n RESULT_ENVELOPE_RESERVE_BYTES,\n );\n let facetBudgetExhausted = false;\n facets = requested.map((facet) => {\n const descriptor = descriptors.get(facet.field);\n if (!descriptor) {\n return queryFail(\n `Content query returned an undeclared facet: ${facet.field}`,\n 'DATA_QUERY_RESULT_NOT_ALLOWED',\n );\n }\n const values = byField.get(facet.field)?.values ?? [];\n // The envelope around one facet: field name, brackets, and flags.\n facetBudget -= jsonByteLength(facet.field) + 48;\n const kept: Array<{\n value: string | number | boolean | null;\n count: number;\n }> = [];\n let boundedOut = false;\n for (const entry of values.slice(0, facet.limit)) {\n const value = toDeclaredValue(\n entry.value,\n descriptor,\n facetTruncation,\n ) as string | number | boolean | null;\n const cost = jsonByteLength({ value, count: entry.count }) + 1;\n if (cost > facetBudget) {\n boundedOut = true;\n facetBudgetExhausted = true;\n break;\n }\n facetBudget -= cost;\n kept.push({ value, count: entry.count });\n }\n return {\n field: facet.field,\n values: kept,\n // The collection bounds this grouping query in the database; an exactly\n // full page may have more values, so report conservatively.\n truncated: boundedOut || values.length >= facet.limit,\n };\n });\n if (facetTruncation.fields.size > 0) {\n pushWarning(\n warnings,\n `Content query shortened over-long values in: ${[...facetTruncation.fields].sort().join(', ')}.`,\n );\n }\n if (facetBudgetExhausted) {\n pushWarning(\n warnings,\n 'Content query facets were truncated to fit their maximum result bytes; request fewer facets or a smaller facet limit.',\n );\n }\n truncated =\n facets.some((facet) => facet.truncated) ||\n facetTruncation.fields.size > 0;\n }\n\n return normalizeDataQueryResult(\n {\n version: 1 as const,\n requestId: request.requestId,\n queryFingerprint,\n identityField: schema.identityField,\n rows: [],\n total: { kind: 'exact' as const, value: total },\n ...(facets === undefined ? {} : { facets }),\n freshness: { state: 'fresh' as const, asOf: new Date().toISOString() },\n warnings,\n truncated,\n },\n request,\n schema,\n );\n}\n","/**\n * Shared SSRF guard for content's outbound fetches (feed sync, mirroring).\n *\n * Any code path that fetches a caller-supplied URL — RSS/Atom feeds, the\n * `Mirror` content type, link previews — is an SSRF vector: an attacker who can\n * influence the URL can make the server fetch `169.254.169.254` cloud metadata,\n * `localhost` admin panels, or other internal services. This module rejects\n * URLs that resolve to private, loopback, link-local, CGNAT, or cloud-metadata\n * ranges before any request is made, and re-validates redirect hops.\n *\n * Unlike a literal-IP-only check, {@link assertSafeRemoteUrl} resolves hostnames\n * via DNS so a public name pointing at a private IP is also caught (S5 #1388).\n */\nimport { lookup as dnsLookup } from 'node:dns/promises';\nimport { isIP } from 'node:net';\n\nexport type ResolvedAddress = { address: string; family?: number };\nexport type ResolveHostname = (hostname: string) => Promise<ResolvedAddress[]>;\n\nexport interface SafeRemoteUrlOptions {\n /** Skip the private-network checks (trusted callers only, e.g. local dev). */\n allowPrivateNetworkHosts?: boolean;\n /** Injectable resolver for tests; defaults to {@link defaultResolveHostname}. */\n resolveHostname?: ResolveHostname;\n}\n\nexport async function defaultResolveHostname(\n hostname: string,\n): Promise<ResolvedAddress[]> {\n return dnsLookup(hostname, { all: true, verbatim: false });\n}\n\nexport function isBlockedIPv4(address: string): boolean {\n const parts = address.split('.');\n if (parts.length !== 4) return true;\n // Strict per-octet validation: `Number('')` is 0, so without a digit check a\n // malformed input like `1..2.3` would parse to `[1,0,2,3]` and be treated as a\n // valid (and possibly allowed) address. Anything not 1-3 digits in 0-255 is\n // unparseable → blocked (review #1562).\n const octets = parts.map((part) =>\n /^\\d{1,3}$/.test(part) ? Number(part) : Number.NaN,\n );\n if (octets.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) {\n return true;\n }\n\n const [first, second] = octets;\n return (\n first === 0 ||\n first === 10 ||\n first === 127 ||\n first >= 224 ||\n (first === 100 && second >= 64 && second <= 127) ||\n (first === 169 && second === 254) ||\n (first === 172 && second >= 16 && second <= 31) ||\n (first === 192 && second === 168) ||\n (first === 198 && (second === 18 || second === 19))\n );\n}\n\n/**\n * Expand an IPv6 string to its 8 hextets (numbers), or null if unparseable.\n * Handles `::` compression, an embedded dotted-IPv4 tail, and bracketed forms.\n */\nfunction expandIPv6(address: string): number[] | null {\n let work = address.toLowerCase().replace(/^\\[|\\]$/g, '');\n if (work.includes('.')) {\n // Embedded dotted IPv4 tail (e.g. ::ffff:127.0.0.1) → convert to 2 hextets.\n const m = work.match(/(\\d{1,3}(?:\\.\\d{1,3}){3})$/);\n if (!m) return null;\n const o = m[1].split('.').map(Number);\n if (o.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;\n const hi = ((o[0] << 8) | o[1]).toString(16);\n const lo = ((o[2] << 8) | o[3]).toString(16);\n work = `${work.slice(0, work.length - m[1].length)}${hi}:${lo}`;\n }\n const halves = work.split('::');\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(':') : [];\n const tail = halves.length === 2 && halves[1] ? halves[1].split(':') : [];\n const groups =\n halves.length === 2\n ? [\n ...head,\n ...Array(Math.max(0, 8 - head.length - tail.length)).fill('0'),\n ...tail,\n ]\n : head;\n if (groups.length !== 8) return null;\n const hextets = groups.map((g) =>\n /^[0-9a-f]{1,4}$/.test(g) ? Number.parseInt(g, 16) : Number.NaN,\n );\n return hextets.some((n) => Number.isNaN(n)) ? null : hextets;\n}\n\nexport function isBlockedIPv6(address: string): boolean {\n const hextets = expandIPv6(address);\n if (hextets) {\n // IPv4-mapped (::ffff:a.b.c.d) and IPv4-compatible (::a.b.c.d, deprecated)\n // both embed an IPv4 in the last 2 hextets — decode and apply the IPv4\n // blocklist so a loopback/private IPv4 can't be smuggled through any IPv6\n // encoding (compressed, expanded, dotted, or hex) (review #1562, P1).\n const firstFiveZero = hextets.slice(0, 5).every((h) => h === 0);\n if (firstFiveZero && (hextets[5] === 0xffff || hextets[5] === 0)) {\n const [, , , , , , g6, g7] = hextets;\n const ipv4 = `${g6 >> 8}.${g6 & 0xff}.${g7 >> 8}.${g7 & 0xff}`;\n return isBlockedIPv4(ipv4);\n }\n }\n\n const normalized = address.toLowerCase().replace(/^\\[|\\]$/g, '');\n return (\n normalized === '::' ||\n normalized === '::1' ||\n normalized.startsWith('fc') ||\n normalized.startsWith('fd') ||\n /^fe[89ab]/.test(normalized) ||\n normalized.startsWith('ff')\n );\n}\n\nexport function isBlockedAddress(address: string): boolean {\n const family = isIP(address);\n if (family === 4) return isBlockedIPv4(address);\n if (family === 6) return isBlockedIPv6(address);\n // Anything that isn't a recognisable IP literal (after DNS resolution should\n // have produced one) is treated as unsafe.\n return true;\n}\n\n/**\n * Parse and validate a remote URL for outbound fetching. Rejects non-http(s)\n * schemes, embedded credentials, and hosts that resolve to non-public ranges.\n * Returns the parsed {@link URL} on success; throws a descriptive `Error`\n * otherwise.\n */\nexport async function assertSafeRemoteUrl(\n rawUrl: string,\n options: SafeRemoteUrlOptions = {},\n): Promise<URL> {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new Error('Remote URL must be an absolute URL');\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new Error('Remote URL must use http or https');\n }\n if (url.username || url.password) {\n throw new Error('Remote URL must not include credentials');\n }\n if (!url.hostname) {\n throw new Error('Remote URL must include a hostname');\n }\n\n if (options.allowPrivateNetworkHosts) return url;\n\n const resolver = options.resolveHostname ?? defaultResolveHostname;\n const addresses =\n isIP(url.hostname) === 0\n ? await resolver(url.hostname)\n : [{ address: url.hostname }];\n\n if (\n !addresses.length ||\n addresses.some(({ address }) => isBlockedAddress(address))\n ) {\n throw new Error('Remote URL must resolve to a public network address');\n }\n\n return url;\n}\n\n/** Redirect status codes that reroute a request to a new Location. */\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\nconst DEFAULT_MAX_REDIRECTS = 5;\nconst DEFAULT_RESOLVE_TIMEOUT_MS = 10_000;\n\nexport interface SafeRedirectOptions extends SafeRemoteUrlOptions {\n /** Maximum redirect hops to follow before failing. Default 5. */\n maxRedirects?: number;\n /** Per-hop timeout in ms. Default 10s. */\n timeoutMs?: number;\n /** Injectable fetch (primarily for tests). Defaults to global `fetch`. */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Resolve a URL's redirect chain and return the final safe {@link URL}, with\n * EVERY hop re-validated through {@link assertSafeRemoteUrl}.\n *\n * Use this before handing a URL to a fetcher that follows redirects on its own\n * (e.g. `fetchDocument`): the up-front {@link assertSafeRemoteUrl} check alone\n * can't stop an allowed public host from `30x`-redirecting into an internal /\n * loopback / metadata host, which would defeat the SSRF guard (review #1562).\n *\n * Redirects are followed with `GET` + `redirect: 'manual'` to match downstream\n * GET-based fetchers; response bodies are discarded (the redirect bodies are\n * empty and the terminal body is left for the caller to re-fetch), so this does\n * not double-download content.\n */\nexport async function resolveSafeFinalUrl(\n rawUrl: string,\n options: SafeRedirectOptions = {},\n): Promise<URL> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const maxRedirects = options.maxRedirects ?? DEFAULT_MAX_REDIRECTS;\n const timeoutMs = options.timeoutMs ?? DEFAULT_RESOLVE_TIMEOUT_MS;\n let current = await assertSafeRemoteUrl(rawUrl, options);\n\n for (let hop = 0; hop <= maxRedirects; hop += 1) {\n const response = await fetchImpl(current, {\n method: 'GET',\n redirect: 'manual',\n signal: AbortSignal.timeout(timeoutMs),\n });\n // Release the connection without downloading the body.\n try {\n await response.body?.cancel();\n } catch {\n // best-effort\n }\n\n if (!REDIRECT_STATUSES.has(response.status)) {\n return current;\n }\n const location = response.headers.get('location');\n if (!location) return current;\n // Re-validate the resolved redirect target before following it.\n current = await assertSafeRemoteUrl(\n new URL(location, current).toString(),\n options,\n );\n }\n\n throw new Error('Remote URL exceeded the maximum number of redirects');\n}\n\n/**\n * Strip userinfo (`user:pass@`) from a URL so it can be safely logged or echoed\n * in an error message. Returns a placeholder for unparseable input. Never let a\n * credential-bearing URL reach logs/errors verbatim (review #1562).\n */\nexport function redactUrlCredentials(raw: string): string {\n try {\n const url = new URL(raw);\n if (url.username || url.password) {\n url.username = '';\n url.password = '';\n return url.toString();\n }\n return raw;\n } catch {\n return '[unparseable url]';\n }\n}\n","import { writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { AIClientOptions } from '@happyvertical/ai';\nimport { fetchDocument } from '@happyvertical/documents';\nimport { ensureDirectoryExists } from '@happyvertical/files';\nimport { createLogger } from '@happyvertical/logger';\nimport type { SmrtCollectionOptions } from '@happyvertical/smrt-core';\nimport { SmrtCollection, smrt } from '@happyvertical/smrt-core';\nimport type { Image } from '@happyvertical/smrt-images';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport type { DataQueryResult } from '@happyvertical/smrt-types';\nimport { makeSlug } from '@happyvertical/utils';\nimport YAML from 'yaml';\nimport { htmlToMarkdown, resolveBodyFormat } from './body-format';\nimport { Content } from './content';\nimport {\n getEffectiveContentGovernanceConfig,\n loadPersistedContentGovernanceDefinitions,\n resolveEffectiveContentGovernance,\n} from './content-governance';\nimport {\n type ContentQueryCollection,\n executeContentQuery,\n} from './content-query';\nimport {\n type ResolveHostname,\n redactUrlCredentials,\n resolveSafeFinalUrl,\n} from './safe-remote-url';\nimport { serializeContent, serializeFact } from './serialization';\nimport type {\n ThumbnailOptions,\n ThumbnailStrategy,\n} from './thumbnail-generator';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Options accepted by {@link Contents.generateMissingThumbnails}. Also reused\n * (as a `Partial`) for the `thumbnail` config block passed through the\n * collection constructor from `smrt.config.js`.\n */\nexport interface GenerateMissingThumbnailsOptions {\n /**\n * Thumbnail generation strategy\n */\n strategy: ThumbnailStrategy;\n\n /**\n * Optional filter for content to process\n */\n where?: Record<string, unknown>;\n\n /**\n * Maximum number of thumbnails to generate\n */\n limit?: number;\n\n // Headline card options\n brandColor?: string;\n backgroundColor?: string;\n logoUrl?: string;\n template?: 'default' | 'news' | 'minimal';\n\n // Static map options\n mapProvider?: 'mapbox' | 'google';\n zoom?: number;\n\n // AI options\n style?: 'photorealistic' | 'illustration' | 'abstract' | 'minimal';\n\n // Common options\n width?: number;\n height?: number;\n}\n\n/**\n * Configuration options for Contents collection\n */\nexport interface ContentsOptions extends SmrtCollectionOptions {\n /**\n * Directory to store content files\n */\n contentDir?: string;\n\n /**\n * Default thumbnail-generation settings sourced from `smrt.config.js`. Merged\n * under per-call options in `generateMissingThumbnails`.\n */\n thumbnail?: Partial<GenerateMissingThumbnailsOptions>;\n}\n\nfunction isAIClientOptions(\n ai: SmrtCollectionOptions['ai'],\n): ai is AIClientOptions {\n return (\n !!ai &&\n typeof ai === 'object' &&\n !('embed' in ai) &&\n !('generateImage' in ai)\n );\n}\n\n/**\n * Collection for managing Content objects\n *\n * The Contents collection provides functionality for managing and manipulating\n * collections of Content objects, including saving to the filesystem and\n * mirroring content from remote URLs.\n */\n@smrt({\n api: {\n include: [\n 'browseFacts',\n 'getBySlug',\n 'getGovernanceDefinitionsAction',\n 'resolveGovernanceAction',\n 'queryAction',\n ],\n routes: {\n queryAction: {\n scope: 'collection',\n method: 'POST',\n path: 'query',\n },\n browseFacts: {\n scope: 'collection',\n method: 'GET',\n path: 'facts',\n },\n getBySlug: {\n scope: 'collection',\n method: 'GET',\n path: 'by-slug',\n },\n getGovernanceDefinitionsAction: {\n scope: 'collection',\n method: 'GET',\n path: 'governance',\n },\n resolveGovernanceAction: {\n scope: 'collection',\n method: 'GET',\n path: 'governance/resolve',\n },\n },\n },\n mcp: false,\n cli: false,\n})\nexport class Contents extends SmrtCollection<Content> {\n /**\n * Class constructor for collection items\n */\n static _itemClass = Content;\n\n /**\n * Configuration options\n */\n public options: ContentsOptions = {} as ContentsOptions;\n\n /**\n * Directory to store content files\n */\n public contentDir?: string;\n\n /**\n * Cache for loaded content\n */\n public loaded: Map<string, Content>;\n\n /**\n * Creates a new Contents collection\n *\n * Use the static `create()` method inherited from SmrtCollection for proper initialization.\n *\n * @param options - Configuration options\n */\n constructor(options: ContentsOptions = {}) {\n super(options);\n this.options = options;\n this.loaded = new Map();\n }\n\n /**\n * Gets the database interface\n *\n * @returns Database interface\n */\n getDb() {\n return this._db;\n }\n\n /**\n * Initializes the collection\n *\n * @returns Promise that resolves to this instance\n */\n public async initialize(): Promise<this> {\n await super.initialize();\n return this;\n }\n\n private async getFactCollection() {\n const { FactCollection } = await import('@happyvertical/smrt-facts');\n return FactCollection.create(this.options);\n }\n\n /**\n * Bounded, tenant-safe content query (`POST /api/v1/contents/query`).\n *\n * Accepts the canonical `DataQueryRequest` envelope (#2444) and returns a\n * normalized `DataQueryResult`, so a list surface can filter, sort, page,\n * count, and facet server-side instead of hydrating the whole collection.\n *\n * The request body carries no authority: only schema-declared field ids are\n * accepted, and tenant scoping is applied by `executeContentQuery` itself\n * (fail-closed to global rows when tenancy is enabled with no active tenant\n * context). Applications that need additional site/organization scoping call\n * `executeContentQuery` directly with a trusted `scope`.\n *\n * The parameter is named `options` deliberately: the route generator passes\n * the raw request body straight through as a single `options` argument, so\n * the wire body IS the `DataQueryRequest` rather than a wrapper object.\n *\n * @param options Untrusted `DataQueryRequest` from the caller.\n * @returns A validated, bounded `DataQueryResult`.\n */\n public async queryAction(options: unknown): Promise<DataQueryResult> {\n return executeContentQuery(\n this as unknown as ContentQueryCollection,\n options,\n );\n }\n\n public async browseFacts(\n options: {\n q?: string;\n query?: string;\n limit?: number | string;\n offset?: number | string;\n minSimilarity?: number | string;\n includeSuperseded?: boolean | string;\n latestOnly?: boolean | string;\n tenantId?: string | null;\n } = {},\n ) {\n try {\n const facts = await this.getFactCollection();\n const query = options.query || options.q || '';\n const limit =\n options.limit !== undefined ? Number(options.limit) : undefined;\n const offset =\n options.offset !== undefined ? Number(options.offset) : undefined;\n const minSimilarity =\n options.minSimilarity !== undefined\n ? Number(options.minSimilarity)\n : undefined;\n const includeSuperseded =\n options.includeSuperseded === true ||\n options.includeSuperseded === 'true';\n const latestOnly =\n options.latestOnly === undefined\n ? true\n : options.latestOnly === true || options.latestOnly === 'true';\n\n const results = await facts.browseCatalog(query, {\n limit: Number.isFinite(limit) ? limit : undefined,\n offset: Number.isFinite(offset) ? offset : undefined,\n minSimilarity: Number.isFinite(minSimilarity)\n ? minSimilarity\n : undefined,\n includeSuperseded,\n latestOnly,\n tenantId: options.tenantId ?? null,\n });\n\n return results.map(serializeFact);\n } catch (error) {\n // Gracefully handle missing facts table (cross-package dependency)\n if (\n typeof error === 'object' &&\n error !== null &&\n (error as { code?: unknown }).code === 'DB_SCHEMA_MISSING'\n ) {\n return [];\n }\n throw error;\n }\n }\n\n public async getBySlug(\n options: {\n slug?: string;\n context?: string;\n status?: string;\n tenantId?: string | null;\n } = {},\n ) {\n if (!options.slug) {\n throw new Error('slug is required');\n }\n\n const where: {\n slug: string;\n context: string;\n tenantId?: string | null;\n } = {\n slug: options.slug,\n context: options.context || '',\n };\n if (options.tenantId !== undefined) {\n where.tenantId = options.tenantId;\n }\n\n const content = await this.get(where);\n\n if (!content) {\n return null;\n }\n\n if (options.status && content.status !== options.status) {\n return null;\n }\n\n return serializeContent(content);\n }\n\n public async getGovernanceDefinitionsAction(\n options: { tenantId?: string | null } = {},\n ) {\n const [effective, persisted] = await Promise.all([\n getEffectiveContentGovernanceConfig({\n db: this.db,\n tenantId: options.tenantId,\n }),\n loadPersistedContentGovernanceDefinitions({\n db: this.db,\n tenantId: options.tenantId,\n }),\n ]);\n\n return {\n effective: {\n policies: effective.policies.map((policy) => ({\n ...policy,\n ...(persisted.policies.find((item) => item.key === policy.key) || {}),\n })),\n profiles: effective.profiles.map((profile) => ({\n ...profile,\n ...(persisted.profiles.find((item) => item.key === profile.key) ||\n {}),\n })),\n assignments: effective.assignments.map((assignment) => ({\n ...assignment,\n ...(persisted.assignments.find(\n (item) => item.key === assignment.key,\n ) || {}),\n })),\n },\n persisted: {\n policies: persisted.policies,\n profiles: persisted.profiles,\n assignments: persisted.assignments,\n },\n };\n }\n\n public async resolveGovernanceAction(\n options: {\n type?: string;\n variant?: string | null;\n tenantId?: string | null;\n } = {},\n ) {\n return resolveEffectiveContentGovernance({\n contentType: options.type || null,\n contentVariant: options.variant || null,\n db: this.db,\n tenantId: options.tenantId,\n });\n }\n\n /**\n * Mirrors content from a remote URL\n *\n * Downloads and stores content from a remote URL, extracting text\n * and saving it as a Content object.\n *\n * @param options - Mirror options\n * @param options.url - URL to mirror\n * @param options.mirrorDir - Directory for caching mirrored files\n * @param options.context - Context for the mirrored content\n * @returns Promise resolving to the mirrored Content object\n * @throws Error if URL is invalid or missing\n */\n public async mirror(options: {\n url: string;\n mirrorDir?: string;\n context?: string;\n /**\n * Skip the SSRF host-blocking checks. Only set this for fully trusted,\n * operator-supplied URLs (e.g. local development), never for URLs that\n * originate from end users or external content.\n */\n allowPrivateNetworkHosts?: boolean;\n /** Injectable DNS resolver (primarily for tests). */\n resolveHostname?: ResolveHostname;\n /** Injectable fetch for redirect resolution (primarily for tests). */\n fetchImpl?: typeof fetch;\n }) {\n if (!options.url) {\n throw new Error('No URL provided');\n }\n // Validate the URL AND block private/loopback/link-local/metadata hosts\n // before fetching — `mirror()` fetches an arbitrary caller-supplied URL,\n // which is a classic SSRF sink (e.g. http://169.254.169.254/ metadata).\n // `fetchDocument` follows redirects on its own, so we resolve the redirect\n // chain ourselves first — re-validating every hop — and hand it the\n // already-validated terminal URL, closing the public-host-30x-to-internal\n // bypass (S5 #1388 / review #1562).\n let url: URL;\n try {\n url = await resolveSafeFinalUrl(options.url, {\n allowPrivateNetworkHosts: options.allowPrivateNetworkHosts,\n resolveHostname: options.resolveHostname,\n fetchImpl: options.fetchImpl,\n });\n } catch (error) {\n // Never echo the raw URL — it may carry userinfo credentials (review #1562).\n const safeUrl = redactUrlCredentials(options.url);\n logger.error('Refusing to mirror unsafe URL', { error, url: safeUrl });\n throw new Error(`Invalid URL provided: ${safeUrl}`);\n }\n const existing = await this.get({ url: options.url });\n if (existing) {\n return existing;\n }\n\n // Fetch and process the document via the already-validated terminal URL.\n const doc = await fetchDocument(url.toString(), {\n cacheDir: options?.mirrorDir,\n });\n\n const filename = url.pathname.split('/').pop();\n const nameWithoutExtension = filename?.replace(/\\.[^/.]+$/, '');\n const title = nameWithoutExtension?.replace(/[-_]/g, ' ');\n const slug = makeSlug(title as string);\n\n // Extract text from all document parts\n const body = doc.parts.map((part) => part.content).join('\\n\\n');\n if (body) {\n const content = new Content({\n url: options.url,\n type: 'mirror',\n title,\n slug,\n context: options.context || '',\n body,\n });\n await content.initialize();\n await content.save();\n return content;\n }\n }\n\n /**\n * Writes a Content object to the filesystem as a markdown file\n *\n * @param options - Options for writing the content file\n * @param options.content - Content object to write\n * @param options.contentDir - Directory to write the file to\n * @returns Promise that resolves when the file is written\n * @throws Error if contentDir is not provided\n */\n public async writeContentFile(options: {\n content: Content;\n contentDir: string;\n }) {\n const { content, contentDir } = options;\n if (!contentDir) {\n throw new Error('No content dir provided');\n }\n\n const { body } = content;\n const frontMatter = {\n title: content.title,\n slug: content.slug,\n context: content.context,\n author: content.author,\n publish_date: content.publish_date,\n };\n\n let output = '';\n if (frontMatter && Object.keys(frontMatter).length > 0) {\n output += '---\\n';\n output += YAML.stringify(frontMatter);\n output += '---\\n';\n }\n\n // Filesystem exports are markdown regardless of the editor save format.\n let formattedBody = body || '';\n const bodyFormat = resolveBodyFormat(content.bodyFormat, body);\n if (bodyFormat === 'html') {\n formattedBody = htmlToMarkdown(body || '');\n } else if (body && !this.isMarkdown(body)) {\n formattedBody = this.formatAsMarkdown(body);\n }\n output += formattedBody;\n\n const pathParts = [\n contentDir,\n content.context || '', // if empty, use empty string\n content.slug,\n 'index.md',\n ].filter(Boolean); // remove empty strings\n\n const outputFile = path.join(...(pathParts as string[]));\n\n // `context` and `slug` are persisted, caller-influenced fields. Without a\n // guard, a value like `../../etc/cron.d/x` escapes `contentDir` and lets an\n // export overwrite arbitrary files (path traversal). Confirm the joined\n // path still lives under the resolved content directory (S5 #1388).\n const resolvedDir = path.resolve(contentDir);\n const resolvedFile = path.resolve(outputFile);\n if (\n resolvedFile !== resolvedDir &&\n !resolvedFile.startsWith(resolvedDir + path.sep)\n ) {\n throw new Error(\n 'Refusing to write content file outside of the content directory',\n );\n }\n\n await ensureDirectoryExists(path.dirname(outputFile));\n await writeFile(outputFile, output);\n }\n\n /**\n * Checks if text appears to be in markdown format\n *\n * @param text - Text to check\n * @returns Boolean indicating if the text contains markdown syntax\n */\n private isMarkdown(text: string): boolean {\n // Basic check for common markdown indicators\n const markdownIndicators = [\n /^#\\s/m, // Headers\n /\\*\\*.+\\*\\*/, // Bold\n /\\*.+\\*/, // Italic\n /\\[.+\\]\\(.+\\)/, // Links\n /^\\s*[-*+]\\s/m, // Lists\n /^\\s*\\d+\\.\\s/m, // Numbered lists\n /```[\\s\\S]*```/, // Code blocks\n /^\\s*>/m, // Blockquotes\n ];\n\n return markdownIndicators.some((indicator) => indicator.test(text));\n }\n\n /**\n * Formats plain text as simple markdown\n *\n * @param text - Plain text to format\n * @returns Text formatted as basic markdown\n */\n private formatAsMarkdown(text: string): string {\n // Basic formatting of plain text to markdown\n return text\n .split(/\\n\\n+/)\n .map((paragraph) => paragraph.trim())\n .filter(Boolean)\n .join('\\n\\n');\n }\n\n /**\n * Synchronizes content to the filesystem\n *\n * Writes all article-type Content objects to the filesystem\n * as markdown files.\n *\n * @param options - Sync options\n * @param options.contentDir - Directory to write content files to\n * @returns Promise that resolves when synchronization is complete\n */\n public async syncContentDir(options: { contentDir?: string }) {\n const contentFilter = {\n type: 'article',\n };\n\n const contents = await this.list({ where: contentFilter });\n for (const content of contents) {\n await this.writeContentFile({\n content,\n contentDir: options.contentDir || this.options.contentDir || '',\n });\n }\n }\n\n /**\n * Generate thumbnails for content that doesn't have one\n *\n * @param options - Options for bulk thumbnail generation\n * @returns Promise resolving to result object with generated images and failed content IDs\n *\n * @example Generate headline cards for all published articles\n * ```typescript\n * const result = await contents.generateMissingThumbnails({\n * strategy: 'headline-card',\n * where: { type: 'article', status: 'published' },\n * brandColor: '#1a56db'\n * });\n * console.log(`Generated ${result.images.length} thumbnails`);\n * if (result.failed.length > 0) {\n * console.warn(`Failed to generate ${result.failed.length} thumbnails`);\n * }\n * ```\n */\n public async generateMissingThumbnails(\n options: GenerateMissingThumbnailsOptions,\n ): Promise<{\n images: Image[];\n failed: Array<{ contentId: string; error: string }>;\n }> {\n // Merge with thumbnail config from smrt.config.js (passed via constructor)\n // This allows CLI users to configure defaults in smrt.config.js:\n // thumbnail: { strategy: 'headline-card', brandColor: '#1976d2' }\n const configDefaults = this.options?.thumbnail || {};\n const mergedOptions = {\n ...configDefaults,\n ...options,\n };\n\n // Build query for content missing thumbnails\n const whereClause = {\n ...mergedOptions.where,\n thumbnailAssetId: null,\n };\n\n const contents = await this.list({\n where: whereClause,\n limit: mergedOptions.limit,\n });\n\n const generatedImages: Image[] = [];\n const failed: Array<{ contentId: string; error: string }> = [];\n\n for (const content of contents) {\n try {\n // Build thumbnail options based on strategy\n let thumbnailOptions: ThumbnailOptions;\n\n switch (mergedOptions.strategy) {\n case 'headline-card':\n thumbnailOptions = {\n strategy: 'headline-card',\n brandColor: mergedOptions.brandColor,\n backgroundColor: mergedOptions.backgroundColor,\n logoUrl: mergedOptions.logoUrl,\n template: mergedOptions.template,\n width: mergedOptions.width,\n height: mergedOptions.height,\n };\n break;\n\n case 'static-map':\n thumbnailOptions = {\n strategy: 'static-map',\n mapProvider: mergedOptions.mapProvider,\n zoom: mergedOptions.zoom,\n width: mergedOptions.width,\n height: mergedOptions.height,\n };\n break;\n\n case 'ai-generate':\n thumbnailOptions = {\n strategy: 'ai-generate',\n style: mergedOptions.style,\n width: mergedOptions.width,\n height: mergedOptions.height,\n ai: isAIClientOptions(this.options.ai)\n ? this.options.ai\n : undefined,\n };\n break;\n\n default:\n throw new Error(`Unknown strategy: ${mergedOptions.strategy}`);\n }\n\n const image = await content.generateThumbnail(thumbnailOptions);\n generatedImages.push(image);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n logger.error(\n `Failed to generate thumbnail for content ${content.id}: ${errorMessage}`,\n );\n failed.push({\n contentId: content.id ?? 'unknown',\n error: errorMessage,\n });\n }\n }\n\n return { images: generatedImages, failed };\n }\n\n // ============================================\n // Tenant Helper Methods\n // ============================================\n\n /**\n * Find all content belonging to a specific tenant\n *\n * @param tenantId - The tenant ID to filter by\n * @returns Promise resolving to array of Content objects for the tenant\n *\n * @example\n * ```typescript\n * const tenantContent = await contents.findByTenant('tenant-123');\n * ```\n */\n async findByTenant(tenantId: string): Promise<Content[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global content (not associated with any tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Promise resolving to array of global Content objects\n *\n * @example\n * ```typescript\n * const globalContent = await contents.findGlobal();\n * ```\n */\n async findGlobal(): Promise<Content[]> {\n return queryGlobal<Content>(this);\n }\n\n /**\n * Find content for a tenant including global content.\n *\n * This returns both tenant-specific content and global content (tenantId is null),\n * useful for showing a tenant their content plus any shared/global resources.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - The tenant ID to include\n * @returns Promise resolving to array of Content objects (tenant + global)\n *\n * @example\n * ```typescript\n * const allAccessibleContent = await contents.findWithGlobals('tenant-123');\n * ```\n */\n async findWithGlobals(tenantId: string): Promise<Content[]> {\n return queryWithGlobals<Content>(this, tenantId, 'Content.findWithGlobals');\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport {\n ObjectRegistry,\n type SmrtCollectionOptions,\n} from '@happyvertical/smrt-core';\nimport {\n type ParsedContentFeedItem,\n parseContentFeed,\n} from './content-feed-parser';\nimport type { ContentFeedSource } from './content-feed-source';\nimport { Mirror } from './content-types';\nimport { Contents } from './contents';\nimport { assertSafeRemoteUrl, type ResolveHostname } from './safe-remote-url';\n\nexport interface ContentFeedSyncOptions extends SmrtCollectionOptions {\n fetch?: typeof fetch;\n maxItems?: number;\n maxResponseBytes?: number;\n fetchTimeoutMs?: number;\n allowPrivateNetworkHosts?: boolean;\n resolveHostname?: ResolveHostname;\n status?: 'published' | 'draft';\n now?: () => Date;\n}\n\nexport interface ContentFeedSyncResult {\n source: ContentFeedSource;\n fetched: boolean;\n notModified: boolean;\n imported: number;\n updated: number;\n skipped: number;\n}\n\ntype QueryableCollection = Pick<Contents, 'query'>;\n\nconst DEFAULT_MAX_RESPONSE_BYTES = 2_000_000;\nconst DEFAULT_FETCH_TIMEOUT_MS = 10_000;\nconst MAX_FEED_REDIRECTS = 5;\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\nconst FALLBACK_MIRROR_META_TYPE = '@happyvertical/smrt-content:Mirror';\n\nfunction getMirrorMetaType(): string {\n return (\n ObjectRegistry.getClassByConstructor(Mirror)?.qualifiedName ??\n FALLBACK_MIRROR_META_TYPE\n );\n}\n\nfunction slugify(value: string): string {\n const slug = value\n .toLowerCase()\n .replace(/&/g, ' and ')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80);\n return slug || 'feed';\n}\n\nfunction normalizeUrlIdentity(value: string): string {\n try {\n const url = new URL(value);\n url.hash = '';\n url.searchParams.sort();\n return url.toString().replace(/\\/$/, '');\n } catch {\n return value.trim();\n }\n}\n\nfunction hashString(value: string): string {\n let hash = 5381;\n for (let index = 0; index < value.length; index += 1) {\n hash = (hash * 33) ^ value.charCodeAt(index);\n }\n return (hash >>> 0).toString(36);\n}\n\nfunction createDedupeKey(\n source: ContentFeedSource,\n item: ParsedContentFeedItem,\n): string {\n return `feed:${source.id ?? source.feedUrl}:${item.guid || normalizeUrlIdentity(item.url)}`;\n}\n\nasync function validateFeedFetchUrl(\n feedUrl: string,\n options: ContentFeedSyncOptions,\n): Promise<URL> {\n return assertSafeRemoteUrl(feedUrl, {\n allowPrivateNetworkHosts: options.allowPrivateNetworkHosts,\n resolveHostname: options.resolveHostname,\n });\n}\n\nfunction createTimeoutSignal(timeoutMs: number): AbortSignal | undefined {\n if (timeoutMs <= 0) return undefined;\n return AbortSignal.timeout(timeoutMs);\n}\n\n/**\n * Fetch a feed following redirects manually so every hop's target is\n * re-validated through {@link validateFeedFetchUrl}. `fetch()`'s default\n * `redirect: 'follow'` would let an allowed public feed 30x-redirect to an\n * internal/link-local/metadata host, defeating the up-front SSRF check\n * (S5 #1388). Returns the response together with the final validated URL so\n * the parser uses the redirect target as the feed base.\n */\nasync function fetchFeedWithRedirectGuard(\n fetchImpl: typeof fetch,\n startUrl: URL,\n headers: Record<string, string>,\n options: ContentFeedSyncOptions,\n): Promise<{ response: Response; finalUrl: URL }> {\n let current = startUrl;\n\n for (let hop = 0; hop <= MAX_FEED_REDIRECTS; hop += 1) {\n const response = await fetchImpl(current, {\n headers,\n redirect: 'manual',\n signal: createTimeoutSignal(\n options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS,\n ),\n });\n\n // Only the redirect statuses (not 304 Not Modified / 305 / 306) reroute the\n // request; everything else (200, 304, 4xx, 5xx) is returned to the caller.\n if (REDIRECT_STATUSES.has(response.status)) {\n const location = response.headers.get('location');\n if (!location) {\n throw new Error('Feed redirect response missing Location header');\n }\n // Re-run the full SSRF validation against the resolved redirect target.\n current = await validateFeedFetchUrl(\n new URL(location, current).toString(),\n options,\n );\n continue;\n }\n\n return { response, finalUrl: current };\n }\n\n throw new Error('Feed URL exceeded the maximum number of redirects');\n}\n\nasync function readResponseText(\n response: Response,\n maxBytes: number,\n): Promise<string> {\n const contentLength = response.headers.get('content-length');\n if (contentLength && Number(contentLength) > maxBytes) {\n throw new Error(`Feed response exceeds ${maxBytes} bytes`);\n }\n\n if (!response.body) {\n const text = await response.text();\n if (new TextEncoder().encode(text).byteLength > maxBytes) {\n throw new Error(`Feed response exceeds ${maxBytes} bytes`);\n }\n return text;\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(`Feed response exceeds ${maxBytes} bytes`);\n }\n chunks.push(value);\n }\n\n const buffer = new Uint8Array(totalBytes);\n let offset = 0;\n for (const chunk of chunks) {\n buffer.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return new TextDecoder().decode(buffer);\n}\n\nasync function findExistingMirror(\n contents: QueryableCollection,\n source: ContentFeedSource,\n item: ParsedContentFeedItem,\n dedupeKey: string,\n): Promise<{ id: string; status: string | null } | null> {\n const tenantWhere = source.tenantId ? 'tenant_id = ?' : 'tenant_id IS NULL';\n const params: unknown[] = [getMirrorMetaType()];\n if (source.tenantId) params.push(source.tenantId);\n\n if (item.guid) {\n const result = await contents.query(\n `SELECT id, status\n FROM contents\n WHERE _meta_type = ?\n AND ${tenantWhere}\n AND feed_source_id = ?\n AND source_guid = ?\n LIMIT 1`,\n [...params, source.id, item.guid],\n );\n if (result[0]) return result[0] as { id: string; status: string | null };\n }\n\n const result = await contents.query(\n `SELECT id, status\n FROM contents\n WHERE _meta_type = ?\n AND ${tenantWhere}\n AND feed_source_id = ?\n AND dedupe_key = ?\n LIMIT 1`,\n [...params, source.id, dedupeKey],\n );\n return (\n (result[0] as { id: string; status: string | null } | undefined) ?? null\n );\n}\n\nasync function upsertMirrorItem(\n contents: QueryableCollection,\n source: ContentFeedSource,\n item: ParsedContentFeedItem,\n status: 'published' | 'draft',\n now: Date,\n): Promise<'imported' | 'updated'> {\n const dedupeKey = createDedupeKey(source, item);\n const existing = await findExistingMirror(contents, source, item, dedupeKey);\n const publishedAt = (item.publishedAt ?? item.updatedAt ?? now).toISOString();\n const sourceSlug = slugify(\n source.sourceGroup || source.name || source.feedUrl,\n );\n const category = source.defaultCategory || item.categories[0] || null;\n const tags = JSON.stringify(item.categories);\n\n if (existing) {\n await contents.query(\n `UPDATE contents\n SET title = ?,\n name = ?,\n description = ?,\n source = ?,\n original_url = ?,\n url = ?,\n feed_source_id = ?,\n source_guid = ?,\n source_name = ?,\n source_homepage_url = ?,\n external_url = ?,\n original_published_at = ?,\n dedupe_key = ?,\n publish_date = ?,\n author = ?,\n category = ?,\n tags = ?,\n status = ?,\n updated_at = ?\n WHERE id = ?`,\n [\n item.title,\n item.title,\n item.summary,\n source.name,\n item.url,\n item.url,\n source.id,\n item.guid,\n source.name,\n source.homepageUrl,\n item.url,\n publishedAt,\n dedupeKey,\n publishedAt,\n item.author,\n category,\n tags,\n existing.status || status,\n now.toISOString(),\n existing.id,\n ],\n );\n return 'updated';\n }\n\n const id = randomUUID();\n await contents.query(\n `INSERT INTO contents (\n id, slug, context, _meta_type, title, name, description, body,\n status, state, metadata, source, original_url, url, feed_source_id,\n source_guid, source_name, source_homepage_url, external_url,\n original_published_at, dedupe_key, tenant_id, publish_date, author,\n category, tags, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n id,\n `mirror-${sourceSlug}-${hashString(dedupeKey)}`,\n '',\n getMirrorMetaType(),\n item.title,\n item.title,\n item.summary,\n '',\n status,\n 'active',\n '{}',\n source.name,\n item.url,\n item.url,\n source.id,\n item.guid,\n source.name,\n source.homepageUrl,\n item.url,\n publishedAt,\n dedupeKey,\n source.tenantId,\n publishedAt,\n item.author,\n category,\n tags,\n now.toISOString(),\n now.toISOString(),\n ],\n );\n return 'imported';\n}\n\nexport async function syncContentFeedSource(\n source: ContentFeedSource,\n options: ContentFeedSyncOptions = {},\n): Promise<ContentFeedSyncResult> {\n if (source.status === 'paused' || source.status === 'archived') {\n return {\n source,\n fetched: false,\n notModified: false,\n imported: 0,\n updated: 0,\n skipped: 0,\n };\n }\n\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (!fetchImpl) throw new Error('No fetch implementation available');\n\n const headers: Record<string, string> = {\n Accept:\n 'application/rss+xml, application/atom+xml, application/xml, text/xml',\n 'User-Agent': '@happyvertical/smrt-content feed sync',\n };\n if (source.etag) headers['If-None-Match'] = source.etag;\n if (source.lastModified) headers['If-Modified-Since'] = source.lastModified;\n\n const now = options.now?.() ?? new Date();\n source.markFetchStarted(now);\n await source.save();\n\n try {\n const feedUrl = await validateFeedFetchUrl(source.feedUrl, options);\n const { response, finalUrl } = await fetchFeedWithRedirectGuard(\n fetchImpl,\n feedUrl,\n headers,\n options,\n );\n\n if (response.status === 304) {\n source.markFetchSucceeded(now);\n await source.save();\n return {\n source,\n fetched: true,\n notModified: true,\n imported: 0,\n updated: 0,\n skipped: 0,\n };\n }\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const parsed = parseContentFeed(\n await readResponseText(\n response,\n options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES,\n ),\n finalUrl.toString(),\n );\n source.format = parsed.format;\n source.homepageUrl = source.homepageUrl || parsed.homepageUrl;\n if (!source.name && parsed.title) source.name = parsed.title;\n\n const contents = await Contents.create(options);\n let imported = 0;\n let updated = 0;\n let skipped = 0;\n\n for (const item of parsed.items.slice(0, options.maxItems)) {\n try {\n const result = await upsertMirrorItem(\n contents,\n source,\n item,\n options.status ?? 'published',\n now,\n );\n if (result === 'imported') imported += 1;\n else updated += 1;\n } catch {\n skipped += 1;\n }\n }\n\n source.markFetchSucceeded(now, {\n etag: response.headers.get('etag'),\n lastModified: response.headers.get('last-modified'),\n });\n await source.save();\n\n return {\n source,\n fetched: true,\n notModified: false,\n imported,\n updated,\n skipped,\n };\n } catch (error) {\n source.markFetchFailed(error, now);\n await source.save();\n throw error;\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { FactContentRelationship } from '@happyvertical/smrt-facts';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n buildContentGovernanceAssignmentKey,\n type ContentGovernanceAssignmentDefinition,\n getEffectiveContentGovernanceConfig,\n} from './content-governance';\n\nexport interface ContentGovernanceAssignmentOptions extends SmrtObjectOptions {\n key?: string;\n label?: string;\n contentType?: string;\n contentVariant?: string | null;\n enabled?: boolean;\n factLinkingEnabled?: boolean;\n transparencyEnabled?: boolean;\n publicationProfileKey?: string | null;\n correctionProfileKey?: string | null;\n enforcePublishReadiness?: boolean;\n defaultFactRelationship?: FactContentRelationship;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_governance_assignments',\n conflictColumns: ['key'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentGovernanceAssignment extends SmrtObject {\n @field({ required: true })\n key = '';\n\n label = '';\n\n @field({ required: true })\n contentType = '';\n\n contentVariant = '';\n enabled = true;\n factLinkingEnabled = false;\n transparencyEnabled = false;\n publicationProfileKey = '';\n correctionProfileKey = '';\n enforcePublishReadiness = false;\n defaultFactRelationship: FactContentRelationship = 'supports';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentGovernanceAssignmentOptions = {}) {\n super(options);\n if (options.contentType !== undefined)\n this.contentType = options.contentType;\n if (options.contentVariant !== undefined)\n this.contentVariant = options.contentVariant || '';\n if (options.label !== undefined) this.label = options.label;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.factLinkingEnabled !== undefined)\n this.factLinkingEnabled = options.factLinkingEnabled;\n if (options.transparencyEnabled !== undefined)\n this.transparencyEnabled = options.transparencyEnabled;\n if (options.publicationProfileKey !== undefined)\n this.publicationProfileKey = options.publicationProfileKey || '';\n if (options.correctionProfileKey !== undefined)\n this.correctionProfileKey = options.correctionProfileKey || '';\n if (options.enforcePublishReadiness !== undefined)\n this.enforcePublishReadiness = options.enforcePublishReadiness;\n if (options.defaultFactRelationship !== undefined)\n this.defaultFactRelationship = options.defaultFactRelationship;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n this.key =\n options.key ||\n buildContentGovernanceAssignmentKey(\n this.contentType,\n this.contentVariant,\n );\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n if (this.metadata && typeof this.metadata === 'object') {\n return { ...(this.metadata as Record<string, unknown>) };\n }\n\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n\n toDefinition(): ContentGovernanceAssignmentDefinition {\n return {\n key: this.key,\n label: this.label || '',\n contentType: this.contentType,\n contentVariant: this.contentVariant || '',\n enabled: this.enabled !== false,\n factLinkingEnabled: this.factLinkingEnabled === true,\n transparencyEnabled: this.transparencyEnabled === true,\n publicationProfileKey: this.publicationProfileKey || null,\n correctionProfileKey: this.correctionProfileKey || null,\n enforcePublishReadiness: this.enforcePublishReadiness === true,\n defaultFactRelationship: this.defaultFactRelationship || 'supports',\n metadata: this.getMetadata(),\n };\n }\n\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n\n this.key = buildContentGovernanceAssignmentKey(\n this.contentType,\n this.contentVariant,\n );\n\n const effective = await getEffectiveContentGovernanceConfig({\n db: this.db,\n });\n const availableProfileKeys = new Set(\n effective.profiles.map((profile) => profile.key),\n );\n const missingProfiles = [\n this.publicationProfileKey,\n this.correctionProfileKey,\n ].filter(\n (profileKey) => profileKey && !availableProfileKeys.has(profileKey),\n );\n\n if (missingProfiles.length > 0) {\n throw new Error(\n `Cannot save governance assignment \"${this.key}\" because these profiles are missing: ${missingProfiles.join(', ')}`,\n );\n }\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentGovernanceAssignment } from './content-governance-assignment';\n\nexport class ContentGovernanceAssignmentCollection extends SmrtCollection<ContentGovernanceAssignment> {\n static readonly _itemClass = ContentGovernanceAssignment;\n\n async getByKey(key: string): Promise<ContentGovernanceAssignment | null> {\n return this.get({ key });\n }\n\n async resolveForContent(options: {\n contentType: string;\n contentVariant?: string | null;\n }): Promise<ContentGovernanceAssignment | null> {\n const exact =\n (await this.get({\n key: `${options.contentType}::${options.contentVariant || ''}`,\n })) || null;\n\n if (exact) {\n return exact;\n }\n\n return this.get({\n key: `${options.contentType}::`,\n });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type ContentReviewKind,\n type ContentReviewPolicyDefinition,\n getEffectiveContentGovernanceConfig,\n getFallbackPolicyKind,\n hasStaticContentGovernancePolicy,\n} from './content-governance';\n\nexport interface ContentGovernancePolicyOptions extends SmrtObjectOptions {\n key?: string;\n label?: string;\n kind?: ContentReviewKind;\n instructions?: string;\n enabled?: boolean;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_governance_policies',\n conflictColumns: ['key'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentGovernancePolicy extends SmrtObject {\n @field({ required: true })\n key = '';\n\n label = '';\n kind: ContentReviewKind = 'custom';\n instructions = '';\n enabled = true;\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentGovernancePolicyOptions = {}) {\n super(options);\n if (options.key !== undefined) this.key = options.key;\n if (options.label !== undefined) this.label = options.label;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.instructions !== undefined)\n this.instructions = options.instructions;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getMetadata(): Record<string, unknown> {\n if (this.metadata && typeof this.metadata === 'object') {\n return { ...(this.metadata as Record<string, unknown>) };\n }\n\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n\n toDefinition(): ContentReviewPolicyDefinition {\n return {\n key: this.key,\n label: this.label || this.key,\n kind: this.kind || getFallbackPolicyKind(this.key),\n instructions: this.instructions || '',\n enabled: this.enabled !== false,\n metadata: this.getMetadata(),\n };\n }\n\n override async delete(): Promise<void> {\n const effective = await getEffectiveContentGovernanceConfig({\n db: this.db,\n });\n const hasFallback = hasStaticContentGovernancePolicy(this.key);\n const referencedBy = effective.profiles\n .filter((profile) =>\n profile.requirements.some(\n (requirement) => requirement.policyKey === this.key,\n ),\n )\n .map((profile) => profile.key);\n\n if (!hasFallback && referencedBy.length > 0) {\n throw new Error(\n `Cannot delete governance policy \"${this.key}\" because it is referenced by profiles: ${referencedBy.join(', ')}`,\n );\n }\n\n return super.delete();\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentGovernancePolicy } from './content-governance-policy';\n\nexport class ContentGovernancePolicyCollection extends SmrtCollection<ContentGovernancePolicy> {\n static readonly _itemClass = ContentGovernancePolicy;\n\n async getByKey(key: string): Promise<ContentGovernancePolicy | null> {\n return this.get({ key });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n type ContentGovernanceProfileDefinition,\n type ContentReviewRequirement,\n getEffectiveContentGovernanceConfig,\n hasStaticContentGovernanceProfile,\n} from './content-governance';\n\nexport interface ContentGovernanceProfileOptions extends SmrtObjectOptions {\n key?: string;\n label?: string;\n description?: string;\n enabled?: boolean;\n requirements?: string | ContentReviewRequirement[];\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_governance_profiles',\n conflictColumns: ['key'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: true,\n})\nexport class ContentGovernanceProfile extends SmrtObject {\n @field({ required: true })\n key = '';\n\n label = '';\n description = '';\n enabled = true;\n requirements = '[]';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentGovernanceProfileOptions = {}) {\n super(options);\n if (options.key !== undefined) this.key = options.key;\n if (options.label !== undefined) this.label = options.label;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.requirements !== undefined) {\n this.requirements =\n typeof options.requirements === 'string'\n ? options.requirements\n : JSON.stringify(options.requirements);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getRequirements(): ContentReviewRequirement[] {\n if (Array.isArray(this.requirements)) {\n return this.requirements as ContentReviewRequirement[];\n }\n\n try {\n return this.requirements ? JSON.parse(this.requirements) : [];\n } catch {\n return [];\n }\n }\n\n getMetadata(): Record<string, unknown> {\n if (this.metadata && typeof this.metadata === 'object') {\n return { ...(this.metadata as Record<string, unknown>) };\n }\n\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n\n toDefinition(): ContentGovernanceProfileDefinition {\n return {\n key: this.key,\n label: this.label || this.key,\n description: this.description || '',\n enabled: this.enabled !== false,\n requirements: this.getRequirements(),\n metadata: this.getMetadata(),\n };\n }\n\n protected override async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n\n const effective = await getEffectiveContentGovernanceConfig({\n db: this.db,\n });\n const availablePolicyKeys = new Set(\n effective.policies.map((policy) => policy.key),\n );\n const missingPolicies = this.getRequirements()\n .map((requirement) => requirement.policyKey)\n .filter((policyKey) => !availablePolicyKeys.has(policyKey));\n\n if (missingPolicies.length > 0) {\n throw new Error(\n `Cannot save governance profile \"${this.key}\" because these policies are missing: ${missingPolicies.join(', ')}`,\n );\n }\n }\n\n override async delete(): Promise<void> {\n const effective = await getEffectiveContentGovernanceConfig({\n db: this.db,\n });\n const hasFallback = hasStaticContentGovernanceProfile(this.key);\n const referencedBy = effective.assignments\n .filter(\n (assignment) =>\n assignment.publicationProfileKey === this.key ||\n assignment.correctionProfileKey === this.key,\n )\n .map((assignment) => assignment.key || assignment.contentType);\n\n if (!hasFallback && referencedBy.length > 0) {\n throw new Error(\n `Cannot delete governance profile \"${this.key}\" because it is referenced by assignments: ${referencedBy.join(', ')}`,\n );\n }\n\n return super.delete();\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { ContentGovernanceProfile } from './content-governance-profile';\n\nexport class ContentGovernanceProfileCollection extends SmrtCollection<ContentGovernanceProfile> {\n static readonly _itemClass = ContentGovernanceProfile;\n\n async getByKey(key: string): Promise<ContentGovernanceProfile | null> {\n return this.get({ key });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n ContentReviewFinding,\n ContentReviewKind,\n ContentReviewStatus,\n} from './content-governance';\n\nexport interface ContentReviewOptions extends SmrtObjectOptions {\n contentId?: string;\n contentVersionId?: string;\n kind?: ContentReviewKind;\n policyKey?: string;\n status?: ContentReviewStatus;\n summary?: string;\n findings?: string | ContentReviewFinding[];\n reviewer?: string;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_reviews',\n api: { include: ['list', 'get', 'create', 'update'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: true,\n})\nexport class ContentReview extends SmrtObject {\n @foreignKey('Content', { required: true })\n contentId = '';\n\n @foreignKey('ContentVersion')\n contentVersionId = '';\n kind: ContentReviewKind = 'custom';\n policyKey = '';\n status: ContentReviewStatus = 'pending';\n summary = '';\n findings = '[]';\n reviewer = 'system';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentReviewOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.contentVersionId !== undefined)\n this.contentVersionId = options.contentVersionId;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.policyKey !== undefined) this.policyKey = options.policyKey;\n if (options.status !== undefined) this.status = options.status;\n if (options.summary !== undefined) this.summary = options.summary;\n if (options.reviewer !== undefined) this.reviewer = options.reviewer;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.findings !== undefined) {\n this.findings =\n typeof options.findings === 'string'\n ? options.findings\n : JSON.stringify(options.findings);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getFindings(): ContentReviewFinding[] {\n if (Array.isArray(this.findings)) {\n return this.findings as ContentReviewFinding[];\n }\n\n try {\n return this.findings ? JSON.parse(this.findings) : [];\n } catch {\n return [];\n }\n }\n\n getMetadata(): Record<string, unknown> {\n if (this.metadata && typeof this.metadata === 'object') {\n return { ...(this.metadata as Record<string, unknown>) };\n }\n\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport type {\n ContentReviewKind,\n ContentReviewResult,\n} from './content-governance';\nimport { ContentReview } from './content-review';\n\nexport class ContentReviewCollection extends SmrtCollection<ContentReview> {\n static readonly _itemClass = ContentReview;\n\n async listForContent(\n contentId: string,\n kind?: ContentReviewKind,\n ): Promise<ContentReview[]> {\n return this.list({\n where: kind ? { contentId, kind } : { contentId },\n orderBy: 'created_at DESC',\n });\n }\n\n async getLatestForContent(\n contentId: string,\n kind?: ContentReviewKind,\n ): Promise<ContentReview | null> {\n const reviews = await this.listForContent(contentId, kind);\n return reviews[0] ?? null;\n }\n\n async listForContentByPolicyKey(\n contentId: string,\n policyKey: string,\n ): Promise<ContentReview[]> {\n return this.list({\n where: { contentId, policyKey },\n orderBy: 'created_at DESC',\n });\n }\n\n async getLatestForPolicyKey(\n contentId: string,\n policyKey: string,\n ): Promise<ContentReview | null> {\n const reviews = await this.listForContentByPolicyKey(contentId, policyKey);\n return reviews[0] ?? null;\n }\n\n async createFromResult(options: {\n contentId: string;\n contentVersionId?: string | null;\n kind: ContentReviewKind;\n policyKey: string;\n reviewer?: string;\n result: ContentReviewResult;\n metadata?: Record<string, unknown>;\n tenantId?: string | null;\n }): Promise<ContentReview> {\n return this.create({\n contentId: options.contentId,\n contentVersionId: options.contentVersionId || '',\n kind: options.kind,\n policyKey: options.policyKey,\n status: options.result.status,\n summary: options.result.summary,\n findings: JSON.stringify(options.result.findings),\n reviewer: options.reviewer || 'system',\n metadata: JSON.stringify(options.metadata || {}),\n tenantId: options.tenantId ?? null,\n });\n }\n}\n","import type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { ContentVersionKind } from './content-governance';\nimport { normalizeContentTransparency } from './content-transparency';\n\n/**\n * Shape of the loosely-structured `metadata` JSON bag stored on a content\n * version. Known fields are typed; unrecognized keys remain accessible via the\n * index signature.\n */\nexport interface ContentVersionMetadata {\n /**\n * Fingerprint of the snapshot captured for the latest publication version,\n * used to detect post-publication drift.\n */\n publicationSnapshotFingerprint?: string;\n [key: string]: unknown;\n}\n\nexport interface ContentVersionOptions extends SmrtObjectOptions {\n contentId?: string;\n version?: number;\n kind?: ContentVersionKind;\n title?: string;\n description?: string;\n body?: string;\n status?: string;\n summary?: string;\n snapshot?: string | Record<string, unknown>;\n metadata?: string | Record<string, unknown>;\n tenantId?: string | null;\n createdAt?: Date;\n updatedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'content_versions',\n conflictColumns: ['content_id', 'version'],\n api: {\n include: ['list', 'get', 'create', 'getTransparencyAction'],\n routes: {\n getTransparencyAction: { method: 'GET', path: 'transparency' },\n },\n },\n mcp: { include: ['list', 'get', 'create'] },\n cli: true,\n})\nexport class ContentVersion extends SmrtObject {\n @foreignKey('Content', { required: true })\n contentId = '';\n\n version = 1;\n kind: ContentVersionKind = 'manual';\n title = '';\n description = '';\n body = '';\n status = 'draft';\n summary = '';\n snapshot = '{}';\n metadata = '';\n\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: ContentVersionOptions = {}) {\n super(options);\n if (options.contentId) this.contentId = options.contentId;\n if (options.version !== undefined) this.version = options.version;\n if (options.kind !== undefined) this.kind = options.kind;\n if (options.title !== undefined) this.title = options.title;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.body !== undefined) this.body = options.body;\n if (options.status !== undefined) this.status = options.status;\n if (options.summary !== undefined) this.summary = options.summary;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n\n if (options.snapshot !== undefined) {\n this.snapshot =\n typeof options.snapshot === 'string'\n ? options.snapshot\n : JSON.stringify(options.snapshot);\n }\n\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : JSON.stringify(options.metadata);\n }\n }\n\n getSnapshot(): Record<string, unknown> {\n try {\n return this.snapshot ? JSON.parse(this.snapshot) : {};\n } catch {\n return {};\n }\n }\n\n getMetadata(): ContentVersionMetadata {\n try {\n return this.metadata ? JSON.parse(this.metadata) : {};\n } catch {\n return {};\n }\n }\n\n getTransparency() {\n const metadata = this.getMetadata();\n const snapshot = this.getSnapshot();\n\n return normalizeContentTransparency(metadata.transparency, {\n snapshotKind: this.kind === 'publication' ? 'published' : 'preview',\n contentId: this.contentId || (snapshot.contentId as string) || null,\n currentContentStatus:\n this.status || (snapshot.status as string) || 'draft',\n publicationVersion: {\n id: (this.id as string) || null,\n version: this.version ?? null,\n kind: this.kind || null,\n summary: this.summary || '',\n createdAt:\n this.createdAt instanceof Date ? this.createdAt.toISOString() : null,\n },\n });\n }\n\n async getTransparencyAction() {\n return this.getTransparency();\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport type { FactContentRelationship } from '@happyvertical/smrt-facts';\nimport type { Content } from './content';\nimport type { CreateContentVersionOptions } from './content-governance';\nimport { resolveEffectiveContentGovernance } from './content-governance';\nimport { ContentVersion } from './content-version';\n\nexport class ContentVersionCollection extends SmrtCollection<ContentVersion> {\n static readonly _itemClass = ContentVersion;\n\n private buildSnapshotFactRelationships(\n snapshot: Record<string, unknown>,\n defaultRelationship: FactContentRelationship,\n ): Map<FactContentRelationship, string[]> {\n const byRelationship = new Map<FactContentRelationship, string[]>();\n const rawLinks: unknown[] = Array.isArray(snapshot.factLinks)\n ? snapshot.factLinks\n : [];\n\n for (const rawLink of rawLinks) {\n const link = rawLink as {\n factId?: unknown;\n relationship?: unknown;\n } | null;\n const factId =\n typeof link?.factId === 'string' && link.factId.length > 0\n ? link.factId\n : null;\n const relationship =\n typeof link?.relationship === 'string' && link.relationship.length > 0\n ? (link.relationship as FactContentRelationship)\n : defaultRelationship;\n\n if (!factId) {\n continue;\n }\n\n byRelationship.set(relationship, [\n ...(byRelationship.get(relationship) || []),\n factId,\n ]);\n }\n\n if (\n byRelationship.size === 0 &&\n Array.isArray(snapshot.factIds) &&\n snapshot.factIds.length > 0\n ) {\n byRelationship.set(\n defaultRelationship,\n snapshot.factIds.filter(\n (factId: unknown): factId is string =>\n typeof factId === 'string' && factId.length > 0,\n ),\n );\n }\n\n return byRelationship;\n }\n\n async listForContent(contentId: string): Promise<ContentVersion[]> {\n return this.list({\n where: { contentId },\n orderBy: 'version ASC',\n });\n }\n\n async getLatestForContent(contentId: string): Promise<ContentVersion | null> {\n const versions = await this.listForContent(contentId);\n return versions.length > 0 ? versions[versions.length - 1] : null;\n }\n\n async getLatestPublishedForContent(\n contentId: string,\n ): Promise<ContentVersion | null> {\n const versions = await this.list({\n where: {\n contentId,\n kind: 'publication',\n },\n orderBy: 'version DESC',\n });\n\n return versions[0] || null;\n }\n\n async getVersion(\n contentId: string,\n versionNumber: number,\n ): Promise<ContentVersion | null> {\n return this.get({\n contentId,\n version: versionNumber,\n });\n }\n\n async getNextVersionNumber(contentId: string): Promise<number> {\n const latest = await this.getLatestForContent(contentId);\n return latest ? latest.version + 1 : 1;\n }\n\n async createSnapshot(\n content: Content,\n options: CreateContentVersionOptions = {},\n ): Promise<ContentVersion> {\n if (!content.id) {\n throw new Error('Cannot create a version for unsaved content');\n }\n\n const version = await this.getNextVersionNumber(content.id as string);\n const governance = await resolveEffectiveContentGovernance({\n contentType: content.type,\n contentVariant: content.variant,\n db: this.db,\n tenantId: content.tenantId ?? null,\n });\n const [references, referenceEdges, assets, factsState] = await Promise.all([\n typeof content.getReferences === 'function'\n ? content.getReferences()\n : [],\n // Capture per-edge citation pins so restore can reconstruct them.\n // `getReferences()` resolves to Content objects and loses targetVersion.\n typeof content.getReferenceEdges === 'function'\n ? content.getReferenceEdges()\n : Promise.resolve([]),\n typeof content.getAssets === 'function' ? content.getAssets() : [],\n typeof content.getFactsState === 'function' &&\n governance.factLinkingEnabled\n ? content.getFactsState()\n : {\n factIds: [],\n facts: [],\n factLinks: [],\n },\n ]);\n\n const baseSnapshot = {\n id: content.id,\n slug: content.slug,\n context: content.context,\n name: content.name,\n type: content.type,\n variant: content.variant,\n fileKey: content.fileKey,\n author: content.author,\n title: content.title,\n description: content.description,\n body: content.body,\n bodyFormat: content.bodyFormat,\n publish_date: content.publish_date,\n url: content.url,\n source: content.source,\n original_url: content.original_url,\n language: content.language,\n tags: [...content.tags],\n category: content.category,\n status: content.status,\n state: content.state,\n metadata: content.metadata,\n thumbnailAssetId: content.thumbnailAssetId,\n referenceIds: references.map((reference) => reference.id).filter(Boolean),\n // Full edges with citation pins; `referenceIds` retained for back-compat\n // with snapshots written before pin-aware restore (#1387 #3).\n referenceEdges: referenceEdges.filter((edge) => Boolean(edge.targetId)),\n assetIds: assets.map((asset) => asset.id).filter(Boolean),\n factIds: factsState.factIds,\n factLinks: factsState.factLinks,\n tenantId: content.tenantId,\n _meta_type: content.toJSON()._meta_type,\n };\n const snapshot = {\n ...baseSnapshot,\n ...(options.snapshot || {}),\n };\n const versionSlugBase =\n snapshot.slug ||\n content.slug ||\n content.name ||\n content.title ||\n content.id;\n const versionSlug = `${versionSlugBase}-v${version}`;\n\n return this.create({\n slug: versionSlug,\n context: content.context || '',\n contentId: content.id as string,\n version,\n kind: options.kind || 'manual',\n title: snapshot.title || '',\n description: snapshot.description || '',\n body: snapshot.body || '',\n status: snapshot.status || 'draft',\n summary: options.summary || '',\n snapshot: JSON.stringify(snapshot),\n metadata: JSON.stringify(options.metadata || {}),\n tenantId: content.tenantId,\n });\n }\n\n async restoreIntoContent(\n content: Content,\n versionNumber: number,\n ): Promise<Content> {\n if (!content.id) {\n throw new Error('Cannot restore an unsaved content item');\n }\n\n const version = await this.getVersion(content.id as string, versionNumber);\n if (!version) {\n throw new Error(\n `Content version ${versionNumber} not found for content ${content.id}`,\n );\n }\n\n const snapshot = version.getSnapshot();\n const keysToRestore = [\n 'name',\n 'type',\n 'variant',\n 'fileKey',\n 'author',\n 'title',\n 'description',\n 'body',\n 'bodyFormat',\n 'publish_date',\n 'url',\n 'source',\n 'original_url',\n 'language',\n 'tags',\n 'category',\n 'status',\n 'state',\n 'metadata',\n 'thumbnailAssetId',\n ];\n\n // Restore snapshot values onto the live Content instance by field name.\n // Indexing a class instance by an arbitrary string key requires a record\n // view; the keys are a fixed, known set of Content fields.\n const writableContent = content as unknown as Record<string, unknown>;\n for (const key of keysToRestore) {\n if (snapshot[key] !== undefined) {\n writableContent[key] = snapshot[key];\n }\n }\n\n // Reference edges with citation pins (#1387 #3). Newer snapshots carry\n // `referenceEdges` ({ targetId, targetVersion }); older ones only have\n // `referenceIds`. Either way, seed the pending `referenceIds` from the\n // target ids so `save()` reconciles the set (adds missing, removes extra),\n // then re-apply the saved pins below so restoring \"to vN\" reconstructs the\n // citation pins that existed at vN instead of dropping them to unpinned.\n const snapshotEdges: Array<{\n targetId: string;\n targetVersion: number | null;\n }> = Array.isArray(snapshot.referenceEdges)\n ? (snapshot.referenceEdges as unknown[])\n .filter(\n (edge): edge is { targetId: string; targetVersion?: unknown } =>\n !!edge &&\n typeof edge === 'object' &&\n typeof (edge as { targetId?: unknown }).targetId === 'string' &&\n (edge as { targetId: string }).targetId.length > 0,\n )\n .map((edge) => ({\n targetId: edge.targetId,\n targetVersion:\n typeof edge.targetVersion === 'number'\n ? edge.targetVersion\n : null,\n }))\n : Array.isArray(snapshot.referenceIds)\n ? snapshot.referenceIds\n .filter(\n (id: unknown): id is string =>\n typeof id === 'string' && id.length > 0,\n )\n .map((targetId: string) => ({ targetId, targetVersion: null }))\n : [];\n\n if (\n Array.isArray(snapshot.referenceEdges) ||\n Array.isArray(snapshot.referenceIds)\n ) {\n writableContent.referenceIds = snapshotEdges.map((edge) => edge.targetId);\n }\n\n if (Array.isArray(snapshot.assetIds)) {\n writableContent.assetIds = [...snapshot.assetIds];\n }\n\n await content.save();\n\n // Re-apply the citation pin of EVERY snapshot edge — including UNPINNED\n // ones (`targetVersion: null`). `save()` only reconciles the target-id set\n // (adds missing / removes extra edges) and leaves the pin of an edge that\n // already existed untouched. So restoring an *unpinned* snapshot over an\n // edge that is currently *pinned* must explicitly clear that pin, otherwise\n // the live pin survives the restore and drift never resets. Passing\n // `addReference(target, { targetVersion: null })` clears the pin in place\n // (the junction's `attach` updates the row when `null !== existing`), while\n // a non-null value (re)sets it — so \"restore to vN\" reconstructs exactly\n // the pins that existed at vN.\n //\n // We pass the resolved Content object (not the raw id) because\n // `addReference(string)` treats the string as a URL, not a content id.\n // `addReference` is idempotent on (source, target) and only adjusts\n // targetVersion.\n if (\n snapshotEdges.length > 0 &&\n typeof content.getReferences === 'function' &&\n typeof content.addReference === 'function'\n ) {\n const resolvedReferences = await content.getReferences();\n const resolvedById = new Map(\n resolvedReferences\n .filter((reference) => reference.id)\n .map((reference) => [reference.id as string, reference]),\n );\n for (const edge of snapshotEdges) {\n const target = resolvedById.get(edge.targetId);\n if (target) {\n await content.addReference(target, {\n targetVersion: edge.targetVersion,\n });\n }\n }\n }\n\n const governance = await resolveEffectiveContentGovernance({\n contentType: content.type,\n contentVariant: content.variant,\n db: this.db,\n tenantId: content.tenantId ?? null,\n });\n\n if (\n governance.isGoverned &&\n governance.factLinkingEnabled &&\n typeof content.getFactLinks === 'function' &&\n typeof content.syncFacts === 'function'\n ) {\n const desiredByRelationship = this.buildSnapshotFactRelationships(\n snapshot,\n governance.defaultFactRelationship,\n );\n const currentLinks = await content.getFactLinks();\n const currentRelationships = new Set(\n currentLinks.map(\n (link) =>\n (link.relationship as FactContentRelationship) ||\n governance.defaultFactRelationship,\n ),\n );\n const relationshipsToSync = new Set<FactContentRelationship>([\n ...currentRelationships,\n ...desiredByRelationship.keys(),\n ]);\n\n for (const relationship of relationshipsToSync) {\n await content.syncFacts(\n desiredByRelationship.get(relationship) || [],\n relationship,\n );\n }\n }\n\n return content;\n }\n}\n","import yaml from 'yaml';\nimport type { Content } from './content';\n\n/**\n * Converts a Content object to a string with YAML frontmatter\n *\n * @param content - Content object to convert\n * @returns String with YAML frontmatter and body content\n */\nexport function contentToString(content: Content) {\n const { body, ...frontmatter } = content;\n const separator = '---';\n const frontmatterYAML = yaml.stringify(frontmatter);\n return `${separator}\\n${frontmatterYAML}\\n${separator}\\n${body}`;\n}\n\n/**\n * Converts a string with YAML frontmatter to a Content object\n *\n * @param data - String with YAML frontmatter and body content\n * @returns Object with parsed frontmatter and body content\n */\nexport function stringToContent(data: string) {\n const separator = '---';\n const frontmatterStart = data.indexOf(separator);\n\n let frontmatter = {};\n let body = data;\n\n if (frontmatterStart !== -1) {\n const frontmatterEnd = data.indexOf(\n separator,\n frontmatterStart + separator.length,\n );\n\n if (frontmatterEnd !== -1) {\n const frontmatterYAML = data\n .substring(frontmatterStart + separator.length, frontmatterEnd)\n .trim();\n // yaml.parse() THROWS on malformed YAML — `|| {}` only covers an\n // empty/null parse. Wrap so a bad frontmatter block degrades to \"no\n // frontmatter\" instead of crashing the caller (#1387).\n try {\n frontmatter = yaml.parse(frontmatterYAML) || {};\n } catch {\n frontmatter = {};\n }\n body = data.substring(frontmatterEnd + separator.length).trim();\n }\n }\n\n return {\n ...frontmatter,\n body,\n };\n}\n"],"x_google_ignoreList":[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54],"mappings":"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CJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EAEb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EAEb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;AACF;;;;;;;;;;;;ACjEA,IAAM,eAAe;CACnB;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EAGb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;AACF;;;;;;;;;;;;;;;;AChEA,IAAM,eAAe;CACnB;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EACb,SAAS;CACX;CACA;EACE,IAAI;EACJ,aAAa;EA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"}
|