/** * This Source Code is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) Infonomic Company Limited */ /** * Richtext populate service — walks a reconstructed document, finds every * rich-text leaf (including those nested inside `group` / `array` / * `blocks` structures), gates each leaf by its `populateRelationsOnRead` * flag, and dispatches to the registered richtext populate adapter. * * Slots into the read pipeline alongside `populateDocuments`: * * findDocuments → reconstruct → populateDocuments → populateRichTextFields → afterRead * * The same `ReadContext` flows through both populate phases, so dedup / * cycle protection / read-budget enforcement covers rich-text fan-out * automatically and any nested reads the adapter performs. * * The adapter is invoked once per leaf rather than once per document so * each call has a precise `fieldPath` for error messages and so future * adapters can implement per-leaf caching if needed. */ import type { RequestContext } from '@byline/auth'; import { type FieldSet, type RichTextField, type RichTextPopulateFn, type RichTextReadDocumentsFn } from '../@types/field-types.js'; import type { CollectionDefinition } from '../@types/collection-types.js'; import type { IDbAdapter, ReadContext, ReadMode } from '../@types/index.js'; /** * One rich-text leaf yielded by `collectRichTextLeaves`. The walker hands * back a reference to the *parent container* (`parent[key]`) rather than * the value alone so adapters that want to *replace* the value (rather * than mutate it in place) have a clean way to do so. */ export interface RichTextLeaf { field: RichTextField; value: unknown; fieldPath: string; } /** * Walk a field set and a matching reconstructed data tree in lockstep, * yielding every rich-text leaf the schema declares regardless of nesting * depth. * * Tree traversal is delegated to the shared `walkFieldTree` walker; this * function is the rich-text-specific filter — it surfaces only leaves * whose declared `type === 'richText'` and re-shapes the leaf as a * `RichTextLeaf` for downstream callers. * * Tolerates missing data — a `group` whose data is absent simply yields * nothing under that subtree. The schema is the source of truth for * *where* a richText might be; the data is the source of truth for * *whether one is currently set*. */ export declare function collectRichTextLeaves(fields: FieldSet, data: Record | null | undefined, pathPrefix?: string): Generator; export interface PopulateRichTextFieldsOptions { /** Source collection's schema fields (used to drive the leaf walk). */ fields: FieldSet; collectionPath: string; documents: Array>; /** Registered server-side populate function from `ServerConfig`. */ populate: RichTextPopulateFn; readContext: ReadContext; requestContext: RequestContext; readMode: ReadMode; readDocuments: RichTextReadDocumentsFn; } /** * Resolve the effective `populateRelationsOnRead` for a richText field. * - explicit `true` / `false` wins * - otherwise default-derived as `!embedRelationsOnSave` * - `embedRelationsOnSave` itself defaults to `true`, so the overall * default for `populateRelationsOnRead` is `false` (snapshot mode). */ export declare function resolvePopulateOnRead(field: RichTextField): boolean; /** * For every document, walk its rich-text leaves and call the registered * populate function for each leaf whose effective `populateRelationsOnRead` * is `true`. Mutates document `fields` in place. */ export declare function populateRichTextFields(options: PopulateRichTextFieldsOptions): Promise; /** Build the secure batch reader exposed to editor-agnostic adapters. */ export declare function createRichTextDocumentReader(options: { db: IDbAdapter; collections: readonly CollectionDefinition[]; requestContext: RequestContext; readContext: ReadContext; readMode: ReadMode; locale?: string; bypassBeforeRead?: true; /** Private cache domain explicitly shared with the originating client read. */ securityDomain?: object; /** Adapter reused recursively for rich-text fields on target documents. */ richTextPopulate?: RichTextPopulateFn; }): RichTextReadDocumentsFn; /** * Which richtext server adapters the host has registered. Pass both * flags so the validator can fail-fast on each missing-adapter case * with a specific message. */ export interface RichTextAdapterPresence { /** `ServerConfig.fields.richText.populate != null` */ populate: boolean; /** `ServerConfig.fields.richText.embed != null` */ embed: boolean; } /** * Validate every richText field across every collection. Throws on: * 1. `embedRelationsOnSave === false && populateRelationsOnRead === false` * — would be unrenderable. * 2. Effective `populateRelationsOnRead === true` and no server-side * `RichTextPopulateFn` registered — populate would be a no-op and * the field would render with stale (or empty) embedded data. * 3. Effective `embedRelationsOnSave === true` and no server-side * `RichTextEmbedFn` registered — saves would silently skip the * walker so internal-link `document.path` envelopes would never * be canonicalised, breaking the renderer's fallback chain. * * Called once at `initBylineCore()` time. Fail-fast at boot is the right * posture; the alternative is a silent broken renderer at request time. */ export declare function validateRichTextFieldFlags(collections: readonly CollectionDefinition[], adapters: RichTextAdapterPresence): void;