import type { CreateItemOp, PushPolicy, RefValue, SetFieldOp } from "../ir/operations.js"; import type { ContentFieldValue } from "../schema/recipe.js"; import { type CompileContext } from "./shared.js"; import { type ImageMediaSink } from "./media.js"; /** * Inline treelist child materialisation. * * AI-generated page recipes commonly carry a component datasource whose * field value is an inline ARRAY of child-item field maps — a card * grid's cards, ranking rows, versus rows: * * ```jsonc * datasourceRef: { * kind: "scoped", * slot: "CardGrid", * fields: { * Heading: "Top players", * Cards: [ * { Title: "Card one", Image: { src: "https://…" } }, * { Title: "Card two" }, * ], * }, * } * ``` * * Sitecore has no "array" field — the canonical shape is a Treelist * (multi-reference) field whose value is a pipe-separated GUID list * pointing at REAL child items under the datasource item. This module * turns an inline array into exactly that: * * 1. One `CreateItem` per entry under the datasource item * (`/-`), conforming to the treelist field's * child template — resolved from the field definition's * `sitecore.source.types[0]` (the picker filter that names the * compatible child recipe handles). * 2. One `SetField` per child field value, flowing through the same * `normalizeFieldValue` → `encodeContentFieldValue` pipeline as * every other content value — so child image fields ride the same * MediaUpload ingest path (via the caller's `ImageMediaSink`). * 3. The parent field's value becomes a `ref-recipe-list` of the * children's refKeys — the executor resolves it to the * `{GUID}|{GUID}` Treelist wire form after the child creates land. * * Nested arrays inside a child entry recurse (grandchildren live under * their own parent child item), resolving each level's child template * against that level's template fields. * * When the child template CANNOT be resolved (the field definition * isn't visible to the compile, or it declares no `source.types`), the * materialiser returns `null` and the caller falls back to the legacy * behaviour (the array value is dropped from emission). */ export interface InlineChildMaterialization { /** * CreateItem ops for the child items, parent-before-child order. * Callers must place these BEFORE the field ops in the final IR so * the executor captures each child's itemId before the parent * treelist `ref-recipe-list` resolves. */ createOps: CreateItemOp[]; /** SetField ops for the children's own field values. */ fieldOps: SetFieldOp[]; /** * The parent field's value — the children's refKeys, resolved by the * executor to a pipe-separated GUID list (Treelist wire form). */ parentValue: RefValue; } /** * True when a raw field value is an inline child-item array: a * non-empty array whose every entry is a plain field-map object. * Entries carrying a value-level discriminator (`shape` — scai-native * ContentFieldValue; `src` — registry image; `href` — registry link) * are field VALUES, not field maps, so such arrays are not treated as * child items. */ export declare const isInlineChildArray: (raw: unknown) => raw is Record[]; /** * Normalise a raw field value (registry flat shape OR scai-native * discriminated shape) to a `ContentFieldValue` for * `encodeContentFieldValue`. Returns `null` when the value is not * recognised (drops it from emission). * * Registry shape mapping: * - string → `{ shape: "text" }` * - boolean → `{ shape: "boolean" }` * - number → `{ shape: "integer" }` when an integer, else `{ shape: "number" }` * - object with `src` → `{ shape: "image", mediaPath: src, alt?, width?, height? }` * - object with `href` → `{ shape: "link-external", href, text?, target?, title? }` * - object with `shape` → already a `ContentFieldValue`, pass through * * Lives here (not in `./page`) so the scoped-datasource materialiser * and the inline-children recursion can import it without a * page-compiler dependency cycle; `./page` re-exports it for * back-compat. */ export declare const normalizeFieldValue: (raw: unknown) => ContentFieldValue | null; export interface MaterializeInlineChildrenParams { /** The inline array — pre-checked via `isInlineChildArray`. */ entries: readonly Record[]; /** Name of the treelist/multilist field carrying the array. */ fieldName: string; /** RefKey of the item that owns the field (the datasource item). */ parentItemRefKey: string; /** Tenant path of that item — children land at `/`. */ parentItemPath: string; /** Handle of the template the parent item conforms to. */ parentTemplateHandle: string; recipeHandle: string; site: string; policy: PushPolicy; context: CompileContext; /** Op-label prefix, e.g. `page-inline:` — labels get `::` suffixes. */ labelPrefix: string; /** * Media sink for child image fields — external-URL images then ride * the same MediaUpload ingest path as every other image value. Omit * (design compilers) for the legacy hotlink form. */ imageMedia?: ImageMediaSink; } /** * Materialise an inline child-item array — see the module docblock. * Returns `null` when the child template can't be resolved; the caller * then keeps the legacy behaviour for the raw value. */ export declare const materializeInlineChildren: (params: MaterializeInlineChildrenParams) => InlineChildMaterialization | null;